zenithangle.py

Knödlseder Jürgen, 10/04/2016 09:43 AM

Download (11.4 KB)

 
1
#! /usr/bin/env python
2
# ==========================================================================
3
# This script computes a visibility cube
4
#
5
# Copyright (C) 2016 Juergen Knoedlseder
6
#
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
#
20
# ==========================================================================
21
import math
22
import gammalib
23
import matplotlib.pyplot as plt
24
import itertools
25

    
26

    
27
# =========================== #
28
# Compute position of the Sun #
29
# =========================== #
30
def sun(jd):
31
    """
32
    From https://en.wikipedia.org/wiki/Position_of_the_Sun
33
    """
34
    # Number of days since Greenwich noon, Terrestrial Time, on 1 January 2000
35
    n = jd - 2451545.0
36

    
37
    # Mean longitude of the Sun, corrected for the aberration of light
38
    L = 280.460 + 0.9856474 * n
39
    while L < 0.0:
40
        L += 360.0
41
    while L >= 360.0:
42
        L -= 360.0
43

    
44
    # Mean anomaly of the Sun
45
    g = 357.528 + 0.9856003 * n
46
    while g < 0.0:
47
        g += 360.0
48
    while g >= 360.0:
49
        g -= 360.0
50

    
51
    # Ecliptic longitude of the Sun
52
    lam = L + 1.915 * math.sin(g*gammalib.deg2rad) + \
53
              0.020 * math.sin(2.0*g*gammalib.deg2rad)
54

    
55
    # Compute Right Ascension and Declination
56
    ra  = math.atan(math.cos(23.43711*gammalib.deg2rad) *
57
                    math.tan(lam*gammalib.deg2rad))*gammalib.rad2deg
58
    dec = math.asin(math.sin(23.43711*gammalib.deg2rad) *
59
                    math.sin(lam*gammalib.deg2rad))*gammalib.rad2deg
60

    
61
    # Put ra in the right quadrant (same quadrant as lam)
62
    while lam < 0.0:
63
        lam += 360.0
64
    while lam >= 360.0:
65
        lam -= 360.0
66
    while ra < 0.0:
67
        ra += 360.0
68
    while ra >= 360.0:
69
        ra -= 360.0
70
    q_lam = int(lam / 90.0)
71
    q_ra  = int(ra / 90.0)
72
    ra   += (q_lam-q_ra)*90.0
73
    print(q_lam,q_ra,q_lam-q_ra,(q_lam-q_ra)*90.0)
74

    
75
    # Return
76
    return (ra, dec)
77

    
78

    
79
# ========================== #
80
# Compute Sun constraint map #
81
# ========================== #
82
def sun_constraint(lim=100.0):
83
    """
84
    Compute Sun constraint map
85
    """
86
    # Initialise sky map
87
    map = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180)
88

    
89
    # Set Julian days for one "orbit" of the Sun
90
    nsteps = 1000
91
    jds    = [float(i)*365.25636/float(nsteps) for i in range(nsteps)]
92
    norm   = 1.0/float(nsteps)
93

    
94
    # Loop over JDs
95
    for jd in jds:
96

    
97
        # Get position of sun
98
        ra, dec = sun(jd)
99
        print(jd,ra,dec)
100
        sundir  = gammalib.GSkyDir()
101
        sundir.radec_deg(ra, dec)
102

    
103
        # Compute for all pixels distance to Sun
104
        for inx in range(map.npix()):
105
            dir = map.inx2dir(inx)
106
            if sundir.dist_deg(dir) > lim:
107
                map[inx] += norm
108

    
109
    # Save map
110
    map.save('sun.fits', True)
111

    
112

    
113
# ================= #
114
# Plot zenith angle #
115
# ================= #
116
def plot_zenith_angle(lat=-24.4, lim=60.0):
117
    """
118
    Plot zenith angle.
119

120
    The zenith angle of a position (ra,dec) depends on the declination and
121
    the hour angle h and is given by
122

123
    zenith(ra,dec) = arccos( sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(h) )
124

125
    The hour angle (or local hour angle, LHA) is defined as the difference
126
    between local siderial time (LST) and the Right Ascension
127
    
128
    h = LST - ra
129

130
    LST is the difference between the Greenwhich siderial time (GST) and the
131
    geographic longitude of the observer
132
    
133
    LST = GST - lon
134
    
135
    where lon is counted positive west from the meridian. Hence
136
    
137
    h = GST - lon - ra
138

139
    and
140

141
    zenith(ra,dec) = arccos( sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(GST-lon-ra) )
142

143
    The zenith angle of the Sun depends of the hour angle in the local solar
144
    time h_sol, the current declination of the Sun dec_sol and the geographic
145
    latitude
146
    
147
    zenith_sol = arccos( sin(lat) * sin(dec_sol) + cos(lat) * cos(dec_sol) * cos(h_sol) )
148

149
    """
