mirror of
https://gitlab.astro-wise.org/micado/atmosphericmodel.git
synced 2026-10-30 05:01:02 +00:00
ported slalib.sla_refro, some refactoring
This commit is contained in:
1 parent
83d0c9ea45
commit
c1f94e4d95
9 files changed
+1241
-234
No files matched your search
@@ -5,6 +5,6 @@ from .ciddorModel import *
|
|||||||
from .dispersionModels import *
|
from .dispersionModels import *
|
||||||
from .refractionModels import *
|
from .refractionModels import *
|
||||||
from .refractionModels import *
|
from .refractionModels import *
|
||||||
from .examples import *
|
from .misc import *
|
||||||
from .refractivityModels import *
|
from .refractivityModels import *
|
||||||
|
from .neoslalib import *
|
||||||
@@ -716,8 +716,8 @@ class dispersion(Observatory):
|
|||||||
_h = heightData
|
_h = heightData
|
||||||
|
|
||||||
|
|
||||||
_rList = []
|
_rList = []
|
||||||
zenith = np.deg2rad(zenith)
|
zenith = np.deg2rad(zenith)
|
||||||
|
|
||||||
# Integration of the refraction integral over the atmosphere layers.
|
# Integration of the refraction integral over the atmosphere layers.
|
||||||
for _n in [n1, n2]:
|
for _n in [n1, n2]:
|
||||||
|
|||||||
@@ -1,207 +0,0 @@
|
|||||||
#!/usr/bin/env Python3
|
|
||||||
from .ciddorModel import Observatory
|
|
||||||
from .refractionModels import refraction
|
|
||||||
from .dispersionModels import dispersion
|
|
||||||
|
|
||||||
'''
|
|
||||||
@ Author: Joost van den Born
|
|
||||||
@ Contact: born@astron.nl
|
|
||||||
@ Description: Some functions for quick testing.
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def quick_refractive_index(l, conditions='STANDARD', T=288.15, p=101325,
|
|
||||||
H=0.0, xc=450, lat=0, h=0):
|
|
||||||
'''
|
|
||||||
Function to quickly calculate the refractive index
|
|
||||||
of atmospheric air at reference conditions, given
|
|
||||||
a wavelength l.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
l : float
|
|
||||||
Wavelength in microns
|
|
||||||
conditions: string
|
|
||||||
'STANDARD' or 'CERRO_ARMAZONES', refers
|
|
||||||
to the standard atmospheric conditions.
|
|
||||||
T : float (optional)
|
|
||||||
Temperature in Kelvin
|
|
||||||
p : float (optional)
|
|
||||||
Atmospheric pressure in Pa
|
|
||||||
H : float (optional)
|
|
||||||
Relative humidity
|
|
||||||
xc : float (optional)
|
|
||||||
CO2 density in parts per million
|
|
||||||
lat : float (optional)
|
|
||||||
Latitude of the observer in degrees
|
|
||||||
h : float (optional)
|
|
||||||
Altitude of the observer in meters
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
n : float
|
|
||||||
Refractive index for the given conditions.
|
|
||||||
'''
|
|
||||||
if conditions == 'STANDARD':
|
|
||||||
# Parameters at Cerro Armazones
|
|
||||||
T = 288.15 # k
|
|
||||||
p = 101325 # Pa
|
|
||||||
H = 0.0
|
|
||||||
xc = 450 # ppm
|
|
||||||
lat = 0 # degrees
|
|
||||||
h = 0 # m
|
|
||||||
|
|
||||||
elif conditions == 'CERRO_ARMAZONES':
|
|
||||||
# Parameters at Cerro Armazones
|
|
||||||
T = 279.65 # k
|
|
||||||
p = 71200 # Pa
|
|
||||||
H = 0.22
|
|
||||||
xc = 450 # ppm
|
|
||||||
lat = -24.5983 # degrees
|
|
||||||
h = 3064 # m
|
|
||||||
|
|
||||||
# Initializing dispersion model
|
|
||||||
at = Observatory()
|
|
||||||
|
|
||||||
# Calculating indices of refraction for l
|
|
||||||
n = at.n_tph(l=l, T=T, p=p, RH=H, xc=xc)
|
|
||||||
return n
|
|
||||||
|
|
||||||
|
|
||||||
def quick_refraction(l, zenith, conditions='STANDARD', T=288.15, p=101325,
|
|
||||||
H=0.0, xc=450, lat=0, h=0):
|
|
||||||
'''
|
|
||||||
Function to quickly calculate the refraction
|
|
||||||
of atmospheric air at reference conditions,
|
|
||||||
given a wavelength l.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
l : float
|
|
||||||
Wavelength in microns
|
|
||||||
zenith : float
|
|
||||||
Angle of observation in degrees.
|
|
||||||
zenith = 0 means the observation is directly overhead.
|
|
||||||
conditions : string
|
|
||||||
'STANDARD' or 'CERRO_ARMAZONES', refers
|
|
||||||
to the standard atmospheric conditions.
|
|
||||||
T : float (optional)
|
|
||||||
Temperature in Kelvin
|
|
||||||
p : float (optional)
|
|
||||||
Atmospheric pressure in Pa
|
|
||||||
H : float (optional)
|
|
||||||
Relative humidity
|
|
||||||
xc : float (optional)
|
|
||||||
CO2 density in parts per million
|
|
||||||
lat : float (optional)
|
|
||||||
Latitude of the observer in degrees
|
|
||||||
h : float (optional)
|
|
||||||
Altitude of the observer in meters
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
R : float
|
|
||||||
Refraction in degrees
|
|
||||||
'''
|
|
||||||
if conditions == 'STANDARD':
|
|
||||||
# Parameters at Cerro Armazones
|
|
||||||
T = 288.15 # k
|
|
||||||
p = 101325 # Pa
|
|
||||||
H = 0.0
|
|
||||||
xc = 450 # ppm
|
|
||||||
lat = 0 # degrees
|
|
||||||
h = 0 # m
|
|
||||||
|
|
||||||
elif conditions == 'CERRO_ARMAZONES':
|
|
||||||
# Parameters at Cerro Armazones
|
|
||||||
T = 279.65 # k
|
|
||||||
p = 71200 # Pa
|
|
||||||
H = 0.22
|
|
||||||
xc = 450 # ppm
|
|
||||||
lat = -24.5983 # degrees
|
|
||||||
h = 3064 # m
|
|
||||||
|
|
||||||
# Initializing dispersion model
|
|
||||||
at = Observatory()
|
|
||||||
|
|
||||||
# Calculating indices of refraction for l
|
|
||||||
n = at.n_tph(l=l, T=T, p=p, RH=H, xc=xc)
|
|
||||||
|
|
||||||
# Density of the atmosphere (following CIPM-81/91 equations)
|
|
||||||
rho = at.rho(p=p, T=T, RH=H, xc=xc)
|
|
||||||
|
|
||||||
# Initializing refraction model and setting the reduced height
|
|
||||||
ref = refraction(lat, h)
|
|
||||||
ref.setReducedHeight(p, rho)
|
|
||||||
return ref.cassini(n, zenith)
|
|
||||||
|
|
||||||
|
|
||||||
def quick_dispersion(l1, l2, zenith, conditions='STANDARD', T=288.15,
|
|
||||||
p=101325, H=0.0, xc=450, lat=0, h=0):
|
|
||||||
'''
|
|
||||||
Function to quickly calculate the dispersion
|
|
||||||
of atmospheric air at reference conditions,
|
|
||||||
given a wavelength l.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
l1 : float
|
|
||||||
Shortest wavelength of interest in micron.
|
|
||||||
l2 : float
|
|
||||||
Longest wavelength of interest in micron.
|
|
||||||
zenith : float
|
|
||||||
Angle of observation in degrees.
|
|
||||||
zenith = 0 means the observation is directly overhead.
|
|
||||||
conditions : string
|
|
||||||
'STANDARD' or 'CERRO_ARMAZONES', refers
|
|
||||||
to the standard atmospheric conditions.
|
|
||||||
T : float (optional)
|
|
||||||
Temperature in Kelvin
|
|
||||||
p : float (optional)
|
|
||||||
Atmospheric pressure in Pa
|
|
||||||
H : float (optional)
|
|
||||||
Relative humidity
|
|
||||||
xc : float (optional)
|
|
||||||
CO2 density in parts per million
|
|
||||||
lat : float (optional)
|
|
||||||
Latitude of the observer in degrees
|
|
||||||
h : float (optional)
|
|
||||||
Altitude of the observer in meters
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dR : float
|
|
||||||
Atmospheric dispersion in degrees
|
|
||||||
'''
|
|
||||||
if conditions == 'STANDARD':
|
|
||||||
# Parameters at Cerro Armazones
|
|
||||||
T = 288.15 # k
|
|
||||||
p = 101325 # Pa
|
|
||||||
H = 0.0
|
|
||||||
xc = 450 # ppm
|
|
||||||
lat = 0 # degrees
|
|
||||||
h = 0 # m
|
|
||||||
|
|
||||||
elif conditions == 'CERRO_ARMAZONES':
|
|
||||||
# Parameters at Cerro Armazones
|
|
||||||
T = 279.65 # k
|
|
||||||
p = 71200 # Pa
|
|
||||||
H = 0.22
|
|
||||||
xc = 450 # ppm
|
|
||||||
lat = -24.5983 # degrees
|
|
||||||
h = 3064 # m
|
|
||||||
|
|
||||||
# Initializing dispersion model
|
|
||||||
at = Observatory()
|
|
||||||
|
|
||||||
# Calculating indices of refraction for l1 and l2
|
|
||||||
n1 = at.n_tph(l=l1, T=T, p=p, RH=H, xc=xc)
|
|
||||||
n2 = at.n_tph(l=l2, T=T, p=p, RH=H, xc=xc)
|
|
||||||
|
|
||||||
# Density of the atmosphere (following CIPM-81/91 equations)
|
|
||||||
rho = at.rho(p=p, T=T, RH=H, xc=xc)
|
|
||||||
|
|
||||||
# Initializing refraction model and setting the reduced height
|
|
||||||
disp = dispersion(lat, h)
|
|
||||||
disp.setReducedHeight(p, rho)
|
|
||||||
return disp.cassini(n1, n2, zenith)
|
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
#!/usr/bin/env Python3
|
||||||
|
from .ciddorModel import Observatory
|
||||||
|
from .refractionModels import refraction
|
||||||
|
from .dispersionModels import dispersion
|
||||||
|
from .neoslalib import sla_refro
|
||||||
|
|
||||||
|
'''
|
||||||
|
@ Author: Joost van den Born
|
||||||
|
@ Contact: born@astron.nl
|
||||||
|
@ Description: Some functions for quick testing.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def quick_refractive_index(l, conditions='STANDARD', T=288.15, p=101325,
|
||||||
|
RH=0.0, xc=450, lat=0, h=0):
|
||||||
|
'''
|
||||||
|
Function to quickly calculate the refractive index
|
||||||
|
of atmospheric air at reference conditions, given
|
||||||
|
a wavelength l.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
l : float
|
||||||
|
Wavelength in microns
|
||||||
|
conditions: string
|
||||||
|
'STANDARD' or 'CERRO_ARMAZONES', refers
|
||||||
|
to the standard atmospheric conditions.
|
||||||
|
T : float (optional)
|
||||||
|
Temperature in Kelvin
|
||||||
|
p : float (optional)
|
||||||
|
Atmospheric pressure in Pa
|
||||||
|
RH : float (optional)
|
||||||
|
Relative humidity
|
||||||
|
xc : float (optional)
|
||||||
|
CO2 density in parts per million
|
||||||
|
lat : float (optional)
|
||||||
|
Latitude of the observer in degrees
|
||||||
|
h : float (optional)
|
||||||
|
Altitude of the observer in meters
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
n : float
|
||||||
|
Refractive index for the given conditions.
|
||||||
|
'''
|
||||||
|
if conditions == 'STANDARD':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 288.15 # k
|
||||||
|
p = 101325 # Pa
|
||||||
|
RH = 0.0
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = 0 # degrees
|
||||||
|
h = 0 # m
|
||||||
|
|
||||||
|
elif conditions == 'CERRO_ARMAZONES':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 279.65 # k
|
||||||
|
p = 71200 # Pa
|
||||||
|
RH = 0.22
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = -24.5983 # degrees
|
||||||
|
h = 3064 # m
|
||||||
|
|
||||||
|
# Initializing dispersion model
|
||||||
|
at = Observatory()
|
||||||
|
|
||||||
|
# Calculating indices of refraction for l
|
||||||
|
n = at.n_tph(l=l, T=T, p=p, RH=RH, xc=xc)
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def quick_refraction(l, zenith, conditions='STANDARD', T=288.15, p=101325,
|
||||||
|
RH=0.0, xc=450, lat=0, h=0):
|
||||||
|
'''
|
||||||
|
Function to quickly calculate the refraction
|
||||||
|
of atmospheric air at reference conditions,
|
||||||
|
given a wavelength l.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
l : float
|
||||||
|
Wavelength in microns
|
||||||
|
zenith : float
|
||||||
|
Angle of observation in degrees.
|
||||||
|
zenith = 0 means the observation is directly overhead.
|
||||||
|
conditions : string
|
||||||
|
'STANDARD' or 'CERRO_ARMAZONES', refers
|
||||||
|
to the standard atmospheric conditions.
|
||||||
|
T : float (optional)
|
||||||
|
Temperature in Kelvin
|
||||||
|
p : float (optional)
|
||||||
|
Atmospheric pressure in Pa
|
||||||
|
RH : float (optional)
|
||||||
|
Relative humidity
|
||||||
|
xc : float (optional)
|
||||||
|
CO2 density in parts per million
|
||||||
|
lat : float (optional)
|
||||||
|
Latitude of the observer in degrees
|
||||||
|
h : float (optional)
|
||||||
|
Altitude of the observer in meters
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
R : float
|
||||||
|
Refraction in degrees
|
||||||
|
'''
|
||||||
|
if conditions == 'STANDARD':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 288.15 # k
|
||||||
|
p = 101325 # Pa
|
||||||
|
RH = 0.0
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = 0 # degrees
|
||||||
|
h = 0 # m
|
||||||
|
|
||||||
|
elif conditions == 'CERRO_ARMAZONES':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 279.65 # k
|
||||||
|
p = 71200 # Pa
|
||||||
|
RH = 0.22
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = -24.5983 # degrees
|
||||||
|
h = 3064 # m
|
||||||
|
|
||||||
|
# Initializing dispersion model
|
||||||
|
at = Observatory()
|
||||||
|
|
||||||
|
# Calculating indices of refraction for l
|
||||||
|
n = at.n_tph(l=l, T=T, p=p, RH=RH, xc=xc)
|
||||||
|
|
||||||
|
# Density of the atmosphere (following CIPM-81/91 equations)
|
||||||
|
rho = at.rho(p=p, T=T, RH=RH, xc=xc)
|
||||||
|
|
||||||
|
# Initializing refraction model and setting the reduced height
|
||||||
|
ref = refraction(lat, h)
|
||||||
|
ref.setReducedHeight(p, rho)
|
||||||
|
return ref.cassini(n, zenith)
|
||||||
|
|
||||||
|
|
||||||
|
def quick_dispersion(l1, l2, zenith, conditions='STANDARD', T=288.15,
|
||||||
|
p=101325, RH=0.0, xc=450, lat=0, h=0):
|
||||||
|
'''
|
||||||
|
Function to quickly calculate the dispersion
|
||||||
|
of atmospheric air at reference conditions,
|
||||||
|
given a wavelength l.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
l1 : float
|
||||||
|
Shortest wavelength of interest in micron.
|
||||||
|
l2 : float
|
||||||
|
Longest wavelength of interest in micron.
|
||||||
|
zenith : float
|
||||||
|
Angle of observation in degrees.
|
||||||
|
zenith = 0 means the observation is directly overhead.
|
||||||
|
conditions : string
|
||||||
|
'STANDARD' or 'CERRO_ARMAZONES', refers
|
||||||
|
to the standard atmospheric conditions.
|
||||||
|
T : float (optional)
|
||||||
|
Temperature in Kelvin
|
||||||
|
p : float (optional)
|
||||||
|
Atmospheric pressure in Pa
|
||||||
|
RH : float (optional)
|
||||||
|
Relative humidity
|
||||||
|
xc : float (optional)
|
||||||
|
CO2 density in parts per million
|
||||||
|
lat : float (optional)
|
||||||
|
Latitude of the observer in degrees
|
||||||
|
h : float (optional)
|
||||||
|
Altitude of the observer in meters
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dR : float
|
||||||
|
Atmospheric dispersion in degrees
|
||||||
|
'''
|
||||||
|
if conditions == 'STANDARD':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 288.15 # k
|
||||||
|
p = 101325 # Pa
|
||||||
|
RH = 0.0
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = 0 # degrees
|
||||||
|
h = 0 # m
|
||||||
|
|
||||||
|
elif conditions == 'CERRO_ARMAZONES':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 279.65 # k
|
||||||
|
p = 71200 # Pa
|
||||||
|
RH = 0.22
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = -24.5983 # degrees
|
||||||
|
h = 3064 # m
|
||||||
|
|
||||||
|
# Initializing dispersion model
|
||||||
|
at = Observatory()
|
||||||
|
|
||||||
|
# Calculating indices of refraction for l1 and l2
|
||||||
|
n1 = at.n_tph(l=l1, T=T, p=p, RH=RH, xc=xc)
|
||||||
|
n2 = at.n_tph(l=l2, T=T, p=p, RH=RH, xc=xc)
|
||||||
|
|
||||||
|
# Density of the atmosphere (following CIPM-81/91 equations)
|
||||||
|
rho = at.rho(p=p, T=T, RH=RH, xc=xc)
|
||||||
|
|
||||||
|
# Initializing refraction model and setting the reduced height
|
||||||
|
disp = dispersion(lat, h)
|
||||||
|
disp.setReducedHeight(p, rho)
|
||||||
|
return disp.cassini(n1, n2, zenith)
|
||||||
|
|
||||||
|
|
||||||
|
def slalib_refraction(l, zenith, conditions='STANDARD', T=288.15, p=101325,
|
||||||
|
RH=0.0, xc=450, lat=0, h=0, tlr=0.0065, eps=1e-8, use_pyslalib=False):
|
||||||
|
'''
|
||||||
|
Calculates the atmospheric refraction assuming the SLALIB
|
||||||
|
package. SLALIB uses the same atmospheric model as ZEMAX
|
||||||
|
based on the works of Hohenkerk & Sinclair and Seidelmann.
|
||||||
|
They take the B&S refraction formula as a basis.
|
||||||
|
|
||||||
|
The pySLALIB FORTRAN has been ported, so pyslalib is not
|
||||||
|
needed, but can still be used. There are minor numerical
|
||||||
|
differences, likely due to precision or declaration issues.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
l : float
|
||||||
|
Wavelength in microns
|
||||||
|
zenith : float
|
||||||
|
Angle of observation in degrees.
|
||||||
|
zenith = 0 means the observation is directly overhead.
|
||||||
|
conditions : string
|
||||||
|
'STANDARD' or 'CERRO_ARMAZONES', refers
|
||||||
|
to the standard atmospheric conditions.
|
||||||
|
T : float (optional)
|
||||||
|
Temperature in Kelvin
|
||||||
|
p : float (optional)
|
||||||
|
Atmospheric pressure in Pa
|
||||||
|
RH : float (optional)
|
||||||
|
Relative humidity
|
||||||
|
xc : float (optional)
|
||||||
|
CO2 density in parts per million
|
||||||
|
lat : float (optional)
|
||||||
|
Latitude of the observer in degrees (default=0)
|
||||||
|
h : float (optional)
|
||||||
|
Altitude of the observer in meters
|
||||||
|
tlr : float
|
||||||
|
Temperature lapse rate (default=0.0065)
|
||||||
|
eps : float
|
||||||
|
Precision at which the calculation is aborted (default=1e-10)
|
||||||
|
use_pyslalib : bool
|
||||||
|
Flag that indicates that the user wants to use the pySlalib
|
||||||
|
package. If False, the internal python port of the original
|
||||||
|
FORTRAN code will be used. (Default=False).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
R : float
|
||||||
|
Refraction in degrees
|
||||||
|
'''
|
||||||
|
|
||||||
|
if conditions == 'STANDARD':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 288.15 # k
|
||||||
|
p = 101325 # Pa
|
||||||
|
RH = 0.0
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = 0 # degrees
|
||||||
|
h = 0 # m
|
||||||
|
|
||||||
|
elif conditions == 'CERRO_ARMAZONES':
|
||||||
|
# Parameters at Cerro Armazones
|
||||||
|
T = 279.65 # k
|
||||||
|
p = 71200 # Pa
|
||||||
|
RH = 0.22
|
||||||
|
xc = 450 # ppm
|
||||||
|
lat = -24.5983 # degrees
|
||||||
|
h = 3064 # m
|
||||||
|
|
||||||
|
# Converting units to the units that sla_refro() requires.
|
||||||
|
_zenith = np.deg2rad(zenith) # From degrees to radians
|
||||||
|
_P = p / 100 # From Pa to mbar
|
||||||
|
_phi = np.deg2rad(lat) # From degrees to radians
|
||||||
|
|
||||||
|
R = []
|
||||||
|
if type(l) == float or type(l) == int:
|
||||||
|
l = [l]
|
||||||
|
if use_pyslalib:
|
||||||
|
try:
|
||||||
|
from pyslalib import slalib
|
||||||
|
except ImportError as e:
|
||||||
|
print('pySLALIB package not found! Try installing it first.')
|
||||||
|
for wl in l:
|
||||||
|
# If using original slalib (e.g. PySLALIB)
|
||||||
|
Refrac = slalib.sla_refro(_zenith, h, T, _P, RH, wl, phi, tlr, eps)
|
||||||
|
R.append(np.degrees(Refrac))
|
||||||
|
|
||||||
|
# If using internal slalib port
|
||||||
|
else:
|
||||||
|
for wl in l:
|
||||||
|
Refrac = sla_refro(_zenith, h, T, _P, RH, wl, phi, tlr, eps)
|
||||||
|
R.append(np.degrees(Refrac))
|
||||||
|
|
||||||
|
if len(l) == 1:
|
||||||
|
return R[0]
|
||||||
|
else:
|
||||||
|
return np.asarray(R)
|
||||||
|
|
||||||
|
def slalib_dispersion(l1, l2, zenith, conditions='STANDARD', T=288.15, p=101325,
|
||||||
|
RH=0.0, xc=450, lat=0, h=0, tlr=0.0065, eps=1e-8, use_pyslalib=False):
|
||||||
|
'''
|
||||||
|
Calculates the atmospheric refraction assuming the SLALIB
|
||||||
|
package. SLALIB uses the same atmospheric model as ZEMAX
|
||||||
|
based on the works of Hohenkerk & Sinclair and Seidelmann.
|
||||||
|
They take the B&S refraction formula as a basis.
|
||||||
|
|
||||||
|
The pySLALIB FORTRAN has been ported, so pyslalib is not
|
||||||
|
needed, but can still be used. There are minor numerical
|
||||||
|
differences, likely due to precision or declaration issues.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
l1 : float
|
||||||
|
Shortest wavelength of interest in micron.
|
||||||
|
l2 : float
|
||||||
|
Longest wavelength of interest in micron.
|
||||||
|
zenith : float
|
||||||
|
Angle of observation in degrees.
|
||||||
|
zenith = 0 means the observation is directly overhead.
|
||||||
|
conditions : string
|
||||||
|
'STANDARD' or 'CERRO_ARMAZONES', refers
|
||||||
|
to the standard atmospheric conditions.
|
||||||
|
T : float (optional)
|
||||||
|
Temperature in Kelvin
|
||||||
|
p : float (optional)
|
||||||
|
Atmospheric pressure in Pa
|
||||||
|
RH : float (optional)
|
||||||
|
Relative humidity
|
||||||
|
xc : float (optional)
|
||||||
|
CO2 density in parts per million
|
||||||
|
lat : float (optional)
|
||||||
|
Latitude of the observer in degrees (default=0)
|
||||||
|
h : float (optional)
|
||||||
|
Altitude of the observer in meters
|
||||||
|
tlr : float
|
||||||
|
Temperature lapse rate (default=0.0065)
|
||||||
|
eps : float
|
||||||
|
Precision at which the calculation is aborted (default=1e-10)
|
||||||
|
use_pyslalib : bool
|
||||||
|
Flag that indicates that the user wants to use the pySlalib
|
||||||
|
package. If False, the internal python port of the original
|
||||||
|
FORTRAN code will be used. (Default=False).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dR : float
|
||||||
|
Refraction in degrees
|
||||||
|
'''
|
||||||
|
R1 = slalib_refraction(l1, zenith, conditions, T, p, RH, xc,\
|
||||||
|
lat, h, tlr, eps, use_pyslalib)
|
||||||
|
|
||||||
|
R2 = slalib_refraction(l2, zenith, conditions, T, p, RH, xc,\
|
||||||
|
lat, h, tlr, eps, use_pyslalib)
|
||||||
|
|
||||||
|
dR = R1 - R2
|
||||||
|
return dR
|
||||||
@@ -0,0 +1,754 @@
|
|||||||
|
#!/usr/bin/env Python3
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def sla_atms(RT, TT, DNT, GAMAL, R):
|
||||||
|
'''
|
||||||
|
Internal routine used by sla_refro(), ported from the SLALIB
|
||||||
|
FORTRAN code (Copyright P.T.Wallace. All rights reserved.)
|
||||||
|
|
||||||
|
The method is also described in "Explanatory Supplement
|
||||||
|
Astronomical Almanac" by P.K. Seidelmann, 1992, section 3.281.
|
||||||
|
|
||||||
|
Refractive index and derivative with respect to height for the
|
||||||
|
stratosphere.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
RT : float
|
||||||
|
Height of tropopause from center of the Earth (meters)
|
||||||
|
TT : float
|
||||||
|
Temperature at the tropopause (K)
|
||||||
|
DNT : float
|
||||||
|
Refractive index at the tropopause
|
||||||
|
GAMAL : float
|
||||||
|
Constant of the atmospheric model = G*MD/R
|
||||||
|
R : float
|
||||||
|
Current distance from the centre of the Earth (metre)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DN : float
|
||||||
|
Refractive index at R
|
||||||
|
RDNDR : float
|
||||||
|
R * rate the refractive index is changing at R
|
||||||
|
'''
|
||||||
|
_B = GAMAL / TT
|
||||||
|
_W = (DNT - 1) * np.exp( -B * (R - RT) )
|
||||||
|
DN = 1+_W
|
||||||
|
RDNDR = -R * _B * _W
|
||||||
|
return DN, RDNDR
|
||||||
|
|
||||||
|
|
||||||
|
def sla_atmt(R0, T0, ALPHA, GAMM2, DELM2, C1, C2, C3, C4, C5, C6, R):
|
||||||
|
'''
|
||||||
|
Internal routine used by sla_refro(), ported from the SLALIB
|
||||||
|
FORTRAN code (Copyright P.T.Wallace. All rights reserved.)
|
||||||
|
|
||||||
|
The method is also described in "Explanatory Supplement
|
||||||
|
Astronomical Almanac" by P.K. Seidelmann, 1992, section 3.281.
|
||||||
|
|
||||||
|
Refractive index and derivative with respect to height for the
|
||||||
|
troposphere.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
R0 : float
|
||||||
|
Height of observer from center of the Earth (meter)
|
||||||
|
T0 : float
|
||||||
|
Temperature at the observer (K)
|
||||||
|
ALPHA : float
|
||||||
|
Alpha
|
||||||
|
GAMM2 : float
|
||||||
|
Gamma minus 2 - see HMNAO paper
|
||||||
|
DELM2 : float
|
||||||
|
Delta minus 2
|
||||||
|
C1 : float
|
||||||
|
Useful term
|
||||||
|
C2 : float
|
||||||
|
Useful term
|
||||||
|
C3 : float
|
||||||
|
Useful term
|
||||||
|
C4 : float
|
||||||
|
Useful term
|
||||||
|
C5 : float
|
||||||
|
Useful term
|
||||||
|
C6 : float
|
||||||
|
Useful term
|
||||||
|
R : float
|
||||||
|
Current distance from the center of the Earth (meters)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
T : float
|
||||||
|
Temperature at R (K)
|
||||||
|
DN : float
|
||||||
|
Refractive index at R
|
||||||
|
RDNDR : float
|
||||||
|
R * rate the refractive index is changing at R
|
||||||
|
|
||||||
|
Note that in the optical case C5 and C6 are zero.
|
||||||
|
'''
|
||||||
|
T = max(min(T0 - ALPHA * (R - R0), 320), 100)
|
||||||
|
TT0 = T / T0
|
||||||
|
TT0GM2 = TT0**GAMM2
|
||||||
|
TT0DM2 = TT0**DELM2
|
||||||
|
DN = 1 + ( C1 * TT0GM2 - (C2 - C5 / T) * TT0DM2) * TT0
|
||||||
|
RDNDR = R * (-C3 * TT0GM2 + (C4 - C6 / TT0) * TT0DM2)
|
||||||
|
return T, DN, RDNDR
|
||||||
|
|
||||||
|
|
||||||
|
def sla_drange(a):
|
||||||
|
'''
|
||||||
|
Helper function for sla_refro().
|
||||||
|
'''
|
||||||
|
drange = a % (2*np.pi)
|
||||||
|
if drange >= np.pi:
|
||||||
|
drange -= np.abs(2*np.pi)*np.sign(a)
|
||||||
|
return drange
|
||||||
|
|
||||||
|
|
||||||
|
def sla_refi(dn, rdndr):
|
||||||
|
'''
|
||||||
|
Helper function for sla_refro(), that calculates
|
||||||
|
the refraction integrand.
|
||||||
|
'''
|
||||||
|
return rdndr/(dn+rdndr)
|
||||||
|
|
||||||
|
def sla_refro(ZOBS, HM, TDK, PMB, RH, WL, PHI, TLR, EPS):
|
||||||
|
'''
|
||||||
|
This is a direct port from the original FORTRAN SLA_REFRO()
|
||||||
|
routine.
|
||||||
|
|
||||||
|
Calculates Atmospheric refraction for radio and
|
||||||
|
optical/IR wavelengths, ported from the original SLALIB
|
||||||
|
FORTRAN code (Copyright P.T.Wallace. All rights reserved.).
|
||||||
|
|
||||||
|
The method is also described in "Explanatory Supplement
|
||||||
|
Astronomical Almanac" by P.K. Seidelmann, 1992, section 3.281.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ZOBS : float
|
||||||
|
Observed zenith distance of the source (radian)
|
||||||
|
HM : float
|
||||||
|
Height of the observer above sea level (meter)
|
||||||
|
TDK : float
|
||||||
|
Ambient temperature at the observer (K)
|
||||||
|
PMB : float
|
||||||
|
Pressure at the observer (millibar)
|
||||||
|
RH : float
|
||||||
|
Relative humidity at the observer (range 0-1)
|
||||||
|
WL : float
|
||||||
|
Effective wavelength of the source (micrometre)
|
||||||
|
PHI : float
|
||||||
|
Latitude of the observer (radian, astronomical)
|
||||||
|
TLR : float
|
||||||
|
Temperature lapse rate in the troposphere (K/metre)
|
||||||
|
EPS : float
|
||||||
|
Precision required to terminate iteration (radian)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
REF : float
|
||||||
|
Refraction: in vacuo ZD minus observed ZD (radian)
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
1 A suggested value for the TLR argument is 0.0065D0. The
|
||||||
|
refraction is significantly affected by TLR, and if studies
|
||||||
|
of the local atmosphere have been carried out a better TLR
|
||||||
|
value may be available. The sign of the supplied TLR value
|
||||||
|
is ignored.
|
||||||
|
2 A suggested value for the EPS argument is 1D-8. The result is
|
||||||
|
usually at least two orders of magnitude more computationally
|
||||||
|
precise than the supplied EPS value.
|
||||||
|
3 The routine computes the refraction for zenith distances up
|
||||||
|
to and a little beyond 90 deg using the method of Hohenkerk
|
||||||
|
and Sinclair (NAO Technical Notes 59 and 63, subsequently adopted
|
||||||
|
in the Explanatory Supplement, 1992 edition - see section 3.281).
|
||||||
|
4 The code is a development of the optical/IR refraction subroutine
|
||||||
|
AREF of C.Hohenkerk (HMNAO, September 1984), with extensions to
|
||||||
|
support the radio case. Apart from merely cosmetic changes, the
|
||||||
|
following modifications to the original HMNAO optical/IR refraction
|
||||||
|
code have been made:
|
||||||
|
. The angle arguments have been changed to radians.
|
||||||
|
. Any value of ZOBS is allowed (see note 6, below).
|
||||||
|
. Other argument values have been limited to safe values.
|
||||||
|
. Murray's values for the gas constants have been used
|
||||||
|
(Vectorial Astrometry, Adam Hilger, 1983).
|
||||||
|
. The numerical integration phase has been rearranged for
|
||||||
|
extra clarity.
|
||||||
|
. A better model for Ps(T) has been adopted (taken from
|
||||||
|
Gill, Atmosphere-Ocean Dynamics, Academic Press, 1982).
|
||||||
|
. More accurate expressions for Pwo have been adopted
|
||||||
|
(again from Gill 1982).
|
||||||
|
. The formula for the water vapour pressure, given the
|
||||||
|
saturation pressure and the relative humidity, is from
|
||||||
|
Crane (1976), expression 2.5.5.
|
||||||
|
. Provision for radio wavelengths has been added using
|
||||||
|
expressions devised by A.T.Sinclair, RGO (private
|
||||||
|
communication 1989). The refractivity model currently
|
||||||
|
used is from J.M.Rueger, "Refractive Index Formulae for
|
||||||
|
Electronic Distance Measurement with Radio and Millimetre
|
||||||
|
Waves", in Unisurv Report S-68 (2002), School of Surveying
|
||||||
|
and Spatial Information Systems, University of New South
|
||||||
|
Wales, Sydney, Australia.
|
||||||
|
. The optical refractivity for dry air is from Resolution 3 of
|
||||||
|
the International Association of Geodesy adopted at the XXIIth
|
||||||
|
General Assembly in Birmingham, UK, 1999.
|
||||||
|
. Various small changes have been made to gain speed.
|
||||||
|
5 The radio refraction is chosen by specifying WL > 100 micrometres.
|
||||||
|
Because the algorithm takes no account of the ionosphere, the
|
||||||
|
accuracy deteriorates at low frequencies, below about 30 MHz.
|
||||||
|
6 Before use, the value of ZOBS is expressed in the range +/- pi.
|
||||||
|
If this ranged ZOBS is -ve, the result REF is computed from its
|
||||||
|
absolute value before being made -ve to match. In addition, if
|
||||||
|
it has an absolute value greater than 93 deg, a fixed REF value
|
||||||
|
equal to the result for ZOBS = 93 deg is returned, appropriately
|
||||||
|
signed.
|
||||||
|
7 As in the original Hohenkerk and Sinclair algorithm, fixed values
|
||||||
|
of the water vapour polytrope exponent, the height of the
|
||||||
|
tropopause, and the height at which refraction is negligible are
|
||||||
|
used.
|
||||||
|
8 The radio refraction has been tested against work done by
|
||||||
|
Iain Coulson, JACH, (private communication 1995) for the
|
||||||
|
James Clerk Maxwell Telescope, Mauna Kea. For typical conditions,
|
||||||
|
agreement at the 0.1 arcsec level is achieved for moderate ZD,
|
||||||
|
worsening to perhaps 0.5-1.0 arcsec at ZD 80 deg. At hot and
|
||||||
|
humid sea-level sites the accuracy will not be as good.
|
||||||
|
9 It should be noted that the relative humidity RH is formally
|
||||||
|
defined in terms of "mixing ratio" rather than pressures or
|
||||||
|
densities as is often stated. It is the mass of water per unit
|
||||||
|
mass of dry air divided by that for saturated air at the same
|
||||||
|
temperature and pressure (see Gill 1982).
|
||||||
|
10 The algorithm is designed for observers in the troposphere. The
|
||||||
|
supplied temperature, pressure and lapse rate are assumed to be
|
||||||
|
for a point in the troposphere and are used to define a model
|
||||||
|
atmosphere with the tropopause at 11km altitude and a constant
|
||||||
|
temperature above that. However, in practice, the refraction
|
||||||
|
values returned for stratospheric observers, at altitudes up to
|
||||||
|
25km, are quite usable.
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Fixed parameters
|
||||||
|
D93 = np.deg2rad(93)# 93 degrees in radians
|
||||||
|
GCR = 8314.32 # Universal gas constant
|
||||||
|
DMD = 28.9644 # Molecular weight of dry air
|
||||||
|
DMW = 18.0152 # Molecular weight of water vapour
|
||||||
|
S = 6378120 # Mean Earth radius (metre)
|
||||||
|
DELTA = 18.36 # Exponent of temperature dependence of water vapour pressure
|
||||||
|
HT = 11000 # Height of tropopause (metre)
|
||||||
|
HS = 80000 # Upper limit for refractive effects (metre)
|
||||||
|
ISMAX = 16384 # Numerical integration: maximum number of strips.
|
||||||
|
|
||||||
|
# Transform ZOBS into the normal range.
|
||||||
|
ZOBS1 = sla_drange(ZOBS)
|
||||||
|
ZOBS2 = min(abs(ZOBS1),D93)
|
||||||
|
|
||||||
|
# Keep other arguments within safe bounds.
|
||||||
|
HMOK = min(max(HM,-1E3),HS)
|
||||||
|
TDKOK = min(max(TDK,100),500)
|
||||||
|
PMBOK = min(max(PMB,0),10000)
|
||||||
|
RHOK = min(max(RH,0),1)
|
||||||
|
WLOK = max(WL,0.1)
|
||||||
|
ALPHA = min(max(abs(TLR),0.001),0.01)
|
||||||
|
|
||||||
|
# Tolerance for iteration.
|
||||||
|
TOL = min(max(abs(EPS),1E-12),0.1)/2
|
||||||
|
|
||||||
|
# Decide whether optical/IR or radio case - switch at 100 microns.
|
||||||
|
OPTIC = (WLOK<=100)
|
||||||
|
|
||||||
|
# Set up model atmosphere parameters defined at the observer.
|
||||||
|
WLSQ = WLOK*WLOK
|
||||||
|
|
||||||
|
# Local gravity, can be made consistent with AstroAtmosphere
|
||||||
|
GB = 9.784*(1-0.0026*np.cos(PHI+PHI)-0.00000028*HMOK)
|
||||||
|
|
||||||
|
if OPTIC:
|
||||||
|
# Refractivity from Barrell & Sears (1939)!
|
||||||
|
A = (287.6155+(1.62887+0.01360/WLSQ)/WLSQ)*273.15E-6/1013.25
|
||||||
|
else:
|
||||||
|
A = 77.6890E-6
|
||||||
|
|
||||||
|
GAMAL = (GB*DMD)/GCR
|
||||||
|
GAMMA = GAMAL/ALPHA
|
||||||
|
GAMM2 = GAMMA-2
|
||||||
|
DELM2 = DELTA-2
|
||||||
|
TDC = TDKOK-273.15
|
||||||
|
PSAT = 10**((0.7859+0.03477*TDC)/(1+0.00412*TDC))\
|
||||||
|
*(1+PMBOK*(4.5E-6+6E-10*TDC*TDC))
|
||||||
|
|
||||||
|
if PMBOK > 0:
|
||||||
|
PWO = RHOK*PSAT/(1-(1-RHOK)*PSAT/PMBOK)
|
||||||
|
else:
|
||||||
|
PWO = 0
|
||||||
|
|
||||||
|
W = PWO*(1-DMW/DMD)*GAMMA/(DELTA-GAMMA)
|
||||||
|
C1 = A*(PMBOK+W)/TDKOK
|
||||||
|
if OPTIC:
|
||||||
|
C2 = (A*W+11.2684E-6*PWO)/TDKOK
|
||||||
|
else:
|
||||||
|
C2 = (A*W+6.3938E-6*PWO)/TDKOK
|
||||||
|
|
||||||
|
C3 = (GAMMA-1)*ALPHA*C1/TDKOK
|
||||||
|
C4 = (DELTA-1)*ALPHA*C2/TDKOK
|
||||||
|
if OPTIC:
|
||||||
|
C5 = 0
|
||||||
|
C6 = 0
|
||||||
|
else:
|
||||||
|
C5 = 375463E-6*PWO/TDKOK
|
||||||
|
C6 = C5*DELM2*ALPHA/(TDKOK*TDKOK)
|
||||||
|
|
||||||
|
# Conditions at the observer.
|
||||||
|
R0 = S+HMOK
|
||||||
|
TEMPO, DN0, RDNDR0 = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,R0)
|
||||||
|
SK0 = DN0*R0*np.sin(ZOBS2)
|
||||||
|
F0 = sla_refi(DN0,RDNDR0)
|
||||||
|
|
||||||
|
# Conditions in the troposphere at the tropopause.
|
||||||
|
RT = S+max(HT,HMOK)
|
||||||
|
TT, DNT, RDNDRT = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,RT)
|
||||||
|
SINE = SK0/(RT*DNT)
|
||||||
|
ZT = np.arctan2(SINE,np.sqrt(max(1-SINE*SINE,0)))
|
||||||
|
FT = sla_refi(DNT,RDNDRT)
|
||||||
|
|
||||||
|
# Conditions in the stratosphere at the tropopause.
|
||||||
|
DNTS,RDNDRP = sla_atms(RT,TT,DNT,GAMAL,RT)
|
||||||
|
SINE = SK0/(RT*DNTS)
|
||||||
|
ZTS = np.arctan2(SINE,np.sqrt(max(1-SINE*SINE,0)))
|
||||||
|
FTS = sla_refi(DNTS,RDNDRP)
|
||||||
|
|
||||||
|
# Conditions at the stratosphere limit.
|
||||||
|
RS = S+HS
|
||||||
|
DNS,RDNDRS = sla_atms(RT,TT,DNT,GAMAL,RS)
|
||||||
|
SINE = SK0/(RS*DNS)
|
||||||
|
ZS = np.arctan2(SINE,np.sqrt(max(1-SINE*SINE,0)))
|
||||||
|
FS = sla_refi(DNS,RDNDRS)
|
||||||
|
|
||||||
|
# Variable initialization to avoid compiler warning.
|
||||||
|
REFT = 0
|
||||||
|
|
||||||
|
# Integrate the refraction integral in two parts; first in the
|
||||||
|
# troposphere (K=1), then in the stratosphere (K=2).
|
||||||
|
|
||||||
|
for K in [1,2]:
|
||||||
|
# Initialize previous refraction to ensure at least two iterations.
|
||||||
|
REFOLD = 1
|
||||||
|
|
||||||
|
# Start off with 8 strips.
|
||||||
|
IS = 8
|
||||||
|
|
||||||
|
# Start Z, Z range, and start and end values.
|
||||||
|
if K==1:
|
||||||
|
Z0 = ZOBS2
|
||||||
|
ZRANGE = ZT-Z0
|
||||||
|
FB = F0
|
||||||
|
FF = FT
|
||||||
|
else:
|
||||||
|
Z0 = ZTS
|
||||||
|
ZRANGE = ZS-Z0
|
||||||
|
FB = FTS
|
||||||
|
FF = FS
|
||||||
|
|
||||||
|
# Sums of odd and even values.
|
||||||
|
FO = 0
|
||||||
|
FE = 0
|
||||||
|
|
||||||
|
# First time through the loop we have to do every point.
|
||||||
|
N = 1
|
||||||
|
|
||||||
|
# Start of iteration loop (terminates at specified precision).
|
||||||
|
LOOP = True
|
||||||
|
while LOOP:
|
||||||
|
|
||||||
|
# Strip width.
|
||||||
|
H = ZRANGE/IS
|
||||||
|
|
||||||
|
# Initialize distance from Earth centre for quadrature pass.
|
||||||
|
if K==1:
|
||||||
|
R = R0
|
||||||
|
else:
|
||||||
|
R = RT
|
||||||
|
|
||||||
|
# One pass (no need to compute evens after first time).
|
||||||
|
# DO I=1,IS-1,N
|
||||||
|
for I in np.arange(1, IS-1, N):
|
||||||
|
|
||||||
|
# Sine of observed zenith distance.
|
||||||
|
SZ = np.sin(Z0+H*I)
|
||||||
|
|
||||||
|
# Find R (to the nearest metre, maximum four iterations).
|
||||||
|
if (SZ>1E-20):
|
||||||
|
W = SK0/SZ
|
||||||
|
RG = R
|
||||||
|
DR = 1E6
|
||||||
|
J = 0
|
||||||
|
while (abs(DR)>1 and J<4):
|
||||||
|
J+=1
|
||||||
|
if K==1:
|
||||||
|
TG,DN,RDNDR = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,RG)
|
||||||
|
else:
|
||||||
|
DN,RDNDR = sla_atms(RT,TT,DNT,GAMAL,RG)
|
||||||
|
DR = (RG*DN-W)/(DN+RDNDR)
|
||||||
|
RG = RG-DR
|
||||||
|
R = RG
|
||||||
|
|
||||||
|
# Find the refractive index and integrand at R.
|
||||||
|
if K==1:
|
||||||
|
T,DN,RDNDR = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,R)
|
||||||
|
else:
|
||||||
|
DN,RDNDR = sla_atms(RT,TT,DNT,GAMAL,R)
|
||||||
|
|
||||||
|
F = sla_refi(DN,RDNDR)
|
||||||
|
|
||||||
|
# Accumulate odd and (first time only) even values.
|
||||||
|
if N==1 and (I%2)==0:
|
||||||
|
FE = FE+F
|
||||||
|
else:
|
||||||
|
FO = FO+F
|
||||||
|
|
||||||
|
|
||||||
|
# Evaluate the integrand using Simpson's Rule.
|
||||||
|
REFP = H*(FB+4*FO+2*FE+FF)/3
|
||||||
|
|
||||||
|
# Has the required precision been achieved (or can't be)?
|
||||||
|
if (abs(REFP-REFOLD)>TOL and IS<ISMAX):
|
||||||
|
# No: prepare for next iteration.
|
||||||
|
|
||||||
|
# Save current value for convergence test.
|
||||||
|
REFOLD = REFP
|
||||||
|
|
||||||
|
# Double the number of strips.
|
||||||
|
IS = IS+IS
|
||||||
|
|
||||||
|
# Sum of all current values = sum of next pass's even values.
|
||||||
|
FE = FE+FO
|
||||||
|
|
||||||
|
# Prepare for new odd values.
|
||||||
|
FO = 0
|
||||||
|
|
||||||
|
# Skip even values next time.
|
||||||
|
N = 2
|
||||||
|
else:
|
||||||
|
# Yes: save troposphere component and terminate the loop.
|
||||||
|
if (K==1):
|
||||||
|
REFT = REFP
|
||||||
|
LOOP = False
|
||||||
|
|
||||||
|
# Result.
|
||||||
|
REF = REFT+REFP
|
||||||
|
if (ZOBS1<0):
|
||||||
|
REF = -REF
|
||||||
|
return REF
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WORK IN PROGRESS; Implementation of the slalib model for
|
||||||
|
# any refractive index model.
|
||||||
|
# Main issue is in the calculation of dn/dr for any model
|
||||||
|
# of the refractive index. One possibility is following
|
||||||
|
# the Corbard error function model, which assumes the
|
||||||
|
# atmospheric density follows the equation rho(r)=exp(-u/a).
|
||||||
|
# But then the SLA_REFRO routine would not have to be modified
|
||||||
|
# and one would simply use the refraction.corbard() function.
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# def get_rc(lat):
|
||||||
|
# '''
|
||||||
|
# Returns the radius of the curvature at the observer in km,
|
||||||
|
# taking into account the latitude of the observer and the corresponding
|
||||||
|
# ellipsoidality of the earth.
|
||||||
|
|
||||||
|
# Returns
|
||||||
|
# -------
|
||||||
|
# rc : float
|
||||||
|
# Local radius of curvature of the Earth in kilometers.
|
||||||
|
# '''
|
||||||
|
# _A = 6378.137 # km
|
||||||
|
# _B = 6356.752 # km
|
||||||
|
# _phi = np.deg2rad(lat)
|
||||||
|
|
||||||
|
# _rc0 = (_A*_B)**2 / (_A**2 * np.cos(_phi)**2 + _B**2 * np.sin(_phi)**2)**(3/2)
|
||||||
|
# return _rc0*1000
|
||||||
|
|
||||||
|
|
||||||
|
# def get_local_g(lat, h):
|
||||||
|
# '''
|
||||||
|
# Returns the local gravitic acceleration for a given latitude
|
||||||
|
# and altitude.
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
# ----------
|
||||||
|
# lat : float
|
||||||
|
# Latitude in degrees
|
||||||
|
# h : float
|
||||||
|
# Altitude above sea level in meters
|
||||||
|
|
||||||
|
# Returns
|
||||||
|
# -------
|
||||||
|
# g : float
|
||||||
|
# Local acceleration due to gravity in m/s^2
|
||||||
|
# '''
|
||||||
|
# _phi = np.deg2rad(lat)
|
||||||
|
# _c1 = 5.2790414e-3
|
||||||
|
# _c2 = 2.32718e-5
|
||||||
|
# _c3 = 1.262e-7
|
||||||
|
# _c4 = 7e-10
|
||||||
|
# _g0 = 9.780327 # m/s^2
|
||||||
|
# _g0_local = _g0 * (1 + _c1 * np.sin(_phi)**2 + _c2 * np.sin(_phi)**4 + _c3 * np.sin(_phi)**6 + _c4 * np.sin(_phi)**8)
|
||||||
|
# _g = _g0_local - (3.0877e-6 - 4.3e-9 * np.sin(_phi)**2) * h + 7.2e-13 * h**2
|
||||||
|
# return _g
|
||||||
|
|
||||||
|
# def aa_refro(zenith, l, h, T, p, RH, xc, lat, TLR, EPS):
|
||||||
|
# '''
|
||||||
|
# Modified version of sla_refro(), updating some constants
|
||||||
|
# and changing the units on input and output parameters.
|
||||||
|
|
||||||
|
# The function calculates the atmospheric refraction
|
||||||
|
# optical/IR wavelengths (not radio!), ported from the original
|
||||||
|
# SLALIB FORTRAN code (Copyright P.T.Wallace. All rights
|
||||||
|
# reserved.).
|
||||||
|
|
||||||
|
# The method is also described in "Explanatory Supplement
|
||||||
|
# Astronomical Almanac" by P.K. Seidelmann, 1992, section 3.281.
|
||||||
|
|
||||||
|
# See also the description of sla_refro().
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
# ----------
|
||||||
|
# zenith : float
|
||||||
|
# Observed zenith distance of the source (degrees)
|
||||||
|
# l : float
|
||||||
|
# Effective wavelength of the source (micrometre)
|
||||||
|
# h : float
|
||||||
|
# Height of the observer above sea level (meter)
|
||||||
|
# T : float
|
||||||
|
# Ambient temperature at the observer (K)
|
||||||
|
# p : float
|
||||||
|
# Pressure at the observer (Pascal)
|
||||||
|
# RH : float
|
||||||
|
# Relative humidity at the observer (range 0-1)
|
||||||
|
# xc : float
|
||||||
|
# CO2 density in parts per million
|
||||||
|
# lat : float
|
||||||
|
# Latitude of the observer (degrees)
|
||||||
|
# TLR : float
|
||||||
|
# Temperature lapse rate in the troposphere (K/metre)
|
||||||
|
# EPS : float
|
||||||
|
# Precision required to terminate iteration (radian)
|
||||||
|
|
||||||
|
# Returns
|
||||||
|
# -------
|
||||||
|
# REF : float
|
||||||
|
# Refraction: in vacuo ZD minus observed ZD (radian)
|
||||||
|
# '''
|
||||||
|
|
||||||
|
# # Converting some units for use later
|
||||||
|
# ZOBS = np.deg2rad(zenith)
|
||||||
|
# HM = h
|
||||||
|
# TDK = T
|
||||||
|
# PMB = p / 100
|
||||||
|
# WL = l
|
||||||
|
# PHI = np.deg2rad(lat)
|
||||||
|
|
||||||
|
# # Fixed parameters
|
||||||
|
# D93 = np.deg2rad(93)# 93 degrees in radians
|
||||||
|
# GCR = 8314.510 # Universal gas constant
|
||||||
|
# DMD = 28.9635 + 12.011e-6 * (xc - 400) # Molar mass in g/mol of dry air containing xc ppm of CO2
|
||||||
|
# DMW = 18.0152 # Molecular weight of water vapour
|
||||||
|
# S = get_rc(lat) # Mean Earth radius (meters)
|
||||||
|
# DELTA = 18.36 # Exponent of temperature dependence of water vapour pressure
|
||||||
|
# HT = 11000 # Height of tropopause (metre)
|
||||||
|
# HS = 80000 # Upper limit for refractive effects (meters)
|
||||||
|
# ISMAX = 16384 # Numerical integration: maximum number of strips.
|
||||||
|
|
||||||
|
# # Transform ZOBS into the normal range.
|
||||||
|
# ZOBS1 = sla_drange(ZOBS)
|
||||||
|
# ZOBS2 = min(abs(ZOBS1),D93)
|
||||||
|
|
||||||
|
# # Keep other arguments within safe bounds.
|
||||||
|
# HMOK = min(max(HM,-1E3),HS)
|
||||||
|
# TDKOK = min(max(TDK,100),500)
|
||||||
|
# PMBOK = min(max(PMB,0),10000)
|
||||||
|
# RHOK = min(max(RH,0),1)
|
||||||
|
# WLOK = max(WL,0.1)
|
||||||
|
# ALPHA = min(max(abs(TLR),0.001),0.01)
|
||||||
|
|
||||||
|
# # Tolerance for iteration.
|
||||||
|
# TOL = min(max(abs(EPS),1E-12),0.1)/2
|
||||||
|
|
||||||
|
# # Set up model atmosphere parameters defined at the observer.
|
||||||
|
# WLSQ = WLOK*WLOK
|
||||||
|
|
||||||
|
# # Local gravity
|
||||||
|
# GB = get_local_g(lat, h)
|
||||||
|
|
||||||
|
# # Refractivity of air
|
||||||
|
# A = (287.6155+(1.62887+0.01360/WLSQ)/WLSQ)*273.15E-6/1013.25
|
||||||
|
|
||||||
|
# GAMAL = (GB * DMD) / GCR
|
||||||
|
# GAMMA = GAMAL / ALPHA
|
||||||
|
# GAMM2 = GAMMA - 2
|
||||||
|
# DELM2 = DELTA - 2
|
||||||
|
# TDC = TDKOK - 273.15
|
||||||
|
# PSAT = 10**( (0.7859+0.03477*TDC) / (1+0.00412*TDC) )\
|
||||||
|
# * (1 + PMBOK * (4.5E-6 + 6E-10 * TDC*TDC) )
|
||||||
|
|
||||||
|
# if PMBOK > 0:
|
||||||
|
# PWO = RHOK*PSAT/(1-(1-RHOK)*PSAT/PMBOK)
|
||||||
|
# else:
|
||||||
|
# PWO = 0
|
||||||
|
|
||||||
|
# W = PWO*(1-DMW/DMD)*GAMMA/(DELTA-GAMMA)
|
||||||
|
# C1 = A*(PMBOK+W)/TDKOK
|
||||||
|
# C2 = (A*W+11.2684E-6*PWO)/TDKOK
|
||||||
|
|
||||||
|
# C3 = (GAMMA-1)*ALPHA*C1/TDKOK
|
||||||
|
# C4 = (DELTA-1)*ALPHA*C2/TDKOK
|
||||||
|
# C5 = 0
|
||||||
|
# C6 = 0
|
||||||
|
|
||||||
|
# # Conditions at the observer.
|
||||||
|
# R0 = S+HMOK
|
||||||
|
# TEMPO, DN0, RDNDR0 = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,R0)
|
||||||
|
# SK0 = DN0*R0*np.sin(ZOBS2)
|
||||||
|
# F0 = sla_refi(DN0,RDNDR0)
|
||||||
|
|
||||||
|
# # Conditions in the troposphere at the tropopause.
|
||||||
|
# RT = S+max(HT,HMOK)
|
||||||
|
# TT, DNT, RDNDRT = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,RT)
|
||||||
|
# SINE = SK0/(RT*DNT)
|
||||||
|
# ZT = np.arctan2(SINE,np.sqrt(max(1-SINE*SINE,0)))
|
||||||
|
# FT = sla_refi(DNT,RDNDRT)
|
||||||
|
|
||||||
|
# # Conditions in the stratosphere at the tropopause.
|
||||||
|
# DNTS,RDNDRP = sla_atms(RT,TT,DNT,GAMAL,RT)
|
||||||
|
# SINE = SK0/(RT*DNTS)
|
||||||
|
# ZTS = np.arctan2(SINE,np.sqrt(max(1-SINE*SINE,0)))
|
||||||
|
# FTS = sla_refi(DNTS,RDNDRP)
|
||||||
|
|
||||||
|
# # Conditions at the stratosphere limit.
|
||||||
|
# RS = S+HS
|
||||||
|
# DNS,RDNDRS = sla_atms(RT,TT,DNT,GAMAL,RS)
|
||||||
|
# SINE = SK0/(RS*DNS)
|
||||||
|
# ZS = np.arctan2(SINE,np.sqrt(max(1-SINE*SINE,0)))
|
||||||
|
# FS = sla_refi(DNS,RDNDRS)
|
||||||
|
|
||||||
|
# # Variable initialization to avoid compiler warning.
|
||||||
|
# REFT = 0
|
||||||
|
|
||||||
|
# # Integrate the refraction integral in two parts; first in the
|
||||||
|
# # troposphere (K=1), then in the stratosphere (K=2).
|
||||||
|
# for K in [1,2]:
|
||||||
|
# # Initialize previous refraction to ensure at least two iterations.
|
||||||
|
# REFOLD = 1
|
||||||
|
|
||||||
|
# # Start off with 8 strips.
|
||||||
|
# IS = 8
|
||||||
|
|
||||||
|
# # Start Z, Z range, and start and end values.
|
||||||
|
# if K==1:
|
||||||
|
# Z0 = ZOBS2
|
||||||
|
# ZRANGE = ZT-Z0
|
||||||
|
# FB = F0
|
||||||
|
# FF = FT
|
||||||
|
# else:
|
||||||
|
# Z0 = ZTS
|
||||||
|
# ZRANGE = ZS-Z0
|
||||||
|
# FB = FTS
|
||||||
|
# FF = FS
|
||||||
|
|
||||||
|
# # Sums of odd and even values.
|
||||||
|
# FO = 0
|
||||||
|
# FE = 0
|
||||||
|
|
||||||
|
# # First time through the loop we have to do every point.
|
||||||
|
# N = 1
|
||||||
|
|
||||||
|
# # Start of iteration loop (terminates at specified precision).
|
||||||
|
# LOOP = True
|
||||||
|
# while LOOP:
|
||||||
|
|
||||||
|
# # Strip width.
|
||||||
|
# H = ZRANGE/IS
|
||||||
|
|
||||||
|
# # Initialize distance from Earth centre for quadrature pass.
|
||||||
|
# if K==1:
|
||||||
|
# R = R0
|
||||||
|
# else:
|
||||||
|
# R = RT
|
||||||
|
|
||||||
|
# # One pass (no need to compute evens after first time).
|
||||||
|
# # DO I=1,IS-1,N
|
||||||
|
# for I in np.arange(1, IS-1, N):
|
||||||
|
|
||||||
|
# # Sine of observed zenith distance.
|
||||||
|
# SZ = np.sin(Z0+H*I)
|
||||||
|
|
||||||
|
# # Find R (to the nearest metre, maximum four iterations).
|
||||||
|
# if (SZ>1E-20):
|
||||||
|
# W = SK0/SZ
|
||||||
|
# RG = R
|
||||||
|
# DR = 1E6
|
||||||
|
# J = 0
|
||||||
|
# while (abs(DR)>1 and J<4):
|
||||||
|
# J+=1
|
||||||
|
# if K==1:
|
||||||
|
# TG,DN,RDNDR = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,RG)
|
||||||
|
# else:
|
||||||
|
# DN,RDNDR = sla_atms(RT,TT,DNT,GAMAL,RG)
|
||||||
|
# DR = (RG*DN-W)/(DN+RDNDR)
|
||||||
|
# RG = RG-DR
|
||||||
|
# R = RG
|
||||||
|
|
||||||
|
# # Find the refractive index and integrand at R.
|
||||||
|
# if K==1:
|
||||||
|
# T,DN,RDNDR = sla_atmt(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,R)
|
||||||
|
# else:
|
||||||
|
# DN,RDNDR = sla_atms(RT,TT,DNT,GAMAL,R)
|
||||||
|
|
||||||
|
# F = sla_refi(DN,RDNDR)
|
||||||
|
|
||||||
|
# # Accumulate odd and (first time only) even values.
|
||||||
|
# if N==1 and (I%2)==0:
|
||||||
|
# FE = FE+F
|
||||||
|
# else:
|
||||||
|
# FO = FO+F
|
||||||
|
|
||||||
|
|
||||||
|
# # Evaluate the integrand using Simpson's Rule.
|
||||||
|
# REFP = H*(FB+4*FO+2*FE+FF)/3
|
||||||
|
|
||||||
|
# # Has the required precision been achieved (or can't be)?
|
||||||
|
# if (abs(REFP-REFOLD)>TOL and IS<ISMAX):
|
||||||
|
# # No: prepare for next iteration.
|
||||||
|
|
||||||
|
# # Save current value for convergence test.
|
||||||
|
# REFOLD = REFP
|
||||||
|
|
||||||
|
# # Double the number of strips.
|
||||||
|
# IS = IS+IS
|
||||||
|
|
||||||
|
# # Sum of all current values = sum of next pass's even values.
|
||||||
|
# FE = FE+FO
|
||||||
|
|
||||||
|
# # Prepare for new odd values.
|
||||||
|
# FO = 0
|
||||||
|
|
||||||
|
# # Skip even values next time.
|
||||||
|
# N = 2
|
||||||
|
# else:
|
||||||
|
# # Yes: save troposphere component and terminate the loop.
|
||||||
|
# if (K==1):
|
||||||
|
# REFT = REFP
|
||||||
|
# LOOP = False
|
||||||
|
|
||||||
|
# # Result.
|
||||||
|
# REF = REFT+REFP
|
||||||
|
# if (ZOBS1<0):
|
||||||
|
# REF = -REF
|
||||||
|
# return REF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import numpy as np
|
|||||||
atmospheric dispersion, given a set of conditions.
|
atmospheric dispersion, given a set of conditions.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def BarrellAndSears(l, T=288.15, p=101325, H=0):
|
def BarrellAndSears(l, T=288.15, p=101325, RH=0):
|
||||||
'''
|
'''
|
||||||
From Barrell & Sears, 1939
|
From Barrell & Sears, 1939
|
||||||
Equation 7.7, page 52
|
Equation 7.7, page 52
|
||||||
@@ -24,7 +24,7 @@ def BarrellAndSears(l, T=288.15, p=101325, H=0):
|
|||||||
Temperature in Kelvin
|
Temperature in Kelvin
|
||||||
p : float
|
p : float
|
||||||
Atmospheric pressure in Pascal
|
Atmospheric pressure in Pascal
|
||||||
H : float
|
RH : float
|
||||||
Relative humidity (0=<H=<1)
|
Relative humidity (0=<H=<1)
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@@ -34,10 +34,11 @@ def BarrellAndSears(l, T=288.15, p=101325, H=0):
|
|||||||
'''
|
'''
|
||||||
T = K2C(T)
|
T = K2C(T)
|
||||||
P = hPA2mmHg(p/100)
|
P = hPA2mmHg(p/100)
|
||||||
R = (0.378125 + 0.0021414 / l**2 + 0.00001793 / l**4) * (P*(1 + (1.049 - 0.0157*T) * P * 1e-6)/(1 + 0.003661*T)) - (0.0624 - 0.000680 / l**2) * H / (1 + 0.003661*T)
|
R = (0.378125 + 0.0021414 / l**2 + 0.00001793 / l**4) * (P*(1 + (1.049 - 0.0157*T) * P * 1e-6)/(1 + 0.003661*T)) - (0.0624 - 0.000680 / l**2) * RH / (1 + 0.003661*T)
|
||||||
n = R/1e6 + 1
|
n = R/1e6 + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def Ciddor(l, CO2=450):
|
def Ciddor(l, CO2=450):
|
||||||
'''
|
'''
|
||||||
From Ciddor, 1996
|
From Ciddor, 1996
|
||||||
@@ -62,6 +63,7 @@ def Ciddor(l, CO2=450):
|
|||||||
n = R/1e8 + 1
|
n = R/1e8 + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def Edlen1953(l):
|
def Edlen1953(l):
|
||||||
'''
|
'''
|
||||||
From Edlen, 1953
|
From Edlen, 1953
|
||||||
@@ -84,6 +86,7 @@ def Edlen1953(l):
|
|||||||
n = R/1e8 + 1
|
n = R/1e8 + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def Edlen1966(l):
|
def Edlen1966(l):
|
||||||
'''
|
'''
|
||||||
From Edlen, 1966
|
From Edlen, 1966
|
||||||
@@ -106,7 +109,8 @@ def Edlen1966(l):
|
|||||||
n = R/1e8 + 1
|
n = R/1e8 + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def Owens(l, T=288.15, p=101325, H=0):
|
|
||||||
|
def Owens(l, T=288.15, p=101325, RH=0):
|
||||||
'''
|
'''
|
||||||
From Owens, 1967
|
From Owens, 1967
|
||||||
Equation 32
|
Equation 32
|
||||||
@@ -121,8 +125,8 @@ def Owens(l, T=288.15, p=101325, H=0):
|
|||||||
Temperature in Kelvin
|
Temperature in Kelvin
|
||||||
p : float
|
p : float
|
||||||
Atmospheric pressure in Pa
|
Atmospheric pressure in Pa
|
||||||
H : float
|
RH : float
|
||||||
Humdity (0<=H<=1)
|
Relative humdity (0<=H<=1)
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@@ -132,10 +136,11 @@ def Owens(l, T=288.15, p=101325, H=0):
|
|||||||
P = hPA2mmHg(p/100)
|
P = hPA2mmHg(p/100)
|
||||||
T = K2C(T)
|
T = K2C(T)
|
||||||
s = 1 / l
|
s = 1 / l
|
||||||
R = 8342.13 + 2406030 / (130 - s**2) + 15997 / (38.9 - s**2) * (P/720.775) * ((1 + P * (0.817 - 0.0133) * 1e-6)/(1 + 0.0036610*T)) - H * (5.722 - 0.0457*s**2)
|
R = 8342.13 + 2406030 / (130 - s**2) + 15997 / (38.9 - s**2) * (P/720.775) * ((1 + P * (0.817 - 0.0133) * 1e-6)/(1 + 0.0036610*T)) - RH * (5.722 - 0.0457*s**2)
|
||||||
n = R/1e8 + 1
|
n = R/1e8 + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def BonschPotulski(l, T=288.15, p=101325, CO2=450):
|
def BonschPotulski(l, T=288.15, p=101325, CO2=450):
|
||||||
'''
|
'''
|
||||||
From Bonsch & Potulski, 1998
|
From Bonsch & Potulski, 1998
|
||||||
@@ -168,6 +173,7 @@ def BonschPotulski(l, T=288.15, p=101325, CO2=450):
|
|||||||
n = Rtp + 1
|
n = Rtp + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def BirchDowns(l):
|
def BirchDowns(l):
|
||||||
'''
|
'''
|
||||||
From Birch & Downs, 1993 and 1994 (correction)
|
From Birch & Downs, 1993 and 1994 (correction)
|
||||||
@@ -190,6 +196,7 @@ def BirchDowns(l):
|
|||||||
n = R/1e8 + 1
|
n = R/1e8 + 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def PeckReeder(l):
|
def PeckReeder(l):
|
||||||
'''
|
'''
|
||||||
From Peck & Reeder, 1972
|
From Peck & Reeder, 1972
|
||||||
@@ -213,8 +220,70 @@ def PeckReeder(l):
|
|||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def slalib(l, T=288.15, p=101325, H=0, h=0, lat=-24, tlr=0.0065, eps=1e-10):
|
def HohenkerkAndSinclair(l, T=288.15, p=101325, RH=0, h=0, lat=0):
|
||||||
'''
|
'''
|
||||||
|
Calcalation of the refractive index following Hohenkerk & Sinclair
|
||||||
|
(1985), which is the same one as used in SLALIB.
|
||||||
|
|
||||||
|
This equation is based on the Barrell & Sears equation, but with
|
||||||
|
slightly different values. The refractive index values here seem
|
||||||
|
to be a bit higher than the original Barrel and Sears values.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
l : float
|
||||||
|
Wavelength in microns
|
||||||
|
T : float
|
||||||
|
Temperature in Kelvin
|
||||||
|
p : float
|
||||||
|
Atmospheric pressure in Pa
|
||||||
|
RH : float
|
||||||
|
fractional humidity (0=<H=<1)
|
||||||
|
h : float
|
||||||
|
Altitude of the observer (default=0 m)
|
||||||
|
lat : float
|
||||||
|
Latitude of the observer (default=-24)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
n : float
|
||||||
|
Refractive index of atmospheric air at the given conditions
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Some constants
|
||||||
|
alpha = 0.0065
|
||||||
|
delta = 18.36
|
||||||
|
R = 8314.36
|
||||||
|
Md = 28.966
|
||||||
|
Mw = 18.016
|
||||||
|
|
||||||
|
# Pressure in millibars
|
||||||
|
P0 = p/100
|
||||||
|
|
||||||
|
# Local acceleration due to gravity
|
||||||
|
g_local = 9.784 * (1 - 0.0026 * np.cos(2*np.deg2rad(lat))) - 0.00000028*h
|
||||||
|
gamma = g_local * Md / (R * alpha)
|
||||||
|
|
||||||
|
# Partial pressure of water vapour at observer
|
||||||
|
Pw = RH * (T/247.1)**18.36
|
||||||
|
|
||||||
|
# Refractivity * 1e6 at reference conditions
|
||||||
|
A = (287.604 + 1.6288*l**-2 + 0.0136*l**-4) * 273.15/1013.25
|
||||||
|
# A = (287.5522 + 1.6288*l**-2 + 0.0136*l**-4) * 273.15/1013.25
|
||||||
|
|
||||||
|
P = (P0 + (1 - Mw/Md) * gamma/(delta - gamma) * Pw )\
|
||||||
|
- (1 - Mw/Md) * gamma / (delta-gamma) * Pw
|
||||||
|
|
||||||
|
# Refractive index at given conditions
|
||||||
|
n = 1 + 1e-6 * (A * P - 11.2684*Pw)/T
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def slalib(l, T=288.15, p=101325, RH=0, h=0, lat=-24, tlr=0.0065, eps=1e-10):
|
||||||
|
'''
|
||||||
|
NOTE: This function still works for now, but is considered DEPRECATED.
|
||||||
|
Use BarrellAndSears() or HohenkerkAndSinclair() instead.
|
||||||
|
|
||||||
SLALIB uses the same atmospheric model as ZEMAX
|
SLALIB uses the same atmospheric model as ZEMAX
|
||||||
based on the works of Hohenkerk & Sinclair and Seidelmann.
|
based on the works of Hohenkerk & Sinclair and Seidelmann.
|
||||||
They take the B&S refraction formula as a basis.
|
They take the B&S refraction formula as a basis.
|
||||||
@@ -228,8 +297,8 @@ def slalib(l, T=288.15, p=101325, H=0, h=0, lat=-24, tlr=0.0065, eps=1e-10):
|
|||||||
Temperature in Kelvin
|
Temperature in Kelvin
|
||||||
p : float
|
p : float
|
||||||
Atmospheric pressure in Pa
|
Atmospheric pressure in Pa
|
||||||
H : float
|
RH : float
|
||||||
fractional humidity (0=<H=<1)
|
Relative humidity (0=<H=<1)
|
||||||
h : float
|
h : float
|
||||||
Altitude of the observer (default=0 m)
|
Altitude of the observer (default=0 m)
|
||||||
lat : float
|
lat : float
|
||||||
@@ -243,6 +312,14 @@ def slalib(l, T=288.15, p=101325, H=0, h=0, lat=-24, tlr=0.0065, eps=1e-10):
|
|||||||
-------
|
-------
|
||||||
n : float
|
n : float
|
||||||
Refractive index of atmospheric air at the given conditions
|
Refractive index of atmospheric air at the given conditions
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
1. I don't understand why this function works. It calculates the
|
||||||
|
refraction and somehow returns the refractive index. Perhaps
|
||||||
|
this is a very particular set of input parameters that make it
|
||||||
|
work. It does closely follow BarrellAndSears() for standard
|
||||||
|
conditions.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
from pyslalib import slalib
|
from pyslalib import slalib
|
||||||
@@ -256,14 +333,14 @@ def slalib(l, T=288.15, p=101325, H=0, h=0, lat=-24, tlr=0.0065, eps=1e-10):
|
|||||||
if type(l) == float:
|
if type(l) == float:
|
||||||
l = [l]
|
l = [l]
|
||||||
for wl in l:
|
for wl in l:
|
||||||
R.append(slalib.sla_refro(zObs, h, T, p, H, wl, lat, tlr, eps))
|
R.append(slalib.sla_refro(zObs, h, T, p, RH, wl, lat, tlr, eps))
|
||||||
if len(l) == 1:
|
if len(l) == 1:
|
||||||
return R[0]+1
|
return R[0]+1
|
||||||
else:
|
else:
|
||||||
return np.asarray(R)+1
|
return np.asarray(R)+1
|
||||||
|
|
||||||
|
|
||||||
def Mathar(l, T=288.15, p=101325, H=0):
|
def Mathar(l, T=288.15, p=101325, RH=0):
|
||||||
'''
|
'''
|
||||||
Refractivity model by Mathar (2007), valid above 1.4 microns
|
Refractivity model by Mathar (2007), valid above 1.4 microns
|
||||||
Code taken from
|
Code taken from
|
||||||
@@ -392,7 +469,7 @@ def Mathar(l, T=288.15, p=101325, H=0):
|
|||||||
|
|
||||||
s = 1e4/l # cm^-1
|
s = 1e4/l # cm^-1
|
||||||
n = 1
|
n = 1
|
||||||
H = H*100
|
H = RH*100
|
||||||
|
|
||||||
for j in range(0, 6):
|
for j in range(0, 6):
|
||||||
n += ( cref[j] + cT[j]*(1/T-1/Tref) + cTT[j]*(1/T-1/Tref)**2
|
n += ( cref[j] + cT[j]*(1/T-1/Tref) + cTT[j]*(1/T-1/Tref)**2
|
||||||
@@ -420,6 +497,7 @@ def K2C(K):
|
|||||||
'''
|
'''
|
||||||
return K - 273.15
|
return K - 273.15
|
||||||
|
|
||||||
|
|
||||||
def hPA2mmHg(hPa):
|
def hPA2mmHg(hPa):
|
||||||
'''
|
'''
|
||||||
hPa to mmHg conversion
|
hPa to mmHg conversion
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ disp = dispersion(lat, h)
|
|||||||
disp.setReducedHeight(P, rho)
|
disp.setReducedHeight(P, rho)
|
||||||
|
|
||||||
# Calculation of the atmopheric dipsersion
|
# Calculation of the atmopheric dipsersion
|
||||||
atm_dispersion = disp.cassini(n1, z0) * 3.6e6
|
atm_dispersion = disp.cassini(n1, n2, z0) * 3.6e6
|
||||||
print ('The dispersion is %.03f milli arc seconds' %(atm_dispersion))
|
print ('The dispersion is %.03f milli arc seconds' %(atm_dispersion))
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ The package contains the following files:
|
|||||||
- `/data/1976USSA.txt` : Data file of the 1976 US Standard Atmosphere.
|
- `/data/1976USSA.txt` : Data file of the 1976 US Standard Atmosphere.
|
||||||
- `ciddorModel.py` : Contains the `Observatory()` class to calculate the refractive index of atmospheric air, using the Ciddor (1996) model.
|
- `ciddorModel.py` : Contains the `Observatory()` class to calculate the refractive index of atmospheric air, using the Ciddor (1996) model.
|
||||||
- `dispersionModels.py` : Contains various atmospheric models that can be used to calculate the atmospheric dispersion
|
- `dispersionModels.py` : Contains various atmospheric models that can be used to calculate the atmospheric dispersion
|
||||||
- `examples.py` : Contains several quick functions, e.g. to quickly calculate the refractive index.
|
- `misc.py` : Contains several miscellaneous functions, e.g. to quickly calculate the refractive index.
|
||||||
- `refractionModels.py` : Contains various atmospheric models that can be used to calculate the atmospheric refraction
|
- `refractionModels.py` : Contains various atmospheric models that can be used to calculate the atmospheric refraction
|
||||||
- `refractivityModels.py` : Contains various functions from literature to calculate the refractive index. Some examples of works included are Ciddor (1996), Barrell & Sears (1939), SLALIB, Owens (1967) and Edlen (1966).
|
- `refractivityModels.py` : Contains various functions from literature to calculate the refractive index. Some examples of works included are Ciddor (1996), Barrell & Sears (1939), SLALIB, Owens (1967) and Edlen (1966).
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ Basic functionality of other refractivity models is included as well and is disc
|
|||||||
|
|
||||||
## The atmospheric refraction and atmospheric dispersion models
|
## The atmospheric refraction and atmospheric dispersion models
|
||||||
|
|
||||||
If the atmospheric refraction is denoted as R(n), then atmospheric dispersion is defined as R(n1) - R(n2). Thus they are very similar in function and also in use.
|
If the atmospheric refraction is denoted as R(n), then atmospheric dispersion is defined as R(n1) - R(n2). Thus they are very similar in function and use.
|
||||||
|
|
||||||
|
|
||||||
The available models in this package:
|
The available models in this package:
|
||||||
@@ -96,6 +96,7 @@ The available models in this package:
|
|||||||
- `tan5()` - Oriani's theorem expanded ( Atan(z) + Btan^3(z) + Ctan^5(z) )
|
- `tan5()` - Oriani's theorem expanded ( Atan(z) + Btan^3(z) + Ctan^5(z) )
|
||||||
- `corbard()` - Error function refraction model
|
- `corbard()` - Error function refraction model
|
||||||
- `matharExponential()` - Barometric exponential model by R. Mathar.
|
- `matharExponential()` - Barometric exponential model by R. Mathar.
|
||||||
|
The package also includes a python native port of the `SLA_REFRO()` FORTRAN routine, ported from pyslalib. See below for more information.
|
||||||
|
|
||||||
|
|
||||||
Input parameters for the spherical atmosphere models (all but the first):
|
Input parameters for the spherical atmosphere models (all but the first):
|
||||||
@@ -190,7 +191,8 @@ Included are
|
|||||||
| Bonsch & Potulski (1998) | `BonschPotulski()` | Function does not include humidity |
|
| Bonsch & Potulski (1998) | `BonschPotulski()` | Function does not include humidity |
|
||||||
| Birch & Downs (1994) | `BirchDowns()` | Only at standard atmosphere. Function requires wavelength only. |
|
| Birch & Downs (1994) | `BirchDowns()` | Only at standard atmosphere. Function requires wavelength only. |
|
||||||
| Peck & Reeder (1972) | `PeckReeder()` | Only at standard atmosphere. Function requires wavelength only. |
|
| Peck & Reeder (1972) | `PeckReeder()` | Only at standard atmosphere. Function requires wavelength only. |
|
||||||
| Hohenkerk & Sinclair (1985) | `slalib()` | Only at standard atmosphere. Function requires wavelength only. |
|
| Hohenkerk & Sinclair (1985) | `HohenkerkAndSinclair()` | Based on Barrell & Sears equation. Generally, gives slightly larger values.|
|
||||||
|
| Hohenkerk & Sinclair (1985) | `slalib()` | Uses pySLALIB to retrieve the refractive index at standard atmospheric conditions.|
|
||||||
|
|
||||||
The last function extracts the refractive index from the `pyslalib.sla_refro()` function and thus requires [pyslalib](https://pypi.org/project/pySLALIB/). This is an optional dependency for the setup of AstroAtmosphere.
|
The last function extracts the refractive index from the `pyslalib.sla_refro()` function and thus requires [pyslalib](https://pypi.org/project/pySLALIB/). This is an optional dependency for the setup of AstroAtmosphere.
|
||||||
|
|
||||||
@@ -210,3 +212,7 @@ rq = quick_refraction(l1, zenith_obs, conditions='STANDARD')
|
|||||||
# Quick dispersion
|
# Quick dispersion
|
||||||
dq = quick_dispersion(l1, l2, zenith_obs, conditions='STANDARD')
|
dq = quick_dispersion(l1, l2, zenith_obs, conditions='STANDARD')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Other
|
||||||
|
|
||||||
|
This package also contains a python native port of the `SLA_REFRO()` routine of the pyslalib package, which was originally written in FORTRAN by P.T. Wallace. It can be called directly using `sla_refro()`. When using the same units as the other refraction functions, you might prefer using `slalib_refraction()` or `slalib_dispersion()`.
|
||||||
@@ -13,7 +13,8 @@ import AstroAtmosphere
|
|||||||
@ Description: A comparison of various atmospheric refraction models.
|
@ Description: A comparison of various atmospheric refraction models.
|
||||||
Also these are used to calculate the difference in
|
Also these are used to calculate the difference in
|
||||||
atmospheric dispersion, given a set of conditions.
|
atmospheric dispersion, given a set of conditions.
|
||||||
This is a reproduction of Figure 1 of Spàno (2014).
|
This is a reproduction of and expansion on Figure 1 of
|
||||||
|
Spàno (2014).
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ class atmosphericModel:
|
|||||||
def Owens(self, l=None):
|
def Owens(self, l=None):
|
||||||
if not l:
|
if not l:
|
||||||
l = self.lrange
|
l = self.lrange
|
||||||
return (AstroAtmosphere.Owens(l, T=self.T, p=self.P, H=self.H)-1)
|
return (AstroAtmosphere.Owens(l, T=self.T, p=self.P, RH=self.H)-1)
|
||||||
|
|
||||||
|
|
||||||
def BonschPotulski(self, l=None):
|
def BonschPotulski(self, l=None):
|
||||||
@@ -72,10 +73,15 @@ class atmosphericModel:
|
|||||||
l = self.lrange
|
l = self.lrange
|
||||||
return (AstroAtmosphere.PeckReeder(l)-1)
|
return (AstroAtmosphere.PeckReeder(l)-1)
|
||||||
|
|
||||||
|
def HohenkerkAndSinclair(self, l=None):
|
||||||
|
if not l:
|
||||||
|
l = self.lrange
|
||||||
|
return (AstroAtmosphere.HohenkerkAndSinclair(l, T=self.T, p=self.P, RH=self.H)-1)
|
||||||
|
|
||||||
def slalib(self, l=None):
|
def slalib(self, l=None):
|
||||||
if not l:
|
if not l:
|
||||||
l = self.lrange
|
l = self.lrange
|
||||||
return(AstroAtmosphere.slalib(l, T=self.T, p=self.P, H=self.H)-1)
|
return(AstroAtmosphere.slalib(l, T=self.T, p=self.P, RH=self.H)-1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -92,6 +98,7 @@ if __name__ == '__main__':
|
|||||||
BonschPotulskiData = 1e8 * at.BonschPotulski()
|
BonschPotulskiData = 1e8 * at.BonschPotulski()
|
||||||
BirchDownsData = 1e8 * at.BirchDowns()
|
BirchDownsData = 1e8 * at.BirchDowns()
|
||||||
PeckReederData = 1e8 * at.PeckReeder()
|
PeckReederData = 1e8 * at.PeckReeder()
|
||||||
|
HSdata = 1e8 * at.HohenkerkAndSinclair()
|
||||||
slalibData = 1e8 * at.slalib()
|
slalibData = 1e8 * at.slalib()
|
||||||
|
|
||||||
# Plotting the results
|
# Plotting the results
|
||||||
@@ -106,13 +113,14 @@ if __name__ == '__main__':
|
|||||||
ax2.plot(at.lrange, BonschPotulskiData - CiddorData, 'g-.', label='Bonsch & Potulski (1998)')
|
ax2.plot(at.lrange, BonschPotulskiData - CiddorData, 'g-.', label='Bonsch & Potulski (1998)')
|
||||||
ax2.plot(at.lrange, BirchDownsData - CiddorData, 'r-.', label='Birch & Downs (1993, 1994)')
|
ax2.plot(at.lrange, BirchDownsData - CiddorData, 'r-.', label='Birch & Downs (1993, 1994)')
|
||||||
ax2.plot(at.lrange, PeckReederData - CiddorData, 'b--', label='Peck & Reeder (1972)')
|
ax2.plot(at.lrange, PeckReederData - CiddorData, 'b--', label='Peck & Reeder (1972)')
|
||||||
|
ax2.plot(at.lrange, HSdata - CiddorData, 'y--', label='Hohenkerk & Sinclair (1985)')
|
||||||
ax2.plot(at.lrange, slalibData - CiddorData, 'y-', label='ZEMAX / SLALIB')
|
ax2.plot(at.lrange, slalibData - CiddorData, 'y-', label='ZEMAX / SLALIB')
|
||||||
box = ax2.get_position()
|
box = ax2.get_position()
|
||||||
ax2.set_position([box.x0, box.y0, box.width, box.height])
|
ax2.set_position([box.x0, box.y0, box.width, box.height])
|
||||||
ax2.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
ax2.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
||||||
ax2.set_xlabel('Wavelength ($\mu m$)')
|
ax2.set_xlabel('Wavelength ($\mu m$)')
|
||||||
ax2.set_ylabel('$\Delta$n $\\times$ $10^{8}$')
|
ax2.set_ylabel('$\Delta$n $\\times$ $10^{8}$')
|
||||||
ax2.set_ylim([-3,3])
|
ax2.set_ylim([-3,7])
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
@@ -128,6 +136,7 @@ if __name__ == '__main__':
|
|||||||
at.BonschPotulski,
|
at.BonschPotulski,
|
||||||
at.BirchDowns,
|
at.BirchDowns,
|
||||||
at.PeckReeder,
|
at.PeckReeder,
|
||||||
|
at.HohenkerkAndSinclair,
|
||||||
at.slalib
|
at.slalib
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -166,11 +175,14 @@ if __name__ == '__main__':
|
|||||||
'Ciddor (1996)',
|
'Ciddor (1996)',
|
||||||
'Edlen (1953)',
|
'Edlen (1953)',
|
||||||
'Edlen (1966)',
|
'Edlen (1966)',
|
||||||
|
'Owens (1967)',
|
||||||
'Bonsch & Potulski (1998)',
|
'Bonsch & Potulski (1998)',
|
||||||
'Birch & Downs (1993, 1994)',
|
'Birch & Downs (1993, 1994)',
|
||||||
'Peck & Reeder (1972)',
|
'Peck & Reeder (1972)',
|
||||||
|
'Hohenkerk & Sinclair (1985)',
|
||||||
'slalib']
|
'slalib']
|
||||||
tableData = []
|
tableData = []
|
||||||
|
|
||||||
for i in range(len(methodStringList)):
|
for i in range(len(methodStringList)):
|
||||||
tableData.append([methodStringList[i], filterDispersion[0][i], filterDispersion[1][i], filterDispersion[2][i], filterDispersion[3][i]])
|
tableData.append([methodStringList[i], filterDispersion[0][i], filterDispersion[1][i], filterDispersion[2][i], filterDispersion[3][i]])
|
||||||
print(tabulate(tableData, headers=['Model', 'I-band\n(0.8 - 0.934)', 'J-band\n(1.15 - 1.345)', 'H-band\n(1.49 - 1.78)', 'K-band\n(1.97 - 2.32)']))
|
print(tabulate(tableData, headers=['Model', 'I-band\n(0.8 - 0.934)', 'J-band\n(1.15 - 1.345)', 'H-band\n(1.49 - 1.78)', 'K-band\n(1.97 - 2.32)']))
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ def setup_package():
|
|||||||
# write_version_py()
|
# write_version_py()
|
||||||
|
|
||||||
setup(name='AstroAtmosphere',
|
setup(name='AstroAtmosphere',
|
||||||
version='1.1',
|
version='1.2',
|
||||||
description='Collection of equations for refractive index, refraction and dispersion calculations of atmospheric air.',
|
description='Collection of equations for refractive index, refraction and dispersion calculations of atmospheric air.',
|
||||||
long_description=__readme__,
|
long_description=__readme__,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
author='Joost van den Born',
|
author='Joost van den Born',
|
||||||
author_email='born@astron.nl',
|
author_email='born@astron.nl',
|
||||||
url='https://gitlab.astro-wise.org/micado/atmosphericmodel',
|
url='https://gitlab.astro-wise.org/micado/atmosphericmodel',
|
||||||
download_url='https://gitlab.astro-wise.org/micado/atmosphericmodel/-/archive/v1.1/atmosphericmodel-v1.1.tar.gz',
|
download_url='https://gitlab.astro-wise.org/micado/atmosphericmodel/-/archive/v1.2/atmosphericmodel-v1.2.tar.gz',
|
||||||
license='GNU General Public License v3',
|
license='GNU General Public License v3',
|
||||||
data_files = [('',['LICENSE.txt'])],
|
data_files = [('',['LICENSE.txt'])],
|
||||||
package_dir={'AstroAtmosphere': 'AstroAtmosphere'},
|
package_dir={'AstroAtmosphere': 'AstroAtmosphere'},
|
||||||
|
|||||||
Reference in new issue
Block a user