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 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 

153 Attributes (set by the scheduler) 

154 ------------------------------- 

155 queue : list of observation objects 

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

157 

158 """ 

159 if nside is None: 

160 nside = set_default_nside() 

161 self.nside = nside 

162 self.site = Site(site) 

163 self.exptime = exptime 

164 self.mjd_start = mjd_start 

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

166 self.season_offset = season_offset 

167 self.sun_RA_start = sun_RA_start 

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

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

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

171 self.nan_map.fill(np.nan) 

172 # The RA, Dec grid we are using 

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

174 

175 # Modified Julian Date (day) 

176 self._mjd = None 

177 # Altitude and azimuth. Dict with degrees and radians 

178 self._alt = None 

179 self._az = None 

180 self._pa = None 

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

182 self.clouds = None 

183 self._slewtime = None 

184 self.current_filter = None 

185 self.mounted_filters = None 

186 self.night = None 

187 self._lmst = None 

188 # Should be a dict with filtername keys 

189 self._skybrightness = {} 

190 self._FWHMeff = {} 

191 self._M5Depth = None 

192 self._airmass = None 

193 

194 # Upcomming scheduled observations 

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

196 

197 # Attribute to hold the current observing queue 

198 self.queue = None 

199 

200 # Moon 

201 self.moonAlt = None 

202 self.moonAz = None 

203 self.moonRA = None 

204 self.moonDec = None 

205 self.moonPhase = None 

206 

207 # Sun 

208 self.sunAlt = None 

209 self.sunAz = None 

210 self.sunRA = None 

211 self.sunDec = None 

212 

213 # Almanac information 

214 self.sunset = None 

215 self.sun_n12_setting = None 

216 self.sun_n18_setting = None 

217 self.sun_n18_rising = None 

218 self.sun_n12_rising = None 

219 self.sunrise = None 

220 self.moonrise = None 

221 self.moonset = None 

222 

223 self.planet_positions = None 

224 

225 # Current telescope pointing 

226 self.telRA = None 

227 self.telDec = None 

228 self.telAlt = None 

229 self.telAz = None 

230 

231 # Full sky cloud map 

232 self._cloud_map = None 

233 self._HA = None 

234 

235 # XXX--document 

236 self.bulk_cloud = None 

237 

238 self.rotTelPos = None 

239 

240 self.targets_of_opportunity = None 

241 

242 self._season = None 

243 

244 self.season_modulo = None 

245 self.season_max_season = None 

246 self.season_length = 365.25 

247 self.season_floor = True 

248 

249 @property 

250 def lmst(self): 

251 return self._lmst 

252 

253 @lmst.setter 

254 def lmst(self, value): 

255 self._lmst = value 

256 self._HA = None 

257 

258 @property 

259 def HA(self): 

260 if self._HA is None: 

261 self.calc_HA() 

262 return self._HA 

263 

264 def calc_HA(self): 

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

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

267 

268 @property 

269 def cloud_map(self): 

270 return self._cloud_map 

271 

272 @cloud_map.setter 

273 def cloud_map(self, value): 

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

275 

276 @property 

277 def slewtime(self): 

278 return self._slewtime 

279 

280 @slewtime.setter 

281 def slewtime(self, value): 

282 # Using 0 for start of night 

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

284 self._slewtime = value 

285 else: 

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

287 

288 @property 

289 def airmass(self): 

290 return self._airmass 

291 

292 @airmass.setter 

293 def airmass(self, value): 

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

295 self._M5Depth = None 

296 

297 @property 

298 def pa(self): 

299 if self._pa is None: 

300 self.calc_pa() 

301 return self._pa 

302 

303 def calc_pa(self): 

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

305 

306 @property 

307 def alt(self): 

308 if self._alt is None: 

309 self.calc_altAz() 

310 return self._alt 

311 

312 @property 

313 def az(self): 

314 if self._az is None: 

315 self.calc_altAz() 

316 return self._az 

317 

318 def calc_altAz(self): 

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

320 self.site.latitude_rad, 

321 self.site.longitude_rad, self._mjd) 

322 

323 @property 

324 def mjd(self): 

325 return self._mjd 

326 

327 @mjd.setter 

328 def mjd(self, value): 

329 self._mjd = value 

330 # Set things that need to be recalculated to None 

331 self._az = None 

332 self._alt = None 

333 self._pa = None 

334 self._HA = None 

335 self._lmst = None 

336 self._az_to_sun = None 

337 self._season = None 

338 

339 @property 

340 def skybrightness(self): 

341 return self._skybrightness 

342 

343 @skybrightness.setter 

344 def skybrightness(self, indict): 

345 for key in indict: 

346 

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

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

349 self._M5Depth = None 

350 

351 @property 

352 def FWHMeff(self): 

353 return self._FWHMeff 

354 

355 @FWHMeff.setter 

356 def FWHMeff(self, indict): 

357 for key in indict: 

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

359 self._M5Depth = None 

360 

361 @property 

362 def M5Depth(self): 

363 if self._M5Depth is None: 

364 self.calc_M5Depth() 

365 return self._M5Depth 

366 

367 def calc_M5Depth(self): 

368 self._M5Depth = {} 

369 for filtername in self._skybrightness: 

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

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

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

373 self._skybrightness[filtername][good], 

374 self._FWHMeff[filtername][good], 

375 self.exptime, 

376 self._airmass[good]) 

377 

378 def calc_az_to_sun(self): 

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

380 self._az_to_sun = diff 

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

382 

383 @property 

384 def az_to_sun(self): 

385 if self._az_to_sun is None: 

386 self.calc_az_to_sun() 

387 return self._az_to_sun 

388 

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

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

391 if self.season_offset is not None: 

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

393 if ~kwargs_match: 

394 self.season_modulo = modulo 

395 self.season_max_season = max_season 

396 self.season_length = season_length 

397 self.season_floor = floor 

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

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

400 modulo=modulo, max_season=max_season, 

401 season_length=season_length, floor=floor) 

402 else: 

403 self._season = None 

404 

405 return self._season