LCOV - code coverage report
Current view: top level - atnf/atca - ATAtmosphere.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 0 151 0.0 %
Date: 2024-12-11 20:54:31 Functions: 0 13 0.0 %

          Line data    Source code
       1             : //# ATAtmosphere.cc: Model of atmospheric opacity
       2             : //# Copyright (C) 2004
       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             : //#---------------------------------------------------------------------------
      27             : 
      28             : // own includes
      29             : #include "ATAtmosphere.h"
      30             : 
      31             : // casa includes
      32             : #include <casacore/casa/Utilities/Assert.h>
      33             : #include <casacore/casa/Quanta.h>
      34             : 
      35             : // std includes
      36             : //#include <cmath>
      37             : 
      38             : using namespace casacore;
      39             : namespace casa{
      40             : 
      41             : /**
      42             :  * Default Constructor (apart from optional parameters).
      43             :  * The class set up this way will assume International Standard Atmosphere (ISA) conditions,
      44             :  * except for humidity. The latter is assumed to be 50%, which seems more realistic for 
      45             :  * Australian telescopes than 0%. 
      46             :  * @param[in] wvScale water vapour scale height (m), default is 1540m to match MIRIAD's model
      47             :  * @param[in] maxAlt maximum altitude of the model atmosphere (m), plane parallel layers are spread linearly up to
      48             :  *            this height, default is 10000m to match MIRIAD.
      49             :  * @param[in] nLayers number of plane parallel layers in the model (essentially for a numberical integration),
      50             :  *            default is 50 to match MIRIAD.
      51             :  **/
      52           0 : ATAtmosphere::ATAtmosphere(Double wvScale, Double maxAlt, Int nLayers) :
      53           0 :    itsHeights(nLayers), itsTemperatures(nLayers), 
      54           0 :    itsDryPressures(nLayers), itsVapourPressures(nLayers),
      55           0 :    itsGndTemperature(288.), itsPressure(101325.), itsGndHumidity(0.5), 
      56           0 :    itsLapseRate(0.0065), itsWVScale(wvScale), itsMaxAlt(maxAlt), itsObsHeight(200.)
      57             : {
      58           0 :   recomputeAtmosphereModel();
      59           0 : }
      60             : 
      61             : /**
      62             :  * Constructor with explicitly given parameters of the atmosphere 
      63             :  * @param[in] temperature air temperature at the observatory (K)
      64             :  * @param[in] pressure air pressure at the sea level if the observatory elevation is set 
      65             :  *            (default is set to 200m) or at the observatory ground level if the elevation 
      66             :  *            is set to 0 (Pascals)
      67             :  * @param[in] humidity air humidity at the observatory (fraction)
      68             :  * @param[in] lapseRate temperature lapse rate (K/m), default is 0.0065 K/m to match MIRIAD and ISA 
      69             :  * @param[in] wvScale water vapour scale height (m), default is 1540m to match MIRIAD's model
      70             :  * @param[in] maxAlt maximum altitude of the model atmosphere (m), plane parallel layers are spread linearly up to
      71             :  *            this height, default is 10000m to match MIRIAD.
      72             :  * @param[in] nLayers number of plane parallel layers in the model (essentially for a numberical integration),
      73             :  *            default is 50 to match MIRIAD.
      74             :  **/
      75           0 : ATAtmosphere::ATAtmosphere(Double temperature, Double pressure, Double humidity, Double lapseRate, 
      76           0 :                Double wvScale, Double maxAlt, Int nLayers) :
      77           0 :    itsHeights(nLayers), itsTemperatures(nLayers), 
      78           0 :    itsDryPressures(nLayers), itsVapourPressures(nLayers),
      79           0 :    itsGndTemperature(temperature), itsPressure(pressure), itsGndHumidity(humidity), 
      80           0 :    itsLapseRate(lapseRate), itsWVScale(wvScale), itsMaxAlt(maxAlt), itsObsHeight(200.)
      81             : {
      82           0 :   recomputeAtmosphereModel();
      83           0 : }
      84             :                
      85             : /**
      86             :  * Set the new weather station data, recompute the model 
      87             :  * @param[in] temperature air temperature at the observatory (K)
      88             :  * @param[in] pressure air pressure at the sea level if the observatory elevation is set to non-zero value
      89             :  *            (default is set to 200m) or at the observatory ground level if the elevation  
      90             :  *            is set to 0 (Pascals)
      91             :  * @param[in] humidity air humidity at the observatory (fraction)
      92             :  **/
      93           0 : void ATAtmosphere::setWeather(Double temperature, Double pressure, Double humidity)
      94             : {
      95           0 :   itsGndTemperature = temperature;
      96           0 :   itsPressure = pressure;
      97           0 :   itsGndHumidity = humidity;
      98           0 :   recomputeAtmosphereModel();
      99           0 : }
     100             : 
     101             : /**
     102             :  * Set the elevation of the observatory (height above mean sea level)
     103             :  * It affects only interpretation of the pressure supplied as part of the weather data, if this value 
     104             :  * is non-zero, the pressure (e.g. in setWeather or constructor) is that at mean sea level. If the
     105             :  * observatory elevation is set to zero, regardless on real elevation, the pressure is that at the 
     106             :  * observatory ground level.
     107             :  * 
     108             :  * By default, 200m is assumed and the pressure should be a mean sea level pressure..
     109             :  * @param[in] elev elevation in metres
     110             :  **/
     111           0 : void ATAtmosphere::setObservatoryElevation(Double elev)
     112             : {
     113           0 :   itsObsHeight = elev;
     114           0 :   recomputeAtmosphereModel();  
     115           0 : }
     116             : 
     117             : 
     118             : /**
     119             :  * Build the atmosphere model based on exponential fall-off, ideal gas and hydrostatic
     120             :  * equilibrium. The model parameters are taken from the data members of this class.
     121             :  **/
     122           0 : void ATAtmosphere::recomputeAtmosphereModel()
     123             : {
     124           0 :   AlwaysAssert(itsGndTemperature > 0, AipsError);
     125           0 :   AlwaysAssert(itsPressure > 0., AipsError);
     126           0 :   AlwaysAssert((itsGndHumidity >= 0.) && (itsGndHumidity<=1.), AipsError);
     127           0 :   AlwaysAssert(itsMaxAlt > 0., AipsError);
     128           0 :   AlwaysAssert(itsWVScale > 0., AipsError);
     129             :   
     130           0 :   const Double heightStep = itsMaxAlt/Double(nLayers());
     131             :   // molar mass of the air
     132           0 :   const Double M = 28.96e-3;
     133             :   // free-fall acceleration
     134           0 :   const Double g = 9.81;
     135             :   
     136           0 :   const Double wvGndSaturationPressure = wvSaturationPressure(itsGndTemperature);
     137           0 :   const Double gndPressure = itsPressure*exp(-M*g/(QC::R( ).get().getValue()*itsGndTemperature)*
     138           0 :                    (itsObsHeight+0.5*itsLapseRate*itsObsHeight*itsObsHeight/itsGndTemperature));
     139           0 :   for (Int layer = 0; layer < nLayers(); ++layer) {
     140           0 :        const Double height = Double(layer)*heightStep;
     141           0 :        itsHeights[layer] = height;
     142           0 :        itsTemperatures[layer] = itsGndTemperature/(1.+itsLapseRate*height/itsGndTemperature);
     143           0 :        const Double pressure = gndPressure * exp(-M*g/(QC::R( ).get().getValue()*itsGndTemperature)*
     144           0 :                    (height+0.5*itsLapseRate*height*height/itsGndTemperature));
     145           0 :        itsVapourPressures[layer] = casacore::min(itsGndHumidity*exp(-height/itsWVScale)*wvGndSaturationPressure,
     146           0 :                                              wvSaturationPressure(itsTemperatures[layer]));
     147           0 :        itsDryPressures[layer] = pressure - itsVapourPressures[layer];                                      
     148             :        //std::cout<<"layer="<<layer<<": H="<<itsHeights[layer]<<" T="<<itsTemperatures[layer]<<
     149             :        //    " Pvap="<<itsVapourPressures[layer]<<" Pdry="<<itsDryPressures[layer]<<endl;
     150             :   }
     151           0 : }
     152             :   
     153             : /**
     154             :  * Obtain the number of model layers, do consistency check that everything is
     155             :  * resized accordingly
     156             :  * @retrun number of model layers
     157             :  **/
     158           0 : Int ATAtmosphere::nLayers() const
     159             : {
     160           0 :   const Int result = itsHeights.size();
     161           0 :   DebugAssert(result > 2, AipsError);
     162           0 :   DebugAssert(itsTemperatures.size() == result, AipsError);
     163           0 :   DebugAssert(itsDryPressures.size() == result, AipsError);
     164           0 :   DebugAssert(itsVapourPressures.size() == result, AipsError);  
     165           0 :   return result;
     166             : }
     167             : 
     168             : /**
     169             :  * Determine the saturation pressure of water vapour for the given temperature.
     170             :  *
     171             :  * Reference:
     172             :  * Waters, Refraction effects in the neutral atmosphere. Methods of
     173             :  * Experimental Physics, vol 12B, p 186-200 (1976).
     174             :  *   
     175             :  * @param[in] temperature temperature in K
     176             :  * @return vapour saturation pressure (Pascals) 
     177             :  **/
     178           0 : Double ATAtmosphere::wvSaturationPressure(Double temperature)
     179             : {
     180           0 :   if (temperature <= 215.) {
     181           0 :       return 0.;
     182             :   }
     183           0 :   const Double theta = 300.0/temperature;
     184           0 :   return 1e5/(41.51/std::pow(theta,5)*std::pow(10.,9.834*theta-10.0));
     185             : }
     186             : 
     187             : /**
     188             :  * Compute the complex refractivity of the dry components of the atmosphere
     189             :  * (oxygen lines) at the given frequency.
     190             :  * @param[in] freq frequency (Hz)
     191             :  * @param[in] temperature air temperature (K)
     192             :  * @param[in] pDry partial pressure of dry components (Pascals)
     193             :  * @param[in] pVapour partial pressure of water vapour (Pascals)
     194             :  * @return complex refractivity
     195             :  * 
     196             :  * Reference:
     197             :  * Liebe, An updated model for millimeter wave propogation in moist air,
     198             :  * Radio Science, 20, 1069-1089 (1985).
     199             :  **/
     200           0 : DComplex ATAtmosphere::dryRefractivity(Double freq, Double temperature, 
     201             :                      Double pDry, Double pVapour)
     202             : {
     203             :   // the number of parameters per atmospheric line and the number of lines taken into account
     204           0 :   const Int nLineParams = 7;
     205           0 :   const Int nLines = 48;
     206             :   // actual tabulated values
     207           0 :   const Double lines[nLines][nLineParams] = 
     208             :     {{49.452379,    0.12E-6, 11.830,  8.40E-3, 0.0,  5.60E-3,  1.7},
     209             :      {49.962257,    0.34E-6, 10.720,  8.50E-3, 0.0,  5.60E-3,  1.7},
     210             :      {50.474238,    0.94E-6,  9.690,  8.60E-3, 0.0,  5.60E-3,  1.7},
     211             :      {50.987748,    2.46E-6,  8.690,  8.70E-3, 0.0,  5.50E-3,  1.7},
     212             :      {51.503350,    6.08E-6,  7.740,  8.90E-3, 0.0,  5.60E-3,  1.8},
     213             :      {52.021409,   14.14E-6,  6.840,  9.20E-3, 0.0,  5.50E-3,  1.8},
     214             :      {52.542393,   31.02E-6,  6.000,  9.40E-3, 0.0,  5.70E-3,  1.8},
     215             :      {53.066906,   64.10E-6,  5.220,  9.70E-3, 0.0,  5.30E-3,  1.9},
     216             :      {53.595748,  124.70E-6,  4.480, 10.00E-3, 0.0,  5.40E-3,  1.8},
     217             :      {54.129999,  228.00E-6,  3.810, 10.20E-3, 0.0,  4.80E-3,  2.0},
     218             :      {54.671157,  391.80E-6,  3.190, 10.50E-3, 0.0,  4.80E-3,  1.9},
     219             :      {55.221365,  631.60E-6,  2.620, 10.79E-3, 0.0,  4.17E-3,  2.1},
     220             :      {55.783800,  953.50E-6,  2.115, 11.10E-3, 0.0,  3.75E-3,  2.1},
     221             :      {56.264777,  548.90E-6,  0.010, 16.46E-3, 0.0,  7.74E-3,  0.9},
     222             :      {56.363387, 1344.00E-6,  1.655, 11.44E-3, 0.0,  2.97E-3,  2.3},
     223             :      {56.968180, 1763.00E-6,  1.255, 11.81E-3, 0.0,  2.12E-3,  2.5},
     224             :      {57.612481, 2141.00E-6,  0.910, 12.21E-3, 0.0,  0.94E-3,  3.7},
     225             :      {58.323874, 2386.00E-6,  0.621, 12.66E-3, 0.0, -0.55E-3, -3.1},
     226             :      {58.446589, 1457.00E-6,  0.079, 14.49E-3, 0.0,  5.97E-3,  0.8},
     227             :      {59.164204, 2404.00E-6,  0.386, 13.19E-3, 0.0, -2.44E-3,  0.1},
     228             :      {59.590982, 2112.00E-6,  0.207, 13.60E-3, 0.0,  3.44E-3,  0.5},
     229             :      {60.306057, 2124.00E-6,  0.207, 13.82E-3, 0.0, -4.13E-3,  0.7},
     230             :      {60.434775, 2461.00E-6,  0.386, 12.97E-3, 0.0,  1.32E-3, -1.0},
     231             :      {61.150558, 2504.00E-6,  0.621, 12.48E-3, 0.0, -0.36E-3,  5.8},
     232             :      {61.800152, 2298.00E-6,  0.910, 12.07E-3, 0.0, -1.59E-3,  2.9},
     233             :      {62.411212, 1933.00E-6,  1.255, 11.71E-3, 0.0, -2.66E-3,  2.3},
     234             :      {62.486253, 1517.00E-6,  0.078, 14.68E-3, 0.0, -4.77E-3,  0.9},
     235             :      {62.997974, 1503.00E-6,  1.660, 11.39E-3, 0.0, -3.34E-3,  2.2},
     236             :      {63.568515, 1087.00E-6,  2.110, 11.08E-3, 0.0, -4.17E-3,  2.0},
     237             :      {64.127764,  733.50E-6,  2.620, 10.78E-3, 0.0, -4.48E-3,  2.0},
     238             :      {64.678900,  463.50E-6,  3.190, 10.50E-3, 0.0, -5.10E-3,  1.8},
     239             :      {65.224067,  274.80E-6,  3.810, 10.20E-3, 0.0, -5.10E-3,  1.9},
     240             :      {65.764769,  153.00E-6,  4.480, 10.00E-3, 0.0, -5.70E-3,  1.8},
     241             :      {66.302088,   80.09E-6,  5.220,  9.70E-3, 0.0, -5.50E-3,  1.8},
     242             :      {66.836827,   39.46E-6,  6.000,  9.40E-3, 0.0, -5.90E-3,  1.7},
     243             :      {67.369595,   18.32E-6,  6.840,  9.20E-3, 0.0, -5.60E-3,  1.8},
     244             :      {67.900862,    8.01E-6,  7.740,  8.90E-3, 0.0, -5.80E-3,  1.7},
     245             :      {68.431001,    3.30E-6,  8.690,  8.70E-3, 0.0, -5.70E-3,  1.7},
     246             :      {68.960306,    1.28E-6,  9.690,  8.60E-3, 0.0, -5.60E-3,  1.7},
     247             :      {69.489021,    0.47E-6, 10.720,  8.50E-3, 0.0, -5.60E-3,  1.7},
     248             :      {70.017342,    0.16E-6, 11.830,  8.40E-3, 0.0, -5.60E-3,  1.7},
     249             :      {118.750341,  945.00E-6,  0.000, 15.92E-3, 0.0, -0.44E-3,  0.9},
     250             :      {368.498350,   67.90E-6,  0.020, 19.20E-3, 0.6,  0.00E00,  1.0},
     251             :      {424.763120,  638.00E-6,  0.011, 19.16E-3, 0.6,  0.00E00,  1.0},
     252             :      {487.249370,  235.00E-6,  0.011, 19.20E-3, 0.6,  0.00E00,  1.0},
     253             :      {715.393150,   99.60E-6,  0.089, 18.10E-3, 0.6,  0.00E00,  1.0},
     254             :      {773.838730,  671.00E-6,  0.079, 18.10E-3, 0.6,  0.00E00,  1.0},
     255             :      {834.145330,  180.00E-6,  0.079, 18.10E-3, 0.6,  0.00E00,  1.0}};
     256             :      
     257             :   // convert to the units of Liebe
     258           0 :   const Double theta = 300./temperature;
     259           0 :   const Double kPaPVap = 0.001*pVapour;
     260           0 :   const Double kPaPDry = 0.001*pDry;
     261           0 :   const Double fGHz = freq * 1e-9;
     262             :   
     263             :   // some coefficients
     264           0 :   const Double ap = 1.4e-10*(1-1.2e-5*std::pow(fGHz,1.5));
     265           0 :   const Double gamma0 = 5.6e-3*(kPaPDry + 1.1*kPaPVap)*std::pow(theta,0.8);
     266             :   // initial refractivity
     267           0 :   DComplex result(2.588*kPaPDry*theta +
     268           0 :          3.07e-4*(1.0/(1.0+std::pow(fGHz/gamma0,2))-1)*kPaPDry*theta*theta,
     269           0 :          (2*3.07e-4/(gamma0*(1+std::pow(fGHz/gamma0,2))*(1+std::pow(fGHz/60,2))) + 
     270           0 :           ap*kPaPDry*std::pow(theta,2.5))*fGHz*kPaPDry*theta*theta);
     271             :           
     272             :   // sum the contributions of all the lines
     273           0 :   for (Int l = 0; l < nLines; ++l) {
     274           0 :        const Double S = lines[l][1]*kPaPDry*std::pow(theta,3)*exp(lines[l][2]*(1.-theta));
     275           0 :        const Double gamma = lines[l][3]*(kPaPDry*std::pow(theta,0.8-lines[l][4]) + 1.1*kPaPVap*theta);
     276           0 :        const Double delta = lines[l][5]*kPaPDry*std::pow(theta,lines[l][6]);
     277           0 :        const Double x = (lines[l][0]-fGHz)*(lines[l][0]-fGHz) + gamma*gamma;
     278           0 :        const Double y = (lines[l][0]+fGHz)*(lines[l][0]+fGHz) + gamma*gamma;
     279           0 :        const Double z = (lines[l][0]+gamma*gamma/lines[l][0]);
     280           0 :        result += DComplex (S*( (z-fGHz)/x + (z+fGHz)/y - 2./lines[l][0] + 
     281           0 :                                   delta*(1/x-1/y)*gamma*fGHz/lines[l][0]),
     282           0 :                S*( (1/x+1/y)*gamma*fGHz/lines[l][0] -
     283           0 :                delta*((lines[l][0]-fGHz)/x + (lines[l][0]+fGHz)/y)*fGHz/lines[l][0]));       
     284             :   }
     285             :   
     286           0 :   return result;
     287             : }
     288             : 
     289             : /**
     290             :  * Compute the complex refractivity of the water vapour monomers
     291             :  * at the given frequency.
     292             :  * @param[in] freq frequency (Hz)
     293             :  * @param[in] temperature air temperature (K)
     294             :  * @param[in] pDry partial pressure of dry components (Pascals)
     295             :  * @param[in] pVapour partial pressure of water vapour (Pascals)
     296             :  * @return complex refractivity
     297             :  * 
     298             :  * Reference:
     299             :  * Liebe, An updated model for millimeter wave propogation in moist air,
     300             :  * Radio Science, 20, 1069-1089 (1985).
     301             :  **/
     302           0 : DComplex ATAtmosphere::vapourRefractivity(Double freq, Double temperature, 
     303             :                      Double pDry, Double pVapour)
     304             : {
     305             :   // the number of parameters per atmospheric line and the number of lines taken into account
     306           0 :   const Int nLineParams = 4;
     307           0 :   const Int nLines = 30;
     308             :   // actual tabulated values
     309           0 :   const Double lines[nLines][nLineParams] = 
     310             :     {{22.235080,  0.1090, 2.143, 27.84E-3},
     311             :      {67.813960,  0.0011, 8.730, 27.60E-3},
     312             :      {119.995940,  0.0007, 8.347, 27.00E-3},
     313             :      {183.310117,  2.3000, 0.653, 28.35E-3},
     314             :      {321.225644,  0.0464, 6.156, 21.40E-3},
     315             :      {325.152919,  1.5400, 1.515, 27.00E-3},
     316             :      {336.187000,  0.0010, 9.802, 26.50E-3},
     317             :      {380.197372, 11.9000, 1.018, 27.60E-3},
     318             :      {390.134508,  0.0044, 7.318, 19.00E-3},
     319             :      {437.346667,  0.0637, 5.015, 13.70E-3},
     320             :      {439.150812,  0.9210, 3.561, 16.40E-3},
     321             :      {443.018295,  0.1940, 5.015, 14.40E-3},
     322             :      {448.001075, 10.6000, 1.370, 23.80E-3},
     323             :      {470.888947,  0.3300, 3.561, 18.20E-3},
     324             :      {474.689127,  1.2800, 2.342, 19.80E-3},
     325             :      {488.491133,  0.2530, 2.814, 24.90E-3},
     326             :      {503.568532,  0.0374, 6.693, 11.50E-3},
     327             :      {504.482692,  0.0125, 6.693, 11.90E-3},
     328             :      {556.936002, 510.000, 0.114, 30.00E-3},
     329             :      {620.700807,  5.0900, 2.150, 22.30E-3},
     330             :      {658.006500,  0.2740, 7.767, 30.00E-3},
     331             :      {752.033227, 250.000, 0.336, 28.60E-3},
     332             :      {841.073593,  0.0130, 8.113, 14.10E-3},
     333             :      {859.865000,  0.1330, 7.989, 28.60E-3},
     334             :      {899.407000,  0.0550, 7.845, 28.60E-3},
     335             :      {902.555000,  0.0380, 8.360, 26.40E-3},
     336             :      {906.205524,  0.1830, 5.039, 23.40E-3},
     337             :      {916.171582,  8.5600, 1.369, 25.30E-3},
     338             :      {970.315022,  9.1600, 1.842, 24.00E-3},
     339             :      {987.926764, 138.000, 0.178, 28.60E-3}};
     340             : 
     341             :   // convert to the units of Liebe
     342           0 :   const Double theta = 300./temperature;
     343           0 :   const Double kPaPVap = 0.001*pVapour;
     344           0 :   const Double kPaPDry = 0.001*pDry;
     345           0 :   const Double fGHz = freq * 1e-9;
     346             :  
     347             :   // initial refractivity
     348           0 :   DComplex result(2.39*kPaPVap*theta + 41.6*kPaPVap*theta*theta +
     349           0 :             6.47e-6*std::pow(fGHz,2.05)*kPaPVap*std::pow(theta,2.4),
     350           0 :             (0.915*1.40e-6*kPaPDry + 5.41e-5*kPaPVap*theta*theta*theta)*
     351           0 :              fGHz*kPaPVap*std::pow(theta,2.5));
     352             :              
     353             :   // sum contributions of all the lines 
     354           0 :   for (Int l = 0; l < nLines; ++l) {
     355           0 :        const Double S = lines[l][1]*kPaPVap*std::pow(theta,3.5)*exp(lines[l][2]*(1.-theta));
     356           0 :        const Double gamma = lines[l][3]*(kPaPDry*std::pow(theta,0.8) + 4.80*kPaPVap*theta);
     357           0 :        const Double x = (lines[l][0]-fGHz)*(lines[l][0]-fGHz) + gamma*gamma;
     358           0 :        const Double y = (lines[l][0]+fGHz)*(lines[l][0]+fGHz) + gamma*gamma;
     359           0 :        const Double z = (lines[l][0]+gamma*gamma/lines[l][0]);
     360           0 :        result += DComplex(S*((z-fGHz)/x + (z+fGHz)/y - 2./lines[l][0]),
     361           0 :                            S*((1./x+1./y)*gamma*fGHz/lines[l][0]));
     362             :   }
     363             :   
     364           0 :   return result; 
     365             : }
     366             : 
     367             : /**
     368             :  * Calculate zenith opacity at the given frequency. This is a simplified version
     369             :  * of the routine implemented in MIRIAD, which calculates just zenith opacity and
     370             :  * nothing else. Note, that if the opacity is high, 1/sin(el) law is not correct 
     371             :  * even in the plane parallel case due to refraction. 
     372             :  * @param[in] freq frequency of interest in Hz
     373             :  * @return zenith opacity (nepers, i.e. dimensionless)
     374             :  **/
     375           0 : Double ATAtmosphere::zenithOpacity(Double freq) const
     376             : {
     377             :   // essentially a numerical integration with the Trapezium method
     378           0 :   Double tau = 0.;
     379           0 :   for (Int layer = Int(nLayers()) - 1; layer>=0; --layer) {
     380           0 :        Double dH = 0.;
     381           0 :        if (layer == 0) {
     382           0 :            dH = 0.5*(itsHeights[1]-itsHeights[0]);
     383           0 :        } else if (layer + 1 == int(nLayers())) {
     384           0 :            dH = 0.5*(itsHeights[nLayers()-1]-itsHeights[nLayers()-2]);
     385             :        } else {
     386           0 :            dH = 0.5*(itsHeights[layer+1]-itsHeights[layer-1]);
     387             :        }
     388             :        // imaginary part of the total complex refractivity
     389           0 :        const Double nImag = 1e-6*std::imag(dryRefractivity(freq,itsTemperatures[layer],itsDryPressures[layer],
     390           0 :              itsVapourPressures[layer])+vapourRefractivity(freq,itsTemperatures[layer],itsDryPressures[layer],
     391           0 :              itsVapourPressures[layer]));
     392           0 :        tau += dH*4.*casacore::C::pi/QC::c( ).get().getValue()*freq*nImag;
     393             :   }
     394           0 :   return tau;
     395             : }
     396             : 
     397             : /**
     398             :  * Calculate zenith opacity for the range of frequencies. Same as zenithOpacity, but
     399             :  * for a vector of frequencies.
     400             :  * @param[in] freqs vector of frequencies in Hz
     401             :  * @return vector of zenith opacities, one value per frequency (nepers, i.e. dimensionless)
     402             :  **/
     403           0 : Vector<Double> ATAtmosphere::zenithOpacities(const Vector<Double> &freqs) const
     404             : {
     405           0 :   Vector<Double> result(freqs.size());
     406           0 :   for (uInt ch = 0; ch<freqs.size(); ++ch) {
     407           0 :        result[ch] = zenithOpacity(freqs[ch]);
     408             :   }
     409           0 :   return result;
     410           0 : }
     411             : 
     412             : /**
     413             :  * Calculate opacity at the given frequency and elevation. This is a simplified 
     414             :  * version of the routine implemented in MIRIAD, which calculates just the opacity and
     415             :  * nothing else. In contract to zenithOpacity, this method takes into account refraction
     416             :  * and is more accurate than if one assumes 1/sin(el) factor.
     417             :  * @param[in] freq frequency of interest in Hz
     418             :  * @param[in] el elevation in radians
     419             :  * @return zenith opacity (nepers, i.e. dimensionless)
     420             :  **/ 
     421           0 : Double ATAtmosphere::opacity(Double freq, Double el) const
     422             : {
     423             :   // essentially a numerical integration with the Trapezium method
     424           0 :   Double tau = 0.;
     425           0 :   const Double sineEl = sin(el);
     426           0 :   for (Int layer = Int(nLayers()) - 1; layer>=0; --layer) {
     427           0 :        Double dH = 0.;
     428           0 :        if (layer == 0) {
     429           0 :            dH = 0.5*(itsHeights[1]-itsHeights[0]);
     430           0 :        } else if (layer + 1 == int(nLayers())) {
     431           0 :            dH = 0.5*(itsHeights[nLayers()-1]-itsHeights[nLayers()-2]);
     432             :        } else {
     433           0 :            dH = 0.5*(itsHeights[layer+1]-itsHeights[layer-1]);
     434             :        }
     435             :        // total complex refractivity
     436           0 :        const DComplex n = dryRefractivity(freq,itsTemperatures[layer],itsDryPressures[layer], 
     437           0 :                                                       itsVapourPressures[layer]) + 
     438           0 :                                       vapourRefractivity(freq,itsTemperatures[layer],itsDryPressures[layer],
     439           0 :                                                       itsVapourPressures[layer]);
     440             :        // real and imaginary part of the total complex refractivity scaled appropriately
     441           0 :        const Double nImag = 1e-6*std::imag(n);
     442           0 :        const Double nReal = 1. + 1e-6*std::real(n);
     443             :        // length increment
     444           0 :        const Double dL = dH*nReal/sqrt(nReal*nReal+sineEl*sineEl-1.);
     445           0 :        tau += dL*4.*casacore::C::pi/QC::c( ).get().getValue()*freq*nImag;
     446             :   }
     447           0 :   return tau;  
     448             : }
     449             : 
     450             : /**
     451             :  * Calculate opacities for the range of frequencies at the given elevation. Same as
     452             :  * opacity, but for a vector of frequencies.
     453             :  * @param[in] freqs vector of frequencies in Hz
     454             :  * @param[in] el elevation in radians
     455             :  * @return vector of opacities, one value per frequency (nepers, i.e. dimensionless)
     456             :  **/
     457           0 : Vector<Double> ATAtmosphere::opacities(const Vector<Double> &freqs, Double el) const
     458             : {
     459           0 :   Vector<Double> result(freqs.size());
     460           0 :   for (uInt ch = 0; ch<freqs.size(); ++ch) {
     461           0 :        result[ch] = opacity(freqs[ch],el);
     462             :   }
     463           0 :   return result;
     464           0 : }
     465             : 
     466             : } //#end casa namespace
     467             : 

Generated by: LCOV version 1.16