Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import numpy as np 

2from lsst.sims.utils import _approx_RaDec2AltAz, Site, _hpid2RaDec, m5_flat_sed, _approx_altaz2pa 

3import healpy as hp 

4from lsst.sims.featureScheduler.utils import set_default_nside, match_hp_resolution, season_calc, smallest_signed_angle 

5 

6__all__ = ['Conditions'] 

7 

8 

9class Conditions(object): 

10 """ 

11 Class to hold telemetry information 

12 

13 If the incoming value is a healpix map, we use a setter to ensure the 

14 resolution matches. 

15 

16 Unless otherwise noted, all values are assumed to be valid at the time 

17 given by self.mjd 

18 """ 

19 def __init__(self, nside=None, site='LSST', exptime=30., mjd_start=59853.5, season_offset=None, 

20 sun_RA_start=None): 

21 """ 

22 Parameters 

23 ---------- 

24 nside : int 

25 The healpixel nside to set the resolution of attributes. 

26 site : str ('LSST') 

27 A site name used to create a sims.utils.Site object. For looking up 

28 observatory paramteres like latitude and longitude. 

29 expTime : float (30) 

30 The exposure time to assume when computing the 5-sigma limiting depth 

31 mjd_start : float (59853.5) 

32 The starting MJD of the survey. 

33 season_offset : np.array 

34 A HEALpix array that specifies the day offset when computing the season for each HEALpix. 

35 sun_RA_start : float (None) 

36 

37 Attributes (Set on init) 

38 ----------- 

39 nside : int 

40 Healpix resolution. All maps are set to this reslution. 

41 site : lsst.sims.Site object ('LSST') 

42 Contains static site-specific data (lat, lon, altitude, etc). Defaults to 'LSST'. 

43 ra : np.array 

44 A healpix array with the RA of each healpixel center (radians). Automatically 

45 generated. 

46 dec : np.array 

47 A healpix array with the Dec of each healpixel center (radians). Automatically generated. 

48 

49 Attributes (to be set by user/telemetry stream/scheduler) 

50 ------------------------------------------- 

51 mjd : float 

52 Modified Julian Date (days). 

53 bulk_cloud : float 

54 The fraction of sky covered by clouds. (In the future might update to transparency map) 

55 cloud_map : np.array 

56 XXX--to be done. HEALpix array with cloudy pixels set to NaN. 

57 slewtime : np.array 

58 Healpix showing the slewtime to each healpixel center (seconds) 

59 current_filter : str 

60 The name of the current filter. (expect one of u, g, r, i, z, y). 

61 mounted_filters : list of str 

62 The filters that are currently mounted and thu available (expect 5 of u, g, r, i, z, y) 

63 night : int 

64 The current night number (days). Probably starts at 1. 

65 skybrightness : dict of np.array 

66 Dictionary keyed by filtername. Values are healpix arrays with the sky brightness at each 

67 healpix center (mag/acsec^2) 

68 FWHMeff : dict of np.array 

69 Dictionary keyed by filtername. Values are the effective seeing FWHM at each healpix 

70 center (arcseconds) 

71 moonAlt : float 

72 The altitude of the Moon (radians) 

73 moonAz : float 

74 The Azimuth of the moon (radians) 

75 moonRA : float 

76 RA of the moon (radians) 

77 moonDec : float 

78 Declination of the moon (radians) 

79 moonPhase : float 

80 The Phase of the moon. (percent, 0=new moon, 100=full moon) 

81 sunAlt : float 

82 The altitude of the sun (radians). 

83 sunAz : float 

84 The Azimuth of the sun (radians). 

85 sunRA : float 

86 The RA of the sun (radians). 

87 sunDec : float 

88 The Dec of the sun (radians). 

89 telRA : float 

90 The current telescope RA pointing (radians). 

91 telDec : float 

92 The current telescope Declination (radians). 

93 telAlt : float 

94 The current telescope altitude (radians). 

95 telAz : float 

96 The current telescope azimuth (radians). 

97 cumulative_azimuth_rad : float 

98 The cummulative telescope azimuth (radians). For tracking cable wrap 

99 cloud_map : np.array 

100 A healpix map with the cloud coverage. XXX-expand, is this bool map? Transparency map? 

101 airmass : np.array 

102 A healpix map with the airmass value of each healpixel. (unitless) 

103 sunset : float 

104 The MJD of sunset that starts the current night. Note MJDs of sunset, moonset, twilight times, etc 

105 are from interpolations. This means the sun may actually be slightly above/below the horizon 

106 at the given sunset time. 

107 sun_n12_setting : float 

108 The MJD of when the sun is at -12 degees altitude and setting during the 

109 current night. From interpolation. 

110 sun_n18_setting : float 

111 The MJD when the sun is at -18 degrees altitude and setting during the current night. 

112 From interpolation. 

113 sun_n18_rising : float 

114 The MJD when the sun is at -18 degrees altitude and rising during the current night. 

115 From interpolation. 

116 sun_n12_rising : float 

117 The MJD when the sun is at -12 degrees altitude and rising during the current night. 

118 From interpolation. 

119 sunrise : float 

120 The MJD of sunrise during the current night. From interpolation 

121 moonrise : float 

122 The MJD of moonrise during the current night. From interpolation. 

123 moonset : float 

124 The MJD of moonset during the current night. From interpolation. 

125 targets_of_opportunity : list of lsst.sims.featureScheduler.targetoO object(s) 

126 targetoO objects. 

127 planet_positions : dict 

128 Dictionary of planet name and coordinate e.g., 'venus_RA', 'mars_dec' 

129 scheduled_observations : np.array 

130 A list of MJD times when there are scheduled observations. Defaults to empty array. 

131 

132 Attributes (calculated on demand and cached) 

133 ------------------------------------------ 

134 alt : np.array 

135 Altitude of each healpixel (radians). Recaclulated if mjd is updated. Uses fast 

136 approximate equation for converting RA,Dec to alt,az. 

137 az : np.array 

138 Azimuth of each healpixel (radians). Recaclulated if mjd is updated. Uses fast 

139 approximate equation for converting RA,Dec to alt,az. 

140 pa : np.array 

141 The parallactic angle of each healpixel (radians). Recaclulated if mjd is updated. 

142 Based on the fast approximate alt,az values. 

143 lmst : float 

144 The local mean sidearal time (hours). Updates is mjd is changed. 

145 M5Depth : dict of np.array 

146 the 5-sigma limiting depth healpix maps, keyed by filtername (mags). Will be recalculated 

147 if the skybrightness, seeing, or airmass are updated. 

148 HA : np.array 

149 Healpix map of the hour angle of each healpixel (radians). Runs from 0 to 2pi. 

150 az_to_sun : np.array 

151 Healpix map of the azimuthal distance to the sun for each healpixel (radians) 

152 az_to_anitsun : np.array 

153 Healpix map of the azimuthal distance to the anit-sun for each healpixel (radians) 

154 

155 Attributes (set by the scheduler) 

156 ------------------------------- 

157 queue : list of observation objects 

158 The current queue of observations core_scheduler is waiting to execute. 

159 

160 """ 

