23 from astropy 
import units
 
   24 from astropy.units 
import cds
 
   28 from lsst.afw.coord.weather 
import Weather
 
   30 __all__ = [
"refraction", 
"differentialRefraction"]
 
   34 deltaRefractScale = 1.0E8
 
   37 def refraction(wavelength, elevation, observatory, weather=None):
 
   38     """Calculate overall refraction under atmospheric and observing conditions. 
   43         wavelength is in nm (valid for 230.2 < wavelength < 2058.6) 
   44     elevation : `lsst.geom.Angle` 
   45         Elevation of the observation, as an Angle. 
   46     observatory : `lsst.afw.coord.Observatory` 
   47         Class containing the longitude, latitude, 
   48         and altitude of the observatory. 
   49     weather : `lsst.afw.coord.Weather`, optional 
   50         Class containing the measured temperature, pressure, and humidity 
   51         at the observatory during an observation 
   52         If omitted, typical conditions for the observatory's elevation will be calculated. 
   56     refraction : `lsst.geom.Angle` 
   57         The angular refraction for light of the given wavelength, 
   58         under the given observing conditions. 
   62     The calculation is taken from [1]_. 
   66     .. [1] R. C. Stone, "An Accurate Method for Computing Atmospheric 
   67        Refraction," Publications of the Astronomical Society of the Pacific, 
   68        vol. 108, p. 1051, 1996. 
   70     if wavelength < 230.2:
 
   71         raise ValueError(
"Refraction calculation is valid for wavelengths between 230.2 and 2058.6 nm.")
 
   72     if wavelength > 2058.6:
 
   73         raise ValueError(
"Refraction calculation is valid for wavelengths between 230.2 and 2058.6 nm.")
 
   74     latitude = observatory.getLatitude()
 
   75     altitude = observatory.getElevation()
 
   78     reducedN = 
deltaN(wavelength, weather)/deltaRefractScale
 
   80     atmosScaleheightRatio = 4.5908E-6*temperature.to_value(units.Kelvin)
 
   84     relativeGravity = (1. + 0.005302*np.sin(latitude.asRadians())**2.
 
   85                        - 0.00000583*np.sin(2.*latitude.asRadians())**2. - 0.000000315*altitude)
 
   88     tanZ = np.tan(np.pi/2. - elevation.asRadians())
 
   89     atmosTerm1 = reducedN*relativeGravity*(1. - atmosScaleheightRatio)
 
   90     atmosTerm2 = reducedN*relativeGravity*(atmosScaleheightRatio - reducedN/2.)
 
   91     result = float(atmosTerm1*tanZ + atmosTerm2*tanZ**3.)*lsst.geom.radians
 
   96     """Calculate the differential refraction between two wavelengths. 
  101         wavelength is in nm (valid for 230.2 < wavelength < 2058.6) 
  102     wavelengthRef : `float` 
  103         Reference wavelength, typically the effective wavelength of a filter. 
  104     elevation : `lsst.geom.Angle` 
  105         Elevation of the observation, as an Angle. 
  106     observatory : `lsst.afw.coord.Observatory` 
  107         Class containing the longitude, latitude, 
  108         and altitude of the observatory. 
  109     weather : `lsst.afw.coord.Weather`, optional 
  110         Class containing the measured temperature, pressure, and humidity 
  111         at the observatory during an observation 
  112         If omitted, typical conditions for the observatory's elevation will be calculated. 
  116     differentialRefraction : `lsst.geom.Angle` 
  117         The refraction at `wavelength` minus the refraction at `wavelengthRef`. 
  119     refractionStart = 
refraction(wavelength, elevation, observatory, weather=weather)
 
  120     refractionEnd = 
