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 

3import healpy as hp 

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

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) 

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 cloud_map : np.array 

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

99 airmass : np.array 

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

101 sunset : float 

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

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

104 at the given sunset time. 

105 sun_n12_setting : float 

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

107 current night. From interpolation. 

108 sun_n18_setting : float 

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

110 From interpolation. 

111 sun_n18_rising : float 

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

113 From interpolation. 

114 sun_n12_rising : float 

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

116 From interpolation. 

117 sunrise : float 

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

119 moonrise : float 

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

121 moonset : float 

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

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

124 targetoO objects. 

125 planet_positions : dict 

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

127 

128 Attributes (calculated on demand and cached) 

129 ------------------------------------------ 

130 alt : np.array 

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

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

133 az : np.array 

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

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

136 pa : np.array 

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

138 Based on the fast approximate alt,az values. 

139 lmst : float 

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

141 M5Depth : dict of np.array 

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

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

144 HA : np.array 

145 Healpix map of the hour angle of each healpixel (radians). 

146 az_to_sun : np.array 

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

148 

149 Attributes (set by the scheduler) 

150 ------------------------------- 

151 queue : list of observation objects 

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

153 

154 """ 

155 if nside is None: 

156 nside = set_default_nside() 

157 self.nside = nside 

158 self.site = Site(site) 

159 self.exptime = exptime 

160 self.mjd_start = mjd_start 

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

162 self.season_offset = season_offset 

163 self.sun_RA_start = sun_RA_start 

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

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

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

167 self.nan_map.fill(np.nan) 

168 # The RA, Dec grid we are using 

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

170 

171 # Modified Julian Date (day) 

172 self._mjd = None 

173 # Altitude and azimuth. Dict with degrees and radians 

174 self._alt = None 

175 self._az = None 

176 self._pa = None 

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

178 self.clouds = None 

179 self._slewtime = None 

180 self.current_filter = None 

181 self.mounted_filters = None 

182 self.night = None 

183 self._lmst = None 

184 # Should be a dict with filtername keys 

185 self._skybrightness = {} 

186 self._FWHMeff = {} 

187 self._M5Depth = None 

188 self._airmass = None 

189 

190 # Attribute to hold the current observing queue 

191 self.queue = None 

192 

193 # Moon 

194 self.moonAlt = None 

195 self.moonAz = None 

196 self.moonRA = None 

197 self.moonDec = None 

198 self.moonPhase = None 

199 

200 # Sun 

201 self.sunAlt = None 

202 self.sunAz = None 

203 self.sunRA = None 

204 self.sunDec = None 

205 

206 # Almanac information 

207 self.sunset = None 

208 self.sun_n12_setting = None 

209 self.sun_n18_setting = None 

210 self.sun_n18_rising = None 

211 self.sun_n12_rising = None 

212 self.sunrise = None 

213 self.moonrise = None 

214 self.moonset = None 

215 

216 self.planet_positions = None 

217 

218 # Current telescope pointing 

219 self.telRA = None 

220 self.telDec = None 

221 self.telAlt = None 

222 self.telAz = None 

223 

224 # Full sky cloud map 

225 self._cloud_map = None 

226 self._HA = None 

227 

228 # XXX--document 

229 self.bulk_cloud = None 

230 

231 self.rotTelPos = None 

232 

233 self.targets_of_opportunity = None 

234 

235 self._season = None 

236 

237 self.season_modulo = None 

238 self.season_max_season = None 

239 self.season_length = 365.25 

240 self.season_floor = True 

241 

242 @property 

243 def lmst(self): 

244 return self._lmst 

245 

246 @lmst.setter 

247 def lmst(self, value): 

248 self._lmst = value 

249 self._HA = None 

250 

251 @property 

252 def HA(self): 

253 if self._HA is None: 

254 self.calc_HA() 

255 return self._HA 

256 

257 def calc_HA(self): 

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

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

260 

261 @property 

262 def cloud_map(self): 

263 return self._cloud_map 

264 

265 @cloud_map.setter 

266 def cloud_map(self, value): 

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

268 

269 @property 

270 def slewtime(self): 

271 return self._slewtime 

272 

273 @slewtime.setter 

274 def slewtime(self, value): 

275 # Using 0 for start of night 

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

277 self._slewtime = value 

278 else: 

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

280 

281 @property 

282 def airmass(self): 

283 return self._airmass 

284 

285 @airmass.setter 

286 def airmass(self, value): 

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

288 self._M5Depth = None 

289 

290 @property 

291 def pa(self): 

292 if self._pa is None: 

293 self.calc_pa() 

294 return self._pa 

295 

296 def calc_pa(self): 

297 self._pa = approx_altaz2pa(self.alt, self.az, self.site.latitude_rad) 

298 

299 @property 

300 def alt(self): 

301 if self._alt is None: 

302 self.calc_altAz() 

303 return self._alt 

304 

305 @property 

306 def az(self): 

307 if self._az is None: 

308 self.calc_altAz() 

309 return self._az 

310 

311 def calc_altAz(self): 

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

313 self.site.latitude_rad, 

314 self.site.longitude_rad, self._mjd) 

315 

316 @property 

317 def mjd(self): 

318 return self._mjd 

319 

320 @mjd.setter 

321 def mjd(self, value): 

322 self._mjd = value 

323 # Set things that need to be recalculated to None 

324 self._az = None 

325 self._alt = None 

326 self._pa = None 

327 self._HA = None 

328 self._lmst = None 

329 self._az_to_sun = None 

330 self._season = None 

331 

332 @property 

333 def skybrightness(self): 

334 return self._skybrightness 

335 

336 @skybrightness.setter 

337 def skybrightness(self, indict): 

338 for key in indict: 

339 

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

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

342 self._M5Depth = None 

343 

344 @property 

345 def FWHMeff(self): 

346 return self._FWHMeff 

347 

348 @FWHMeff.setter 

349 def FWHMeff(self, indict): 

350 for key in indict: 

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

352 self._M5Depth = None 

353 

354 @property 

355 def M5Depth(self): 

356 if self._M5Depth is None: 

357 self.calc_M5Depth() 

358 return self._M5Depth 

359 

360 def calc_M5Depth(self): 

361 self._M5Depth = {} 

362 for filtername in self._skybrightness: 

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

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

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

366 self._skybrightness[filtername][good], 

367 self._FWHMeff[filtername][good], 

368 self.exptime, 

369 self._airmass[good]) 

370 

371 def calc_az_to_sun(self): 

372 diff = np.abs(self.ra - self.sunRA) 

373 self._az_to_sun = diff 

374 self._az_to_sun[np.where(diff > np.pi)] = 2.*np.pi-diff[np.where(diff > np.pi)] 

375 

376 @property 

377 def az_to_sun(self): 

378 if self._az_to_sun is None: 

379 self.calc_az_to_sun() 

380 return self._az_to_sun 

381 

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

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

384 if self.season_offset is not None: 

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

386 if ~kwargs_match: 

387 self.season_modulo = modulo 

388 self.season_max_season = max_season 

389 self.season_length = season_length 

390 self.season_floor = floor 

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

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

393 modulo=modulo, max_season=max_season, 

394 season_length=season_length, floor=floor) 

395 else: 

396 self._season = None 

397 

398 return self._season