161 if nside is None: 

162 nside = set_default_nside() 

163 self.nside = nside 

164 self.site = Site(site) 

165 self.exptime = exptime 

166 self.mjd_start = mjd_start 

167 hpids = np.arange(hp.nside2npix(nside)) 

168 self.season_offset = season_offset 

169 self.sun_RA_start = sun_RA_start 

170 # Generate an empty map so we can copy when we need a new map 

171 self.zeros_map = np.zeros(hp.nside2npix(nside), dtype=float) 

172 self.nan_map = np.zeros(hp.nside2npix(nside), dtype=float) 

173 self.nan_map.fill(np.nan) 

174 # The RA, Dec grid we are using 

175 self.ra, self.dec = _hpid2RaDec(nside, hpids) 

176 

177 # Modified Julian Date (day) 

178 self._mjd = None 

179 # Altitude and azimuth. Dict with degrees and radians 

180 self._alt = None 

181 self._az = None 

182 self._pa = None 

183 # The cloud level. Fraction, but could upgrade to transparency map 

184 self.clouds = None 

185 self._slewtime = None 

186 self.current_filter = None 

187 self.mounted_filters = None 

188 self.night = None 

189 self._lmst = None 

190 # Should be a dict with filtername keys 

191 self._skybrightness = {} 

192 self._FWHMeff = {} 

193 self._M5Depth = None 

194 self._airmass = None 

195 

196 # Upcomming scheduled observations 

197 self.scheduled_observations = np.array([], dtype=float) 

198 

199 # Attribute to hold the current observing queue 

200 self.queue = None 

201 

202 # Moon 

203 self.moonAlt = None 

204 self.moonAz = None 

205 self.moonRA = None 

206 self.moonDec = None 

207 self.moonPhase = None 

208 

209 # Sun 

210 self.sunAlt = None 

211 self.sunAz = None 

212 self.sunRA = None 

213 self.sunDec = None 

214 

215 # Almanac information 

216 self.sunset = None 

217 self.sun_n12_setting = None 

218 self.sun_n18_setting = None 

219 self.sun_n18_rising = None 

220 self.sun_n12_rising = None 

221 self.sunrise = None 

222 self.moonrise = None 

223 self.moonset = None 

224 

225 self.planet_positions = None 

226 

227 # Current telescope pointing 

228 self.telRA = None 

229 self.telDec = None 

230 self.telAlt = None 

231 self.telAz = None 

232 

233 # Full sky cloud map 

234 self._cloud_map = None 

235 self._HA = None 

236 

237 # XXX--document 

238 self.bulk_cloud = None 

239 

240 self.rotTelPos = None 

241 

242 self.targets_of_opportunity = None 

243 

244 self._season = None 

245 

246 self.season_modulo = None 

247 self.season_max_season = None 

248 self.season_length = 365.25 

249 self.season_floor = True 

250 

251 @property 

252 def lmst(self): 

253 return self._lmst 

254 

255 @lmst.setter 

256 def lmst(self, value): 

257 self._lmst = value 

258 self._HA = None 

259 

260 @property 

261 def HA(self): 

262 if self._HA is None: 