150
    # Initialise sky map
151
    map = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180,60)
152
    
153
    # Set hour angle array in radians
154
    hours = [float(i)*0.1 for i in range(3600)]
155
    norm  = 24.0 / float(len(hours)) * 365.0    # hours per year per time step
156

    
157
    # Create figure
158
    plt.figure(1)
159
    plt.title('Zenith angles')
160

    
161
    # Loop over declinations
162
    decs    = [float(i)-89.5 for i in range(180)]
163
    visible = [0.0 for i in range(180)]
164
    for k, dec in enumerate(decs):
165

    
166
        # Compute zenith angles for a given declination as function of hour
167
        # angle
168
        cos_dec = math.cos(dec*gammalib.deg2rad)
169
        sin_dec = math.sin(dec*gammalib.deg2rad)
170
        cos_phi = math.cos(lat*gammalib.deg2rad)
171
        sin_phi = math.sin(lat*gammalib.deg2rad)
172
        zeniths = [math.acos(sin_phi*sin_dec +
173
                             cos_phi*cos_dec*math.cos(h*gammalib.deg2rad))*
174
                   gammalib.rad2deg for h in hours]
175

    
176
        # Select only zenith angles < lim
177
        x = []
178
        y = []
179
        for i in range(len(hours)):
180
            if zeniths[i] <= lim:
181
                x.append(hours[i])
182
                y.append(zeniths[i])
183

    
184
        # Update visibility
185
        visible[k] = float(len(x)) * norm
186

    
187
        # Plot zenith angle
188
        if len(x) > 0:
189
            plt.plot(x, y, 'r-')
190

    
191
        # Fill visibility cube for all hour angles. For each hour angle compute
192
        # the appropriate zenith angle index iz, and set all Right Ascensions.
193
        # This implies that we loop uniformly over all local siderial times.
194
        #
195
        # In reality, however, the Sun constraint (Sun under horizon) translates
196
        # into a set of time intervals during which observations are performed.
197
        for i in range(len(hours)):
198
            iz = int(zeniths[i]/1.0)
199
            if iz < 60:
200
                for ira in range(360):
201
                    index = ira + 360 * k
202
                    map[index,iz] += norm
203

    
204
    # Set axes
205
    plt.xlabel('Hour angle (degrees)')
206
    plt.ylabel('Zenith angle (degrees)')
207

    
208
    # Create figure
209
    plt.figure(2)
210
    plt.title('Visibility')
211
    plt.plot(decs, visible, 'r-')
212
    plt.xlabel('Declination (degrees)')
213
    plt.ylabel('Visibility (hours)')
214

    
215
    # Save sky map
216
    map.save('viscube_north.fits', True)
217

    
218
    # Show plot
219
    plt.show()
220

    
221
    # Return
222
    return
223

    
224

    
225
# ======================== #
226
# Compute zenith angle map #
227
# ======================== #
228
def zenith_angle_map(lat=-24.4):
229
    """
230
    Compute zenith angle map
231

232
    The zenith angle of a position (ra,dec) depends on the declination and
233
    the hour angle h and is given by
234

235
    zenith(h,dec) = arccos( sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(h) )
236

237
    The hour angle h (or local hour angle, LHA) is defined as the difference
238
    between local siderial time (LST) and the Right Ascension
239
    
240
    h = LST - ra
241

242
    LST is the difference between the Greenwich siderial time (GST) and the
243
    geographic longitude of the observer
244
    
245
    LST = GST - lon
246
    
247
    where lon is counted positive west from the meridian. Hence
248
    
249
    h = GST - lon - ra
250

251
    The map is computed for h=-ra which is equivalent to GST=lon (or LST=0).
252
    In other words, the map corresponds to the time when ra=0 passes the
253
    local meridian.
254
    """
255
    # Initialise zenith angle map
256
    map = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180)
257

    
258
    # Set hour angle and declination vectors
259
    hours = [float(i) for i in range(360)]
260
    decs  = [float(i)-89.5 for i in range(180)]