refraction(wavelengthRef, elevation, observatory, weather=weather)
 
  121     return refractionStart - refractionEnd
 
  125     """Calculate the differential refractive index of air. 
  130         wavelength is in nanometers 
  131     weather : `lsst.afw.coord.Weather` 
  132         Class containing the measured temperature, pressure, and humidity 
  133         at the observatory during an observation 
  138         The difference of the refractive index of air from 1., 
  139         calculated as (n_air - 1)*10^8 
  143     The differential refractive index is the difference of 
  144     the refractive index from 1., multiplied by 1E8 to simplify 
  145     the notation and equations. Calculated as (n_air - 1)*10^8 
  147     This replicates equation 14 of [1]_ 
  151     .. [1] R. C. Stone, "An Accurate Method for Computing Atmospheric 
  152        Refraction," Publications of the Astronomical Society of the Pacific, 
  153        vol. 108, p. 1051, 1996. 
  155     waveNum = 1E3/wavelength  
 
  156     dryAirTerm = 2371.34 + (683939.7/(130. - waveNum**2.)) + (4547.3/(38.9 - waveNum**2.))
 
  157     wetAirTerm = 6487.31 + 58.058*waveNum**2. - 0.71150*waveNum**4. + 0.08851*waveNum**6.
 
  162     """Calculate dry air pressure term to refractive index calculation. 
  166     weather : `lsst.afw.coord.Weather` 
  167         Class containing the measured temperature, pressure, and humidity 
  168         at the observatory during an observation 
  172     densityFactor : `float` 
  173         Returns the relative density of dry air 
  174         at the given pressure and temperature. 
  178     This replicates equation 15 of [1]_ 
  182     .. [1] R. C. Stone, "An Accurate Method for Computing Atmospheric 
  183        Refraction," Publications of the Astronomical Society of the Pacific, 
  184        vol. 108, p. 1051, 1996. 
  188     airPressure = weather.getAirPressure()*units.pascal
 
  189     dryPressure = airPressure - waterVaporPressure
 
  190     eqn = dryPressure.to_value(cds.mbar)*(57.90E-8 - 9.3250E-4/temperature.to_value(units.Kelvin)
 
  191                                           + 0.25844/temperature.to_value(units.Kelvin)**2.)
 
  192     densityFactor = (1. + eqn)*dryPressure.to_value(cds.mbar)/temperature.to_value(units.Kelvin)
 
  197     """Calculate water vapor pressure term to refractive index calculation. 
  201     weather : `lsst.afw.coord.Weather` 
  202         Class containing the measured temperature, pressure, and humidity 
  203         at the observatory during an observation 
  207     densityFactor : `float` 
  208         Returns the relative density of water vapor 
  209         at the given pressure and temperature. 
  213     This replicates equation 16 of [1]_ 
  217     .. [1] R. C. Stone, "An Accurate Method for Computing Atmospheric 
  218        Refraction," Publications of the Astronomical Society of the Pacific, 
  219        vol. 108, p. 1051, 1996. 
  223     densityEqn1 = (-2.37321E-3 + 2.23366/temperature.to_value(units.Kelvin)
 
  224                    - 710.792/temperature.to_value(units.Kelvin)**2.
 
  225                    + 7.75141E-4/temperature.to_value(units.Kelvin)**3.)
 
  226     densityEqn2 = waterVaporPressure.to_value(cds.mbar)*(1. + 3.7E-4*waterVaporPressure.to_value(cds.mbar))
 
  227     relativeDensity = waterVaporPressure.to_value(cds.mbar)/temperature.to_value(units.Kelvin)
 
  228     densityFactor = (1 + densityEqn2*densityEqn1)*relativeDensity
 
  234     """Convert humidity and temperature to water vapor pressure. 
  238     weather : `lsst.afw.coord.Weather` 
  239         Class containing the measured temperature, pressure, and humidity 
  240         at the observatory during an observation 
  244     pressure : `astropy.units.Quantity` 
  245         The water vapor pressure in Pascals 
  246         calculated from the given humidity and temperature. 
  250     This replicates equations 18 & 20 of [1]_ 
  254     .. [1] R. C. Stone, "An Accurate Method for Computing Atmospheric 
  255        Refraction," Publications of the Astronomical Society of the Pacific, 
  256        vol. 108, p. 1051, 1996. 
  258     humidity = weather.getHumidity()
 
  259     x = np.log(humidity/100.0)
 
  261     temperatureEqn1 = (temperature + 238.3*units.Celsius)*x + 17.2694*temperature
 
  262     temperatureEqn2 = (temperature + 238.3*units.Celsius)*(17.2694 - x) - 17.2694*temperature
 
  263     dewPoint = 238.3*temperatureEqn1/temperatureEqn2
 
  264     waterVaporPressure = (4.50874 + 0.341724*dewPoint + 0.0106778*dewPoint**2 + 0.184889E-3*dewPoint**3
 
  265                           + 0.238294E-5*dewPoint**4 + 0.203447E-7*dewPoint**5)*133.32239*units.pascal
 
  267     return waterVaporPressure
 
  271     """Thin wrapper to return the measured temperature from an observation. 
  275     weather : `lsst.afw.coord.Weather` 
  276         Class containing the measured temperature, pressure, and humidity 
  277         at the observatory during an observation 
  278     useKelvin : bool, optional 
  279         Set to True to return the temperature in Kelvin instead of Celsius 
  280         This is needed because Astropy can't easily convert 
  281         between Kelvin and Celsius. 
  285     temperature : `astropy.units.Quantity` 
  286         The temperature in Celsius, unless `useKelvin` is set. 
  288     temperature = weather.getAirTemperature()*units.Celsius
 
  290         temperature = temperature.to(units.Kelvin, equivalencies=units.temperature())
 
  295     """Set default local weather conditions if they are missing. 
  299     weather : `lsst.afw.coord.Weather` 
  300         Class containing the measured temperature, pressure, and humidity 
  301         at the observatory during an observation 
  302     altitude : `astropy.units.Quantity` 
  303         The altitude of the observatory, in meters. 
  307     default : `lsst.afw.coord.Weather` 
  308         Updated Weather class with any `nan` values replaced by defaults. 
  310     if isinstance(altitude, units.quantity.Quantity):
 
  313         altitude2 = altitude*units.meter
 
  314     p0 = 101325.*units.pascal  
 
  315     g = 9.80665*units.meter/units.second**2  
 
  316     R0 = 8.31447*units.Joule/(units.mol*units.Kelvin)  
 
  317     T0 = 19.*units.Celsius  
 
  318     lapseRate = -6.5*units.Celsius/units.km  
 
  319     M = 0.0289644*units.kg/units.mol  
 
  321     temperature = T0 + lapseRate*altitude2
 
  322     temperatureK = temperature.to(units.Kelvin, equivalencies=units.temperature())
 
  323     pressure = p0*np.exp(-(g*M*altitude2)/(R0*temperatureK))
 
  325     weather = Weather((temperature/units.Celsius).value, (pressure/units.pascal).value, humidity)