Updated documentation, descriptions for v1.0 release.

This commit is contained in:
yosata committed 2020-06-24 16:47:25 +02:00
1 parent 56051bb6a5
commit 8d50899db6
31 files changed
+17833 -192

No files matched your search

+193
View File
@@ -0,0 +1,193 @@
#!/usr/bin/env Python3
import numpy as np
from scipy.optimize import fsolve
class ADC:
def __init__(self, wedge=(1.6554, 6.28927, -1.48306)):
self.p1 = prism('S-LAH71', 15, wedge[0], wedge[1])
self.p2 = prism('S-FPL51', 15, wedge[1], wedge[2])
self.p3 = prism('S-FPL51', 15, wedge[2], wedge[1])
self.p4 = prism('S-LAH71', 15, wedge[1], wedge[0])
self.theta = 90
def setOrientation(self, theta):
self.p1.setPrismOrientation(theta)
self.p2.setPrismOrientation(theta)
self.p3.setPrismOrientation(180-1*theta)
self.p4.setPrismOrientation(180-1*theta)
self.theta = theta
def setFieldRotation(self, FR):
self.p1.setPrismOrientation(self.theta-FR)
self.p2.setPrismOrientation(self.theta-FR)
self.p3.setPrismOrientation(180-1*self.theta-FR)
self.p4.setPrismOrientation(180-1*self.theta-FR)
def calculateDispersion(self, l1, l2, hx=0, hy=0, refraction=False):
'''
Calculates the dispersion for the ADC using the current position,
for a normalized on sky field angle (hx, hy). Default field position
is in the center of the FoV.
'''
_prismList = [self.p1, self.p2, self.p3, self.p4]
_d1x_list, _d1y_list = [], []
_pupilRatio = 0.07943/38.542
hx = hx * 7.5e-3
hy = hy * 7.5e-3
ix = hx * 1/_pupilRatio
iy = hy * 1/_pupilRatio
for l in [l1,l2]:
_deltaListX = np.zeros(len(_prismList))
_deltaListY = np.zeros(len(_prismList))
for p in range(4):
_n_g = _prismList[p].indexOfRefraction(l)
_b1x = _prismList[p].tilt1x
_b1y = _prismList[p].tilt1y
_b2x = _prismList[p].tilt2x
_b2y = _prismList[p].tilt2y
if p==0:
_t_iz_n_x = ix # Zero before first prism
_t_iz_n_y = iy # Zero before first prism
# X direction
_t_i1_n_x = _t_iz_n_x
_d1x = deviationCalculation3(_t_i1_n_x, _b1x, _b2x, _n_g)
_t_tz_n_x = _t_iz_n_x - _d1x
_t_iz_n_x = _t_tz_n_x # Set output to new input (for the next prism)
# Y direction
_t_i1_n_y = _t_iz_n_y
_d1y = deviationCalculation3(_t_i1_n_y, _b1y, _b2y, _n_g)
_t_tz_n_y = _t_iz_n_y - _d1y
_t_iz_n_y = _t_tz_n_y # Set output to new input (for the next prism)
# Adding the deviations to the list with delta terms
_deltaListX[p] = _d1x
_deltaListY[p] = _d1y
_d1x_list.append(_deltaListX.sum())
_d1y_list.append(_deltaListY.sum())
self.dispX = ( _d1x_list[1] - _d1x_list[0] ) * 3600 * 1000 * _pupilRatio
self.dispY = ( _d1y_list[1] - _d1y_list[0] ) * 3600 * 1000 * _pupilRatio
if refraction:
rx = ( _d1x_list[0] ) * 3600 * 1000 * _pupilRatio
ry = ( _d1y_list[0] ) * 3600 * 1000 * _pupilRatio
return rx, ry
return self.dispX, self.dispY
def optimize(self, inputDispersion, l1, l2, x0=50):
_solver = fsolve(optimizeFunction, x0=x0, args=(self, inputDispersion, l1, l2))
return _solver[0]
class prism:
def __init__(self, glass, thickness, tilt1, tilt2=None):
self.glass = glass
self.thickness = thickness
self.tilt1x = tilt1
self.tilt1y = 0
self.tilt2x = tilt2
self.tilt2y = 0
# Remember original tilts for reset:
self.tilt1x_OG = tilt1
self.tilt1y_OG = 0
self.tilt2x_OG = tilt2
self.tilt2y_OG = 0
def indexOfRefraction(self, l):
if self.glass == 'S-FPL51':
a = [1.170105050E+00, 4.757107830E-02, 7.638324450E-01]
b = [6.162039240E-03, 2.633728760E-02, 1.418826420E+02]
elif self.glass == 'S-LAH71':
a = [1.982800310E+00, 3.167584500E-01, 2.444726460E+00]
b = [1.189874560E-02, 5.271560010E-02, 2.132206970E+02]
return dispersionFormula(l, a, b)
def setPrismOrientation(self, theta):
self.tilt1x, self.tilt1y = tiltRotation(self.tilt1x_OG, self.tilt1y_OG, theta)
self.tilt2x, self.tilt2y = tiltRotation(self.tilt2x_OG, self.tilt2y_OG, theta)
def tiltRotation(tx,ty, theta):
theta = np.deg2rad(theta-90)
tx_new = tx * np.cos(theta) - ty * np.sin(theta)
ty_new = tx * np.sin(theta) + ty * np.cos(theta)
return tx_new, ty_new
def dispersionFormula(l, a, b):
n = np.sqrt( (a[0]* l**2)/(l**2 - b[0]) + (a[1] * l**2) / (l**2 - b[1]) + (a[2] * l**2) / (l**2 - b[2]) + 1)
return n
# def deviationCalculation(t_i1, b1, b2, n):
# Returns deviation of the ray after the prism with apex angle alpha
# Units: degrees
# DO NOT USE!
# alpha = b2 - b1
# delta = t_i1 + np.arcsin( np.sin(np.deg2rad(alpha)) * np.sqrt(n**2-(np.sin(np.deg2rad(t_i1)))**2) - np.sin(np.deg2rad(t_i1)) * np.cos(np.deg2rad(alpha)) )*180/np.pi - alpha
# return delta
def deviationCalculation2(t_i1, b1, b2, n):
# Returns deviation of the ray after the prism with apex angle alpha
# only now using Snell's law only
# Units: degrees
t_i1 = np.deg2rad(t_i1)
b1 = np.deg2rad(b1)
b2 = np.deg2rad(b2)
t_i = b1 - t_i1
t_f = np.arcsin( 1 / n * np.sin( t_i ) )
t_f_z = b1 - t_f
t_i = b2 - t_f_z
t_f = np.arcsin( n / 1 * np.sin( t_i ) )
t_f_z = b2 - t_f
delta = t_i1 - t_f_z
delta = np.degrees(delta)
return delta
def deviationCalculation3(t_i1, b1, b2, n):
# Using the formula from Hagen & Tkazcyk (2011)
# Gives exactly the same results as deviationCaclulation2(), above
t_i1 = np.deg2rad(t_i1)
b1 = np.deg2rad(b1)
b2 = np.deg2rad(b2)
alpha = b2 - b1
delta = t_i1 - b2 - np.arcsin( n * np.sin( np.arcsin(1/n * np.sin(t_i1 - b1) ) - alpha ) )
return np.degrees(delta)
def optimizeFunction(theta, adcObject, inputDispersion, l1, l2, FR=None):
if FR:
FRdeg = np.deg2rad(FR) # 30 degrees field rotation
inputDispersionX = np.cos(FRdeg) * inputDispersion
inputDispersionY = np.sin(FRdeg) * inputDispersion
adcObject.setOrientation(theta)
ADCdisp = adcObject.calculateDispersion(l1,l2)
return np.sqrt((ADCdisp[1] - inputDispersionY)**2 + (ADCdisp[0] - inputDispersionX)**2)
else:
adcObject.setOrientation(theta)
ADCdisp = adcObject.calculateDispersion(l1,l2)
ADCdisp = ADCdisp[1]
return ADCdisp - inputDispersion
def angleFunc(x, a):
'''
Function to fit the ideal angle on
'''
return np.arccos(a * np.tan(np.deg2rad(x))) * 180/np.pi
File diff suppressed because it is too large. Load diff
+156
View File
@@ -0,0 +1,156 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Atmospheric Dispersion Corrector model\n",
"\n",
"This notebook explains how you can use the ADC.py analytic ADC model for some quick simulations. For the model to be representative, the following assumptions must apply:\n",
"* The ADC consists of two amici prisms, totaling 4 glass prisms in total\n",
"* The ADC is located in the pupil plane of the instrument (or very close to it)\n",
"* The rotation of the prisms is defined as follows: for $\\theta=0$ the ADC is in it's maximum dispersive configuration. For any other rotation angle, the top prism set rotates by $\\theta$ degrees and the bottom prism set also rotates by $\\theta$ degrees in opposite direction. The total relative angle is thus $2\\theta$. From this also follows that the minimum dispersion magnitude is reached at $\\theta=90$ degrees.\n",
"\n",
"The ADC.py model is really not made for very general use, so it will likely require some editing in the source code by the user to make it work for a different instrument than MICADO.\n",
"\n",
"\n",
"We first start by importing the necessary files. Here we'll use the `AstroAtmosphere` package to calculate the atmospheric dispersion, but a manual input is also possible."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import ADC\n",
"import AstroAtmosphere"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we calculate the atmospheric dispersion for a given set of conditions."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# Atmospheric conditions\n",
"T = 288.15 \t# k\n",
"P = 101325 \t# Pa\n",
"H = 0.3\n",
"xc = 400 \t# ppm\n",
"lat = -25 \t\t# degrees\n",
"h = 2500 \t# m\n",
"l1 = 1.49 \t\t# micron, start H-band\n",
"l2 = 1.78 \t\t# micron, end H-band\n",
"zenith = 30 \t# degrees\n",
"\n",
"# Initializing dispersion model\n",
"at = AstroAtmosphere.Observatory()\n",
"rho = at.rho(p=P, T=T, RH=H, xc=xc)\n",
"disp = AstroAtmosphere.dispersion(lat, h)\n",
"disp.setReducedHeight(P, rho)\n",
"\n",
"# Calculation of indices of refraction at l1 and l2\n",
"n1 = at.n_tph(l=l1, T=T, p=P, RH=H, xc=xc)\n",
"n2 = at.n_tph(l=l2, T=T, p=P, RH=H, xc=xc)\n",
"\n",
"# Calculation of atmospheric dispersion in milli arc sec\n",
"dispersion = disp.cassini(n1, n2, zenith)*3.6e6"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we initialize the ADC class. We can calculate the dispersion in sky angles, i.e. in terms of the Field of View of the telescope, for a given rotation of the ADC prisms. In this case, if we set $\\theta=30$ degrees, we find that the ADC produces 58 milli arc seconds of dispersion in H-band."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The dispersion of the ADC for 30 degrees of rotation: 58.011 mas\n"
]
}
],
"source": [
"# Initialize ADC object\n",
"adc = ADC.ADC()\n",
"\n",
"# Calculate the dispersion for a given rotation\n",
"theta = 30 # degrees\n",
"adc.setOrientation(theta)\n",
"adcDispersion = adc.calculateDispersion(l1,l2)[1]\n",
"print(\n",
" 'The dispersion of the ADC for {:.0f} degrees of rotation: {:.3f} mas'\\\n",
" .format(theta, adcDispersion))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Of course, we're often much more interested in the optimum positioning of the ADC given an amount of atmospheric dispersion. This is done as follows."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The atmospheric dispersion: 24.771 mas\n",
"The optimum ADC angle in this case: 68.1 degrees\n",
"The residual dispersion: 0.000 mas\n"
]
}
],
"source": [
"# Optimize for atmospheric dispersion, given wavelength range {l1,l2}\n",
"adcOptimumAngle = adc.optimize(dispersion, l1, l2) \n",
"adcDispersionOptimized = adc.calculateDispersion(l1,l2)[1]\n",
"\n",
"print('The atmospheric dispersion: {:.3f} mas'.format(dispersion) )\n",
"print('The optimum ADC angle in this case: {:.1f} degrees'.format(adcOptimumAngle))\n",
"print('The residual dispersion: {:.3f} mas'.format(dispersion - adcDispersionOptimized))\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env Python3
import numpy as np
from AstroAtmosphere import *
import matplotlib.pyplot as plt
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: This script uses the equations of the paper to calculate the
residual dispersion due to the finite step size of the ADC.
'''
def angleFunc(x, a):
'''
Function to fit the ideal angle on
'''
return np.arccos(a * np.tan(np.deg2rad(x))) * 180/np.pi
def angleFuncDeriv(x, a):
return (a * (1+np.tan(np.deg2rad(x))**2)) / np.sqrt(1 - a**2 * np.tan(np.deg2rad(x))**2)
def cassiniDispersionDeriv(n1, n2, zenith, alpha):
a1 = n1 * alpha * np.cos(np.deg2rad(zenith)) / np.sqrt(1 - n1**2 * alpha**2 * np.sin(np.deg2rad(zenith))**2)
a2 = n2 * alpha * np.cos(np.deg2rad(zenith)) / np.sqrt(1 - n2**2 * alpha**2 * np.sin(np.deg2rad(zenith))**2)
return a1 - a2
# Parameters at Cerro Armazones
T = 279.65 # k
P = 71200 # Pa
H = 0.22
xc = 450 # ppm
lat = -24.5983 # degrees
h = 3064 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 30 # degrees
at = dispersion(lat, h)
at.setReducedHeight(P, at.rho(P, T, H, xc))
n1 = at.n_tph(l1, T, P, H, xc)
n2 = at.n_tph(l2, T, P, H, xc)
alpha = at.rc / (at.rc+at.h)
# c_fH = 0.499413 # From Zemax
c_fH = 0.46816061517198737 # Completely analytical cf
# f_fH = 0.46462883308144837 # Best fit to analytical ADC model
dispersion = at.cassini(n1, n2, zenith)*3.6e6
dispersionDeriv = cassiniDispersionDeriv(n1, n2, zenith, alpha)*3.6e6
optAngle = angleFunc(zenith, c_fH)
optAngleDeriv = angleFuncDeriv(zenith, c_fH)
maxDev = optAngleDeriv / dispersionDeriv
print('Zenith = %s degrees' %zenith)
print('Dispersion in mas: ', dispersion)
print('Dispersion/degree zenith at z=%s: %s mas/degree z' %(zenith, dispersionDeriv))
print('Optimum angle ADC: %s' %(optAngle))
print('Optimum angle ADC/degree zenith at z=%s: %s' %(zenith, optAngleDeriv))
print('Max deviation per mas dispersion: %s degrees' %(maxDev))
print('At designed stepsize at most %s mas of residual dispersion' %(1/maxDev*(360/1262.5)))
# Plotting as a function of zenith angle
dataList = []
for zenith in np.linspace(5,65,100):
dispersion = at.cassini(n1, n2, zenith)*3.6e6
dispersionDeriv = cassiniDispersionDeriv(n1, n2, zenith, alpha)*3.6e6
optAngle = angleFunc(zenith, c_fH)
optAngleDeriv = angleFuncDeriv(zenith, c_fH)
maxDev = optAngleDeriv / dispersionDeriv
dataList.append(1/maxDev*(360/1262.5))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(np.linspace(5,65,100), dataList)
ax.grid(True, alpha=0.2)
ax.set_xlabel('Zenith (degrees)')
ax.set_ylabel('Residual dispersion (mas)')
plt.show()
@@ -0,0 +1,21 @@
NARR data provided by the NOAA/OAR/ESRL PSL, Boulder, Colorado, USA, from their Web
site at https://psl.noaa.gov/
The original "air.201806.nc" datafile was downloaded from
https://psl.noaa.gov/data/gridded/data.narr.pressure.html
Specific download link (~650 MB):
ftp://ftp.cdc.noaa.gov/Datasets/NARR/pressure/air.201806.nc [Last accessed on
2020-06-17]
The following files are a direct slice of this data:
- export_dist.txt
- export_heights.txt
- export_press_atmosphere.txt
- export_temp_atmosphere.txt
This data file was not included here, because it has a very niche use for the
AstroAtmosphere package and is a large file. The tradeoff seems to me to not very
favourable, and therefore it is omitted.
@@ -0,0 +1,97 @@
NCEP North American Regional Reanalysis (NARR) gridded weather data.
Data from https://www.esrl.noaa.gov/psd/data/gridded/data.narr.pressure.html
###############
# Header info #
###############
$ ncdump -h air.201806.nc
netcdf air.201806 {
dimensions:
time = UNLIMITED ; // (240 currently)
level = 29 ;
y = 277 ;
x = 349 ;
variables:
double time(time) ;
time:axis = "T" ;
time:coordinate_defines = "point" ;
time:delta_t = "0000-00-00 03:00:00" ;
time:long_name = "Time" ;
time:standard_name = "time" ;
time:units = "hours since 1800-1-1 00:00:0.0" ;
time:actual_range = 1914576., 1915293. ;
float level(level) ;
level:GRIB_id = 100s ;
level:GRIB_name = "hPa" ;
level:actual_range = 1000.f, 100.f ;
level:axis = "Z" ;
level:coordinate_defines = "point" ;
level:long_name = "Level" ;
level:positive = "down" ;
level:standard_name = "level" ;
level:units = "millibar" ;
float lat(y, x) ;
lat:axis = "Y" ;
lat:coordinate_defines = "point" ;
lat:long_name = "Latitude" ;
lat:standard_name = "latitude" ;
lat:units = "degrees_north" ;
float lon(y, x) ;
lon:axis = "X" ;
lon:coordinate_defines = "point" ;
lon:long_name = "Longitude" ;
lon:standard_name = "longitude" ;
lon:units = "degrees_east" ;
float y(y) ;
y:long_name = "northward distance from southwest corner of domain in projection coordinates" ;
y:standard_name = "projection_y_coordinate" ;
y:units = "m" ;
float x(x) ;
x:long_name = "eastward distance from southwest corner of domain in projection coordinates" ;
x:standard_name = "projection_x_coordinate" ;
x:units = "m" ;
int Lambert_Conformal ;
Lambert_Conformal:false_easting = 5632642.22547 ;
Lambert_Conformal:false_northing = 4612545.65137 ;
Lambert_Conformal:grid_mapping_name = "lambert_conformal_conic" ;
Lambert_Conformal:latitude_of_projection_origin = 50. ;
Lambert_Conformal:longitude_of_central_meridian = -107. ;
Lambert_Conformal:standard_parallel = 50., 50. ;
float air(time, level, y, x) ;
air:GRIB_id = 11s ;
air:GRIB_name = "TMP" ;
air:_FillValue = 9.96921e+36f ;
air:coordinates = "lat lon" ;
air:dataset = "NARR 3-hourly" ;
air:grid_mapping = "Lambert_Conformal" ;
air:level_desc = "Pressure Levels" ;
air:long_name = "3-hourly Air Temperature on Pressure Levels" ;
air:parent_stat = "Other" ;
air:standard_name = "air_temperature" ;
air:statistic = "Individual Obs" ;
air:units = "degK" ;
air:valid_range = 137.5f, 362.5f ;
air:var_desc = "Air temperature" ;
air:missing_value = -9.96921e+36f ;
air:actual_range = 187.1279f, 320.1487f ;
// global attributes:
:Conventions = "CF-1.2" ;
:centerlat = 50. ;
:centerlon = -107. ;
:comments = "" ;
:institution = "National Centers for Environmental Prediction" ;
:latcorners = 1.000001f, 0.897945f, 46.3544f, 46.63433f ;
:loncorners = -145.5f, -68.32005f, -2.569891f, 148.6418f ;
:platform = "Model" ;
:standardpar1 = 50. ;
:standardpar2 = 50.000001 ;
:title = "8x Daily NARR" ;
:history = "created Mon Jul 18 21:01:07 MDT 2016 by NOAA/ESRL/PSD" ;
:dataset_title = "NCEP North American Regional Reanalysis (NARR)" ;
:references = "https://www.esrl.noaa.gov/psd/data/gridded/data.narr.html" ;
:source = "http://www.emc.ncep.noaa.gov/mmb/rreanl/index.html" ;
}
@@ -0,0 +1 @@
[0.0, 0.23179527183433432, 0.46359054366866864, 0.6953858155030029, 0.9271810873373373, 1.1589763591716715, 1.3907716310060059, 1.6225669028403402, 1.8543621746746746, 2.086157446509009, 2.317952718343343, 2.5497479901776776, 2.7815432620120117, 3.0133385338463463, 3.2451338056806804, 3.476929077515015, 3.708724349349349, 3.9405196211836833, 4.172314893018018, 4.404110164852352, 4.635905436686686, 4.867700708521021, 5.099495980355355, 5.33129125218969, 5.5630865240240235, 5.794881795858358, 6.026677067692693, 6.258472339527026, 6.490267611361361, 6.7220628831956954, 6.95385815503003, 7.185653426864364, 7.417448698698698, 7.649243970533033, 7.8810392423673665, 8.112834514201701, 8.344629786036036, 8.57642505787037, 8.808220329704705, 9.04001560153904, 9.271810873373372, 9.503606145207707, 9.735401417042041, 9.967196688876376, 10.19899196071071, 10.430787232545045, 10.66258250437938, 10.894377776213712, 11.126173048048047, 11.357968319882382, 11.589763591716716, 11.82155886355105, 12.053354135385385, 12.28514940721972, 12.516944679054053, 12.748739950888387, 12.980535222722722, 13.212330494557056, 13.444125766391391, 13.675921038225725, 13.90771631006006, 14.139511581894393, 14.371306853728727, 14.603102125563062, 14.834897397397397, 15.066692669231731, 15.298487941066066, 15.5302832129004, 15.762078484734733, 15.993873756569068, 16.225669028403402, 16.45746430023774, 16.68925957207207, 16.921054843906404, 17.15285011574074, 17.384645387575073, 17.61644065940941, 17.848235931243742, 18.08003120307808, 18.31182647491241, 18.543621746746744, 18.77541701858108, 19.007212290415413, 19.23900756224975, 19.470802834084083, 19.70259810591842, 19.93439337775275, 20.166188649587085, 20.39798392142142, 20.629779193255754, 20.86157446509009, 21.093369736924423, 21.32516500875876, 21.556960280593092, 21.788755552427425, 22.02055082426176, 22.252346096096094, 22.48414136793043, 22.715936639764763, 22.9477319115991, 23.179527183433432, 23.411322455267765, 23.6431177271021, 23.874912998936434, 24.10670827077077, 24.338503542605103, 24.57029881443944, 24.802094086273772, 25.033889358108105, 25.26568462994244, 25.497479901776774, 25.72927517361111, 25.961070445445444, 26.19286571727978, 26.424660989114113, 26.656456260948445, 26.888251532782782, 27.120046804617115, 27.35184207645145, 27.583637348285784, 27.81543262012012, 28.047227891954453, 28.279023163788786, 28.510818435623122, 28.742613707457455, 28.97440897929179, 29.206204251126124, 29.43799952296046, 29.669794794794793, 29.901590066629126, 30.133385338463462, 30.365180610297795, 30.59697588213213, 30.828771153966464, 31.0605664258008, 31.292361697635133, 31.524156969469466, 31.755952241303802, 31.987747513138135, 32.21954278497247, 32.451338056806804, 32.68313332864114, 32.91492860047548, 33.146723872309806, 33.37851914414414, 33.61031441597848, 33.84210968781281, 34.073904959647145, 34.30570023148148, 34.53749550331582, 34.76929077515015, 35.00108604698448, 35.23288131881882, 35.46467659065315, 35.696471862487485, 35.92826713432182, 36.16006240615616, 36.39185767799049, 36.62365294982482, 36.85544822165916, 37.08724349349349, 37.319038765327825, 37.55083403716216, 37.7826293089965, 38.01442458083083, 38.24621985266516, 38.4780151244995, 38.70981039633383, 38.941605668168165, 39.1734009400025, 39.40519621183684, 39.63699148367117, 39.8687867555055, 40.10058202733984, 40.33237729917417, 40.564172571008505, 40.79596784284284, 41.02776311467718, 41.25955838651151, 41.491353658345844, 41.72314893018018, 41.95494420201451, 42.186739473848846, 42.41853474568318, 42.65033001751752, 42.88212528935185, 43.113920561186184, 43.34571583302052, 43.57751110485485, 43.809306376689186, 44.04110164852352, 44.27289692035786, 44.50469219219219, 44.736487464026524, 44.96828273586086, 45.20007800769519, 45.431873279529526, 45.66366855136386, 45.8954638231982, 46.12725909503253, 46.359054366866864, 46.5908496387012, 46.82264491053553, 47.054440182369866, 47.2862354542042, 47.51803072603854, 47.74982599787287, 47.981621269707205, 48.21341654154154, 48.44521181337587, 48.67700708521021, 48.90880235704454, 49.14059762887888, 49.37239290071321, 49.604188172547545, 49.83598344438188, 50.06777871621621, 50.29957398805055, 50.53136925988488, 50.76316453171922, 50.99495980355355, 51.226755075387885, 51.45855034722222, 51.69034561905655, 51.92214089089089, 52.15393616272522, 52.38573143455956, 52.61752670639389, 52.849321978228225, 53.08111725006256, 53.31291252189689, 53.54470779373123, 53.776503065565564, 54.0082983373999, 54.24009360923423, 54.471888881068566, 54.7036841529029, 54.93547942473723, 55.16727469657157, 55.399069968405904, 55.63086524024024, 55.86266051207457, 56.094455783908906, 56.32625105574324, 56.55804632757757, 56.78984159941191, 57.021636871246244, 57.25343214308058, 57.48522741491491, 57.717022686749246, 57.94881795858358, 58.18061323041791, 58.41240850225225, 58.644203774086584, 58.87599904592092, 59.10779431775525, 59.339589589589586, 59.57138486142392, 59.80318013325825, 60Line truncated
@@ -0,0 +1 @@
[0.0, 0.21553884711779447, 0.43107769423558895, 0.6466165413533834, 0.8621553884711779, 1.0776942355889725, 1.2932330827067668, 1.5087719298245612, 1.7243107769423558, 1.9398496240601504, 2.155388471177945, 2.370927318295739, 2.5864661654135337, 2.8020050125313283, 3.0175438596491224, 3.233082706766917, 3.4486215538847116, 3.664160401002506, 3.8796992481203008, 4.095238095238095, 4.31077694235589, 4.526315789473684, 4.741854636591478, 4.957393483709273, 5.172932330827067, 5.3884711779448615, 5.6040100250626566, 5.819548872180451, 6.035087719298245, 6.25062656641604, 6.466165413533834, 6.681704260651629, 6.897243107769423, 7.112781954887217, 7.328320802005012, 7.5438596491228065, 7.7593984962406015, 7.974937343358396, 8.19047619047619, 8.406015037593985, 8.62155388471178, 8.837092731829573, 9.052631578947368, 9.268170426065163, 9.483709273182956, 9.699248120300751, 9.914786967418546, 10.13032581453634, 10.345864661654135, 10.56140350877193, 10.776942355889723, 10.992481203007518, 11.208020050125313, 11.423558897243106, 11.639097744360901, 11.854636591478696, 12.07017543859649, 12.285714285714285, 12.50125313283208, 12.716791979949875, 12.932330827067668, 13.147869674185463, 13.363408521303258, 13.578947368421051, 13.794486215538846, 14.010025062656641, 14.225563909774435, 14.44110275689223, 14.656641604010025, 14.872180451127818, 15.087719298245613, 15.303258145363408, 15.518796992481203, 15.734335839598996, 15.949874686716791, 16.165413533834585, 16.38095238095238, 16.596491228070175, 16.81203007518797, 17.027568922305765, 17.24310776942356, 17.45864661654135, 17.674185463659146, 17.88972431077694, 18.105263157894736, 18.32080200501253, 18.536340852130326, 18.751879699248118, 18.967418546365913, 19.182957393483708, 19.398496240601503, 19.614035087719298, 19.829573934837093, 20.045112781954884, 20.26065162907268, 20.476190476190474, 20.69172932330827, 20.907268170426065, 21.12280701754386, 21.338345864661655, 21.553884711779446, 21.76942355889724, 21.984962406015036, 22.20050125313283, 22.416040100250626, 22.63157894736842, 22.847117794486213, 23.062656641604008, 23.278195488721803, 23.493734335839598, 23.709273182957393, 23.924812030075188, 24.14035087719298, 24.355889724310774, 24.57142857142857, 24.786967418546364, 25.00250626566416, 25.218045112781954, 25.43358395989975, 25.64912280701754, 25.864661654135336, 26.08020050125313, 26.295739348370926, 26.51127819548872, 26.726817042606516, 26.942355889724308, 27.157894736842103, 27.373433583959898, 27.588972431077693, 27.804511278195488, 28.020050125313283, 28.235588972431078, 28.45112781954887, 28.666666666666664, 28.88220551378446, 29.097744360902254, 29.31328320802005, 29.528822055137844, 29.744360902255636, 29.95989974937343, 30.175438596491226, 30.39097744360902, 30.606516290726816, 30.82205513784461, 31.037593984962406, 31.253132832080198, 31.468671679197993, 31.684210526315788, 31.899749373433583, 32.11528822055138, 32.33082706766917, 32.54636591478697, 32.76190476190476, 32.97744360902256, 33.19298245614035, 33.40852130325814, 33.62406015037594, 33.83959899749373, 34.05513784461153, 34.27067669172932, 34.48621553884712, 34.70175438596491, 34.9172932330827, 35.1328320802005, 35.34837092731829, 35.56390977443609, 35.77944862155388, 35.994987468671674, 36.21052631578947, 36.426065162907264, 36.64160401002506, 36.857142857142854, 37.07268170426065, 37.288220551378444, 37.503759398496236, 37.719298245614034, 37.934837092731826, 38.150375939849624, 38.365914786967416, 38.581453634085214, 38.796992481203006, 39.0125313283208, 39.228070175438596, 39.44360902255639, 39.659147869674186, 39.87468671679198, 40.09022556390977, 40.30576441102757, 40.52130325814536, 40.73684210526316, 40.95238095238095, 41.16791979949875, 41.38345864661654, 41.59899749373433, 41.81453634085213, 42.03007518796992, 42.24561403508772, 42.46115288220551, 42.67669172932331, 42.8922305764411, 43.10776942355889, 43.32330827067669, 43.53884711779448, 43.75438596491228, 43.96992481203007, 44.185463659147864, 44.40100250626566, 44.616541353383454, 44.83208020050125, 45.047619047619044, 45.26315789473684, 45.478696741854634, 45.694235588972425, 45.909774436090224, 46.125313283208015, 46.340852130325814, 46.556390977443606, 46.771929824561404, 46.987468671679196, 47.20300751879699, 47.418546365914786, 47.63408521303258, 47.849624060150376, 48.06516290726817, 48.28070175438596, 48.49624060150376, 48.71177944862155, 48.92731829573935, 49.14285714285714, 49.35839598997494, 49.57393483709273, 49.78947368421052, 50.00501253132832, 50.22055137844611, 50.43609022556391, 50.6516290726817, 50.8671679197995, 51.08270676691729, 51.29824561403508, 51.51378446115288, 51.72932330827067, 51.94486215538847, 52.16040100250626, 52.37593984962406, 52.59147869674185, 52.807017543859644, 53.02255639097744, 53.238095238095234, 53.45363408521303, 53.669172932330824, 53.884711779448615, 54.100250626566414, 54.315789473684205, 54.531328320802004, 54.746867167919795, 54.962406015037594, 55.177944862155385, 55.39348370Line truncated
@@ -0,0 +1 @@
[[100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 99999.99999999999, 99999.99999999999, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.00000000001, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.00000000001, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.00000000001, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 99999.99999999999, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100000.0, 100Line truncated
@@ -0,0 +1 @@
[[284.050537109375, 284.05081584207153, 284.0510945747681, 284.0513733074647, 284.0516520401612, 284.0519307728577, 284.0522095055543, 284.05248823825076, 284.05276697094735, 284.05304570364393, 284.05332443634046, 284.053603169037, 284.0538819017336, 284.05416063443016, 284.0544393671267, 284.05471809982316, 284.05499683251975, 284.0552755652163, 284.0555542979128, 284.05583303060934, 284.0561117633059, 284.05639049600245, 284.05666922869904, 284.0569479613956, 284.05722669409215, 284.0575054267887, 284.0577841594852, 284.05806289218174, 284.05834162487827, 284.0586203575748, 284.0588990902713, 284.05917782296797, 284.0594565556645, 284.059735288361, 284.06001402105755, 284.06029275375414, 284.06057148645067, 284.06085021914726, 284.0611289518438, 284.0614076845403, 284.06168641723684, 284.06196514993337, 284.06224388262996, 284.0625226153265, 284.062801348023, 284.0630800807196, 284.06335881341613, 284.0636375461127, 284.06391627880924, 284.0641950115058, 284.06447374420236, 284.06475247689883, 284.0650312095954, 284.06530994229195, 284.06558867498853, 284.06586740768506, 284.0661461403816, 284.0664248730782, 284.0667036057747, 284.06698233847123, 284.0672610711678, 284.06753980386435, 284.0678185365609, 284.0680972692574, 284.06837600195394, 284.0686547346505, 284.06893346734705, 284.06921220004364, 284.06949093274017, 284.0697696654367, 284.0700483981333, 284.0703271308298, 284.0706058635264, 284.07088459622287, 284.0711633289194, 284.071442061616, 284.0717207943125, 284.0719995270091, 284.0722782597056, 284.07255699240216, 284.0728357250987, 284.07311445779527, 284.07339319049186, 284.0736719231884, 284.0739506558849, 284.07422938858144, 284.07450812127803, 284.0747868539745, 284.0750655866711, 284.0753443193677, 284.0756230520642, 284.07590178476073, 284.07618051745726, 284.0764592501538, 284.0767379828504, 284.0770167155469, 284.0772954482435, 284.07757418094, 284.07785291363655, 284.0781316463331, 284.07841037902966, 284.0786891117262, 284.0789678444228, 284.0792465771193, 284.07952530981584, 284.07980404251236, 284.08008277520895, 284.0803615079055, 284.080640240602, 284.0809189732986, 284.0811977059951, 284.08147643869165, 284.0817551713882, 284.08203390408477, 284.0823126367813, 284.0825913694779, 284.08287010217435, 284.08314883487094, 284.08342756756747, 284.08370630026405, 284.0839850329606, 284.0842637656571, 284.0845424983537, 284.0848212310502, 284.08509996374676, 284.08537869644334, 284.0856574291398, 284.0859361618364, 284.08621489453293, 284.0864936272295, 284.08677235992604, 284.0870510926226, 284.08732982531916, 284.0876085580157, 284.0878872907122, 284.0881660234088, 284.08844475610533, 284.08872348880186, 284.0890022214984, 284.089280954195, 284.0895596868915, 284.08983841958803, 284.0901171522846, 284.09039588498115, 284.0906746176777, 284.09095335037426, 284.0912320830708, 284.0915108157673, 284.09178954846385, 284.09206828116044, 284.09234701385697, 284.0926257465535, 284.0929044792501, 284.0931832119466, 284.09346194464314, 284.0937406773397, 284.09401941003625, 284.09429814273284, 284.0945768754293, 284.0948556081259, 284.0951343408224, 284.095413073519, 284.09569180621554, 284.09597053891207, 284.0962492716086, 284.0965280043052, 284.0968067370017, 284.0970854696983, 284.09736420239483, 284.106526646489, 284.1175328703515, 284.1285390942141, 284.13954531807667, 284.15055154193925, 284.1615577658018, 284.17256398966435, 284.1835702135269, 284.19457643738946, 284.205582661252, 284.21658888511456, 284.22759510897714, 284.23860133283966, 284.24960755670224, 284.2606137805648, 284.2716200044274, 284.2826262282899, 284.2936324521525, 284.3046386760151, 284.31564489987767, 284.3266511237402, 284.33765734760277, 284.3486635714653, 284.35966979532793, 284.37067601919046, 284.38168224305304, 284.39268846691556, 284.40369469077814, 284.4147009146407, 284.4257071385033, 284.4367133623658, 284.4477195862284, 284.458725810091, 284.4697320339535, 284.4807382578161, 284.49174448167867, 284.5027507055412, 284.51375692940377, 284.52476315326635, 284.53576937712893, 284.54677560099145, 284.55778182485403, 284.5687880487166, 284.5797942725792, 284.5908004964417, 284.6018067203043, 284.6128129441669, 284.6238191680294, 284.634825391892, 284.64583161575456, 284.6568378396171, 284.66784406347966, 284.67885028734224, 284.68985651120477, 284.70086273506735, 284.7118689589299, 284.7228751827925, 284.73388140665503, 284.7448876305176, 284.7558938543802, 284.76690007824277, 284.7779063021053, 284.7889125259679, 284.7999187498304, 284.810924973693, 284.82193119755556, 284.8329374214181, 284.84394364528066, 284.8549498691432, 284.86595609300576, 284.87696231686834, 284.8879685407309, 284.89897476459345, 284.909980988456, 284.9209872123186, 284.9319934361812, 284.9429996600437, 284.9540058839063, 284.96501210776887, 284.9760183316314, 284.987024555494, 284.99803077935655, 285.0090370032191, 285.0200432270817, 285.03104945094424, 285.04205567480676, 285.05306189866934, 285.0640681225319, 285.07507434639444, Line truncated
@@ -0,0 +1,168 @@
#!/usr/bin/env Python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib import cm
from matplotlib.colors import LogNorm
from AstroAtmosphere import *
from scipy import interpolate
import json
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: This script uses the NCEP NARR data to model a real structured atmosphere
from data with a resolution of 2.5 km per data point. We interpolate this
data to a higher resolution.
Then a full integration over this atmosphere is done and compared to the
values for the refraction and dispersion if we had only taken the atmospheric
conditions directly at the observatory.
'''
# Load a variable from a json text file.
def loadVariableFromTXT(filename):
variableFile = open(filename, 'r')
variable = json.load(variableFile)
variableFile.close()
return np.asarray(variable)
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
# h, _, _, _, Temp, Press, Dens, vSound, _,_ = np.genfromtxt('si1py_modified.txt', unpack=True, skip_header=6)
dist = loadVariableFromTXT('export_dist.txt')
heights = loadVariableFromTXT('export_heights.txt')
press = loadVariableFromTXT('export_press_atmosphere.txt')
temp = loadVariableFromTXT('export_temp_atmosphere.txt')
print("Loading complete!")
# Conditions at Cerro Armazones
T = temp[0,0] # k
P = press[0,0]# Pa
H = 0.0
xc = 314 # ppm
lat = -24.5983 # degrees
h0 = 0 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 60 # degrees
# Calculate the index of refraction
at = Observatory()
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)
rho = at.rho(p=P, T=T, RH=H, xc=xc)
RHlist = np.asarray([H]*len(press[:,0]))
xclist = np.asarray([xc]*len(press[:,0]))
Dens = at.rho(p=press[:,0], T=temp[:,0], RH=RHlist, xc=xclist)
refr = refraction(lat, h0)
refr.setReducedHeight(P, rho)
disp = dispersion(lat, h0)
disp.setReducedHeight(P, rho)
# We must create a cube of weather data:
# We assume a flat atmosphere for now, so that h does not need to be adjusted
# x_max = 200 km, gives zenith_max = ~66.7 degrees
# Every 20 km in horizontal direction a new perturbation and interpolation
# for every 500 m.
x_max = dist[-1] # km
Temp_cube_n = []
Press_cube_n = []
for x in range(len(dist)):
Temp_cube_n.append(temp[:,0])
Press_cube_n.append(press[:,0])
Temp_cube_n = np.asarray(Temp_cube_n).T
Press_cube_n = np.asarray(Press_cube_n).T
fig = plt.figure()
ax1 = fig.add_subplot(211)
im = ax1.pcolormesh(dist,heights, temp, cmap=cm.coolwarm)
fig.colorbar(im, ax=ax1, label='Temperature (K)')
ax1.set_aspect(1)
ax1.set_ylabel('Height (km)')
ax2 = fig.add_subplot(212)
im2 = ax2.pcolormesh(dist, heights, press, cmap=cm.coolwarm, norm=LogNorm())
ax2.set_aspect(1)
ax2.set_xlabel('Horizontal distance (km)')
ax2.set_ylabel('Height (km)')
fig.colorbar(im2, ax=ax2, label='Pressure (Pa)')
plt.tight_layout()
plt.show(block=False)
# Now for a given zenith angle we need to find the temp and press along the line of sight
f_Temp_2d = interpolate.interp2d(dist, heights, temp, kind='linear')
f_Press_2d = interpolate.interp2d(dist, heights, press, kind='linear')
f_Temp_2d_n = interpolate.interp2d(dist, heights, Temp_cube_n, kind='linear')
f_Press_2d_n = interpolate.interp2d(dist, heights, Press_cube_n, kind='linear')
print('Interpolation complete!')
zenithRange = np.linspace(30,65,10)
refrDiff, dispDiff = [], []
for zenith in zenithRange:
z = np.deg2rad(zenith)
y_los = heights
x_los = np.linspace(0, heights.max()*np.tan(z), len(heights))
Temp_los, Press_los = [], []
Temp_los_n, Press_los_n = [], []
for i in range(len(heights)):
Temp_los.append(f_Temp_2d(x_los[i], y_los[i])[0])
Press_los.append(f_Press_2d(x_los[i], y_los[i])[0])
Temp_los_n.append(f_Temp_2d_n(x_los[i], y_los[i])[0])
Press_los_n.append(f_Press_2d_n(x_los[i], y_los[i])[0])
Temp_los = np.asarray(Temp_los)
Press_los = np.asarray(Press_los)
Temp_los_n = np.asarray(Temp_los_n)
Press_los_n = np.asarray(Press_los_n)
rho_los = at.rho(p=Press_los, T=Temp_los, RH=H, xc=xc)
rho_los_n = at.rho(p=Press_los_n, T=Temp_los_n, RH=H, xc=xc)
refrInt = refr.refractionIntegral(n1, z, useStandardAtmosphere=False, heightData=heights, rhoData=rho_los_n)*3600*1000
refrInt_pert = refr.refractionIntegral(n1, z, useStandardAtmosphere=False, heightData=heights, rhoData=rho_los)*3600*1000
refrDiff.append(refrInt_pert - refrInt)
dispInt = disp.refractionIntegral(n1, n2, z, useStandardAtmosphere=False, heightData=heights, rhoData=rho_los_n)*3.6e6
dispInt_pert = disp.refractionIntegral(n1, n2, z, useStandardAtmosphere=False, heightData=heights, rhoData=rho_los)*3.6e6
dispDiff.append(dispInt_pert - dispInt)
fig3 = plt.figure(figsize=(9,7))
ax1 = fig3.add_subplot(211)
ax2 = fig3.add_subplot(212)
ax1.tick_params(axis ='both', direction='in', which='both', labelsize=16)
ax2.tick_params(axis ='both', direction='in', which='both', labelsize=16)
ax1.grid(True, alpha=0.3)#, which='major')
ax2.grid(True, alpha=0.3)#, which='major')
ax1.plot(zenithRange, np.log10(np.abs(refrDiff)), 'k-', linewidth=2)
ax2.plot(zenithRange, np.log10(np.abs(dispDiff)), 'k-', linewidth=2)
ax1.set_xlabel('Zenith (degrees)', fontsize=18)
ax2.set_xlabel('Zenith (degrees)', fontsize=18)
ax1.set_ylabel('$\log_{10}($|$R - R_{o}$|$)$ (mas)', fontsize=18)
ax2.set_ylabel('$\log_{10}($|$\Delta R - \Delta R_{o}$|$)$ (mas)', fontsize=18)
plt.tight_layout()
plt.show()
@@ -0,0 +1,251 @@
#!/usr/bin/env Python3
import numpy as np
import sys
from AstroAtmosphere import *
import matplotlib.pyplot as plt
from netCDF4 import Dataset
from pdb import set_trace
from matplotlib import cm
from scipy.interpolate import interp1d, interp2d
from matplotlib.colors import LogNorm
import json
# Saves a variable, preferably a list or string to json text file.
# Useful for long simulations, where you're making a plot
def saveVariableToTXT(variable, filename):
variableFile = open(filename, 'w')
if type(variable) == np.ndarray:
json.dump(variable.tolist(), variableFile)
elif type(variable) in [list, str, float]:
json.dump(variable, variableFile)
else:
print('Not a list, array or string. Check variable type.')
variableFile.close()
return
# Load a variable from a json text file.
def loadVariableFromTXT(filename):
variableFile = open(filename, 'r')
variable = json.load(variableFile)
variableFile.close()
return variable
def calculateDistanceResolution(lat, lon):
# Calculate local radius of the earth
_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)
# Calculate circumference of the earth at constant latitude.
latR = 2 * np.pi * np.cos(_phi) * _rc0
distRes = latR/360 * lon
return distRes
def H_isotherm(T, lat):
'''
Calculates the reduced height of the atmosphere, assuming an isotherm distribution.
'''
_kb = 1.38064852e-23 # kg m2 s-2 K-1
_m = 4.809651698e-26 # kg (weight per molecule of air, assuming dry air)
_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)
return (_kb*T) / (_m * _g0_local)
def calculatePressure(Th, P0, h, lat):
redH = H_isotherm(Th, lat)
Ph = P0 * np.exp(-h/redH)
return Ph
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
# Import the real data
nc = Dataset('air.201806.nc', 'r')
latitudes = np.asarray(nc.variables['lat'][:])
longitudes = np.asarray(nc.variables['lon'][:])
air_temp = nc.variables['air']
air_press = np.asarray(nc.variables['level'])
at = Observatory()
# Parameters at Cerro Armazones
T = air_temp[0,:,0,0][0] # k
P = air_press[0] # Pa
H = 0
xc = 450 # ppm
lat = 24.44768
h = 0 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
rho = at.rho(p=P, T=T, RH=H, xc=xc)
disp = dispersion(lat, h)
disp.setReducedHeight(P, rho)
air_dens = at.rho(p=air_press, T=air_temp[0,:,0,0], RH=np.asarray([0]*len(air_press)), xc=np.asarray([0]*len(air_press)))
heights = disp.calcHeight(air_press, air_temp[0,:,0,0], RH=np.asarray([0]*len(air_press)), xc=np.asarray([0]*len(air_press)), P0=air_press[0]) / 1e3
distance_lat = calculateDistanceResolution(latitudes[100], longitudes[100,:]) - calculateDistanceResolution(latitudes[100,0], longitudes[100,0])
temp_selection_ground = air_temp[0,0,100,:]
temp_selection_upper = air_temp[0,-1,100,:]
print(distance_lat[1])
#########################################################
# Extrapolating using the U.S. Standard Atmosphere 1976.
# The end point of the real data is used to shift the USSA data accordingly (linearly)
h_ussa1976, _, _, _, air_temp_ussa1976, air_press_ussa1976,_,_,_,_ = np.genfromtxt('../US1976 Standard Atmosphere/si1py_modified.txt', unpack=True, skip_header=6)
temp_ip = interp1d(h_ussa1976, air_temp_ussa1976)
press_ip = interp1d(h_ussa1976, air_press_ussa1976)
ussa1976_start = temp_ip(heights[-1])
diff_temp_list = temp_selection_upper - ussa1976_start
start_i = np.argmax(h_ussa1976 > heights[-1])
ussa1976_temp_array = np.tile(air_temp_ussa1976[start_i:], (len(temp_selection_upper),1))
ussa1976_temp_array = np.asarray([ussa1976_temp_array[i] + diff_temp_list[i] for i in range(len(diff_temp_list))]).T
# Combining the (masked) arrays
new_heights = np.append(heights, h_ussa1976[start_i:])
tempData = np.append(air_temp[0,:,100,:], ussa1976_temp_array, axis=0)
tempData_mask = np.append(air_temp[0,:,100,:].mask, np.zeros(ussa1976_temp_array.shape), axis=0)
tempData = np.ma.masked_array(tempData, mask=tempData_mask)
# Now the same for the pressure data
# Combining the (masked) arrays
start_i = np.argmax(h_ussa1976 > heights[-1])
new_heights = np.append(heights, h_ussa1976[start_i:])
pressData = []
for i in range(len(temp_selection_upper)):
calc_press_data = calculatePressure(Th=ussa1976_temp_array[:,i], P0=air_press[0], h=h_ussa1976[start_i:]*1000, lat=lat)
pressData_i = np.append(air_press*100, calc_press_data*100, axis=0)
pressData.append(pressData_i)
pressData = np.asarray(pressData).T
#########################################################
# We'd now like to export a subset of the atmospheric data for a distance of ~200 km.
# Let's use interpolation to create a uniform gridded dataset
export_heights = np.linspace(0,new_heights.max(),400)
export_dist = np.linspace(0,distance_lat[136]-distance_lat[130],1000)
atmosphere_temp_func = interp2d(distance_lat[130:136]-distance_lat[130], new_heights, tempData[:,130:136], kind='linear')
export_temp_atmosphere = atmosphere_temp_func(export_dist, export_heights)
atmosphere_press_func = interp2d(distance_lat[130:136]-distance_lat[130], new_heights, pressData[:,130:136], kind='linear')
export_press_atmosphere = atmosphere_press_func(export_dist, export_heights)
# saveVariableToTXT(export_heights, 'export_heights.txt')
# saveVariableToTXT(export_dist, 'export_dist.txt')
# saveVariableToTXT(export_temp_atmosphere, 'export_temp_atmosphere.txt')
# saveVariableToTXT(export_press_atmosphere, 'export_press_atmosphere.txt')
# Plot the real data + extrapolated standard atmosphere.
# fig = plt.figure()
# ax = fig.add_subplot(211)
# ax2 = fig.add_subplot(212)
# im1 = ax.pcolormesh(export_dist, export_heights, export_temp_atmosphere, cmap=cm.coolwarm)
# im2 = ax2.pcolormesh(export_dist, export_heights, export_press_atmosphere, cmap=cm.coolwarm, norm=LogNorm())
# ax2.set_xlabel('Lateral distance (km)')
# ax.set_ylabel('Height (km)')
# ax2.set_ylabel('Height (km)')
# cbar = fig.colorbar(im1, ax=ax, label='Temperature (K)')
# lbls = cbar.get_ticks()
# cbar.ax.invert_yaxis()
# cbar.set_ticks(lbls[::-1])
# cbar2 = fig.colorbar(im2, ax=ax2, label='Pressure (Pa)')
# lbls = cbar2.get_ticks()
# cbar2.ax.invert_yaxis()
# cbar2.set_ticks(lbls[::-1])
# plt.tight_layout()
# plt.show(block=False)
# set_trace()
# # Plot the real data + extrapolated standard atmosphere.
# fig = plt.figure()
# ax = fig.add_subplot(211)
# ax2 = fig.add_subplot(212)
# im1 = ax.imshow(tempData[:,130:137], cmap=cm.coolwarm, interpolation= 'bicubic', aspect='auto', origin='lower')
# # im1 = ax.pcolormesh(distance_lat[130:137], new_heights, tempData[:,130:137], cmap=cm.coolwarm)
# im2 = ax2.pcolormesh(distance_lat, new_heights, pressData, cmap=cm.coolwarm, norm=LogNorm())
# ax2.set_xlabel('Lateral distance (km)')
# ax.set_ylabel('Height (km)')
# ax2.set_ylabel('Height (km)')
# cbar = fig.colorbar(im1, ax=ax, label='Temperature (K)')
# lbls = cbar.get_ticks()
# cbar.ax.invert_yaxis()
# cbar.set_ticks(lbls[::-1])
# cbar2 = fig.colorbar(im2, ax=ax2, label='Pressure (Pa)')
# lbls = cbar2.get_ticks()
# cbar2.ax.invert_yaxis()
# cbar2.set_ticks(lbls[::-1])
# plt.tight_layout()
# plt.show(block=False)
# Plot the real data and properties
fig = plt.figure(figsize=(9,4))
fig2 = plt.figure()
ax1 = fig2.add_subplot(221)
ax2 = fig2.add_subplot(222)
ax3 = fig2.add_subplot(223)
ax4 = fig.add_subplot(121)
ax5 = fig2.add_subplot(224)
ax6 = fig.add_subplot(122)
ax1.grid(alpha=0.3)
ax2.grid(alpha=0.3)
ax3.grid(alpha=0.3)
ax5.grid(alpha=0.3)
ax1.plot(air_temp[0,:,0,0], heights)
ax1.set_xlabel('Temperature (K)')
ax1.set_ylabel('Height (km)')
ax2.plot(air_press, heights)
ax2.set_xlabel('Pressure (Pa)')
ax2.set_ylabel('Height (km)')
ax3.plot(air_dens, heights)
ax3.set_xlabel('Density (g cm$^{-3}$)')
ax3.set_ylabel('Height (km)')
im = ax4.imshow(np.rot90(air_temp[0,0,:,:].T), aspect=1, cmap='jet')
ax4.set_yticks([])
ax4.set_xticks([])
# fig.colorbar(im, ax=ax4, label='Temperature (K)')
ax5.plot(distance_lat, temp_selection_ground, 'b-', label='ground')
ax5.plot(distance_lat, temp_selection_upper, 'r-', label='upper atmosphere')
ax5.legend()
ax5.set_ylabel('Temperature (K)')
ax5.set_xlabel('Distance (km)')
im2 = ax6.imshow(air_temp[0,:,100,:], aspect='auto', origin='lower', interpolation='bilinear', extent=[0,distance_lat[-1],0,heights[-1] ], cmap='jet')
ax6.set_xlabel('Distance (km)', fontsize=18)
ax6.set_ylabel('Height (km)', fontsize=18)
cbar = fig.colorbar(im2, ax=ax6)#, label='Temperature (K)')
cbar.ax.tick_params(labelsize=16)
cbar.set_label('Temperature (K)', size=18)
lbls = cbar.get_ticks()
cbar.ax.invert_yaxis()
cbar.set_ticks(lbls[::-1])
ax6.tick_params(axis ='both', direction='in', labelsize=16)
plt.tight_layout()
plt.show()
+232
View File
@@ -0,0 +1,232 @@
#!/usr/bin/env python
""" Tables.py - Make tables of atmospheric properties (Python 3)
Adapted by
Richard J. Kwan, Lightsaber Computing
from original programs by
Ralph L. Carmichael, Public Domain Aeronautical Software
Revision History
Date Vers Person Statement of Changes
2004 Oct 04 1.0 RJK Initial program
2017 Jun 04 1.1 RLC All indents are with spaces; all prints in ( )
New version for Python 3 that does integer div with //
"""
import sys, math
version = "1.1 (2017 Jun 04)"
greeting = "Tables - A Python program to compute atmosphere tables"
author = "Ralph L. Carmichael, Public Domain Aeronautical Software"
modifier = "Richard J. Kwan, Lightsaber Computing"
farewell = "Four files added to your directory."
finalmess = "Normal termination of tables."
# P H Y S I C A L C O N S T A N T S
FT2METERS = 0.3048 # mult. ft. to get meters (exact)
KELVIN2RANKINE = 1.8 # mult deg K to get deg R
PSF2NSM = 47.880258 # mult lb/sq.ft to get sq.m
SCF2KCM = 515.379 # mult slugs/cu.ft to get kg/cu.m
TZERO = 288.15 # sea-level temperature, kelvins
PZERO = 101325.0 # sea-level pressure, N/sq.m
RHOZERO = 1.225 # sea-level density, kg/cu.m
AZERO = 340.294 # speed of sound at S.L. m/sec
BETAVISC = 1.458E-6 # viscosity constant
SUTH = 110.4 # Sutherland's constant, kelvins
def LongUSTable():
Itxt = open('us1py.prt', 'w')
Itxt.write(' alt sigma delta theta ')
Itxt.write(' temp press dens a visc k.visc\n')
Itxt.write(' Kft ')
Itxt.write(' degR lb/sq.ft s/cu.ft fps s/ft-s sq.ft/s\n')
for i in range(-1, 57):
altKm=5*i*FT2METERS
(sigma, delta, theta) = Atmosphere(altKm)
Itxt.write("%4i %9.3E %9.3E %6.4f " %
(5*i, sigma, delta, theta))
temp=(KELVIN2RANKINE*TZERO)*theta
pressure=(PZERO/PSF2NSM)*delta
density=(RHOZERO/SCF2KCM)*sigma
asound=(AZERO/FT2METERS)*math.sqrt(theta)
Itxt.write("%5.1f %8.3E %9.3E %6.1f" %
(temp, pressure, density, asound))
viscosity=(1.0/PSF2NSM)*MetricViscosity(theta)
kinematicViscosity=viscosity/density
Itxt.write("%6.3f %8.2E\n" %
(1.0E6*viscosity, kinematicViscosity))
Itxt.close()
def ShortUSTable():
Itxt = open('us2py.prt', 'w')
Itxt.write(' alt sigma delta theta ')
Itxt.write(' temp press dens a visc k.visc ratio\n')
Itxt.write(' Kft ')
Itxt.write(' degR psf s/cu.ft fps s/ft-sec sq.ft/s 1/ft\n')
for i in range(-1, 66):
altKm=i*FT2METERS
(sigma, delta, theta) = SimpleAtmosphere(altKm)
Itxt.write("%4i %6.4f %6.4f %6.4f" %
(i, sigma, delta, theta))
temp=KELVIN2RANKINE*TZERO*theta
pressure=PZERO*delta/47.88
density=RHOZERO*sigma/515.379
asound=(AZERO/FT2METERS)*math.sqrt(theta);
Itxt.write("%6.1f %6.1f %9.7f %6.1f" %
(temp, pressure, density, asound))
viscosity=(1.0/PSF2NSM)*MetricViscosity(theta)
kinematicViscosity=viscosity/density
vratio=asound/kinematicViscosity
Itxt.write("%6.3f %8.2E %4.2f\n"%
(1.0E6*viscosity, kinematicViscosity, 1.0E-6*vratio))
Itxt.close()
def LongSITable():
Itxt = open('si1py.prt', 'w')
Itxt.write(' alt sigma delta theta ')
Itxt.write(' temp press dens a visc k.visc\n')
Itxt.write(' Km ')
Itxt.write(' K N/sq.m kg/cu.m m/sec kg/m-s sq.m/s\n')
for i in range(-4,173):
altKm=0.5*i
(sigma, delta, theta) = Atmosphere(altKm)
Itxt.write("%4.1f %8.4E %8.4E %6.4f" %
(0.5*i, sigma,delta,theta))
temp=TZERO*theta
pressure=PZERO*delta
density=RHOZERO*sigma
asound=AZERO*math.sqrt(theta)
Itxt.write("%6.1f %8.5E %8.3E %5.1f" %
(temp,pressure,density,asound))
viscosity=MetricViscosity(theta)
kinematicViscosity=viscosity/density
Itxt.write("%6.2f %8.2E\n" %
(1.0E6*viscosity, kinematicViscosity))
Itxt.close()
def ShortSITable():
Itxt = open('si2py.prt', 'w')
Itxt.write(' alt sigma delta theta ')
Itxt.write(' temp press dens a visc k.visc ratio\n')
Itxt.write(' Km ')
Itxt.write(' K N/sq.m kcm m/sec kg/m-s sq.m/s 1/m\n')
for i in range(-1, 41):
altKm=0.5*i
(sigma, delta, theta) = SimpleAtmosphere(altKm)
Itxt.write("%4.1f %6.4f %6.4f %6.4f" %
(altKm, sigma, delta, theta))
temp=TZERO*theta
pressure=PZERO*delta
density=RHOZERO*sigma
asound=AZERO*math.sqrt(theta)
Itxt.write("%6.1f %6.0f %5.3f %5.1f" %
(temp, pressure,density,asound))
viscosity=MetricViscosity(theta)
kinematicViscosity=viscosity/density
vratio=asound/kinematicViscosity
Itxt.write("%6.2f %8.2E %5.2f\n" %
(1.0E6*viscosity, kinematicViscosity, 1.0E-6*vratio))
Itxt.close()
def Atmosphere(alt):
""" Compute temperature, density, and pressure in standard atmosphere.
Correct to 86 km. Only approximate thereafter.
Input:
alt geometric altitude, km.
Return: (sigma, delta, theta)
sigma density/sea-level standard density
delta pressure/sea-level standard pressure
theta temperature/sea-level std. temperature
"""
REARTH = 6369.0 # radius of the Earth (km)
GMR = 34.163195
NTAB = 8 # length of tables
htab = [ 0.0, 11.0, 20.0, 32.0, 47.0,
51.0, 71.0, 84.852 ]
ttab = [ 288.15, 216.65, 216.65, 228.65, 270.65,
270.65, 214.65, 186.946 ]
ptab = [ 1.0, 2.2336110E-1, 5.4032950E-2, 8.5666784E-3, 1.0945601E-3,
6.6063531E-4, 3.9046834E-5, 3.68501E-6 ]
gtab = [ -6.5, 0.0, 1.0, 2.8, 0, -2.8, -2.0, 0.0 ]
h = alt*REARTH/(alt+REARTH) # geometric to geopotential altitude
i=0; j=len(htab)-1
while (j > i+1):
k = (i+j)//2 # this is floor division in Python 3
if h < htab[k]:
j = k
else:
i = k
tgrad = gtab[i] # temp. gradient of local layer
tbase = ttab[i] # base temp. of local layer
deltah=h-htab[i] # height above local base
tlocal=tbase+tgrad*deltah # local temperature
theta = tlocal/ttab[0] # temperature ratio
if 0.0 == tgrad:
delta=ptab[i]*math.exp(-GMR*deltah/tbase)
else:
delta=ptab[i]*math.pow(tbase/tlocal, GMR/tgrad)
sigma = delta/theta
return ( sigma, delta, theta )
def SimpleAtmosphere(alt):
""" Compute temperature, density, and pressure in simplified
standard atmosphere.
Correct to 20 km. Only approximate thereafter.
Input:
alt geometric altitude, km.
Return: (sigma, delta, theta)
sigma density/sea-level standard density
delta pressure/sea-level standard pressure
theta temperature/sea-level std. temperature
"""
REARTH = 6369.0 # radius of the Earth (km)
GMR = 34.163195 # gas constant
h = alt*REARTH/(alt+REARTH) # geometric to geopotential altitude
if h<11.0: # troposphere
theta=(288.15-6.5*h)/288.15
delta=math.pow(theta, GMR/6.5)
else: # stratosphere
theta=216.65/288.15
delta=0.2233611*math.exp(-GMR*(h-11.0)/216.65)
sigma = delta/theta
return ( sigma, delta, theta )
def MetricViscosity(theta):
t=theta*TZERO
return BETAVISC*math.sqrt(t*t*t)/(t+SUTH)
def main():
print ("Executing ", sys.argv[0])
print (greeting)
print (author)
if modifier != "":
print ("Modified by ", modifier)
print (" version ", version)
LongUSTable()
ShortUSTable()
LongSITable()
ShortSITable()
print (farewell)
print (finalmess)
main()
+242
View File
@@ -0,0 +1,242 @@
#!/usr/bin/env Python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib import cm
from matplotlib.colors import LogNorm
import sys
from AstroAtmosphere import *
from scipy import interpolate
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: This script uses the US Standard Atmosphere and perturbs it
at several points. Then full integration is used to calculate
the atmospheric dispersion. This is then compared to when we
would have only used the atmospheric conditions directly at
the observatory. This is done to assess whether we should
monitor the weather at every height along the line of sight of
the telescope.
'''
def perturbAtmosphere(h, Temp_perturbed, Press_perturbed):
for _i in range(len(h)):
if h[_i] >= 1.0 and h[_i] <= 2.0:
Tnoise = np.random.normal(0,5,1)
Temp_perturbed[_i] = Temp_perturbed[_i] + Tnoise
Pnoise = np.random.normal(0,1000,1)
Press_perturbed[_i] = Press_perturbed[_i] + Pnoise
if h[_i] >= 7.0 and h[_i] <= 10.0:
Tnoise = np.random.normal(0,5,1)
Temp_perturbed[_i] = Temp_perturbed[_i] + Tnoise
Pnoise = np.random.normal(0,500,1)
Press_perturbed[_i] = Press_perturbed[_i] + Pnoise
if h[_i] >= 25.0 and h[_i] <= 30.0:
Tnoise = np.random.normal(0,10,1)
Temp_perturbed[_i] = Temp_perturbed[_i] + Tnoise
Pnoise = np.random.normal(0,250,1)
Press_perturbed[_i] = Press_perturbed[_i] + Pnoise
if h[_i] >= 50.0 and h[_i] <= 60.0:
Tnoise = np.random.normal(0,15,1)
Temp_perturbed[_i] = Temp_perturbed[_i] + Tnoise
Pnoise = np.random.normal(0,4,1)
Press_perturbed[_i] = Press_perturbed[_i] + Pnoise
return Temp_perturbed, Press_perturbed
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
h, _, _, _, Temp, Press, Dens, vSound, _,_ = np.genfromtxt('si1py_modified.txt', unpack=True, skip_header=6)
# Conditions at Cerro Armazones
T = 288.15 # k
P = 101324 # Pa
H = 0.0
xc = 314 # ppm
lat = -24.5983 # degrees
h0 = 0 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 60 # degrees
# Calculate the index of refraction
at = Observatory()
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)
rho = at.rho(p=P, T=T, RH=H, xc=xc)
refr = refraction(lat, h0)
refr.setReducedHeight(P, rho)
disp = dispersion(lat, h0)
disp.setReducedHeight(P, rho)
# We must create a cube of weather data:
# We assume a flat atmosphere for now, so that h does not need to be adjusted
# x_max = 200 km, gives zenith_max = ~66.7 degrees
# Every 20 km in horizontal direction a new perturbation and interpolation
# for every 500 m.
x_max = 200 # km
Temp_cube = []
Press_cube = []
Temp_cube_n = []
Press_cube_n = []
Temp_cube.append(Temp)
Press_cube.append(Press)
Temp_cube_n.append(Temp)
Press_cube_n.append(Press)
for x in range(int(x_max/(2))):
# Adding noise
# 1-2 km: 2 degree noise, 100 Pa noise
# 7-10 km: 3 degree noise, 150 Pa noise
# 25-30 km: 5 degree noise, 250 Pa noise
Temp_perturbed = Temp.copy()
Press_perturbed = Press.copy()
Temp_perturbed, Press_perturbed = perturbAtmosphere(h, Temp_perturbed, Press_perturbed)
Temp_cube.append(Temp_perturbed)
Press_cube.append(Press_perturbed)
Temp_cube_n.append(Temp)
Press_cube_n.append(Press)
f_Temp_n = interpolate.interp1d(np.linspace(0,x_max, int(x_max/2)+1), Temp_cube_n, axis=0)
f_Press_n = interpolate.interp1d(np.linspace(0,x_max, int(x_max/2)+1), Press_cube_n, axis=0)
f_Temp = interpolate.interp1d(np.linspace(0,x_max, int(x_max/2)+1), Temp_cube, axis=0)
f_Press = interpolate.interp1d(np.linspace(0,x_max, int(x_max/2)+1), Press_cube, axis=0)
Temp_interpolated_n = f_Temp_n(np.linspace(0,x_max, int(x_max/0.5)+1) )
Press_interpolated_n = f_Press_n(np.linspace(0,x_max, int(x_max/0.5)+1) )
Temp_interpolated = f_Temp(np.linspace(0,x_max, int(x_max/0.5)+1) )
Press_interpolated = f_Press(np.linspace(0,x_max, int(x_max/0.5)+1) )
x, y = np.mgrid[0:200:(int(x_max/0.5)+1)*1j, 0:np.max(h):len(h)*1j]
fig = plt.figure()
ax1 = fig.add_subplot(211)
im = ax1.pcolor(x,y, Temp_interpolated, cmap=cm.coolwarm)
fig.colorbar(im, ax=ax1, label='Temperature (K)')
ax1.set_aspect(1)
ax1.set_ylabel('Height (km)')
ax2 = fig.add_subplot(212)
im2 = ax2.pcolor(x,y, Press_interpolated, cmap=cm.coolwarm, norm=LogNorm())
ax2.set_aspect(1)
ax2.set_xlabel('Horizontal distance (km)')
ax2.set_ylabel('Height (km)')
fig.colorbar(im2, ax=ax2, label='Pressure (Pa)')
plt.tight_layout()
plt.savefig('2d_atmosphere.png', bbox_inches='tight')
plt.savefig('2d_atmosphere.pdf', bbox_inches='tight')
plt.show(block=False)
# Now for a given zenith angle we need to find the temp and press along the line of sight
x = np.linspace(0,200,(int(x_max/0.5)+1))
y = h
f_Temp_2d_n = interpolate.interp2d(y,x,Temp_interpolated_n, kind='linear')
f_Press_2d_n = interpolate.interp2d(y,x,Press_interpolated_n, kind='linear')
f_Temp_2d = interpolate.interp2d(y,x,Temp_interpolated, kind='linear')
f_Press_2d = interpolate.interp2d(y,x,Press_interpolated, kind='linear')
fig2 = plt.figure()
ax = fig2.add_subplot(121)
ax.grid(True, alpha=0.3)
ax.plot(Temp, h, 'k-')
ax2 = fig2.add_subplot(122)
ax2.grid(True, alpha=0.3)
ax2.plot(Press, h, 'k-')
zenithRange = np.linspace(30,65,100)
refrDiff, dispDiff = [], []
for zenith in zenithRange:
z = np.deg2rad(zenith)
y_los = np.linspace(0, 86, len(h))
x_los = np.linspace(0, 86*np.tan(z), len(h))
Temp_los, Press_los = [], []
Temp_los_n, Press_los_n = [], []
for i in range(len(h)):
Temp_los.append(f_Temp_2d(y_los[i], x_los[i])[0])
Press_los.append(f_Press_2d(y_los[i], x_los[i])[0])
Temp_los_n.append(f_Temp_2d_n(y_los[i], x_los[i])[0])
Press_los_n.append(f_Press_2d_n(y_los[i], x_los[i])[0])
Temp_los = np.asarray(Temp_los)
Press_los = np.asarray(Press_los)
Temp_los_n = np.asarray(Temp_los_n)
Press_los_n = np.asarray(Press_los_n)
rho_los = at.rho(p=Press_los, T=Temp_los, RH=H, xc=xc)
rho_los_n = at.rho(p=Press_los_n, T=Temp_los_n, RH=H, xc=xc)
ax.plot(Temp_los, h, label='z=%i' %zenith)
ax2.plot(Press_los, h, label='z=%i' %zenith)
refrInt = refr.refractionIntegral(n1, z, useStandardAtmosphere=False, heightData=h, rhoData=rho_los_n)*3600*1000
refrInt_pert = refr.refractionIntegral(n1, z, useStandardAtmosphere=False, heightData=h, rhoData=rho_los)*3600*1000
refrDiff.append(refrInt_pert - refrInt)
dispInt = disp.refractionIntegral(n1, n2, z, useStandardAtmosphere=False, heightData=h, rhoData=rho_los_n)*3.6e6
dispInt_pert = disp.refractionIntegral(n1, n2, z, useStandardAtmosphere=False, heightData=h, rhoData=rho_los)*3.6e6
dispDiff.append(dispInt_pert - dispInt)
ax.legend()
ax2.legend()
# fig3 = plt.figure()
# ax1 = fig3.add_subplot(211)
# ax2 = fig3.add_subplot(212)
# ax1.tick_params(axis ='both', direction='in')
# ax2.tick_params(axis ='both', direction='in')
# ax1.grid(True, alpha=0.3)
# ax2.grid(True, alpha=0.3)
# ax1.semilogy(zenithRange, np.abs(refrDiff), 'k-')
# ax2.semilogy(zenithRange, np.abs(dispDiff), 'k-')
# ax1.set_xlabel('Zenith (degrees)')
# ax2.set_xlabel('Zenith (degrees)')
# ax1.set_ylabel('|$R_{p} - R_{SA}$| (mas)')
# ax2.set_ylabel('|$\Delta R_{p} - \Delta R_{SA}$| (mas)')
# plt.tight_layout()
# plt.savefig('differential_USSA_for_different_z.png', bbox_inches='tight')
# plt.savefig('differential_USSA_for_different_z.pdf', bbox_inches='tight')
# plt.show()
fig3 = plt.figure(figsize=(9,7))
ax1 = fig3.add_subplot(211)
ax2 = fig3.add_subplot(212)
ax1.tick_params(axis ='both', direction='in', which='both', labelsize=16)
ax2.tick_params(axis ='both', direction='in', which='both', labelsize=16)
ax1.grid(True, alpha=0.3)
ax2.grid(True, alpha=0.3)
ax1.plot(zenithRange, np.log10(np.abs(refrDiff)), 'k-', linewidth=2)
ax2.plot(zenithRange, np.log10(np.abs(dispDiff)), 'k-', linewidth=2)
ax1.set_xlabel('Zenith (degrees)', fontsize=18)
ax2.set_xlabel('Zenith (degrees)', fontsize=18)
ax1.set_ylabel('$\log_{10}($|$R - R_{o}$|$)$ (mas)', fontsize=18)
ax2.set_ylabel('$\log_{10}($|$\Delta R - \Delta R_{o}$|$)$ (mas)', fontsize=18)
plt.tight_layout()
# plt.savefig('differential_USSA_for_different_z.png', bbox_inches='tight')
# plt.savefig('differential_USSA_for_different_z.pdf', bbox_inches='tight')
plt.show()
# Comparison of different air columns.
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env Python3
import numpy as np
from pdb import set_trace
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import sys
from AstroAtmosphere import *
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: We plot the total refraction as a function of height.
We find that most refraction happens in the lower atmosphere
i.e. <10 km.
'''
def refractionIntegral2(n, R, z0, n0, R0):
z0 = np.deg2rad(z0)
return (n0 * R0 * np.sin(z0))/(n*np.sqrt((n*R)**2 - (n0 * R0 * np.sin(z0))**2 ))
def n_for_heights(n0, z0, R0=None, useStandardAtmosphere=True, heightData=None, rhoData=None):
'''
Using data that gives temperature, pressure and density as a function of height,
the gladstone-dale relation is invoked to calculate the path of a light ray
along the atmosphere. This is allows us to also include atmospheric activity in
different atmosphere layers.
Recommended use with the US Standard Atmosphere from 1976.
USSA1976 data generated from code supplied by http://www.pdas.com/atmos.html
'''
if useStandardAtmosphere:
_h, _, _, _, _Temp, _Press, _Dens,_,_,_ = np.genfromtxt('si1py_modified.txt', unpack=True, skip_header=6)
else:
_Dens = rhoData
_h = heightData
z0 = np.deg2rad(z0)
_Dens = np.asarray(_Dens)
_h = np.asarray(_h)
_R = R0 + _h * 1000
# Gladstone-Dale relation between refractive index and density
_n_h = 1 + (n0 - 1) / _Dens[0] * _Dens
_z_h = np.arcsin( (n0 * R0) / (_n_h * _R) * np.sin(z0) )
return _h, _n_h, np.degrees(_z_h)
h, _, _, _, Temp, Press, Dens, vSound, _,_ = np.genfromtxt('si1py_modified.txt', unpack=True, skip_header=6)
# Conditions
T = 288.15 # k
P = 101324 # Pa
H = 0.0
xc = 314 # ppm
lat = -24.5983 # degrees
h0 = 0 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 60 # degrees
# Calculate the index of refraction
at = Observatory()
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)
rho = at.rho(p=P, T=T, RH=H, xc=xc)
refr = refraction(lat, h0)
refr.setReducedHeight(P, rho)
disp = dispersion(lat, h0)
disp.setReducedHeight(P, rho)
fig = plt.figure(figsize=(5,4))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.set_xlabel('Height (km)')
ax.set_ylabel('Normalized cumulative refraction')
ax.set_xlim([0, h[-1]])
for z in [30]:
refList = []
dispList = []
for i in range(len(h)):
refList.append(refr.refractionIntegral(n1, z, useStandardAtmosphere=False, heightData=h[:i+1], rhoData=Dens[:i+1])*3600)
dispList.append(disp.refractionIntegral(n1, n2, z, useStandardAtmosphere=False, heightData=h[:i+1], rhoData=Dens[:i+1])*3.6e6)
ax.plot(h, np.asarray(refList)/refList[-1], 'k-', label='$\lambda=1.49$ $\mu$m, z=%i$^{\\circ}$' %z)
ax.set_ylim([0,1.1])
plt.tight_layout()
plt.show()
+179
View File
@@ -0,0 +1,179 @@
alt sigma delta theta temp press dens a visc k.visc
Km K N/sq.m kg/cu.m m/sec kg/m-s sq.m/s
-2.0 1.2067E+00 1.2611E+00 1.0451 301.2 1.27783E+05 1.478E+00 347.9 18.51 1.25E-05
-1.5 1.1522E+00 1.1912E+00 1.0338 297.9 1.20696E+05 1.411E+00 346.0 18.36 1.30E-05
-1.0 1.0996E+00 1.1244E+00 1.0226 294.7 1.13931E+05 1.347E+00 344.1 18.21 1.35E-05
-0.5 1.0489E+00 1.0607E+00 1.0113 291.4 1.07478E+05 1.285E+00 342.2 18.05 1.40E-05
0.0 1.0000E+00 1.0000E+00 1.0000 288.1 1.01325E+05 1.225E+00 340.3 17.89 1.46E-05
0.5 9.5288E-01 9.4213E-01 0.9887 284.9 9.54613E+04 1.167E+00 338.4 17.74 1.52E-05
1.0 9.0748E-01 8.8701E-01 0.9774 281.7 8.98763E+04 1.112E+00 336.4 17.58 1.58E-05
1.5 8.6376E-01 8.3454E-01 0.9662 278.4 8.45597E+04 1.058E+00 334.5 17.42 1.65E-05
2.0 8.2168E-01 7.8462E-01 0.9549 275.2 7.95014E+04 1.007E+00 332.5 17.26 1.71E-05
2.5 7.8119E-01 7.3715E-01 0.9436 271.9 7.46917E+04 9.570E-01 330.6 17.10 1.79E-05
3.0 7.4225E-01 6.9204E-01 0.9324 268.7 7.01211E+04 9.093E-01 328.6 16.94 1.86E-05
3.5 7.0482E-01 6.4920E-01 0.9211 265.4 6.57804E+04 8.634E-01 326.6 16.78 1.94E-05
4.0 6.6885E-01 6.0854E-01 0.9098 262.2 6.16604E+04 8.193E-01 324.6 16.61 2.03E-05
4.5 6.3432E-01 5.6997E-01 0.8986 258.9 5.77525E+04 7.770E-01 322.6 16.45 2.12E-05
5.0 6.0117E-01 5.3341E-01 0.8873 255.7 5.40482E+04 7.364E-01 320.5 16.28 2.21E-05
5.5 5.6936E-01 4.9878E-01 0.8760 252.4 5.05393E+04 6.975E-01 318.5 16.12 2.31E-05
6.0 5.3887E-01 4.6600E-01 0.8648 249.2 4.72176E+04 6.601E-01 316.5 15.95 2.42E-05
6.5 5.0964E-01 4.3499E-01 0.8535 245.9 4.40754E+04 6.243E-01 314.4 15.78 2.53E-05
7.0 4.8165E-01 4.0568E-01 0.8423 242.7 4.11052E+04 5.900E-01 312.3 15.61 2.65E-05
7.5 4.5485E-01 3.7799E-01 0.8310 239.5 3.82996E+04 5.572E-01 310.2 15.44 2.77E-05
8.0 4.2921E-01 3.5185E-01 0.8198 236.2 3.56515E+04 5.258E-01 308.1 15.27 2.90E-05
8.5 4.0470E-01 3.2721E-01 0.8085 233.0 3.31541E+04 4.958E-01 306.0 15.10 3.05E-05
9.0 3.8128E-01 3.0398E-01 0.7973 229.7 3.08006E+04 4.671E-01 303.8 14.93 3.20E-05
9.5 3.5891E-01 2.8211E-01 0.7860 226.5 2.85846E+04 4.397E-01 301.7 14.75 3.36E-05
10.0 3.3756E-01 2.6153E-01 0.7748 223.3 2.64998E+04 4.135E-01 299.5 14.58 3.53E-05
10.5 3.1720E-01 2.4219E-01 0.7635 220.0 2.45401E+04 3.886E-01 297.4 14.40 3.71E-05
11.0 2.9780E-01 2.2403E-01 0.7523 216.8 2.26998E+04 3.648E-01 295.2 14.22 3.90E-05
11.5 2.7545E-01 2.0710E-01 0.7519 216.7 2.09847E+04 3.374E-01 295.1 14.22 4.21E-05
12.0 2.5464E-01 1.9146E-01 0.7519 216.7 1.93993E+04 3.119E-01 295.1 14.22 4.56E-05
12.5 2.3541E-01 1.7699E-01 0.7519 216.7 1.79340E+04 2.884E-01 295.1 14.22 4.93E-05
13.0 2.1763E-01 1.6363E-01 0.7519 216.7 1.65795E+04 2.666E-01 295.1 14.22 5.33E-05
13.5 2.0119E-01 1.5127E-01 0.7519 216.7 1.53275E+04 2.465E-01 295.1 14.22 5.77E-05
14.0 1.8600E-01 1.3985E-01 0.7519 216.7 1.41702E+04 2.279E-01 295.1 14.22 6.24E-05
14.5 1.7196E-01 1.2929E-01 0.7519 216.7 1.31005E+04 2.107E-01 295.1 14.22 6.75E-05
15.0 1.5898E-01 1.1953E-01 0.7519 216.7 1.21117E+04 1.948E-01 295.1 14.22 7.30E-05
15.5 1.4698E-01 1.1051E-01 0.7519 216.7 1.11976E+04 1.801E-01 295.1 14.22 7.90E-05
16.0 1.3589E-01 1.0217E-01 0.7519 216.7 1.03527E+04 1.665E-01 295.1 14.22 8.54E-05
16.5 1.2564E-01 9.4465E-02 0.7519 216.7 9.57164E+03 1.539E-01 295.1 14.22 9.24E-05
17.0 1.1616E-01 8.7339E-02 0.7519 216.7 8.84961E+03 1.423E-01 295.1 14.22 9.99E-05
17.5 1.0740E-01 8.0752E-02 0.7519 216.7 8.18215E+03 1.316E-01 295.1 14.22 1.08E-04
18.0 9.9302E-02 7.4662E-02 0.7519 216.7 7.56512E+03 1.216E-01 295.1 14.22 1.17E-04
18.5 9.1815E-02 6.9032E-02 0.7519 216.7 6.99471E+03 1.125E-01 295.1 14.22 1.26E-04
19.0 8.4893E-02 6.3828E-02 0.7519 216.7 6.46738E+03 1.040E-01 295.1 14.22 1.37E-04
19.5 7.8494E-02 5.9017E-02 0.7519 216.7 5.97989E+03 9.616E-02 295.1 14.22 1.48E-04
20.0 7.2578E-02 5.4569E-02 0.7519 216.7 5.52921E+03 8.891E-02 295.1 14.22 1.60E-04
20.5 6.6979E-02 5.0460E-02 0.7534 217.1 5.11291E+03 8.205E-02 295.4 14.24 1.74E-04
21.0 6.1807E-02 4.6670E-02 0.7551 217.6 4.72884E+03 7.571E-02 295.7 14.27 1.88E-04
21.5 5.7045E-02 4.3173E-02 0.7568 218.1 4.37446E+03 6.988E-02 296.0 14.29 2.05E-04
22.0 5.2660E-02 3.9945E-02 0.7585 218.6 4.04741E+03 6.451E-02 296.4 14.32 2.22E-04
22.5 4.8622E-02 3.6965E-02 0.7603 219.1 3.74551E+03 5.956E-02 296.7 14.35 2.41E-04
23.0 4.4902E-02 3.4214E-02 0.7620 219.6 3.46678E+03 5.500E-02 297.0 14.38 2.61E-04
23.5 4.1474E-02 3.1674E-02 0.7637 220.1 3.20939E+03 5.081E-02 297.4 14.40 2.83E-04
24.0 3.8316E-02 2.9328E-02 0.7654 220.6 2.97167E+03 4.694E-02 297.7 14.43 3.07E-04
24.5 3.5404E-02 2.7161E-02 0.7672 221.1 2.75206E+03 4.337E-02 298.1 14.46 3.33E-04
25.0 3.2721E-02 2.5158E-02 0.7689 221.6 2.54915E+03 4.008E-02 298.4 14.48 3.61E-04
25.5 3.0246E-02 2.3308E-02 0.7706 222.0 2.36163E+03 3.705E-02 298.7 14.51 3.92E-04
26.0 2.7964E-02 2.1597E-02 0.7723 222.5 2.18831E+03 3.426E-02 299.1 14.54 4.24E-04
26.5 2.5858E-02 2.0016E-02 0.7740 223.0 2.02807E+03 3.168E-02 299.4 14.57 4.60E-04
27.0 2.3916E-02 1.8553E-02 0.7758 223.5 1.87991E+03 2.930E-02 299.7 14.59 4.98E-04
27.5 2.2124E-02 1.7201E-02 0.7775 224.0 1.74289E+03 2.710E-02 300.1 14.62 5.39E-04
28.0 2.0470E-02 1.5950E-02 0.7792 224.5 1.61614E+03 2.508E-02 300.4 14.65 5.84E-04
28.5 1.8943E-02 1.4793E-02 0.7809 225.0 1.49888E+03 2.320E-02 300.7 14.67 6.32E-04
29.0 1.7533E-02 1.3722E-02 0.7826 225.5 1.39037E+03 2.148E-02 301.0 14.70 6.84E-04
29.5 1.6231E-02 1.2731E-02 0.7844 226.0 1.28994E+03 1.988E-02 301.4 14.73 7.41E-04
30.0 1.5028E-02 1.1813E-02 0.7861 226.5 1.19698E+03 1.841E-02 301.7 14.75 8.01E-04
30.5 1.3917E-02 1.0964E-02 0.7878 227.0 1.11092E+03 1.705E-02 302.0 14.78 8.67E-04
31.0 1.2891E-02 1.0177E-02 0.7895 227.5 1.03122E+03 1.579E-02 302.4 14.81 9.38E-04
31.5 1.1942E-02 9.4488E-03 0.7912 228.0 9.57402E+02 1.463E-02 302.7 14.83 1.01E-03
32.0 1.1065E-02 8.7740E-03 0.7930 228.5 8.89024E+02 1.355E-02 303.0 14.86 1.10E-03
32.5 1.0228E-02 8.1493E-03 0.7968 229.6 8.25726E+02 1.253E-02 303.8 14.92 1.19E-03
33.0 9.4469E-03 7.5724E-03 0.8016 231.0 7.67272E+02 1.157E-02 304.7 14.99 1.30E-03
33.5 8.7297E-03 7.0395E-03 0.8064 232.4 7.13279E+02 1.069E-02 305.6 15.07 1.41E-03
34.0 8.0709E-03 6.5470E-03 0.8112 233.7 6.63380E+02 9.887E-03 306.5 15.14 1.53E-03
34.5 7.4654E-03 6.0917E-03 0.8160 235.1 6.17243E+02 9.145E-03 307.4 15.21 1.66E-03
35.0 6.9085E-03 5.6705E-03 0.8208 236.5 5.74564E+02 8.463E-03 308.3 15.29 1.81E-03
35.5 6.3961E-03 5.2807E-03 0.8256 237.9 5.35066E+02 7.835E-03 309.2 15.36 1.96E-03
36.0 5.9245E-03 4.9198E-03 0.8304 239.3 4.98495E+02 7.257E-03 310.1 15.43 2.13E-03
36.5 5.4901E-03 4.5854E-03 0.8352 240.7 4.64618E+02 6.725E-03 311.0 15.51 2.31E-03
37.0 5.0899E-03 4.2756E-03 0.8400 242.1 4.33223E+02 6.235E-03 311.9 15.58 2.50E-03
37.5 4.7209E-03 3.9883E-03 0.8448 243.4 4.04115E+02 5.783E-03 312.8 15.65 2.71E-03
38.0 4.3806E-03 3.7218E-03 0.8496 244.8 3.77116E+02 5.366E-03 313.7 15.72 2.93E-03
38.5 4.0666E-03 3.4746E-03 0.8544 246.2 3.52061E+02 4.982E-03 314.6 15.79 3.17E-03
39.0 3.7767E-03 3.2450E-03 0.8592 247.6 3.28801E+02 4.626E-03 315.4 15.87 3.43E-03
39.5 3.5089E-03 3.0318E-03 0.8640 249.0 3.07198E+02 4.298E-03 316.3 15.94 3.71E-03
40.0 3.2616E-03 2.8337E-03 0.8688 250.4 2.87125E+02 3.995E-03 317.2 16.01 4.01E-03
40.5 3.0329E-03 2.6496E-03 0.8736 251.7 2.68467E+02 3.715E-03 318.1 16.08 4.33E-03
41.0 2.8214E-03 2.4783E-03 0.8784 253.1 2.51116E+02 3.456E-03 318.9 16.15 4.67E-03
41.5 2.6257E-03 2.3190E-03 0.8832 254.5 2.34974E+02 3.216E-03 319.8 16.22 5.04E-03
42.0 2.4445E-03 2.1708E-03 0.8880 255.9 2.19952E+02 2.995E-03 320.7 16.29 5.44E-03
42.5 2.2768E-03 2.0327E-03 0.8928 257.3 2.05965E+02 2.789E-03 321.5 16.36 5.87E-03
43.0 2.1214E-03 1.9041E-03 0.8976 258.6 1.92937E+02 2.599E-03 322.4 16.43 6.32E-03
43.5 1.9774E-03 1.7843E-03 0.9024 260.0 1.80799E+02 2.422E-03 323.3 16.50 6.81E-03
44.0 1.8438E-03 1.6727E-03 0.9072 261.4 1.69484E+02 2.259E-03 324.1 16.57 7.34E-03
44.5 1.7199E-03 1.5685E-03 0.9120 262.8 1.58932E+02 2.107E-03 325.0 16.64 7.90E-03
45.0 1.6050E-03 1.4714E-03 0.9168 264.2 1.49090E+02 1.966E-03 325.8 16.71 8.50E-03
45.5 1.4983E-03 1.3808E-03 0.9216 265.5 1.39905E+02 1.835E-03 326.7 16.78 9.14E-03
46.0 1.3992E-03 1.2961E-03 0.9263 266.9 1.31330E+02 1.714E-03 327.5 16.85 9.83E-03
46.5 1.3071E-03 1.2171E-03 0.9311 268.3 1.23322E+02 1.601E-03 328.4 16.92 1.06E-02
47.0 1.2215E-03 1.1433E-03 0.9359 269.7 1.15841E+02 1.496E-03 329.2 16.99 1.14E-02
47.5 1.1437E-03 1.0743E-03 0.9393 270.6 1.08849E+02 1.401E-03 329.8 17.04 1.22E-02
48.0 1.0748E-03 1.0095E-03 0.9393 270.6 1.02287E+02 1.317E-03 329.8 17.04 1.29E-02
48.5 1.0100E-03 9.4865E-04 0.9393 270.6 9.61215E+01 1.237E-03 329.8 17.04 1.38E-02
49.0 9.4912E-04 8.9147E-04 0.9393 270.6 9.03286E+01 1.163E-03 329.8 17.04 1.47E-02
49.5 8.9193E-04 8.3776E-04 0.9393 270.6 8.48858E+01 1.093E-03 329.8 17.04 1.56E-02
50.0 8.3819E-04 7.8728E-04 0.9393 270.6 7.97716E+01 1.027E-03 329.8 17.04 1.66E-02
50.5 7.8770E-04 7.3986E-04 0.9393 270.6 7.49663E+01 9.649E-04 329.8 17.04 1.77E-02
51.0 7.4026E-04 6.9530E-04 0.9393 270.6 7.04511E+01 9.068E-04 329.8 17.04 1.88E-02
51.5 6.9630E-04 6.5342E-04 0.9384 270.4 6.62082E+01 8.530E-04 329.7 17.02 2.00E-02
52.0 6.5758E-04 6.1395E-04 0.9336 269.0 6.22084E+01 8.055E-04 328.8 16.96 2.10E-02
52.5 6.2085E-04 5.7668E-04 0.9289 267.7 5.84321E+01 7.605E-04 328.0 16.89 2.22E-02
53.0 5.8599E-04 5.4150E-04 0.9241 266.3 5.48678E+01 7.178E-04 327.1 16.82 2.34E-02
53.5 5.5293E-04 5.0831E-04 0.9193 264.9 5.15047E+01 6.773E-04 326.3 16.75 2.47E-02
54.0 5.2158E-04 4.7700E-04 0.9145 263.5 4.83322E+01 6.389E-04 325.4 16.68 2.61E-02
54.5 4.9187E-04 4.4748E-04 0.9098 262.1 4.53405E+01 6.025E-04 324.6 16.61 2.76E-02
55.0 4.6370E-04 4.1964E-04 0.9050 260.8 4.25201E+01 5.680E-04 323.7 16.54 2.91E-02
55.5 4.3702E-04 3.9341E-04 0.9002 259.4 3.98620E+01 5.354E-04 322.9 16.47 3.08E-02
56.0 4.1175E-04 3.6869E-04 0.8954 258.0 3.73576E+01 5.044E-04 322.0 16.40 3.25E-02
56.5 3.8782E-04 3.4541E-04 0.8907 256.6 3.49988E+01 4.751E-04 321.2 16.33 3.44E-02
57.0 3.6516E-04 3.2349E-04 0.8859 255.3 3.27778E+01 4.473E-04 320.3 16.26 3.64E-02
57.5 3.4373E-04 3.0286E-04 0.8811 253.9 3.06871E+01 4.211E-04 319.4 16.19 3.85E-02
58.0 3.2344E-04 2.8344E-04 0.8763 252.5 2.87198E+01 3.962E-04 318.6 16.12 4.07E-02
58.5 3.0426E-04 2.6518E-04 0.8716 251.1 2.68692E+01 3.727E-04 317.7 16.05 4.31E-02
59.0 2.8612E-04 2.4800E-04 0.8668 249.8 2.51289E+01 3.505E-04 316.8 15.98 4.56E-02
59.5 2.6897E-04 2.3186E-04 0.8620 248.4 2.34929E+01 3.295E-04 315.9 15.91 4.83E-02
60.0 2.5276E-04 2.1668E-04 0.8573 247.0 2.19554E+01 3.096E-04 315.1 15.84 5.11E-02
60.5 2.3746E-04 2.0243E-04 0.8525 245.6 2.05110E+01 2.909E-04 314.2 15.77 5.42E-02
61.0 2.2300E-04 1.8904E-04 0.8477 244.3 1.91546E+01 2.732E-04 313.3 15.69 5.75E-02
61.5 2.0935E-04 1.7647E-04 0.8430 242.9 1.78811E+01 2.565E-04 312.4 15.62 6.09E-02
62.0 1.9647E-04 1.6468E-04 0.8382 241.5 1.66860E+01 2.407E-04 311.5 15.55 6.46E-02
62.5 1.8431E-04 1.5361E-04 0.8334 240.2 1.55648E+01 2.258E-04 310.7 15.48 6.86E-02
63.0 1.7285E-04 1.4323E-04 0.8287 238.8 1.45132E+01 2.117E-04 309.8 15.41 7.28E-02
63.5 1.6204E-04 1.3351E-04 0.8239 237.4 1.35275E+01 1.985E-04 308.9 15.33 7.72E-02
64.0 1.5185E-04 1.2439E-04 0.8191 236.0 1.26036E+01 1.860E-04 308.0 15.26 8.20E-02
64.5 1.4225E-04 1.1585E-04 0.8144 234.7 1.17382E+01 1.743E-04 307.1 15.19 8.72E-02
65.0 1.3321E-04 1.0785E-04 0.8096 233.3 1.09277E+01 1.632E-04 306.2 15.12 9.26E-02
65.5 1.2470E-04 1.0036E-04 0.8048 231.9 1.01690E+01 1.528E-04 305.3 15.04 9.85E-02
66.0 1.1668E-04 9.3354E-05 0.8001 230.5 9.45909E+00 1.429E-04 304.4 14.97 1.05E-01
66.5 1.0914E-04 8.6800E-05 0.7953 229.2 8.79502E+00 1.337E-04 303.5 14.90 1.11E-01
67.0 1.0204E-04 8.0672E-05 0.7906 227.8 8.17408E+00 1.250E-04 302.6 14.82 1.19E-01
67.5 9.5371E-05 7.4944E-05 0.7858 226.4 7.59372E+00 1.168E-04 301.7 14.75 1.26E-01
68.0 8.9101E-05 6.9593E-05 0.7811 225.1 7.05149E+00 1.091E-04 300.7 14.67 1.34E-01
68.5 8.3209E-05 6.4595E-05 0.7763 223.7 6.54509E+00 1.019E-04 299.8 14.60 1.43E-01
69.0 7.7675E-05 5.9929E-05 0.7715 222.3 6.07234E+00 9.515E-05 298.9 14.53 1.53E-01
69.5 7.2478E-05 5.5576E-05 0.7668 221.0 5.63120E+00 8.879E-05 298.0 14.45 1.63E-01
70.0 6.7601E-05 5.1515E-05 0.7620 219.6 5.21972E+00 8.281E-05 297.1 14.38 1.74E-01
70.5 6.3026E-05 4.7728E-05 0.7573 218.2 4.83607E+00 7.721E-05 296.1 14.30 1.85E-01
71.0 5.8734E-05 4.4199E-05 0.7525 216.8 4.47851E+00 7.195E-05 295.2 14.23 1.98E-01
71.5 5.4712E-05 4.0912E-05 0.7478 215.5 4.14543E+00 6.702E-05 294.3 14.15 2.11E-01
72.0 5.0905E-05 3.7852E-05 0.7436 214.3 3.83531E+00 6.236E-05 293.4 14.08 2.26E-01
72.5 4.7296E-05 3.5007E-05 0.7402 213.3 3.54709E+00 5.794E-05 292.8 14.03 2.42E-01
73.0 4.3927E-05 3.2365E-05 0.7368 212.3 3.27939E+00 5.381E-05 292.1 13.98 2.60E-01
73.5 4.0786E-05 2.9912E-05 0.7334 211.3 3.03083E+00 4.996E-05 291.4 13.92 2.79E-01
74.0 3.7856E-05 2.7635E-05 0.7300 210.3 2.80012E+00 4.637E-05 290.7 13.87 2.99E-01
74.5 3.5125E-05 2.5522E-05 0.7266 209.4 2.58605E+00 4.303E-05 290.1 13.81 3.21E-01
75.0 3.2580E-05 2.3563E-05 0.7232 208.4 2.38749E+00 3.991E-05 289.4 13.76 3.45E-01
75.5 3.0209E-05 2.1746E-05 0.7198 207.4 2.20338E+00 3.701E-05 288.7 13.70 3.70E-01
76.0 2.8001E-05 2.0061E-05 0.7164 206.4 2.03272E+00 3.430E-05 288.0 13.65 3.98E-01
76.5 2.5946E-05 1.8501E-05 0.7131 205.5 1.87458E+00 3.178E-05 287.4 13.59 4.28E-01
77.0 2.4033E-05 1.7055E-05 0.7097 204.5 1.72810E+00 2.944E-05 286.7 13.54 4.60E-01
77.5 2.2253E-05 1.5716E-05 0.7063 203.5 1.59247E+00 2.726E-05 286.0 13.48 4.95E-01
78.0 2.0597E-05 1.4477E-05 0.7029 202.5 1.46692E+00 2.523E-05 285.3 13.43 5.32E-01
78.5 1.9058E-05 1.3331E-05 0.6995 201.6 1.35076E+00 2.335E-05 284.6 13.37 5.73E-01
79.0 1.7627E-05 1.2270E-05 0.6961 200.6 1.24331E+00 2.159E-05 283.9 13.32 6.17E-01
79.5 1.6298E-05 1.1290E-05 0.6927 199.6 1.14396E+00 1.996E-05 283.2 13.26 6.64E-01
80.0 1.5063E-05 1.0384E-05 0.6893 198.6 1.05213E+00 1.845E-05 282.5 13.21 7.16E-01
80.5 1.3917E-05 9.5464E-06 0.6860 197.7 9.67291E-01 1.705E-05 281.8 13.15 7.71E-01
81.0 1.2853E-05 8.7731E-06 0.6826 196.7 8.88933E-01 1.574E-05 281.1 13.10 8.32E-01
81.5 1.1866E-05 8.0591E-06 0.6792 195.7 8.16590E-01 1.454E-05 280.4 13.04 8.97E-01
82.0 1.0950E-05 7.4002E-06 0.6758 194.7 7.49827E-01 1.341E-05 279.7 12.98 9.68E-01
82.5 1.0101E-05 6.7924E-06 0.6724 193.8 6.88237E-01 1.237E-05 279.0 12.93 1.04E+00
83.0 9.3145E-06 6.2318E-06 0.6690 192.8 6.31441E-01 1.141E-05 278.3 12.87 1.13E+00
83.5 8.5856E-06 5.7151E-06 0.6657 191.8 5.79087E-01 1.052E-05 277.6 12.82 1.22E+00
84.0 7.9106E-06 5.2391E-06 0.6623 190.8 5.30848E-01 9.690E-06 276.9 12.76 1.32E+00
84.5 7.2857E-06 4.8006E-06 0.6589 189.9 4.86416E-01 8.925E-06 276.2 12.70 1.42E+00
85.0 6.7074E-06 4.3968E-06 0.6555 188.9 4.45509E-01 8.217E-06 275.5 12.65 1.54E+00
85.5 6.1724E-06 4.0253E-06 0.6521 187.9 4.07863E-01 7.561E-06 274.8 12.59 1.67E+00
86.0 5.6777E-06 3.6835E-06 0.6488 186.9 3.73232E-01 6.955E-06 274.1 12.53 1.80E+00
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env Python3
import numpy as np
import matplotlib.pyplot as plt
from AstroAtmosphere import *
from ADC import *
import sys
import scipy.optimize as opt
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: This script finds the filter constant using
- Direct application of equation 18 and 19
- The minimization algorithm implemented in the ADC class
and compares these two.
'''
class filter:
def __init__(self, name, l1, l2):
self.name = name
self.lmin = np.min([l1,l2])
self.lmax = np.max([l1,l2])
self.bandwidth = self.lmax - self.lmin
self.optimumAngle = None
self.residualDispersion = None
self.optimumAngleForAllZ = None
self.cf = None
def analytic_optimum_position_ADC(self, z, l1, l2, dispersion_object, adc_object):
disp = dispersion_object
adc = adc_object
_R = disp.rc / (disp.rc + disp.H)
n1 = at.n_tph(l=self.lmin, T=T, p=P, RH=H, xc=xc)
n2 = at.n_tph(l=self.lmax, T=T, p=P, RH=H, xc=xc)
dn_atm = n2 - n1
_dispersion = np.deg2rad(disp.cassini(n1,n2,z))
_A1 = np.deg2rad(adc.p1.tilt2x_OG - adc.p1.tilt1x_OG)
_A2 = np.deg2rad(adc.p2.tilt2x_OG - adc.p2.tilt1x_OG)
_A3 = -1 * np.deg2rad(adc.p3.tilt2x_OG - adc.p3.tilt1x_OG) # -1 because doublet gets flipped after setting rotation
_A4 = -1 * np.deg2rad(adc.p4.tilt2x_OG - adc.p4.tilt1x_OG) # -1 because doublet gets flipped after setting rotation
_dn_p1 = adc.p1.indexOfRefraction(l1) - adc.p1.indexOfRefraction(l2)
_dn_p2 = adc.p2.indexOfRefraction(l1) - adc.p2.indexOfRefraction(l2)
_pupilRatio = 0.07943/38.542
_Psi = (_dn_p1 * (_A1 + _A4) + _dn_p2 * (_A2 + _A3)) * _pupilRatio
_cf = -1/_pupilRatio * (dn_atm)/((_dn_p1 * (_A1 + _A4) + _dn_p2 * (_A2 + _A3)))
_thetaOpt = np.arccos(1/_Psi * _dispersion)
return np.degrees(_thetaOpt), _cf
def ADCposAnalytical(z, Cf):
return np.arccos(Cf*np.tan(np.deg2rad(z)))*180/np.pi
if __name__ == "__main__":
Iband = filter('I-band',0.8,0.934)
Jband = filter('J-band',1.15,1.345)
Hband = filter('H-band',1.49,1.78)
Kband = filter('K-band',1.97,2.32)
##########################################################
# Atmospheric model
# 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()
# Density of the atmosphere (following CIPM-81/91 equations)
rho = at.rho(p=P, T=T, RH=H, xc=xc)
# Initializing dispersion object
disp = dispersion(lat, h)
disp.setReducedHeight(P, rho)
##########################################################
adc = ADC()
# Unlock this when the above works
# zRange = range(2,62)
zRange = np.arange(5,65.1,0.1)
optimumAngleFilterList = []
fig = plt.figure()
ax = fig.add_subplot(111)
for f in [Hband, Iband, Jband, Kband]:
# For new filters or if the zenith values are different, the new values are calculated
print ('Starting %s...' %f.name )
# Calculating indices of refraction for l1 and l2
n1 = at.n_tph(l=f.lmin, T=T, p=P, RH=H, xc=xc)
n2 = at.n_tph(l=f.lmax, T=T, p=P, RH=H, xc=xc)
dispersion_obs = disp.cassini(n1, n2, zRange)*3.6e6
adc.setOrientation(0)
theta_opt = [adc.optimize(disp, f.lmin, f.lmax) for disp in dispersion_obs]
f.optimumAngleForAllZ = theta_opt
# Plotting the data
ax.plot(zRange, f.optimumAngleForAllZ, label=f.name)
for f in [Hband, Iband, Jband, Kband]:
popt, pcov = opt.curve_fit(ADCposAnalytical, zRange[:-50], f.optimumAngleForAllZ[:-50], p0=0.5)
f.cf = popt[0]
ax.plot(zRange, ADCposAnalytical(zRange, f.cf), 'k--', label='best fit %s' %f.name)
theta_opt_analytical, cf_analytical = f.analytic_optimum_position_ADC(zRange, f.lmin, f.lmax, disp, adc)
ax.plot(zRange, ADCposAnalytical(zRange, cf_analytical), 'b--', label='analytical cf %s' %f.name)
print('{} - Best fit: {} - Analytical: {}'.format(f.name, f.cf, cf_analytical))
# Plot properties
ax.set_title('Optimum rotation angle of the first ADC prism', fontsize=9)
ax.set_ylabel('Rotation angle (deg)', fontsize=9)
ax.set_xlabel('Zenith angle (deg)', fontsize=9)
ax.grid(True, alpha=0.5)
ax.legend(fontsize=9)
plt.show()
+299
View File
@@ -0,0 +1,299 @@
#!/usr/bin/env Python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
from AstroAtmosphere import *
import pandas as pd
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: Modeling the atmospheric refraction using multiple
models of the atmosphere and then comparing them.
Models included:
- Full integration method
- Plane-parallel atmosphere
- Cassini's model
- Tan5 model (A*tan(z) + B*tan^3(z) + C*tan^5(z))
- Oriani's theorem (A*tan(z) + B*tan^3(z))
- Erf model (from Corbard et al., 2019)
- SLALIB
One of the figures and all three tables are used in the paper.
'''
if __name__ == '__main__':
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
# Conditions at Cerro Armazones
# T = 279.65 # k
# P = 71200 # Pa
# H = 0.22
# xc = 450 # ppm
# Conditions for the standard atmosphere
T = 288.15 # k
P = 101325 # Pa
H = 0.0
xc = 314 # ppm
lat = -24.5983 # degrees
h = 3064 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 30 # degrees
# Calculate the index of refraction
at = Observatory()
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)
rho = at.rho(p=P, T=T, RH=H, xc=xc)
refractionAnalysis = True
dispersionAnalysis = True
tableCompare = True
errorTable = True
if dispersionAnalysis:
# Initiate the refraction class
ref = dispersion(lat, h)
ref.setReducedHeight(P, rho)
# Compare the refraction models to check if the output is okay
print ('Plane - parallel atmosphere dispersion: %s mas' %(ref.planeParallel(n1, n2, zenith) * 3.6e6))
print ('Cassini dispersion: %s mas' %(ref.cassini(n1, n2, zenith) * 3.6e6))
print ('Tan5 dispersion: %s mas' %(ref.tan5(n1, n2, zenith) * 3.6e6))
print ('Oriani dispersion: %s mas' %(ref.oriani(n1, n2, zenith) * 3.6e6))
print ('Corbard dispersion: %s mas' %(ref.corbard(n1, n2, zenith)*3.6e6))
print ('Full integration: %s mas' %(ref.refractionIntegral(n1, n2, zenith)*3.6e6))
print('-'*35)
# Now we calculate the dispersion over a wide range of zenith angles
# for ideal gas atmosphere
zenithRange = np.linspace(5, 85, 1000)
ppList = ref.planeParallel(n1, n2, zenithRange) * 3.6e6
cassiniList = ref.cassini(n1, n2, zenithRange) * 3.6e6
tan5List = ref.tan5(n1, n2, zenithRange) * 3.6e6
orianiList = ref.oriani(n1, n2, zenithRange) * 3.6e6
corbardList = [ref.corbard(n1, n2, z) * 3.6e6 for z in zenithRange]
fullIntegrationList = [ref.refractionIntegral(n1, n2, z) * 3.6e6 for z in zenithRange]
# for isothermal atmosphere
cassiniList_isotherm = ref.cassini(n1, n2, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e6
tan5List_isotherm = ref.tan5(n1, n2, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e6
orianiList_isotherm = ref.oriani(n1, n2, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e6
corbardList_isotherm = [ref.corbard(n1, n2, z, reduced_height=ref.H_isotherm(T)) * 3.6e6 for z in zenithRange]
# check when the variation between the different models pass different values
dispersionWidth = []
widthList = []
for z in range(len(zenithRange)):
compList = [cassiniList[z], tan5List[z], orianiList[z], corbardList[z], cassiniList_isotherm[z], tan5List_isotherm[z], orianiList_isotherm[z], corbardList_isotherm[z], fullIntegrationList[z]]
width = np.max(compList) - np.min(compList)
widthList.append(width)
dispersionWidth.append((zenithRange[z], width))
masBorder = np.argmax(np.asarray(widthList)>1)
p1masBorder = np.argmax(np.asarray(widthList)>0.1)
p01masBorder = np.argmax(np.asarray(widthList)>0.01)
p001masBorder = np.argmax(np.asarray(widthList)>0.001)
plotAbsolute = 1
if plotAbsolute:
# Plotting it all, with vertical lines where the variation between the models exceed 1, 0.1, 0.01 and 0.001 mas.
fig = plt.figure(figsize=(9,5))
ax = fig.add_subplot(111)
ax.set_xlim([30,85])
ax.set_xlabel('Zenith (degrees)')
ax.set_ylabel('Dispersion (mas)')
ax.grid(True, alpha=0.3)
ax.axvline(dispersionWidth[masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[masBorder][0]-0.5, 402, '1 mas', ha='center', va='bottom', rotation='vertical')
ax.axvline(dispersionWidth[p1masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[p1masBorder][0]-0.5, 402, '0.1 mas', ha='center', va='bottom', rotation='vertical')
ax.axvline(dispersionWidth[p01masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[p01masBorder][0]-0.5, 402, '10 $\mu$as', ha='center', va='bottom', rotation='vertical')
ax.axvline(dispersionWidth[p001masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[p001masBorder][0]-0.5, 402, '1 $\mu$as', ha='center', va='bottom', rotation='vertical')
ax.plot(zenithRange, ppList, 'k-', label='Plane-parallel', alpha=0.2)
ax.plot(zenithRange, cassiniList, 'g-', label='Cassini')
ax.plot(zenithRange, tan5List, 'b-', label='$Atan(z) - Btan^3(z) + Ctan^5(z)$')
ax.plot(zenithRange, orianiList, 'y-', label='$Atan(z) - Btan^3(z)$')
ax.plot(zenithRange, corbardList, 'r-', label='Error function')
ax.plot(zenithRange, fullIntegrationList, 'c-', label='Full integration')
ax.plot(zenithRange, cassiniList_isotherm, 'g--', label='Cassini (isotherm)')
ax.plot(zenithRange, tan5List_isotherm, 'b--', label='$Atan(z) - Btan^3(z) + Ctan^5(z)$ (isotherm)')
ax.plot(zenithRange, orianiList_isotherm, 'y--', label='$Atan(z) - Btan^3(z)$ (isotherm)')
ax.plot(zenithRange, corbardList_isotherm, 'r--', label='Error function (isotherm)')
ax.tick_params(axis ='both', direction='in')
ax.legend()
plt.tight_layout()
# plt.show()
plotRelative = 1
if plotRelative:
# RELATIVE TO FULL INTEGRATION
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111)
ax.set_xlim([30,85])
ax.set_xlabel('Zenith (degrees)', fontsize=18)
ax.set_ylabel(r'$\log_{10}(|(\Delta R - \Delta R_{int})|$ mas$^{-1})$', fontsize=18)
ax.axhline(np.log10(0.4), linestyle='-', linewidth=1, color='red')
ax.text(30.5, np.log10(0.4)+0.05, 'Residual dispersion requirement', fontsize=12)
ax.grid(True, which='major', alpha=0.3)
ax.grid(True, which='minor', alpha=0.2)
ax.plot(zenithRange, np.log10(np.abs(ppList-fullIntegrationList)), color='k', linestyle='-', linewidth=2, label='Plane-parallel')
ax.plot(zenithRange, np.log10(np.abs(cassiniList-fullIntegrationList)), color='g', linestyle=(0, (1, 1)), linewidth=2, label='Cassini')
ax.plot(zenithRange, np.log10(np.abs(orianiList-fullIntegrationList)), color='y', linestyle='-.', linewidth=2, label='Oriani')
ax.plot(zenithRange, np.log10(np.abs(tan5List-fullIntegrationList)), color='b', linestyle=(0, (5, 1)), linewidth=2, label='Oriani incl. fifth order')
ax.plot(zenithRange, np.log10(np.abs(np.asarray(corbardList)-fullIntegrationList)), color='r', linestyle=(0, (3, 1, 1, 1, 1, 1)), linewidth=2, label='Error function')
ax.plot(zenithRange, np.log10(np.abs(cassiniList-fullIntegrationList)), color='g', linestyle=(0, (1, 1)), linewidth=2)
ax.set_ylim(top=2.6, bottom=-7.2)
ax.tick_params(axis ='both', direction='in', labelsize=16)
ax.legend(fontsize=14, labelspacing=0.3, columnspacing=0.5, handletextpad=0.3, ncol=1, loc='lower right')
plt.tight_layout()
# plt.savefig('./figures/dispersionModels_with_SLALIB_minimal2.pdf', transparent=True, bbox_inches='tight')
# plt.savefig('./figures/dispersionModels_with_SLALIB_minimal2.png', transparent=True, bbox_inches='tight')
if refractionAnalysis:
# Initiate the refraction class
ref = refraction(lat, h)
ref.setReducedHeight(P, rho)
# Compare the refraction models to check if the output is okay
print ('Plane - parallel atmosphere refraction: %s arcsec' %(ref.planeParallel(n1, zenith) * 3.6e3))
print ('Cassini refraction: %s arcsec' %(ref.cassini(n1, zenith) * 3.6e3))
print ('Tan5 refraction: %s arcsec' %(ref.tan5(n1, zenith) * 3.6e3))
print ('Oriani refraction: %s arcsec' %(ref.oriani(n1, zenith) * 3.6e3))
print ('Corbard refraction: %s arcsec' %(ref.corbard(n1, zenith)*3.6e3))
print ('Full integration: %s mas' %(ref.refractionIntegral(n1, zenith)*3.6e3))
print('-'*35)
# Now we calculate the dispersion over a wide range of zenith angles
# for ideal gas atmosphere
zenithRange = np.linspace(0, 88, 1000)
ppList = ref.planeParallel(n1, zenithRange) * 3.6e3
cassiniList = ref.cassini(n1, zenithRange) * 3.6e3
tan5List = ref.tan5(n1, zenithRange) * 3.6e3
orianiList = ref.oriani(n1, zenithRange) * 3.6e3
corbardList = [ref.corbard(n1, z) * 3.6e3 for z in zenithRange]
fullIntegrationList = [ref.refractionIntegral(n1, z) * 3.6e3 for z in zenithRange]
# for isothermal atmosphere
cassiniList_isotherm = ref.cassini(n1, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e3
tan5List_isotherm = ref.tan5(n1, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e3
orianiList_isotherm = ref.oriani(n1, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e3
corbardList_isotherm = [ref.corbard(n1, z, reduced_height=ref.H_isotherm(T)) * 3.6e3 for z in zenithRange]
# Plotting it all, with vertical lines where the variation between the models exceed 1, 0.1, 0.01 and 0.001 arcsec.
fig = plt.figure(figsize=(9,5))
ax = fig.add_subplot(111)
ax.set_xlim([80,88])
ax.set_xlabel('Zenith (degrees)')
ax.set_ylabel('Refraction (arcsec)')
ax.grid(True, alpha=0.3)
ax.plot(zenithRange, ppList, 'k-', label='Plane-parallel', alpha=0.2)
ax.plot(zenithRange, cassiniList, 'g-', label='Cassini')
ax.plot(zenithRange, tan5List, 'b-', label='$Atan(z) - Btan^3(z) + Ctan^5(z)$')
ax.plot(zenithRange, orianiList, 'y-', label='$Atan(z) - Btan^3(z)$')
ax.plot(zenithRange, corbardList, 'r-', label='Error function')
ax.plot(zenithRange, fullIntegrationList, 'c-', label='Full integration')
ax.tick_params(axis ='both', direction='in')
ax.legend()
plt.tight_layout()
if tableCompare:
refTableData = []
dispTableData = []
ref = refraction(lat, h)
ref.setReducedHeight(P, rho)
disp = dispersion(lat, h)
disp.setReducedHeight(P, rho)
zenithRange = [10, 30, 50, 60, 70, 80, 85]
headerList = np.array(['z', 'Plane-parallel', 'Cassini', 'tan3', 'tan5', 'Error function', 'Full integration'])
refTableData.append(headerList)
dispTableData.append(headerList)
for z in zenithRange:
ppValue = round(ref.planeParallel(n1, z) * 3.6e3, 3)
cassiniValue = round(ref.cassini(n1, z) * 3.6e3, 3)
tan3Value = round(ref.oriani(n1, z) * 3.6e3, 3)
tan5Value = round(ref.tan5(n1, z) * 3.6e3, 3)
erfValue = round(ref.corbard(n1, z) * 3.6e3, 3)
fullIntValue = round(ref.refractionIntegral(n1, z) * 3.6e3, 3)
refTableData.append([z, ppValue, cassiniValue, tan3Value, tan5Value, erfValue, fullIntValue])
ppValue = round(disp.planeParallel(n1, n2, z) * 3.6e6, 3)
cassiniValue = round(disp.cassini(n1, n2, z) * 3.6e6, 3)
tan3Value = round(disp.oriani(n1, n2, z) * 3.6e6, 3)
tan5Value = round(disp.tan5(n1, n2, z) * 3.6e6, 3)
erfValue = round(disp.corbard(n1, n2, z) * 3.6e6, 3)
fullIntValue = round(disp.refractionIntegral(n1, n2, z) * 3.6e6, 3)
dispTableData.append([z, ppValue, cassiniValue, tan3Value, tan5Value, erfValue, fullIntValue])
dfr = pd.DataFrame(np.asarray(refTableData))
dfd = pd.DataFrame(np.asarray(dispTableData))
print('-'*35,'\nREFRACTION COMPARISON:\n')
print(dfr.to_string(index=False))
print('-'*35)
print('-'*35,'\nDISPERSION COMPARISON:\n')
print(dfd.to_string(index=False))
print('-'*35)
# print(dfr.to_latex(index=False))
# print(dfd.to_latex(index=False))
if errorTable:
ref = dispersion(lat, h)
ref.setReducedHeight(P, rho)
dispTableData = []
headerList = np.array(['s_P', 's_T', 's_H', 's_disp', 's_disp(dl=0)'])
dispTableData.append(headerList)
# dPlist = [2, 20, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 20, 200]
# dTlist = [0, 0, 0, 0.02, 0.2, 2, 0, 0, 0, 0, 0, 0, 0.02, 0.2, 2 ]
# dRHlist= [0, 0, 0, 0, 0, 0, 0.002, 0.02, 0.2, 0, 0, 0, 0.002, 0.02, 0.2]
# dCO2list=[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 20, 200, 2, 20, 200]
dPlist = [0, 10, 10, 20, 100]
dTlist = [0, 0.1, 0.2, 0.2, 1.0]
dRHlist= [0, 0.01, 0.01, 0.02, 0.05]
dCO2list=[0, 20, 20, 20, 20]
for i in range(len(dPlist)):
# nerr = at.dn_tph(l1, T, P, H, xc, dl=0, dT=dTlist[i], dP=dPlist[i], dRH=dRHlist[i], dCO2=dCO2list[i])
disp_error = ref.cassiniError(zenith, l1, l2, T, P, H, xc, dl1=0.001, dl2=0.001, dT=dTlist[i], dP=dPlist[i], dRH=dRHlist[i],
dCO2=dCO2list[i], dz=0) * 3.6e9
disp_error_dl0 = ref.cassiniError(zenith, l1, l2, T, P, H, xc, dl1=0, dl2=0, dT=dTlist[i], dP=dPlist[i], dRH=dRHlist[i],
dCO2=dCO2list[i], dz=0) * 3.6e9
dispTableData.append([dPlist[i], dTlist[i], dRHlist[i], disp_error, disp_error_dl0])
dfd = pd.DataFrame(np.asarray(dispTableData))
print('-'*35,'\nUNCERTAINTY ANALYSIS:\n')
print(dfd.to_string(index=False))
print('-'*35)
plt.show()
+247
View File
@@ -0,0 +1,247 @@
#!/usr/bin/env Python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
from AstroAtmosphere import *
from pyslalib import slalib
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: This script was used to compare the impact of using the old
B&S equations, and the particular implementation of SLALIB.
The rest of the script is almost a one-to-one copy of other
script that compared the atmospheric models.
Modeling the atmospheric refraction using multiple models of
the atmosphere and then comparing them.
Models included:
- Full integration method
- Plane-parallel atmosphere
- Cassini's model
- Tan5 model (A*tan(z) + B*tan^3(z) + C*tan^5(z))
- Oriani's theorem (A*tan(z) + B*tan^3(z))
- Erf model (from Corbard et al., 2019)
- SLALIB
'''
class atmosphereSLALIB:
def __init__(self, T=279.65, P=712, H=0.22):
self.name = 'SLALIB atmosphere'
self.T = T
self.P = P
self.H = H
self.h = 3064
self.lat = -24.5983
self.TLR = 0.0065
self.prec = 1e-8
def atmModel(self, A, B, zenith):
zenith = np.radians(zenith)
return A * np.tan(zenith) + B * (np.tan(zenith)**3)
def dispersion(self, l1, l2, z):
A1, B1 = slalib.sla_refco(self.h, self.T, self.P, self.H, l1, np.deg2rad(self.lat), self.TLR, self.prec)
A2, B2 = slalib.sla_atmdsp(self.T, self.P, self.H, l1, A1, B1, l2)
D1 = np.degrees(self.atmModel(A1, B1, z)) * 3600
D2 = np.degrees(self.atmModel(A2, B2, z)) * 3600
dispersionValue = ((D1 - D2) * 1000)
return dispersionValue
def refractivity(self, l=None):
'''
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.
'''
zObs = np.deg2rad(45.06563)
h = 0
T = self.T
P = self.P
H = self.H
lat = 24
tlr = 0.0065
eps = 1e-10
if not l:
l = self.lrange
R = []
if type(l) == float:
l = [l]
for wl in l:
R.append(slalib.sla_refro(zObs, h, T, P, H, wl, lat, tlr, eps)+1)
return np.asarray(R)
if __name__ == '__main__':
plt.rc('font', family='serif')
# plt.rc('xtick', labelsize='x-small')
# plt.rc('ytick', labelsize='x-small')
plt.rc('axes', labelsize='large')
# Standard atmospheric conditions
# T = 279.65 # k
# P = 71200 # Pa
# H = 0.22
# xc = 450 # ppm
T = 288.15 # k
P = 101325 # Pa
H = 0.0
xc = 314 # ppm
lat = -24.5983 # degrees
h = 3064 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 30 # degrees
# Calculate the index of refraction
at = Observatory()
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)
rho = at.rho(p=P, T=T, RH=H, xc=xc)
#######################################################
# DIRECT COMPARISON
ref = dispersion(lat, h)
ref.setReducedHeight(P, rho)
print('Ciddor refractivity: ', n1, n2)
print ('Cassini dispersion: %s mas' %(ref.cassini(n1, n2, zenith) * 3.6e6))
n1c = n1
n2c = n2
refraction = refraction(lat, h)
refraction.setReducedHeight(P, rho)
refractionValueCiddor = refraction.cassini(n1c, zenith) * 3.6e6 # in mas
# SLALIB
atSLALIB = atmosphereSLALIB(T, P/100, H)
n1 = atSLALIB.refractivity(l1)
n2 = atSLALIB.refractivity(l2)
print('B&S refractivity: ', n1, n2)
print ('SLALIB model: %s mas' %(atSLALIB.dispersion(l1, l2, zenith) ) )
# SLALIB refraction:
A1, B1 = slalib.sla_refco(atSLALIB.h, atSLALIB.T, atSLALIB.P, atSLALIB.H, l1, np.deg2rad(atSLALIB.lat), atSLALIB.TLR, atSLALIB.prec)
refractionValueSLALIB = np.degrees(atSLALIB.atmModel(A1, B1, zenith)) * 3.6e6 # in mas
print('Differential dispersion = {} mas'.format(atSLALIB.dispersion(l1, l2, zenith) - ref.cassini(n1c, n2c, zenith) * 3.6e6) )
print('Differential refraction = {} mas'.format( refractionValueSLALIB - refractionValueCiddor ) )
#######################################################
refractionAnalysis = True
dispersionAnalysis = True
tableCompare = True
if dispersionAnalysis:
# Initiate the refraction class
ref = dispersion(lat, h)
ref.setReducedHeight(P, rho)
# Compare the refraction models to check if the output is okay
print ('Plane - parallel atmosphere dispersion: %s mas' %(ref.planeParallel(n1, n2, zenith) * 3.6e6))
print ('Cassini dispersion: %s mas' %(ref.cassini(n1, n2, zenith) * 3.6e6))
print ('Tan5 dispersion: %s mas' %(ref.tan5(n1, n2, zenith) * 3.6e6))
print ('Oriani dispersion: %s mas' %(ref.oriani(n1, n2, zenith) * 3.6e6))
print ('Corbard dispersion: %s mas' %(ref.corbard(n1, n2, zenith)*3.6e6))
print ('Full integration: %s mas' %(ref.refractionIntegral(n1, n2, zenith)*3.6e6))
print ('SLALIB model: %s mas' %(atSLALIB.dispersion(l1, l2, zenith) ) )
# Now we calculate the dispersion over a wide range of zenith angles
# for ideal gas atmosphere
zenithRange = np.linspace(30, 85, 1000)
ppList = ref.planeParallel(n1, n2, zenithRange) * 3.6e6
cassiniList = ref.cassini(n1, n2, zenithRange) * 3.6e6
tan5List = ref.tan5(n1, n2, zenithRange) * 3.6e6
orianiList = ref.oriani(n1, n2, zenithRange) * 3.6e6
corbardList = [ref.corbard(n1, n2, z)[0] * 3.6e6 for z in zenithRange]
fullIntegrationList = np.asarray([ref.refractionIntegral(n1, n2, z) * 3.6e6 for z in zenithRange])
slalibList = atSLALIB.dispersion(l1, l2, zenithRange)
# for isothermal atmosphere
cassiniList_isotherm = ref.cassini(n1, n2, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e6
tan5List_isotherm = ref.tan5(n1, n2, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e6
orianiList_isotherm = ref.oriani(n1, n2, zenithRange, reduced_height=ref.H_isotherm(T)) * 3.6e6
corbardList_isotherm = [ref.corbard(n1, n2, z, reduced_height=ref.H_isotherm(T))[0] * 3.6e6 for z in zenithRange]
# check when the variation between the different models pass different values
dispersionWidth = []
widthList = []
for z in range(len(zenithRange)):
compList = [cassiniList[z], tan5List[z], orianiList[z], corbardList[z], cassiniList_isotherm[z], tan5List_isotherm[z], orianiList_isotherm[z], corbardList_isotherm[z], slalibList[z]]#, fullIntegrationList[z]]
width = np.max(compList) - np.min(compList)
widthList.append(width)
dispersionWidth.append((zenithRange[z], width))
masBorder = np.argmax(np.asarray(widthList)>1)
p1masBorder = np.argmax(np.asarray(widthList)>0.1)
p01masBorder = np.argmax(np.asarray(widthList)>0.01)
p001masBorder = np.argmax(np.asarray(widthList)>0.001)
plotAbsolute = 1
if plotAbsolute:
# Plotting it all, with vertical lines where the variation between the models exceed 1, 0.1, 0.01 and 0.001 mas.
fig = plt.figure(figsize=(9,5))
ax = fig.add_subplot(111)
ax.set_xlim([30,85])
ax.set_xlabel('Zenith (degrees)')
ax.set_ylabel('Dispersion (mas)')
ax.grid(True, alpha=0.3)
ax.axvline(dispersionWidth[masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[masBorder][0]-0.5, 402, '1 mas', ha='center', va='bottom', rotation='vertical')
ax.axvline(dispersionWidth[p1masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[p1masBorder][0]-0.5, 402, '0.1 mas', ha='center', va='bottom', rotation='vertical')
ax.axvline(dispersionWidth[p01masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[p01masBorder][0]-0.5, 402, '10 $\mu$as', ha='center', va='bottom', rotation='vertical')
ax.axvline(dispersionWidth[p001masBorder][0], linestyle='--', color='k', linewidth=0.5)
ax.text(dispersionWidth[p001masBorder][0]-0.5, 402, '1 $\mu$as', ha='center', va='bottom', rotation='vertical')
ax.plot(zenithRange, ppList, 'k-', label='Plane-parallel', alpha=0.2)
ax.plot(zenithRange, cassiniList, 'g-', label='Cassini')
ax.plot(zenithRange, tan5List, 'b-', label='$Atan(z) - Btan^3(z) + Ctan^5(z)$')
ax.plot(zenithRange, orianiList, 'y-', label='$Atan(z) - Btan^3(z)$')
ax.plot(zenithRange, corbardList, 'r-', label='Error function')
ax.plot(zenithRange, fullIntegrationList, 'c-', label='Full integration')
ax.plot(zenithRange, cassiniList_isotherm, 'g--', label='Cassini (isotherm)')
ax.plot(zenithRange, tan5List_isotherm, 'b--', label='$Atan(z) - Btan^3(z) + Ctan^5(z)$ (isotherm)')
ax.plot(zenithRange, orianiList_isotherm, 'y--', label='$Atan(z) - Btan^3(z)$ (isotherm)')
ax.plot(zenithRange, corbardList_isotherm, 'r--', label='Error function (isotherm)')
ax.tick_params(axis ='both', direction='in')
ax.legend()
plt.tight_layout()
plt.show()
plotRelative = 1
if plotRelative:
# RELATIVE TO FULL INTEGRATION
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111)
ax.set_xlim([30,85])
ax.set_xlabel('Zenith (degrees)', fontsize=18)
ax.set_ylabel(r'$\log_{10}(|(\Delta R - \Delta R_{int})|$ mas$^{-1})$', fontsize=18)
ax.axhline(np.log10(0.4), linestyle='-', linewidth=1, color='red')
ax.text(30.5, np.log10(0.4)+0.05, 'Residual dispersion requirement', fontsize=12)
ax.grid(True, which='major', alpha=0.3)
ax.grid(True, which='minor', alpha=0.2)
ax.plot(zenithRange, np.log10(np.abs(ppList-fullIntegrationList)), color='k', linestyle='-', linewidth=2, label='Plane-parallel')
ax.plot(zenithRange, np.log10(np.abs(cassiniList-fullIntegrationList)), color='g', linestyle=(0, (1, 1)), linewidth=2, label='Cassini')
ax.plot(zenithRange, np.log10(np.abs(orianiList-fullIntegrationList)), color='y', linestyle='-.', linewidth=2, label='Oriani')
ax.plot(zenithRange, np.log10(np.abs(tan5List-fullIntegrationList)), color='b', linestyle=(0, (5, 1)), linewidth=2, label='Oriani incl. fifth order')
ax.plot(zenithRange, np.log10(np.abs(np.asarray(corbardList)-fullIntegrationList)), color='r', linestyle=(0, (3, 1, 1, 1, 1, 1)), linewidth=2, label='Error function')
ax.plot(zenithRange, np.log10(np.abs(np.asarray(slalibList)-fullIntegrationList)), color='m', linewidth=2, label='SLALIB')
ax.plot(zenithRange, np.log10(np.abs(cassiniList-fullIntegrationList)), color='g', linestyle=(0, (1, 1)), linewidth=2)
ax.set_ylim(top=2.6, bottom=-7.2)
ax.tick_params(axis ='both', direction='in', labelsize=16)
ax.legend(fontsize=14, labelspacing=0.3, columnspacing=0.5, handletextpad=0.3, ncol=1, loc='lower right')
plt.tight_layout()
plt.show()
+177
View File
@@ -0,0 +1,177 @@
#!/usr/bin/env Python3
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
from matplotlib.colors import LogNorm
from tabulate import tabulate
import AstroAtmosphere
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: A comparison of various atmospheric refraction models.
Also these are used to calculate the difference in
atmospheric dispersion, given a set of conditions.
This is a reproduction of Figure 1 of Spàno (2014).
'''
class atmosphericModel:
def __init__(self, T, P, H, CO2, l1, l2):
self.T = T
self.P = P
self.H = H
self.CO2 = CO2
self.lrange = np.linspace(l1, l2, 100)
def BarrellAndSears(self, l=None):
if not l:
l = self.lrange
return (AstroAtmosphere.BarrellAndSears(l, T=self.T, p=self.P)-1)
def Ciddor(self, l=None):
if not l:
l = self.lrange
return (AstroAtmosphere.Ciddor(l, CO2=self.CO2)-1)
def Edlen1953(self, l=None):
if not l:
l = self.lrange
return(AstroAtmosphere.Edlen1953(l)-1)
def Edlen1966(self, l=None):
if not l:
l = self.lrange
return(AstroAtmosphere.Edlen1966(l)-1)
def Owens(self, l=None):
if not l:
l = self.lrange
return (AstroAtmosphere.Owens(l, T=self.T, p=self.P, H=self.H)-1)
def BonschPotulski(self, l=None):
if not l:
l = self.lrange
return (AstroAtmosphere.BonschPotulski(l, T=self.T, p=self.P, CO2=self.CO2)-1)
def BirchDowns(self, l=None):
if not l:
l = self.lrange
return (AstroAtmosphere.BirchDowns(l)-1)
def PeckReeder(self, l=None):
if not l:
l = self.lrange
return (AstroAtmosphere.PeckReeder(l)-1)
def slalib(self, l=None):
if not l:
l = self.lrange
return(AstroAtmosphere.slalib(l, T=self.T, p=self.P, H=self.H)-1)
if __name__ == '__main__':
# Calculation of the refractivity of the different models
# multiplication by 1e8 to make the differentials more
# pronounced.
at = atmosphericModel(T=288.15, P=101325, H=0, CO2=300, l1=0.3, l2=2.5)
BSdata = 1e8 * at.BarrellAndSears()
CiddorData = 1e8 * at.Ciddor()
Edlen1953Data = 1e8 * at.Edlen1953()
Edlen1966Data = 1e8 * at.Edlen1966()
OwensData = 1e8 * at.Owens()
BonschPotulskiData = 1e8 * at.BonschPotulski()
BirchDownsData = 1e8 * at.BirchDowns()
PeckReederData = 1e8 * at.PeckReeder()
slalibData = 1e8 * at.slalib()
# Plotting the results
fig = plt.figure(figsize=(9,5))
ax2 = fig.add_subplot(111)
ax2.grid(True, alpha=0.3)
ax2.plot(at.lrange, BSdata - CiddorData, 'b-', label='Barrell & Sears (1939)')
ax2.plot(at.lrange, CiddorData - CiddorData, 'k-', label='Ciddor (1996)')
ax2.plot(at.lrange, Edlen1953Data - CiddorData, 'r:', label='Edlen (1953)')
ax2.plot(at.lrange, Edlen1966Data - CiddorData, 'g--', label='Edlen (1966)')
ax2.plot(at.lrange, OwensData - CiddorData, 'm--', label='Owens (1967)')
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, PeckReederData - CiddorData, 'b--', label='Peck & Reeder (1972)')
ax2.plot(at.lrange, slalibData - CiddorData, 'y-', label='ZEMAX / SLALIB')
box = ax2.get_position()
ax2.set_position([box.x0, box.y0, box.width, box.height])
ax2.legend(loc='center left', bbox_to_anchor=(1, 0.5))
ax2.set_xlabel('Wavelength ($\mu m$)')
ax2.set_ylabel('$\Delta$n $\\times$ $10^{8}$')
ax2.set_ylim([-3,3])
plt.tight_layout()
plt.show()
# Let's compare the impact on the atmospheric dispersion.
# For the dispersion we must compare the delta for two lambda values
methodList = [ at.BarrellAndSears,
at.Ciddor,
at.Edlen1953,
at.Edlen1966,
at.Owens,
at.BonschPotulski,
at.BirchDowns,
at.PeckReeder,
at.slalib
]
filters = [ [0.800, 0.934], # I-band
[1.150, 1.345], # J-band
[1.490, 1.780], # H-band
[1.970, 2.320] # K-band
]
filterDispersion = []
# Iterate over the four MICADO broad band filters
for f in filters:
# Wavelengths of this particular filter
l1 = f[0]
l2 = f[1]
dispersionList = []
for m in methodList:
d1 = m(l=l1)
d2 = m(l=l2)
deltan = d1 - d2
# We assume, for sake of simplicity, a plane parallel atmosphere
# and calculate the dispersion in milli arcseconds.
dispersion = deltan * np.tan(np.deg2rad(45)) * 206265 * 1000
dispersionList.append(dispersion)
# We compare the atmospheric dispersion relative to the atmospheric
# dispersion as calculated using the Ciddor model.
dispersionList = np.asarray(dispersionList) - dispersionList[1]
filterDispersion.append(dispersionList)
# Print the results in a table.
methodStringList = ['Barrell & Sears (1939)',
'Ciddor (1996)',
'Edlen (1953)',
'Edlen (1966)',
'Bonsch & Potulski (1998)',
'Birch & Downs (1993, 1994)',
'Peck & Reeder (1972)',
'slalib']
tableData = []
for i in range(len(methodStringList)):
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)']))
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env Python3
import numpy as np
import matplotlib.pyplot as plt
import AstroAtmosphere as atm
from ADC import *
'''
@author: Joost van den Born
@Contact: born@astron.nl
@Description: Caclulates the differential dispersion between the ADC and
the atmosphere following the ciddor and cassini dispersion
model.
This script generates figure 4 from the paper.
'''
# Initializing ADC object and defining the filter bands
adc = ADC()
bandName = ['I-band', 'J-band', 'H-band', 'K-band']
bandList = [(0.8, 0.934), (1.15, 1.345), (1.49, 1.78), (1.97, 2.32)]
# Some empty lists for later use
wavelengthList = []
atDispersionList = []
adcDispersionList = []
for i in range(len(bandList)):
# We want to find the dispersion of the atmosphere and
# and of the ADC, at z=30 deg.
lMin = bandList[i][0]
lMax = bandList[i][1]
z = 30 # degrees
lRange = np.linspace(lMin, lMax, 100)
wavelengthList.append(lRange)
# Set ADC position optimized for H-band, z=30 deg.
atDispersion = atm.quick_dispersion(lMin, lMax, z, conditions='CERRO_ARMAZONES') * 3.6e6
adcOptimumAngle = adc.optimize(atDispersion, lMin, lMax)
adc.setOrientation(adcOptimumAngle)
# Now iterate over the band to see how the differential dispersion changes
atDispersionValues = []
adcDispersionValues = []
for l in lRange:
atDispersionValues.append(atm.quick_dispersion(np.min(lRange), l, z, conditions='CERRO_ARMAZONES')* 3.6e6 )
adcDispersionValues.append(adc.calculateDispersion(np.min(lRange),l)[1])
atDispersionList.append(atDispersionValues)
adcDispersionList.append(adcDispersionValues)
# Calculating the differential dispersion
atDispersionList = np.asarray(atDispersionList)
adcDispersionList = np.asarray(adcDispersionList)
deltaDispersion = atDispersionList - adcDispersionList
# Plot the results
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111)
linestyleList = [':', '-.', '--', '-']
for i in range(len(bandList)):
ax.plot(wavelengthList[i], deltaDispersion[i], label=bandName[i], linestyle=linestyleList[i], linewidth=2, color='k')
ax.set_ylabel('Differential $\Delta$R (mas)', fontsize=18)
ax.set_xlabel('$\lambda$ ($\mu$m)', fontsize=18)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=16, loc='lower right')
ax.tick_params(axis ='both', direction='in', labelsize=16)
plt.show()
+324
View File
@@ -0,0 +1,324 @@
#!/usr/bin/env Python
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import AstroAtmosphere as atm
import sys
from ADC import *
'''
@author: Joost van den Born
@Description: Calculates differential refraction (dispersion) between stars with a different
effective temperature. Similar to Gubler & Tytler 1998
'''
def blackbody(l, T):
'''
Calculates the intensity output of a star with effective temperature T at wavelength l
'''
h = 6.626070150e-34 # J*s
k = 1.38064852e-23 # J/K
c = 299792458 # m/s
return 2 * np.pi * h * c**2 / l**5 * 1 / (np.exp( h * c / (l * k * T)) - 1)
def effectiveWavelength(T, lmin, lmax):
'''
Calculates effective wavelength of a star from the effective temperature.
'''
lmin = lmin/1e6
lmax = lmax/1e6
_l = np.linspace(lmin, lmax, 1000)
_I = blackbody(_l, T)
return np.sum(_I*_l)/np.sum(_I)*1e6
if __name__ == '__main__':
# Parameters at Cerro Armazones
T = 279.65 # k
P = 71200 # Pa
H = 0.22
xc = 450 # ppm
lat = -24.5983 # degrees
h = 3064 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 60 # degrees
offset = 0#.285/2 # degrees, ADC offset to simulate finite step size
S2temps = [2500, 4000, 5500, 7000, 8500]
S1temps = np.linspace(2500, 1e4, 100)
########################################################################################
########################################################################################
# 1. We look at the differential refraction through the atmosphere, without an ADC
# at = atm.dispersion(lat, h)
at = atm.refraction(lat, h)
at.setReducedHeight(P, at.rho(P, T, H, xc) )
# Calculating the differential atmospheric refraction for stars of different temperatures
data = []
for T2 in S2temps:
# Properties of star 2
l_eff_2 = effectiveWavelength(T2, l1, l2)
n2 = at.n_tph(l_eff_2, T, P, H, xc)
R2 = at.cassini(n2, zenith)
# Calculating the differences with star 1
dataEntry = []
for T1 in S1temps:
l_eff_1 = effectiveWavelength(T1, l1, l2)
n1 = at.n_tph(l_eff_1, T, P, H, xc)
R1 = at.cassini(n1, zenith)
diffRef = (R1 - R2) * 3.6e9
dataEntry.append(diffRef)
data.append(dataEntry)
# Plotting the results
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111)
ax2 = ax.twiny()
ax.grid(True, alpha=0.3)
linestylesList = [':', '-.', '--', '-', (0, (3, 1, 1, 1, 1, 1))]
for i in range(len(data)):
ax.plot(S1temps, data[i], label=str(S2temps[i]) + ' K', linestyle=linestylesList[i], color='k', linewidth=2)
S1temp_ticks = ax.get_xticks()
S1temp_tick_loc = ax.get_xticks()
l_eff_ticks = []
for T1 in S1temp_ticks:
l_eff_ticks.append(round( (effectiveWavelength(T1, l1, l2)*1000-1620),1))
ax2.set_xticklabels(l_eff_ticks)
ax2.set_xlim(np.min(S1temps), np.max(S1temps))
ax2.set_xlabel('$\lambda_{eff} - 1620$ (nm) ', fontsize=18)
ax2.tick_params(axis ='both', direction='in', labelsize=16)
ax.set_xlabel('Temperature of Star 1 (K)', fontsize=18)
ax.set_ylabel('$\Delta$R ($\mu$as)', fontsize=18)
ax.legend(fontsize=16, labelspacing=0.3)
ax.set_xlim(np.min(S1temps), np.max(S1temps))
ax.tick_params(axis ='both', direction='in', labelsize=16)
# plt.show()
########################################################################################
########################################################################################
########################################################################################
########################################################################################
# 2. We look now at the differential refraction through the atmosphere, including an ADC
at = atm.refraction(lat, h)
at.setReducedHeight(P, at.rho(P, T, H, xc) )
adc = ADC()
# Conditions at the band cut-on and cut-off wavelength
n_l1 = at.n_tph(l1, T, P, H, xc)
n_l2 = at.n_tph(l2, T, P, H, xc)
atd = atm.dispersion(lat, h)
atd.setReducedHeight(P, at.rho(P, T, H, xc) )
# Optimize ADC for broad band edges at zenith distance.
atm_disp_full_band = atd.cassini(n_l1, n_l2, zenith)
print(atm_disp_full_band*3.6e6)
adcOptimumAngle = adc.optimize(atm_disp_full_band*3.6e6, l1, l2)
adc.setOrientation(adcOptimumAngle+offset)
# Calculating the differential atmospheric refraction for stars of different temperatures
data = []
for T2 in S2temps:
# Properties of star 2
l_eff_2 = effectiveWavelength(T2, l1, l2)
n2 = at.n_tph(l_eff_2, T, P, H, xc)
R2_atm = at.cassini(n2, zenith)
R2_adc = adc.calculateDispersion(l_eff_2, l1, refraction=True)
# Calculating the differences with star 1
dataEntry = []
for T1 in S1temps:
l_eff_1 = effectiveWavelength(T1, l1, l2)
n1 = at.n_tph(l_eff_1, T, P, H, xc)
R1_atm = at.cassini(n1, zenith)
R1_adc = adc.calculateDispersion(l_eff_1, l1, refraction=True)
diffRef = ( (R1_adc[1] - R2_adc[1]) + ((R1_atm - R2_atm) * 3.6e6 ) ) * 1e3
dataEntry.append(diffRef)
data.append(dataEntry)
# Plotting the results
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111)
ax2 = ax.twiny()
ax.grid(True, alpha=0.3)
linestylesList = [':', '-.', '--', '-', (0, (3, 1, 1, 1, 1, 1))]
for i in range(len(data)):
ax.plot(S1temps, data[i], label=str(S2temps[i]) + ' K', linestyle=linestylesList[i], color='k', linewidth=2)
S1temp_ticks = ax.get_xticks()
S1temp_tick_loc = ax.get_xticks()
l_eff_ticks = []
for T1 in S1temp_ticks:
l_eff_ticks.append(round( (effectiveWavelength(T1, l1, l2)*1000-1620),1))
ax2.set_xticklabels(l_eff_ticks)
ax2.set_xlim(np.min(S1temps), np.max(S1temps))
ax2.set_xlabel('$\lambda_{eff} - 1620$ (nm) ', fontsize=18)
ax2.tick_params(axis ='both', direction='in', labelsize=16)
ax.set_xlabel('Temperature of Star 1 (K)', fontsize=18)
ax.set_ylabel('$\Delta$R ($\mu$as)', fontsize=18)
ax.legend(fontsize=16, labelspacing=0.3, loc='lower right')
ax.set_xlim(np.min(S1temps), np.max(S1temps))
ax.tick_params(axis ='both', direction='in', labelsize=16)
plt.show()
########################################################################################
########################################################################################
########################################################################################
########################################################################################
# 3. Now we look at the differential dispersion, without an ADC
at = atm.dispersion(lat, h)
at.setReducedHeight(P, at.rho(P, T, H, xc) )
# Conditions at the band cut-on and cut-off wavelength
n_l1 = at.n_tph(l1, T, P, H, xc)
n_l2 = at.n_tph(l2, T, P, H, xc)
# Optimize ADC for broad band edges at zenith distance.
atm_disp_full_band = at.cassini(n_l1, n_l2, zenith)
print(atm_disp_full_band*3.6e6)
# Check that we can use dispersion? If not, how to calculate refraction...?
# We plot below the difference in atmospheric dispersion of the two stars, incl. an ADC.
data = []
for T2 in S2temps:
l_eff_2 = effectiveWavelength(T2, l1, l2)
n2 = at.n_tph(l_eff_2, T, P, H, xc)
dataEntry = []
for T1 in S1temps:
l_eff_1 = effectiveWavelength(T1, l1, l2)
n1 = at.n_tph(l_eff_1, T, P, H, xc)
atm_disp = at.cassini(n1, n2, zenith) * 3.6e9
dataEntry.append(atm_disp)
data.append(dataEntry)
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
fig2 = plt.figure(figsize=(9,6))
ax = fig2.add_subplot(111)
ax2 = ax.twiny()
ax.grid(True, alpha=0.3)
linestylesList = [':', '-.', '--', '-', (0, (3, 1, 1, 1, 1, 1))]
for i in range(len(data)):
ax.plot(S1temps, data[i], label=str(S2temps[i]) + ' K', linestyle=linestylesList[i], color='k', linewidth=2)
S1temp_ticks = ax.get_xticks()
S1temp_tick_loc = ax.get_xticks()
l_eff_ticks = []
for T1 in S1temp_ticks:
l_eff_ticks.append(round( (effectiveWavelength(T1, l1, l2)*1000-1620),1))
# ax2.plot(S1temps, data[0], alpha=0)
ax2.set_xticklabels(l_eff_ticks)
ax2.set_xlim(np.min(S1temps), np.max(S1temps))
ax2.set_xlabel('$\lambda_{eff} - 1620$ (nm) ', fontsize=18)
ax2.tick_params(axis ='both', direction='in', labelsize=16)
ax.set_xlabel('Temperature of Star 1 (K)', fontsize=18)
ax.set_ylabel('$\Delta(\Delta$R) ($\mu$as), excl. ADC', fontsize=18)
ax.legend(fontsize=16, labelspacing=0.3, loc='lower right')
ax.set_xlim(np.min(S1temps), np.max(S1temps))
ax.tick_params(axis ='both', direction='in', labelsize=16)
# plt.show()
########################################################################################
########################################################################################
########################################################################################
########################################################################################
########################################################################################
# 4. Now we add the ADC and look again at the differential refraction, but on the detector
at = atm.dispersion(lat, h)
at.setReducedHeight(P, at.rho(P, T, H, xc) )
adc = ADC()
# Conditions at the band cut-on and cut-off wavelength
n_l1 = at.n_tph(l1, T, P, H, xc)
n_l2 = at.n_tph(l2, T, P, H, xc)
# Optimize ADC for broad band edges at zenith distance.
atm_disp_full_band = at.cassini(n_l1, n_l2, zenith)
print(atm_disp_full_band*3.6e6)
adcOptimumAngle = adc.optimize(atm_disp_full_band*3.6e6, l1, l2)
adc.setOrientation(adcOptimumAngle+offset)
# Check that we can use dispersion? If not, how to calculate refraction...?
# We plot below the difference in atmospheric dispersion of the two stars, incl. an ADC.
data = []
for T2 in S2temps:
l_eff_2 = effectiveWavelength(T2, l1, l2)
n2 = at.n_tph(l_eff_2, T, P, H, xc)
atm_disp2 = at.cassini(n_l1, n2, zenith) * 3.6e6
adc_disp2 = adc.calculateDispersion(l1, l_eff_2)[1]
dataEntry = []
for T1 in S1temps:
l_eff_1 = effectiveWavelength(T1, l1, l2)
n1 = at.n_tph(l_eff_1, T, P, H, xc)
atm_disp1 = at.cassini(n_l1, n1, zenith) * 3.6e6
adc_disp1 = adc.calculateDispersion(l1,l_eff_1)[1]
diffDisp = ((adc_disp1 - adc_disp2) - (atm_disp1 - atm_disp2)) * 1e3
dataEntry.append(diffDisp)
data.append(dataEntry)
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
fig2 = plt.figure(figsize=(9,6))
ax = fig2.add_subplot(111)
ax2 = ax.twiny()
ax.grid(True, alpha=0.3)
linestylesList = [':', '-.', '--', '-', (0, (3, 1, 1, 1, 1, 1))]
for i in range(len(data)):
ax.plot(S1temps, data[i], label=str(S2temps[i]) + ' K', linestyle=linestylesList[i], color='k', linewidth=2)
S1temp_ticks = ax.get_xticks()
S1temp_tick_loc = ax.get_xticks()
l_eff_ticks = []
for T in S1temp_ticks:
l_eff_ticks.append(round( (effectiveWavelength(T, l1, l2)*1000-1620),1))
# ax2.plot(S1temps, data[0], alpha=0)
ax2.set_xticklabels(l_eff_ticks)
ax2.set_xlim(np.min(S1temps), np.max(S1temps))
ax2.set_xlabel('$\lambda_{eff} - 1620$ (nm) ', fontsize=18)
ax2.tick_params(axis ='both', direction='in', labelsize=16)
ax.set_xlabel('Temperature of Star 1 (K)', fontsize=18)
ax.set_ylabel('$\Delta(\Delta$R) ($\mu$as), incl. ADC', fontsize=18)
ax.legend(fontsize=16, labelspacing=0.3, loc='lower right')
ax.set_xlim(np.min(S1temps), np.max(S1temps))
ax.tick_params(axis ='both', direction='in', labelsize=16)
plt.show()
########################################################################################
########################################################################################
########################################################################################
+353
View File
@@ -0,0 +1,353 @@
#!/usr/bin/env Python
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
from AstroAtmosphere import *
from tabulate import tabulate
'''
@ Author: Joost van den Born
@ Contact: born@astron.nl
@ Description: An exploration of the parameter space for the Ciddor-Cassini model.
The results here are given in table 3 and Appendix A of the paper.
'''
if __name__ == '__main__':
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
at = Observatory()
pdrFigs = False
errorSpace = False
generate_images = True
T = 279.65 # k
P = 71200 # Pa
H = 0.22
xc = 450 # ppm
lat = -24.5983 # degrees
h = 3064 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 30 # degrees
rho = at.rho(p=P, T=T, RH=H, xc=xc)
disp = dispersion(lat, h)
disp.setReducedHeight(P, rho)
# Change dl, dT, dP, dRH, dCO2 to the values of interest.
n1 = at.n_tph(l=1.49, T=279.65, p=71200, RH=0.22, xc=450)
n2 = at.n_tph(l=1.78, T=279.65, p=71200, RH=0.22, xc=450)
R1ca = at.dn_tph(l=1.49, T=279.65, p=71200, RH=0.22, xc=450,
dl=0.001, dT=0.4, dP=10, dRH=0.01, dCO2=20)
R2ca = at.dn_tph(l=1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl=0.001, dT=0.4, dP=10, dRH=0.01, dCO2=20)
# print (n1, at.dn_tph(l=1.49, T=288.0, p=101325, RH=0.0, xc=450, dl=0.0, dT=0.5, dP=0.0, dRH=0.0, dCO2=0) )
# print(disp.cassiniError( zenith, l1, l2, T, P, H, xc, dl1=0.001, dl2=0.001, dT=0.4, dP=10, dRH=0.01, dCO2=20 )*3.6e9)
zenith_true = 30 # degrees
dispValue = disp.cassini(n1, n2, zenith_true)
defaultDispCA = disp.cassini(n1, n2, zenith_true)*3.6e6
errorDispValue = disp.cassiniError(zenith, l1, l2, T, P, H, xc, dl1=0.001, dl2=0.001, dT=0.4, dP=10, dRH=0.01, dCO2=20 )
print ('Dispersion between 1.49 and 1.78 micron at z = %i deg: %.3f +/- %.3f mas' %(zenith_true, dispValue*3.6e6, errorDispValue*3.6e6))
R1s = at.n_tph(l=1.49, T=288.15, p=101325, RH=0.0, xc=450)
R2s = at.n_tph(l=1.78, T=288.15, p=101325, RH=0.0, xc=450)
# Varying the parameters in the non-errror formula:
if pdrFigs:
Trange = np.linspace(262, 298, 1000)
Prange = np.linspace(69000, 73000, 1000)
Hrange = np.linspace(0.1, 0.3, 1000)
fig = plt.figure()
# Variation in T
dispListS, dispListCA = [], []
for t in Trange:
R1ca = at.n_tph(l=1.49, T=t, p=71200, RH=0.22, xc=450)
R2ca = at.n_tph(l=1.78, T=t, p=71200, RH=0.22, xc=450)
dispValueS = disp.cassini(R1s, R2s, zenith_true) * 3.6e6
dispValueCA = disp.cassini(R1ca, R2ca, zenith_true) * 3.6e6
dispListS.append(dispValueS)
dispListCA.append(dispValueCA)
ax1 = fig.add_subplot(131)
ax1.grid(True, alpha=0.3)
ax1.plot(Trange, np.abs(dispListCA-defaultDispCA), 'r-', label='Cerro Armazones (p=712 mbar, T=6.5C, H=22%)')
ax1.set_xlabel('Temperature (K)')
ax1.set_ylabel('Dispersion (mas)')
ax1.tick_params(axis ='both', direction='in')
# Variation in P
dispListS, dispListCA = [], []
for p in Prange:
R1ca = at.n_tph(l=1.49, T=279.65, p=p, RH=0.22, xc=450)
R2ca = at.n_tph(l=1.78, T=279.65, p=p, RH=0.22, xc=450)
dispValueS = disp.cassini(R1s, R2s, zenith_true) * 3.6e6
dispValueCA = disp.cassini(R1ca, R2ca, zenith_true) * 3.6e6
dispListS.append(dispValueS)
dispListCA.append(dispValueCA)
ax2 = fig.add_subplot(132)
ax2.grid(True, alpha=0.3)
ax2.plot(Prange/100, np.abs(dispListCA-defaultDispCA), 'r-', label='Cerro Armazones (p=712 mbar, T=6.5C, H=22%)')
ax2.set_xlabel('Pressure (mbar)')
ax2.set_ylabel('Dispersion (mas)')
ax2.tick_params(axis ='both', direction='in')
# Variation in T
dispListS, dispListCA = [], []
for h in Hrange:
R1ca = at.n_tph(l=1.49, T=279.65, p=71200, RH=h, xc=450)
R2ca = at.n_tph(l=1.78, T=279.65, p=71200, RH=h, xc=450)
dispValueS = disp.cassini(R1s, R2s, zenith_true) * 3.6e6
dispValueCA = disp.cassini(R1ca, R2ca, zenith_true) * 3.6e6
dispListS.append(dispValueS)
dispListCA.append(dispValueCA)
ax3 = fig.add_subplot(133)
ax3.grid(True, alpha=0.3)
ax3.plot(Hrange*100, np.abs(dispListCA-defaultDispCA), 'r-', label='Cerro Armazones (p=712 mbar, T=6.5C, H=22%)')
ax3.set_xlabel('Humidity (%)')
ax3.set_ylabel('Dispersion (mas)')
ax3.tick_params(axis ='both', direction='in')
plt.tight_layout()
plt.show()
# Plotting variations in parameters with corresponding errors.
if errorSpace:
Trange = np.linspace(-50, 50, 100)
Prange = np.linspace(-50, 50, 10)
Hrange = np.linspace(-0.025, 0.025, 10)
fig = plt.figure()
# Variation in T
errorRange = [0.1, 0.2, 0.3, 1.0]
errorRange = errorRange[::-1]
ax1 = fig.add_subplot(111)
ax1.grid(True, alpha=0.3)
for e in range(len(errorRange)):
dispList = []
errorList = []
for t in Trange:
R1n = at.n_tph(l=1.49, T=279.65+t, p=71200, RH=0.22, xc=450)
R2n = at.n_tph(l=1.78, T=279.65+t, p=71200, RH=0.22, xc=450)
dispValue = disp.cassini(R1n, R2n, zenith_true) * 3.6e6
errorValue = disp.cassiniError(zenith_true, l1, l2, T, P, H, xc, dl1=0.0, dl2=0.0, dT=errorRange[e], dP=0, dRH=0, dCO2=0 ) * 3.6e6
dispList.append(dispValue)
errorList.append(errorValue)
dispList = np.asarray(dispList)
errorList = np.asarray(errorList)
ax1.fill_between(Trange+279.65, dispList-errorList, dispList+errorList, label='$\delta p$ = %s $^{\circ}$C' %(errorRange[e]))
ax1.plot(Trange+279.65, dispList, 'k-')
ax1.set_xlabel('Temperature (K)')
ax1.set_ylabel('Dispersion (mas)')
ax1.set_xlim([np.min(Trange)+279.65,np.max(Trange)+279.65])
ax1.set_ylim([19,32])
handles, labels = ax1.get_legend_handles_labels()
ax1.legend(handles[::-1], labels[::-1], title='Error in measurements')
plt.tight_layout()
plt.show()
# Varying the parameters in the uncertainty formula:
if generate_images:
################################################
# Listing some values and the results in a table
pList = [101325, 101325, 71200, 71200, 71200]
TList = [288.15, 288.15, 279.65, 279.65, 279.65]
HList = [0, 0, 0.22, 0.22, 0.6]
CO2List = [450] * len(pList)
lList = [1.35] * len(pList)
dpList = [0, 20, 20, 10, 20]
dTlist = [0, 0.2, 0.2, 0.1, 0.2]
dRHlist = [0, 0.02, 0.02, 0.01, 0.02]
dCO2list = [0, 20, 20, 10, 20]
dlList = [0, 0.001, 0.001, 0.001, 0.001]
refractionList, sigmaList = [], []
for i in range(len(pList)):
n = at.n_tph(l=lList[i], T=TList[i], p=pList[i], RH=HList[i], xc=CO2List[i])
s = at.dn_tph(l=lList[i], T=TList[i], p=pList[i], RH=HList[i], xc=CO2List[i],
dl=dlList[i], dT=dTlist[i], dP=dpList[i], dRH=dRHlist[i], dCO2=dCO2list[i])
refractionList.append((n-1)*1e8)
sigmaList.append(s*1e8)
tableData = []
for i in range(len(pList)):
tableData.append([pList[i], TList[i], HList[i], CO2List[i], lList[i], dpList[i], dTlist[i], dRHlist[i], dCO2list[i], dlList[i], refractionList[i], sigmaList[i]])
print('REFRACTION: ')
print (tabulate(tableData, headers = ['p', 'T', 'H', 'x_c', 'l', 'dp', 'dT', 'dH', 'dx_c', 'dl', 'n', 's']))
#################################################
print ('\n\n\n')
################################################
# Listing some values and the results in a table
pList = [101325, 101325, 101325, 101325, 101325]
TList = [288.15, 288.15, 288.15, 288.15, 288.15]
HList = [0.0, 0.0, 0.0, 0.0, 0.0]
CO2List = [450] * len(pList)
# lList = [1.35] * len(pList)
dpList = [0, 10, 10, 20, 100]
dTlist = [0, 0.1, 0.2, 0.2, 1.0]
dRHlist = [0, 0.01, 0.01, 0.02, 0.05]
dCO2list = [0, 20, 20, 20, 20]
# dlList = [0, 0.001, 0.001, 0.001, 0.001] # Uncomment this and comment the next
# line to check the impact with uncertainty in wavelength
dlList = [0, 0, 0, 0, 0]
dzList = [0] * len(pList)
dispersionList, errorList = [], []
for i in range(len(pList)):
zenith_true = 30 # degrees
n1 = at.n_tph(l=1.49, T=TList[i], p=pList[i], RH=HList[i], xc=CO2List[i])
n2 = at.n_tph(l=1.78, T=TList[i], p=pList[i], RH=HList[i], xc=CO2List[i])
dn1 = at.dn_tph(l=1.49, T=TList[i], p=pList[i], RH=HList[i], xc=CO2List[i],
dl=dlList[i], dT=dTlist[i], dP=dpList[i], dRH=dRHlist[i], dCO2=dCO2list[i])
dn2 = at.dn_tph(l=1.78, T=TList[i], p=pList[i], RH=HList[i], xc=CO2List[i],
dl=dlList[i], dT=dTlist[i], dP=dpList[i], dRH=dRHlist[i], dCO2=dCO2list[i])
dispValue = disp.cassini(n1, n2, zenith_true) * 3.6e6
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, TList[i], pList[i], HList[i], CO2List[i],
dl1=dlList[i], dl2=dlList[i], dT=dTlist[i], dP=dpList[i], dRH=dRHlist[i], dCO2=dCO2list[i], dz=dzList[i] ) * 3.6e9
dispersionList.append(dispValue)
errorList.append(errorDispValue)
tableData = []
for i in range(len(pList)):
tableData.append([pList[i], TList[i], HList[i], CO2List[i], dpList[i], dTlist[i], dRHlist[i], dCO2list[i], dlList[i], dispersionList[i], errorList[i]])
print('DISPERSION: ')
print (tabulate(tableData, headers = ['p', 'T', 'H', 'x_c', 'dp', 'dT', 'dH', 'dx_c', 'dl', 'dispersion', 'error']))
# Variation of the uncertainty in P
pRange = np.linspace(0, 100, 1000)
errorList = []
for p in pRange:
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl1=0.001, dl2=0.001, dT=0.2, dP=p, dRH=0.01, dCO2=20, dz=0 ) * 3.6e9
errorList.append(errorDispValue)
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.plot(pRange, errorList, 'k-')
ax.set_xlabel('$\sigma_p$ (Pa)')
ax.set_ylabel('$\sigma_{\Delta R}$ ($\mu$as)')
ax.set_xlim([np.min(pRange), np.max(pRange)])
ax.set_ylim([np.min(errorList)-0.05*(np.max(errorList) - np.min(errorList)), np.max(errorList)])
ax.tick_params(axis ='both', direction='in')
plt.tight_layout()
# Variation of the uncertainty in T
TRange = np.linspace(0.0, 1, 1000)
errorList = []
for T in TRange:
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl1=0.001, dl2=0.001, dT=T, dP=10, dRH=0.01, dCO2=20, dz=0 ) * 3.6e9
errorList.append(errorDispValue)
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.plot(TRange, errorList, 'k-')
ax.set_xlabel('$\sigma_T$ (K)')
ax.set_ylabel('$\sigma_{\Delta R}$ ($\mu$as)')
ax.set_xlim([np.min(TRange), np.max(TRange)])
ax.set_ylim([np.min(errorList)-0.05*(np.max(errorList) - np.min(errorList)), np.max(errorList)])
ax.tick_params(axis ='both', direction='in')
plt.tight_layout()
# Variation of the uncertainty in H
HRange = np.linspace(0.000, 0.05, 1000)
errorList = []
for H in HRange:
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl1=0.001, dl2=0.001, dT=0.2, dP=10, dRH=H, dCO2=20, dz=0 ) * 3.6e9
errorList.append(errorDispValue)
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.plot(HRange, errorList, 'k-')
ax.set_xlabel('$\sigma_H$')
ax.set_ylabel('$\sigma_{\Delta R}$ ($\mu$as)')
ax.set_xlim([np.min(HRange), np.max(HRange)])
ax.set_ylim([np.min(errorList)-0.05*(np.max(errorList) - np.min(errorList)), np.max(errorList)])
ax.tick_params(axis ='both', direction='in')
plt.tight_layout()
# Variation of the uncertainty in xc
CO2Range = np.linspace(0, 200, 1000)
errorList = []
for CO2 in CO2Range:
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl1=0.001, dl2=0.001, dT=0.2, dP=10, dRH=0.01, dCO2=CO2, dz=0 ) * 3.6e9
errorList.append(errorDispValue)
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.plot(CO2Range, errorList, 'k-')
ax.set_xlabel('$\sigma_{x_c}$ (ppm)')
ax.set_ylabel('$\sigma_{\Delta R}$ ($\mu$as)')
ax.set_xlim([np.min(CO2Range), np.max(CO2Range)])
ax.set_ylim([np.min(errorList)-0.05*(np.max(errorList) - np.min(errorList)), np.max(errorList)])
ax.tick_params(axis ='both', direction='in')
plt.tight_layout()
# Variation of the uncertainty in wavelength
lRange = np.linspace(0.0, 0.001, 1000)
errorList = []
for l in lRange:
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl1=l, dl2=l, dT=0.2, dP=10, dRH=0.01, dCO2=20, dz=0 ) * 3.6e9
errorList.append(errorDispValue)
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.plot(lRange*1000, errorList, 'k-')
ax.set_xlabel('$\sigma_{\lambda}$ (nm)')
ax.set_ylabel('$\sigma_{\Delta R}$ ($\mu$as)')
ax.set_xlim([np.min(lRange)*1000, np.max(lRange)*1000])
ax.set_ylim([np.min(errorList)-0.05*(np.max(errorList) - np.min(errorList)), np.max(errorList)])
ax.tick_params(axis ='both', direction='in')
plt.tight_layout()
# Variation of the uncertainty in zenith
zRange = np.linspace(0, 60/3600, 1000)
errorList = []
for z in zRange:
errorDispValue = disp.cassiniError( zenith_true, 1.49, 1.78, T=279.65, p=71200, RH=0.22, xc=450,
dl1=0.001, dl2=0.001, dT=0.2, dP=10, dRH=0.01, dCO2=20, dz=z ) * 3.6e9
errorList.append(errorDispValue)
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.grid(True, alpha=0.3)
ax.plot(zRange*3600, errorList, 'k-')
ax.set_xlim([np.min(zRange)*3600, np.max(zRange)*3600])
ax.set_ylim([np.min(errorList)-0.05*(np.max(errorList) - np.min(errorList)), np.max(errorList)])
ax.set_xlabel('$\sigma_z$ (arcseconds)')
ax.set_ylabel('$\sigma_{\Delta R}$ ($\mu$as)')
ax.tick_params(axis ='both', direction='in')
plt.tight_layout()
plt.show()
@@ -0,0 +1,85 @@
import numpy as np
from AstroAtmosphere import *
'''
Date: 02 May 2019
Author: Joost van den Born
Description: This script compares the uncertainty propagation results to a Monte Carlo method.
'''
# Parameters at Cerro Armazones
T = 288.15 # k
P = 101325 # Pa
H = 0.0
xc = 450 # ppm
lat = -24.5983 # degrees
h = 3064 # m
l1 = 1.49 # micron
l2 = 1.78 # micron
zenith = 45 # degrees
dl1 = 0
dl2 = 0
dT = 0
dP = 2
dRH = 0
dCO2= 0
dz = 1/3600 # degrees
# Initializing dispersion model
at = Observatory()
dPlist = [2, 20, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 20, 200]
dTlist = [0, 0, 0, 0.02, 0.2, 2, 0, 0, 0, 0, 0, 0, 0.02, 0.2, 2 ]
dRHlist= [0, 0, 0, 0, 0, 0, 0.002, 0.02, 0.2, 0, 0, 0, 0.002, 0.02, 0.2]
dCO2list=[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 20, 200, 2, 20, 200]
for i in range(len(dPlist)):
nerr = at.dn_tph(l1, T, P, H, xc, dl=0, dT=dTlist[i], dP=dPlist[i], dRH=dRHlist[i], dCO2=dCO2list[i])
print(nerr, dPlist[i], dTlist[i], dRHlist[i], dCO2list[i])
disp = dispersion(lat, h)
rho_obs = at.rho(P, T, H, xc)
disp.setReducedHeight(P, rho_obs)
zenith_true = 10
# We do a Monte Carlo error analysis on the dispersion.
print('MONTE-CARLO UNCERTAINTY ANALYSIS')
print('-'*35)
for j in range(len(dPlist)):
sigmaP = dPlist[j] # mbar
sigmaT = dTlist[j] # K
sigmaH = dRHlist[j]
sigmaCO2 = dCO2list[j] # ppm
N = 10000
l1sample = np.random.normal(l1, 0, N)
l2sample = np.random.normal(l2, 0, N)
Tsample = np.random.normal(T, sigmaT, N)
Psample = np.random.normal(P, sigmaP, N)
Hsample = np.random.normal(H, sigmaH, N)
CO2sample = np.random.normal(xc, sigmaCO2, N)
dzsample = np.random.normal(zenith_true, dz, N)
results = []
nList = []
for i in range(N):
# Calculate the refraction at l1 and l2
R1ca2 = at.n_tph(l=l1sample[i], T=Tsample[i], p=Psample[i], RH=Hsample[i], xc=CO2sample[i])
R2ca2 = at.n_tph(l=l2sample[i], T=Tsample[i], p=Psample[i], RH=Hsample[i], xc=CO2sample[i])
dispersionValue = disp.cassini(R1ca2, R2ca2, zenith_true)
results.append(dispersionValue)
nList.append(R1ca2)
cassErrMC = np.std(results)*3.6e9
nerrMC = np.std(nList)
print(nerrMC, dPlist[j], dTlist[j], dRHlist[j], dCO2list[j])