261

    
262
    # Precompute latitude terms
263
    cos_lat = math.cos(lat*gammalib.deg2rad)
264
    sin_lat = math.sin(lat*gammalib.deg2rad)
265

    
266
    # Loop over all declination and hour angles and compute the zenith
267
    # angle. Store the zenith angle in the map
268
    index = 0
269
    for dec in decs:
270
        cos_dec = math.cos(dec*gammalib.deg2rad)
271
        sin_dec = math.sin(dec*gammalib.deg2rad)
272
        for h in hours:
273
            cos_h = math.cos(h*gammalib.deg2rad)
274
            zenith = math.acos(sin_lat*sin_dec +
275
                               cos_lat*cos_dec*cos_h)*gammalib.rad2deg
276
            map[index] = zenith
277
            index     += 1
278

    
279
    # Save zenith angle map
280
    map.save('zenith_angles.fits', True)
281

    
282
    # Return
283
    return
284

    
285

    
286
# ======================= #
287
# Compute visibility cube #
288
# ======================= #
289
def visibility_cube():
290
    """
291
    Compute visibility cube by displacing the zenith angle map for all
292
    hour angles. The visibility cube contains the number of hours a given
293
    celestial position is visible under a given zenith angle interval.
294
    Summing over all zenith angle intervals specifies for how long a given
295
    celestial position will be visible.
296
    """
297
    # Set zenith angle range and bin size
298
    dz = 1.0
299
    nz = int(60.0/dz)
300
    
301
    # Initialise visibility cube
302
    cube = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180,nz)
303

    
304
    # Load zenith angle map
305
    map = gammalib.GSkyMap('zenith_angles.fits')
306

    
307
    # Setup hour angle weights. They specify for how many hours a given
308
    # hour angle will be observed during one year.
309
    hours = hour_angle_weight()
310

    
311
    # Compute normalisation factor
312
    norm = 365.0/float(len(hours))
313

    
314
    # Loop over all hour angle weights
315
    for h, weight in enumerate(hours):
316

    
317
        # Compute temporal shift
318
        shift = 360.0/float(len(hours)) * float(h)
319

    
320
        # Initialise shifted zenith angle map
321
        map_shift = gammalib.GSkyMap('CAR','CEL',shift,0.0,-1.0,1.0,360,180)
322

    
323
        # Merge zenith angle map into shifted map
324
        map_shift += map
325
        map_shift.save('zenith_angles_shifted.fits', True)
326

    
327
        # Loop over all pixels of shifted map
328
        for i, zenith in enumerate(map_shift):
329
            iz     = int(zenith/dz)
330
            if iz < nz:
331
                cube[i,iz] += weight * norm
332

    
333
    # Save visibility cube
334
    cube.save('viscube.fits', True)
335

    
336
    # Return
337
    return
338

    
339

    
340
# ======================= #
341
# Compute visibility cube #
342
# ======================= #
343
def hour_angle_weight():
344
    """
345
    Compute during which time the array was observing due to hour angle
346
    constraints. We take here a very simple model where the night lasts
347
    between 6 and 10 hours (or 90-150 degrees), with a sinusoidal variation
348
    along the year. Every day the hour angle of the Sun set is advancing by 
349
    4 minutes.
350
    """
351
    # Set number of hour angle bins
352
    nsteps = 1000
353
    d_min  =  6.0/24.0*nsteps
354
    d_max  = 10.0/24.0*nsteps
355
    
356
    # Initialise hour angle array and weight
357
    hours  = [0.0 for i in range(nsteps)]
358
    weight = 24.0/float(nsteps)  # Weight per hour angle step
359

    
360
    # Loop over all days in one year
361
    for day in range(nsteps):
362
        h_set  = day
363
        h_rise = day + int(d_min + (d_max-d_min)*math.sin(day/float(nsteps)*gammalib.pi))
364
        for i in range(h_set,h_rise):
365
            if i >= nsteps:
366
                hours[i-nsteps] += weight
367
            else:
368
                hours[i] += weight
369

    
370
    # Return
371
    return hours
372

    
373

    
374
#==========================#
375
# Main routine entry point #
376
#==========================#
377
if __name__ == '__main__':
378

    
379
    # Plot zenith angle
380
    #zenith_angle_map(lat=-24.4)        # South
381
    zenith_angle_map(lat=+28.7569)     # North
382
    #visibility_cube()
383
    visibility_cube()
384