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 

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

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

129 

130 Attributes (calculated on demand and cached) 

131 ------------------------------------------ 

132 alt : np.array 

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

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

135 az : np.array 

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

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

138 pa : np.array 

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

140 Based on the fast approximate alt,az values. 

141 lmst : float 

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

143 M5Depth : dict of np.array 

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

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

146 HA : np.array 

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

148 az_to_sun : np.array 

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

150 

151 Attributes (set by the scheduler) 

152 ------------------------------- 

153 queue : list of observation objects 

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

155 

156 """ 

157 if nside is None: 

158 nside = set_default_nside() 

159 self.nside = nside 

160 self.site = Site(site) 

161 self.exptime = exptime 

162 self.mjd_start = mjd_start 

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

164 self.season_offset = season_offset 

165 self.sun_RA_start = sun_RA_start 

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

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

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

169 self.nan_map.fill(np.nan) 

170 # The RA, Dec grid we are using 

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

172 

173 # Modified Julian Date (day) 

174 self._mjd = None 

175 # Altitude and azimuth. Dict with degrees and radians 

176 self._alt = None 

177 self._az = None 

178 self._pa = None 

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

180 self.clouds = None 

181 self._slewtime = None 

182 self.current_filter = None 

183 self.mounted_filters = None 

184 self.night = None 

185 self._lmst = None 

186 # Should be a dict with filtername keys 

187 self._skybrightness = {} 

188 self._FWHMeff = {} 

189 self._M5Depth = None 

190 self._airmass = None 

191 

192 # Upcomming scheduled observations 

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

194 

195 # Attribute to hold the current observing queue 

196 self.queue = None 

197 

198 # Moon 

199 self.moonAlt = None 

200 self.moonAz = None 

201 self.moonRA = None 

202 self.moonDec = None 

203 self.moonPhase = None 

204 

205 # Sun 

206 self.sunAlt = None 

207 self.sunAz = None 

208 self.sunRA = None 

209 self.sunDec = None 

210 

211 # Almanac information 

212 self.sunset = None 

213 self.sun_n12_setting = None 

214 self.sun_n18_setting = None 

215 self.sun_n18_rising = None 

216 self.sun_n12_rising = None 

217 self.sunrise = None 

218 self.moonrise = None 

219 self.moonset = None 

220 

221 self.planet_positions = None 

222 

223 # Current telescope pointing 

224 self.telRA = None 

225 self.telDec = None 

226 self.telAlt = None 

227 self.telAz = None 

228 

229 # Full sky cloud map 

230 self._cloud_map = None 

231 self._HA = None 

232 

233 # XXX--document 

234 self.bulk_cloud = None 

235 

236 self.rotTelPos = None 

237 

238 self.targets_of_opportunity = None 

239 

240 self._season = None 

241 

242 self.season_modulo = None 

243 self.season_max_season = None 

244 self.season_length = 365.25 

245 self.season_floor = True 

246 

247 @property 

248 def lmst(self): 

249 return self._lmst 

250 

251 @lmst.setter 

252 def lmst(self, value): 

253 self._lmst = value 

254 self._HA = None 

255 

256 @property 

257 def HA(self): 

258 if self._HA is None: 

259 self.calc_HA() 

260 return self._HA 

261 

262 def calc_HA(self): 

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

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

265 

266 @property 

267 def cloud_map(self): 

268 return self._cloud_map 

269 

270 @cloud_map.setter 

271 def cloud_map(self, value): 

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

273 

274 @property 

275 def slewtime(self): 

276 return self._slewtime 

277 

278 @slewtime.setter 

279 def slewtime(self, value): 

280 # Using 0 for start of night 

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

282 self._slewtime = value 

283 else: 

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

285 

286 @property 

287 def airmass(self): 

288 return self._airmass 

289 

290 @airmass.setter 

291 def airmass(self, value): 

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

293 self._M5Depth = None 

294 

295 @property 

296 def pa(self): 

297 if self._pa is None: 

298 self.calc_pa() 

299 return self._pa 

300 

301 def calc_pa(self): 

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

303 

304 @property 

305 def alt(self): 

306 if self._alt is None: 

307 self.calc_altAz() 

308 return self._alt 

309 

310 @property 

311 def az(self): 

312 if self._az is None: 

313 self.calc_altAz() 

314 return self._az 

315 

316 def calc_altAz(self): 

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

318 self.site.latitude_rad, 

319 self.site.longitude_rad, self._mjd) 

320 

321 @property 

322 def mjd(self): 

323 return self._mjd 

324 

325 @mjd.setter 

326 def mjd(self, value): 

327 self._mjd = value 

328 # Set things that need to be recalculated to None 

329 self._az = None 

330 self._alt = None 

331 self._pa = None 

332 self._HA = None 

333 self._lmst = None 

334 self._az_to_sun = None 

335 self._season = None 

336 

337 @property 

338 def skybrightness(self): 

339 return self._skybrightness 

340 

341 @skybrightness.setter 

342 def skybrightness(self, indict): 

343 for key in indict: 

344 

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

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

347 self._M5Depth = None 

348 

349 @property 

350 def FWHMeff(self): 

351 return self._FWHMeff 

352 

353 @FWHMeff.setter 

354 def FWHMeff(self, indict): 

355 for key in indict: 

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

357 self._M5Depth = None 

358 

359 @property 

360 def M5Depth(self): 

361 if self._M5Depth is None: 

362 self.calc_M5Depth() 

363 return self._M5Depth 

364 

365 def calc_M5Depth(self): 

366 self._M5Depth = {} 

367 for filtername in self._skybrightness: 

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

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

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

371 self._skybrightness[filtername][good], 

372 self._FWHMeff[filtername][good], 

373 self.exptime, 

374 self._airmass[good]) 

375 

376 def calc_az_to_sun(self): 

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

378 self._az_to_sun = diff 

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

380 

381 @property 

382 def az_to_sun(self): 

383 if self._az_to_sun is None: 

384 self.calc_az_to_sun() 

385 return self._az_to_sun 

386 

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

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

389 if self.season_offset is not None: 

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

391 if ~kwargs_match: 

392 self.season_modulo = modulo 

393 self.season_max_season = max_season 

394 self.season_length = season_length 

395 self.season_floor = floor 

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

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

398 modulo=modulo, max_season=max_season, 

399 season_length=season_length, floor=floor) 

400 else: 

401 self._season = None 

402 

403 return self._season