Commit db97908e4eecd708999e1e74e4424232bb6981ac
1 parent
28cac9e0
Implementation of visibility computation using the Sun constraint (Moon constraint is still missing)
Showing
3 changed files
with
330 additions
and
103 deletions
cscripts/csviscube.par
| @@ -28,7 +28,9 @@ outfile, f, a, viscube.fits,,, "Visibility cube output file" | @@ -28,7 +28,9 @@ outfile, f, a, viscube.fits,,, "Visibility cube output file" | ||
| 28 | # | 28 | # |
| 29 | # Visibility cube parameters | 29 | # Visibility cube parameters |
| 30 | #=========================== | 30 | #=========================== |
| 31 | -binsz, r, h, 1.0,,, "Image scale (in degrees/pixel)" | 31 | +binsz, r, h, 1.0,,, "Image scale (deg/pixel)" |
| 32 | +dz, r, h, 1.0,,, "Zenith angle bin size (deg)" | ||
| 33 | +zmax, r, h, 60.0,,, "Maximum zenith angle (deg)" | ||
| 32 | 34 | ||
| 33 | # | 35 | # |
| 34 | # Standard parameters | 36 | # Standard parameters |
cscripts/csviscube.py
| @@ -37,7 +37,7 @@ class csviscube(ctools.cscript): | @@ -37,7 +37,7 @@ class csviscube(ctools.cscript): | ||
| 37 | # Constructor | 37 | # Constructor |
| 38 | def __init__(self, *argv): | 38 | def __init__(self, *argv): |
| 39 | """ | 39 | """ |
| 40 | - Constructor. | 40 | + Constructor |
| 41 | 41 | ||
| 42 | Parameters | 42 | Parameters |
| 43 | ---------- | 43 | ---------- |
| @@ -54,8 +54,10 @@ class csviscube(ctools.cscript): | @@ -54,8 +54,10 @@ class csviscube(ctools.cscript): | ||
| 54 | self._name = 'csviscube' | 54 | self._name = 'csviscube' |
| 55 | self._version = '1.0.0' | 55 | self._version = '1.0.0' |
| 56 | 56 | ||
| 57 | - # Initialise application by calling the appropriate class | ||
| 58 | - # constructor. | 57 | + # Initialise members |
| 58 | + self._cube = gammalib.GSkyMap() | ||
| 59 | + | ||
| 60 | + # Initialise application by calling the appropriate class constructor | ||
| 59 | self._init_cscript(argv) | 61 | self._init_cscript(argv) |
| 60 | 62 | ||
| 61 | # Return | 63 | # Return |
| @@ -72,10 +74,7 @@ class csviscube(ctools.cscript): | @@ -72,10 +74,7 @@ class csviscube(ctools.cscript): | ||
| 72 | self['tmax'].real() | 74 | self['tmax'].real() |
| 73 | self['geolon'].real() | 75 | self['geolon'].real() |
| 74 | self['geolat'].real() | 76 | self['geolat'].real() |
| 75 | - self['sunzenith'].real() | ||
| 76 | - self['moonzenith'].real() | ||
| 77 | self['outfile'].filename() | 77 | self['outfile'].filename() |
| 78 | - self['binsz'].real() | ||
| 79 | 78 | ||
| 80 | # Write input parameters into logger | 79 | # Write input parameters into logger |
| 81 | self._log_parameters(gammalib.TERSE) | 80 | self._log_parameters(gammalib.TERSE) |
| @@ -83,38 +82,301 @@ class csviscube(ctools.cscript): | @@ -83,38 +82,301 @@ class csviscube(ctools.cscript): | ||
| 83 | # Return | 82 | # Return |
| 84 | return | 83 | return |
| 85 | 84 | ||
| 86 | - def _hour_angle_weight(self): | 85 | + def _zenith_angle_map(self, nx, ny, dx, dy): |
| 86 | + """ | ||
| 87 | + Compute zenith angle map | ||
| 88 | + | ||
| 89 | + Parameters | ||
| 90 | + ---------- | ||
| 91 | + nx : int | ||
| 92 | + Number of Right Ascension pixels. | ||
| 93 | + ny : int | ||
| 94 | + Number of Declination pixels. | ||
| 95 | + dx : float | ||
| 96 | + Right Ascension pixel size in degrees. | ||
| 97 | + dy : float | ||
| 98 | + Declination pixel size in degrees. | ||
| 99 | + | ||
| 100 | + Returns | ||
| 101 | + ------- | ||
| 102 | + map : `~gammalib.GSkyMap` | ||
| 103 | + Allsky map comprising the zenith angle for an hour angle of 0. | ||
| 104 | + | ||
| 105 | + The zenith angle of a position (ra,dec) depends on the declination and | ||
| 106 | + the hour angle h and is given by | ||
| 107 | + | ||
| 108 | + zenith(h,dec) = arccos( sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(h) ) | ||
| 109 | + | ||
| 110 | + The hour angle h (or local hour angle, LHA) is defined as the difference | ||
| 111 | + between local siderial time (LST) and the Right Ascension | ||
| 112 | + | ||
| 113 | + h = LST - ra | ||
| 114 | + | ||
| 115 | + The map is computed for h=-ra which is equivalent to GST=lon (or LST=0). | ||
| 116 | + In other words, the map corresponds to the time when ra=0 passes through | ||
| 117 | + the local meridian. | ||
| 87 | """ | 118 | """ |
| 88 | - Compute hour angle weight vector | 119 | + # Initialise zenith angle map |
| 120 | + map = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-dx,dy,nx,ny) | ||
| 121 | + | ||
| 122 | + # Set hour angle and declination vectors | ||
| 123 | + hours = [(float(i)+0.5)*dx for i in range(nx)] | ||
| 124 | + decs = [(float(i)+0.5)*dy-90.0 for i in range(ny)] | ||
| 125 | + | ||
| 126 | + # Get array geographic latitude | ||
| 127 | + geolat = self['geolat'].real() | ||
| 128 | + | ||
| 129 | + # Precompute latitude terms | ||
| 130 | + cos_lat = math.cos(geolat*gammalib.deg2rad) | ||
| 131 | + sin_lat = math.sin(geolat*gammalib.deg2rad) | ||
| 132 | + | ||
| 133 | + # Loop over all declinations and hour angles and compute the zenith | ||
| 134 | + # angle. Store the zenith angle in the map | ||
| 135 | + index = 0 | ||
| 136 | + for dec in decs: | ||
| 137 | + cos_dec = math.cos(dec*gammalib.deg2rad) | ||
| 138 | + sin_dec = math.sin(dec*gammalib.deg2rad) | ||
| 139 | + for h in hours: | ||
| 140 | + cos_h = math.cos(h*gammalib.deg2rad) | ||
| 141 | + zenith = math.acos(sin_lat*sin_dec + | ||
| 142 | + cos_lat*cos_dec*cos_h)*gammalib.rad2deg | ||
| 143 | + map[index] = zenith | ||
| 144 | + index += 1 | ||
| 145 | + | ||
| 146 | + # Log zenith angle map | ||
| 147 | + self._log_header2(gammalib.EXPLICIT, 'Zenith angle map') | ||
| 148 | + self._log_string(gammalib.EXPLICIT, str(map)) | ||
| 149 | + | ||
| 150 | + # Return zenith angle map | ||
| 151 | + return map | ||
| 152 | + | ||
| 153 | + def _sun_radec(self, time): | ||
| 154 | + """ | ||
| 155 | + Compute Right Ascension and Declination of the Sun | ||
| 156 | + | ||
| 157 | + Parameters | ||
| 158 | + ---------- | ||
| 159 | + time : `~gammalib.GTime()` | ||
| 160 | + Time for which declination is to be computed | ||
| 161 | + | ||
| 162 | + Returns | ||
| 163 | + ------- | ||
| 164 | + ra, dec : tuple of float | ||
| 165 | + Right Ascension and Declination of the Sun in degrees | ||
| 166 | + | ||
| 167 | + The formulae are inspired from | ||
| 168 | + https://en.wikipedia.org/wiki/Position_of_the_Sun | ||
| 169 | + """ | ||
| 170 | + # Compute number of days since Greenwich noon, Terrestrial Time, on | ||
| 171 | + # 1 January 2000 | ||
| 172 | + n = time.jd() - 2451545.0 | ||
| 173 | + | ||
| 174 | + # Compute mean longitude of the Sun in degrees, corrected for the | ||
| 175 | + # aberration of light | ||
| 176 | + L = 280.460 + 0.9856474 * n | ||
| 177 | + while L < 0.0: | ||
| 178 | + L += 360.0 | ||
| 179 | + while L >= 360.0: | ||
| 180 | + L -= 360.0 | ||
| 181 | + | ||
| 182 | + # Compute the mean anomaly of the Sun in radians | ||
| 183 | + g = (357.528 + 0.9856003 * n) * gammalib.deg2rad | ||
| 184 | + | ||
| 185 | + # Compute the ecliptic longitude of the Sun in degrees | ||
| 186 | + lam = L + 1.915 * math.sin(g) + 0.020 * math.sin(2.0*g) | ||
| 187 | + | ||
| 188 | + # Compute Right Ascension and Declination of the Sun in degrees | ||
| 189 | + ra = math.atan(math.cos(23.43711*gammalib.deg2rad) * | ||
| 190 | + math.tan(lam*gammalib.deg2rad))*gammalib.rad2deg | ||
| 191 | + dec = math.asin(math.sin(23.43711*gammalib.deg2rad) * | ||
| 192 | + math.sin(lam*gammalib.deg2rad))*gammalib.rad2deg | ||
| 193 | + | ||
| 194 | + # Put ra in the right quadrant (same quadrant as lam) | ||
| 195 | + while lam < 0.0: | ||
| 196 | + lam += 360.0 | ||
| 197 | + while lam >= 360.0: | ||
| 198 | + lam -= 360.0 | ||
| 199 | + while ra < 0.0: | ||
| 200 | + ra += 360.0 | ||
| 201 | + while ra >= 360.0: | ||
| 202 | + ra -= 360.0 | ||
| 203 | + q_lam = int(lam / 90.0) | ||
| 204 | + q_ra = int(ra / 90.0) | ||
| 205 | + ra += (q_lam-q_ra)*90.0 | ||
| 206 | + | ||
| 207 | + # Return declination of the Sun | ||
| 208 | + return ra, dec | ||
| 209 | + | ||
| 210 | + def _sun_ra_exclusion(self, time): | ||
| 211 | + """ | ||
| 212 | + Compute the Right Ascension difference in degrees before and after | ||
| 213 | + noon that are excluded due to the Sun constraint | ||
| 214 | + | ||
| 215 | + Parameters | ||
| 216 | + ---------- | ||
| 217 | + time : `~gammalib.GTime()` | ||
| 218 | + Time for which the Sun exclusion is to be computed | ||
| 219 | + | ||
| 220 | + Returns | ||
| 221 | + ------- | ||
| 222 | + dra : float | ||
| 223 | + Positive Right Ascension difference in degrees. | ||
| 224 | + """ | ||
| 225 | + # Get array geographic latitude and minimum Sun zenith angle in radians | ||
| 226 | + geolat = self['geolat'].real() * gammalib.deg2rad | ||
| 227 | + sunzenith = self['sunzenith'].real() * gammalib.deg2rad | ||
| 228 | + | ||
| 229 | + # Compute Right Ascension and Declination of Sun in degrees and | ||
| 230 | + # convert the Declination into radians | ||
| 231 | + sunra, sundec = self._sun_radec(time) | ||
| 232 | + sundec *= gammalib.deg2rad | ||
| 233 | + | ||
| 234 | + # Compute some sines and cosines | ||
| 235 | + cos_sunzenith = math.cos(sunzenith) | ||
| 236 | + sin_geolat = math.sin(geolat) | ||
| 237 | + cos_geolat = math.cos(geolat) | ||
| 238 | + sin_sundec = math.sin(sundec) | ||
| 239 | + cos_sundec = math.cos(sundec) | ||
| 89 | 240 | ||
| 241 | + # Compute Right Ascension difference when Sun is below the mimimum | ||
| 242 | + # zenith angle in degrees | ||
| 243 | + dra = math.acos((cos_sunzenith - sin_geolat * sin_sundec) / | ||
| 244 | + (cos_geolat * cos_sundec)) * gammalib.rad2deg | ||
| 245 | + | ||
| 246 | + # Return | ||
| 247 | + return dra | ||
| 248 | + | ||
| 249 | + def _hour_angle_weight(self): | ||
| 250 | + """ | ||
| 251 | + Compute hour angle weights | ||
| 252 | + | ||
| 253 | + Computes an array specifying how many hours the array was observing | ||
| 254 | + for a given hour angle during the time interval [tmin,tmax]. The hour | ||
| 255 | + angle runs from 0 to 360 degrees. | ||
| 256 | + | ||
| 90 | Compute during which time the array was observing due to hour angle | 257 | Compute during which time the array was observing due to hour angle |
| 91 | constraints. We take here a very simple model where the night lasts | 258 | constraints. We take here a very simple model where the night lasts |
| 92 | between 6 and 10 hours (or 90-150 degrees), with a sinusoidal variation | 259 | between 6 and 10 hours (or 90-150 degrees), with a sinusoidal variation |
| 93 | along the year. Every day the hour angle of the Sun set is advancing by | 260 | along the year. Every day the hour angle of the Sun set is advancing by |
| 94 | 4 minutes. | 261 | 4 minutes. |
| 95 | """ | 262 | """ |
| 96 | - # Set number of hour angle bins | 263 | + # Write header |
| 264 | + self._log_header2(gammalib.NORMAL, 'Hour angle weights') | ||
| 265 | + | ||
| 266 | + # Get array geographic longitude | ||
| 267 | + geolon = self['geolon'].real() | ||
| 268 | + | ||
| 269 | + # Get time interval | ||
| 270 | + tmin = gammalib.GTime(self['tmin'].real(), 's') | ||
| 271 | + tmax = gammalib.GTime(self['tmax'].real(), 's') | ||
| 272 | + | ||
| 273 | + # Initialise hour angle list | ||
| 274 | + hour_angles = [] | ||
| 275 | + | ||
| 276 | + # Set number of hour angle bins and compute conversion factor and weight | ||
| 97 | nsteps = 1000 | 277 | nsteps = 1000 |
| 98 | - d_min = 6.0/24.0*nsteps | ||
| 99 | - d_max = 10.0/24.0*nsteps | 278 | + ra2inx = float(nsteps)/360.0 # Conversion from RA (deg) to index |
| 279 | + weight = 24.0/float(nsteps) # Weight per hour angle step | ||
| 100 | 280 | ||
| 101 | # Initialise hour angle array and weight | 281 | # Initialise hour angle array and weight |
| 102 | hours = [0.0 for i in range(nsteps)] | 282 | hours = [0.0 for i in range(nsteps)] |
| 103 | - weight = 24.0/float(nsteps) # Weight per hour angle step | ||
| 104 | 283 | ||
| 105 | - # Loop over all days in one year | ||
| 106 | - for day in range(nsteps): | ||
| 107 | - h_set = day | ||
| 108 | - h_rise = day + \ | ||
| 109 | - int(d_min + (d_max-d_min)*math.sin(day/float(nsteps)*gammalib.pi)) | ||
| 110 | - for i in range(h_set,h_rise): | ||
| 111 | - if i >= nsteps: | ||
| 112 | - hours[i-nsteps] += weight | ||
| 113 | - else: | 284 | + # Initialise time and loop until the end time is reached |
| 285 | + time = tmin | ||
| 286 | + while time <= tmax: | ||
| 287 | + | ||
| 288 | + # Compute Right Ascension and Declination of Sun in degrees | ||
| 289 | + sunra, sundec = self._sun_radec(time) | ||
| 290 | + | ||
| 291 | + # Compute by how much the zenith angle map needs to be shifted | ||
| 292 | + # to correspond to the actual time | ||
| 293 | + # | ||
| 294 | + # NOTE: The local apparent siderial time only is relevant if | ||
| 295 | + # the time interval is shorter than a day. Only in that case | ||
| 296 | + # we have to set the corresponding hour angles to zero. We need | ||
| 297 | + # to think how to properly implement that. | ||
| 298 | + #last = time.last(geolon) * 15.0 | ||
| 299 | + #print(last) | ||
| 300 | + | ||
| 301 | + # Compute the time from now when the Sun will culminate. The time | ||
| 302 | + # is here expressed in degrees | ||
| 303 | + dra_sun = self._sun_ra_exclusion(time) | ||
| 304 | + | ||
| 305 | + # Set [0,ra_start] and [ra_stop,360.0] | ||
| 306 | + ra_start = sunra - dra_sun | ||
| 307 | + ra_stop = sunra + dra_sun | ||
| 308 | + | ||
| 309 | + # Case 1: The RA interval during which the Sun is above the zenith | ||
| 310 | + # angle constraint is fully comprised within the [0,360] interval. | ||
| 311 | + # In that case the dark time is comprised of two intervals: | ||
| 312 | + # [0,ra_start] and [ra_stop,360] | ||
| 313 | + if ra_start >= 0.0 and ra_stop <= 360.0: | ||
| 314 | + inx_stop = int(ra_start * ra2inx + 0.5) | ||
| 315 | + inx_start = int(ra_stop * ra2inx + 0.5) | ||
| 316 | + for i in range(0,inx_stop): | ||
| 317 | + hours[i] += weight | ||
| 318 | + for i in range(inx_start,nsteps): | ||
| 114 | hours[i] += weight | 319 | hours[i] += weight |
| 115 | 320 | ||
| 116 | - # Return | ||
| 117 | - return hours | 321 | + # Log setting |
| 322 | + logs = 'Sun=(%.3f,%.3f) dRA=%.3f RA_excl=[%.3f,%.3f] '\ | ||
| 323 | + 'Indices=[0-%d] & [%d-%d] Dark time=%.2f h %s' % \ | ||
| 324 | + (sunra, sundec, dra_sun, ra_start, ra_stop, inx_stop, inx_start, | ||
| 325 | + nsteps, float(inx_stop+(nsteps-inx_start))*weight, | ||
| 326 | + '(Case 1)') | ||
| 327 | + self._log_value(gammalib.VERBOSE, time.utc(), logs) | ||
| 328 | + | ||
| 329 | + # Case 2: The RA interval during which the Sun is above the zenith | ||
| 330 | + # angle constraint is starting at negative RA. In that case the | ||
| 331 | + # dark time is comprised of a single interval | ||
| 332 | + # [ra_stop,ra_start+360] | ||
| 333 | + elif ra_start < 0.0: | ||
| 334 | + inx_start = int(ra_stop * ra2inx + 0.5) | ||
| 335 | + inx_stop = int((ra_start+360.0) * ra2inx + 0.5) | ||
| 336 | + for i in range(inx_start,inx_stop): | ||
| 337 | + hours[i] += weight | ||
| 338 | + | ||
| 339 | + # Log setting | ||
| 340 | + logs = 'Sun=(%.3f,%.3f) dRA=%.3f RA_excl=[%.3f,%.3f] '\ | ||
| 341 | + 'Indices=[%d-%d] Dark time=%.2f h %s' % \ | ||
| 342 | + (sunra, sundec, dra_sun, ra_start, ra_stop, inx_start, inx_stop, | ||
| 343 | + float(inx_stop-inx_start)*weight, '(Case 2)') | ||
| 344 | + self._log_value(gammalib.VERBOSE, time.utc(), logs) | ||
| 345 | + | ||
| 346 | + # Case 3: The RA interval during which the Sun is above the zenith | ||
| 347 | + # angle constraint is stopping at RA>360. In that case the dark | ||
| 348 | + # time is comprised of a single interval [ra_stop-360,ra_start] | ||
| 349 | + elif ra_stop > 360.0: | ||
| 350 | + inx_start = int((ra_stop-360.0) * ra2inx + 0.5) | ||
| 351 | + inx_stop = int(ra_start * ra2inx + 0.5) | ||
| 352 | + for i in range(inx_start,inx_stop): | ||
| 353 | + hours[i] += weight | ||
| 354 | + | ||
| 355 | + # Log setting | ||
| 356 | + logs = 'Sun=(%.3f,%.3f) dRA=%.3f RA_excl=[%.3f,%.3f] '\ | ||
| 357 | + 'Indices=[%d-%d] Dark time=%.2f h %s' % \ | ||
| 358 | + (sunra, sundec, dra_sun, ra_start, ra_stop, inx_start, inx_stop, | ||
| 359 | + float(inx_stop-inx_start)*weight, '(Case 3)') | ||
| 360 | + self._log_value(gammalib.VERBOSE, time.utc(), logs) | ||
| 361 | + | ||
| 362 | + # Add seconds of one day and start with next day | ||
| 363 | + time += 86400.0 | ||
| 364 | + | ||
| 365 | + # Build hour angle list | ||
| 366 | + dh = 360.0/float(nsteps) | ||
| 367 | + for i in range(nsteps): | ||
| 368 | + hour_angle = {'angle': dh*float(i), 'hours': hours[i]} | ||
| 369 | + hour_angles.append(hour_angle) | ||
| 370 | + | ||
| 371 | + # Log hour angle weights | ||
| 372 | + total = 0.0 | ||
| 373 | + for hour_angle in hour_angles: | ||
| 374 | + total += hour_angle['hours'] | ||
| 375 | + self._log_value(gammalib.EXPLICIT, 'Observing time', str(total)+' h') | ||
| 376 | + self._log_value(gammalib.EXPLICIT, 'Number of hour angle bins', len(hour_angles)) | ||
| 377 | + | ||
| 378 | + # Return hour angle weights | ||
| 379 | + return hour_angles | ||
| 118 | 380 | ||
| 119 | def _visibility_cube(self): | 381 | def _visibility_cube(self): |
| 120 | """ | 382 | """ |
| @@ -126,102 +388,64 @@ class csviscube(ctools.cscript): | @@ -126,102 +388,64 @@ class csviscube(ctools.cscript): | ||
| 126 | Summing over all zenith angle intervals specifies for how long a given | 388 | Summing over all zenith angle intervals specifies for how long a given |
| 127 | celestial position will be visible. | 389 | celestial position will be visible. |
| 128 | """ | 390 | """ |
| 391 | + # Write header | ||
| 392 | + self._log_header1(gammalib.TERSE, 'Compute visibility cube') | ||
| 393 | + | ||
| 394 | + # Set visibility cube and zenith angle map dimensions and bin size | ||
| 395 | + binsz = self['binsz'].real() | ||
| 396 | + nx = int(360.0/binsz+0.5) | ||
| 397 | + ny = int(180.0/binsz+0.5) | ||
| 398 | + dx = 360.0/float(nx) | ||
| 399 | + dy = 180.0/float(ny) | ||
| 400 | + | ||
| 401 | + # Set zenith angle dimension and bin size | ||
| 402 | + dz = self['dz'].real() | ||
| 403 | + zmax = self['zmax'].real() | ||
| 404 | + nz = int(zmax/dz+0.5) | ||
| 405 | + dz = zmax/float(nz) | ||
| 406 | + | ||
| 407 | + # Log dimensions | ||
| 408 | + self._log_header2(gammalib.NORMAL, 'Visibility cube dimensions') | ||
| 409 | + self._log_value(gammalib.NORMAL, 'Number of RA bins', nx) | ||
| 410 | + self._log_value(gammalib.NORMAL, 'Number of Dec bins', ny) | ||
| 411 | + self._log_value(gammalib.NORMAL, 'Number of zenith angles', nz) | ||
| 412 | + self._log_value(gammalib.NORMAL, 'RA pixel size', str(dx)+' deg') | ||
| 413 | + self._log_value(gammalib.NORMAL, 'Dec pixel size', str(dy)+' deg') | ||
| 414 | + self._log_value(gammalib.NORMAL, 'Zenith angle bin size', str(dz)+' deg') | ||
| 415 | + self._log_value(gammalib.NORMAL, 'Maximum zenith angle', str(zmax)+' deg') | ||
| 416 | + | ||
| 129 | # Compute zenith angle map | 417 | # Compute zenith angle map |
| 130 | - map = self._zenith_angle_map() | ||
| 131 | - | ||
| 132 | - # Set zenith angle range and bin size | ||
| 133 | - dz = 1.0 | ||
| 134 | - nz = int(60.0/dz) | ||
| 135 | - | ||
| 136 | - # Initialise visibility cube | ||
| 137 | - self._cube = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180,nz) | 418 | + map = self._zenith_angle_map(nx,ny,dx,dy) |
| 138 | 419 | ||
| 139 | # Setup hour angle weights. They specify for how many hours a given | 420 | # Setup hour angle weights. They specify for how many hours a given |
| 140 | - # hour angle will be observed during one year. | ||
| 141 | - hours = self._hour_angle_weight() | 421 | + # hour angle is observed during the covered time period. |
| 422 | + hour_angles = self._hour_angle_weight() | ||
| 142 | 423 | ||
| 143 | - # Compute normalisation factor | ||
| 144 | - norm = 365.0/float(len(hours)) | 424 | + # Initialise visibility cube |
| 425 | + self._cube = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-dx,dy,nx,ny,nz) | ||
| 145 | 426 | ||
| 146 | # Loop over all hour angle weights | 427 | # Loop over all hour angle weights |
| 147 | - for h, weight in enumerate(hours): | 428 | + for hour_angle in hour_angles: |
| 148 | 429 | ||
| 149 | - # Compute temporal shift | ||
| 150 | - shift = 360.0/float(len(hours)) * float(h) | 430 | + # Compute temporal shift in degrees |
| 431 | + shift = hour_angle['angle'] | ||
| 151 | 432 | ||
| 152 | # Initialise shifted zenith angle map | 433 | # Initialise shifted zenith angle map |
| 153 | - map_shift = gammalib.GSkyMap('CAR','CEL',shift,0.0,-1.0,1.0,360,180) | 434 | + map_shift = gammalib.GSkyMap('CAR','CEL',shift,0.0,-dx,dy,nx,ny) |
| 154 | 435 | ||
| 155 | # Merge zenith angle map into shifted map | 436 | # Merge zenith angle map into shifted map |
| 156 | map_shift += map | 437 | map_shift += map |
| 157 | 438 | ||
| 158 | - # Loop over all pixels of shifted map | 439 | + # Loop over all pixels of the shifted map and add the hours during |
| 440 | + # which the shifted map occurs to the relevant zenith angle bin | ||
| 159 | for i, zenith in enumerate(map_shift): | 441 | for i, zenith in enumerate(map_shift): |
| 160 | - iz = int(zenith/dz) | 442 | + iz = int(zenith/dz) |
| 161 | if iz < nz: | 443 | if iz < nz: |
| 162 | - self._cube[i,iz] += weight * norm | 444 | + self._cube[i,iz] += hour_angle['hours'] |
| 163 | 445 | ||
| 164 | # Return | 446 | # Return |
| 165 | return | 447 | return |
| 166 | 448 | ||
| 167 | - def _zenith_angle_map(self): | ||
| 168 | - """ | ||
| 169 | - Compute zenith angle map | ||
| 170 | - | ||
| 171 | - The zenith angle of a position (ra,dec) depends on the declination and | ||
| 172 | - the hour angle h and is given by | ||
| 173 | - | ||
| 174 | - zenith(h,dec) = arccos( sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(h) ) | ||
| 175 | - | ||
| 176 | - The hour angle h (or local hour angle, LHA) is defined as the difference | ||
| 177 | - between local siderial time (LST) and the Right Ascension | ||
| 178 | - | ||
| 179 | - h = LST - ra | ||
| 180 | - | ||
| 181 | - LST is the difference between the Greenwich siderial time (GST) and the | ||
| 182 | - geographic longitude of the observer | ||
| 183 | - | ||
| 184 | - LST = GST - lon | ||
| 185 | - | ||
| 186 | - where lon is counted positive west from the meridian. Hence | ||
| 187 | - | ||
| 188 | - h = GST - lon - ra | ||
| 189 | - | ||
| 190 | - The map is computed for h=-ra which is equivalent to GST=lon (or LST=0). | ||
| 191 | - In other words, the map corresponds to the time when ra=0 passes the | ||
| 192 | - local meridian. | ||
| 193 | - """ | ||
| 194 | - # Initialise zenith angle map | ||
| 195 | - map = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180) | ||
| 196 | - | ||
| 197 | - # Set hour angle and declination vectors | ||
| 198 | - hours = [float(i) for i in range(360)] | ||
| 199 | - decs = [float(i)-89.5 for i in range(180)] | ||
| 200 | - | ||
| 201 | - # Get array geographic longitude and latitude | ||
| 202 | - geolon = self['geolon'].real() | ||
| 203 | - geolat = self['geolat'].real() | ||
| 204 | - | ||
| 205 | - # Precompute latitude terms | ||
| 206 | - cos_lat = math.cos(geolat*gammalib.deg2rad) | ||
| 207 | - sin_lat = math.sin(geolat*gammalib.deg2rad) | ||
| 208 | - | ||
| 209 | - # Loop over all declination and hour angles and compute the zenith | ||
| 210 | - # angle. Store the zenith angle in the map | ||
| 211 | - index = 0 | ||
| 212 | - for dec in decs: | ||
| 213 | - cos_dec = math.cos(dec*gammalib.deg2rad) | ||
| 214 | - sin_dec = math.sin(dec*gammalib.deg2rad) | ||
| 215 | - for h in hours: | ||
| 216 | - cos_h = math.cos(h*gammalib.deg2rad) | ||
| 217 | - zenith = math.acos(sin_lat*sin_dec + | ||
| 218 | - cos_lat*cos_dec*cos_h)*gammalib.rad2deg | ||
| 219 | - map[index] = zenith | ||
| 220 | - index += 1 | ||
| 221 | - | ||
| 222 | - # Return zenith angle map | ||
| 223 | - return map | ||
| 224 | - | ||
| 225 | 449 | ||
| 226 | # Public methods | 450 | # Public methods |
| 227 | def run(self): | 451 | def run(self): |
| @@ -252,7 +476,7 @@ class csviscube(ctools.cscript): | @@ -252,7 +476,7 @@ class csviscube(ctools.cscript): | ||
| 252 | outfile = self['outfile'].filename() | 476 | outfile = self['outfile'].filename() |
| 253 | 477 | ||
| 254 | # Log file name | 478 | # Log file name |
| 255 | - self._log_value(gammalib.TERSE, 'Visibility cube file', outfile.url()) | 479 | + self._log_value(gammalib.NORMAL, 'Visibility cube file', outfile.url()) |
| 256 | 480 | ||
| 257 | # Save the visibility cube | 481 | # Save the visibility cube |
| 258 | self._cube.save(outfile, self['clobber'].boolean()) | 482 | self._cube.save(outfile, self['clobber'].boolean()) |
test/test_csviscube.py
| @@ -67,7 +67,7 @@ class Test(test): | @@ -67,7 +67,7 @@ class Test(test): | ||
| 67 | 67 | ||
| 68 | # Setup csviscube command | 68 | # Setup csviscube command |
| 69 | cmd = csviscube+' tmin=0.0 tmax=31557600.0 geolon=79.4041'+\ | 69 | cmd = csviscube+' tmin=0.0 tmax=31557600.0 geolon=79.4041'+\ |
| 70 | - ' geolat=-24.6272 outfile=csviscube_cmd1.fits'+\ | 70 | + ' geolat=-24.6272 binsz=3.0 outfile=csviscube_cmd1.fits'+\ |
| 71 | ' logfile="csviscube_cmd1.log" chatter=1' | 71 | ' logfile="csviscube_cmd1.log" chatter=1' |
| 72 | 72 | ||
| 73 | # Check if execution of wrong command fails | 73 | # Check if execution of wrong command fails |
| @@ -92,6 +92,7 @@ class Test(test): | @@ -92,6 +92,7 @@ class Test(test): | ||
| 92 | viscube['tmax'] = 31557600.0 | 92 | viscube['tmax'] = 31557600.0 |
| 93 | viscube['geolon'] = 79.4041 | 93 | viscube['geolon'] = 79.4041 |
| 94 | viscube['geolat'] = -24.6272 | 94 | viscube['geolat'] = -24.6272 |
| 95 | + viscube['binsz'] = 3.0 | ||
| 95 | viscube['outfile'] = 'csviscube_py1.fits' | 96 | viscube['outfile'] = 'csviscube_py1.fits' |
| 96 | viscube['logfile'] = 'csviscube_py1.log' | 97 | viscube['logfile'] = 'csviscube_py1.log' |
| 97 | viscube['chatter'] = 2 | 98 | viscube['chatter'] = 2 |