263 self.calc_HA() 

264 return self._HA 

265 

266 def calc_HA(self): 

267 self._HA = np.radians(self._lmst*360./24.) - self.ra 

268 self._HA[np.where(self._HA < 0)] += 2.*np.pi 

269 

270 @property 

271 def cloud_map(self): 

272 return self._cloud_map 

273 

274 @cloud_map.setter 

275 def cloud_map(self, value): 

276 self._cloud_map = match_hp_resolution(value, nside_out=self.nside) 

277 

278 @property 

279 def slewtime(self): 

280 return self._slewtime 

281 

282 @slewtime.setter 

283 def slewtime(self, value): 

284 # Using 0 for start of night 

285 if np.size(value) == 1: 

286 self._slewtime = value 

287 else: 

288 self._slewtime = match_hp_resolution(value, nside_out=self.nside) 

289 

290 @property 

291 def airmass(self): 

292 return self._airmass 

293 

294 @airmass.setter 

295 def airmass(self, value): 

296 self._airmass = match_hp_resolution(value, nside_out=self.nside) 

297 self._M5Depth = None 

298 

299 @property 

300 def pa(self): 

301 if self._pa is None: 

302 self.calc_pa() 

303 return self._pa 

304 

305 def calc_pa(self): 

306 self._pa = _approx_altaz2pa(self.alt, self.az, self.site.latitude_rad) 

307 

308 @property 

309 def alt(self): 

310 if self._alt is None: 

311 self.calc_altAz() 

312 return self._alt 

313 

314 @property 

315 def az(self): 

316 if self._az is None: 

317 self.calc_altAz() 

318 return self._az 

319 

320 def calc_altAz(self): 

321 self._alt, self._az = _approx_RaDec2AltAz(self.ra, self.dec, 

322 self.site.latitude_rad, 

323 self.site.longitude_rad, self._mjd) 

324 

325 @property 

326 def mjd(self): 

327 return self._mjd 

328 

329 @mjd.setter 

330 def mjd(self, value): 

331 self._mjd = value 

332 # Set things that need to be recalculated to None 

333 self._az = None 

334 self._alt = None 

335 self._pa = None 

336 self._HA = None 

337 self._lmst = None 

338 self._az_to_sun = None 

339 self._az_to_antisun = None 

340 self._season = None 

341 

342 @property 

343 def skybrightness(self): 

344 return self._skybrightness 

345 

346 @skybrightness.setter 

347 def skybrightness(self, indict): 

348 for key in indict: 

349 

350 self._skybrightness[key] = match_hp_resolution(indict[key], nside_out=self.nside) 

351 # If sky brightness changes, need to recalc M5 depth. 

352 self._M5Depth = None 

353 

354 @property 

355 def FWHMeff(self): 

356 return self._FWHMeff 

357 

358 @FWHMeff.setter 

359 def FWHMeff(self, indict): 

360 for key in indict: 

361 self._FWHMeff[key] = match_hp_resolution(indict[key], nside_out=self.nside) 

362 self._M5Depth = None 

363 

364 @property 

365 def M5Depth(self): 

366 if self._M5Depth is None: 

367 self.calc_M5Depth() 

368 return self._M5Depth 

369 

370 def calc_M5Depth(self): 

371 self._M5Depth = {} 

372 for filtername in self._skybrightness: 

373 good = ~np.isnan(self._skybrightness[filtername]) 

374 self._M5Depth[filtername] = self.nan_map.copy() 

375 self._M5Depth[filtername][good] = m5_flat_sed(filtername, 

376 self._skybrightness[filtername][good], 

377 self._FWHMeff[filtername][good], 

378 self.exptime, 

379 self._airmass[good]) 

380 

381 def calc_az_to_sun(self): 

382 self._az_to_sun = smallest_signed_angle(self.ra, self.sunRA) 

383 

384 def calc_az_to_antisun(self): 

385 self._az_to_antisun = smallest_signed_angle(self.ra+np.pi, self.sunRA) 

386 

387 @property 

388 def az_to_sun(self): 

389 if self._az_to_sun is None: 

390 self.calc_az_to_sun() 

391 return self._az_to_sun 

392 

393 @property 

394 def az_to_antisun(self): 

395 if self._az_to_antisun is None: 

396 self.calc_az_to_antisun() 

397 return self._az_to_antisun 

398 

399 # XXX, there's probably an elegant decorator that could do this caching automatically 

400 def season(self, modulo=None, max_season=None, season_length=365.25, floor=True): 

401 if self.season_offset is not None: 

402 kwargs_match = (modulo == self.season_modulo) & (max_season == self.season_max_season) & (season_length == self.season_length) & (floor == self.season_floor) 

403 if ~kwargs_match: 

404 self.season_modulo = modulo 

405 self.season_max_season = max_season 

406 self.season_length = season_length 

407 self.season_floor = floor 

408 if (self._season is None) | (~kwargs_match): 

409 self._season = season_calc(self.night, offset=self.season_offset, 

410 modulo=modulo, max_season=max_season, 

411 season_length=season_length, floor=floor) 

412 else: 

413 self._season = None 

414 

415 return self._season