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

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

155 Healpix map of the solar elongation (angular distance to the sun) for each healpixel (radians) 

156 

157 Attributes (set by the scheduler) 

158 ------------------------------- 

159 queue : list of observation objects 

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

161 

162 """ 

163 if nside is None: 

164 nside = set_default_nside() 

165 self.nside = nside 

166 self.site = Site(site) 

167 self.exptime = exptime 

168 self.mjd_start = mjd_start 

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

170 self.season_offset = season_offset 

171 self.sun_RA_start = sun_RA_start 

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

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

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

175 self.nan_map.fill(np.nan) 

176 # The RA, Dec grid we are using 

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

178 

179 # Modified Julian Date (day) 

180 self._mjd = None 

181 # Altitude and azimuth. Dict with degrees and radians 

182 self._alt = None 

183 self._az = None 

184 self._pa = None 

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

186 self.clouds = None 

187 self._slewtime = None 

188 self.current_filter = None 

189 self.mounted_filters = None 

190 self.night = None 

191 self._lmst = None 

192 # Should be a dict with filtername keys 

193 self._skybrightness = {} 

194 self._FWHMeff = {} 

195 self._M5Depth = None 

196 self._airmass = None 

197 

198 # Upcomming scheduled observations 

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

200 

201 # Attribute to hold the current observing queue 

202 self.queue = None 

203 

204 # Moon 

205 self.moonAlt = None 

206 self.moonAz = None 

207 self.moonRA = None 

208 self.moonDec = None 

209 self.moonPhase = None 

210 

211 # Sun 

212 self.sunAlt = None 

213 self.sunAz = None 

214 self.sunRA = None 

215 self.sunDec = None 

216 

217 # Almanac information 

218 self.sunset = None 

219 self.sun_n12_setting = None 

220 self.sun_n18_setting = None 

221 self.sun_n18_rising = None 

222 self.sun_n12_rising = None 

223 self.sunrise = None 

224 self.moonrise = None 

225 self.moonset = None 

226 

227 self.planet_positions = None 

228 

229 # Current telescope pointing 

230 self.telRA = None 

231 self.telDec = None 

232 self.telAlt = None 

233 self.telAz = None 

234 

235 # Full sky cloud map 

236 self._cloud_map = None 

237 self._HA = None 

238 

239 # XXX--document 

240 self.bulk_cloud = None 

241 

242 self.rotTelPos = None 

243 

244 self.targets_of_opportunity = None 

245 

246 self._season = None 

247 

248 self.season_modulo = None 

249 self.season_max_season = None 

250 self.season_length = 365.25 

251 self.season_floor = True 

252 

253 @property 

254 def lmst(self): 

255 return self._lmst 

256 

257 @lmst.setter 

258 def lmst(self, value): 

259 self._lmst = value 

260 self._HA = None 

261 

262 @property 

263 def HA(self): 

264 if self._HA is None: 

265 self.calc_HA() 

266 return self._HA 

267 

268 def calc_HA(self): 

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

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

271 

272 @property 

273 def cloud_map(self): 

274 return self._cloud_map 

275 

276 @cloud_map.setter 

277 def cloud_map(self, value): 

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

279 

280 @property 

281 def slewtime(self): 

282 return self._slewtime 

283 

284 @slewtime.setter 

285 def slewtime(self, value): 

286 # Using 0 for start of night 

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

288 self._slewtime = value 

289 else: 

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

291 

292 @property 

293 def airmass(self): 

294 return self._airmass 

295 

296 @airmass.setter 

297 def airmass(self, value): 

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

299 self._M5Depth = None 

300 

301 @property 

302 def pa(self): 

303 if self._pa is None: 

304 self.calc_pa() 

305 return self._pa 

306 

307 def calc_pa(self): 

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

309 

310 @property 

311 def alt(self): 

312 if self._alt is None: 

313 self.calc_altAz() 

314 return self._alt 

315 

316 @property 

317 def az(self): 

318 if self._az is None: 

319 self.calc_altAz() 

320 return self._az 

321 

322 def calc_altAz(self): 

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

324 self.site.latitude_rad, 

325 self.site.longitude_rad, self._mjd) 

326 

327 @property 

328 def mjd(self): 

329 return self._mjd 

330 

331 @mjd.setter 

332 def mjd(self, value): 

333 self._mjd = value 

334 # Set things that need to be recalculated to None 

335 self._az = None 

336 self._alt = None 

337 self._pa = None 

338 self._HA = None 

339 self._lmst = None 

340 self._az_to_sun = None 

341 self._az_to_antisun = None 

342 self._season = None 

343 self._solar_elongation = None 

344 

345 @property 

346 def skybrightness(self): 

347 return self._skybrightness 

348 

349 @skybrightness.setter 

350 def skybrightness(self, indict): 

351 for key in indict: 

352 

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

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

355 self._M5Depth = None 

356 

357 @property 

358 def FWHMeff(self): 

359 return self._FWHMeff 

360 

361 @FWHMeff.setter 

362 def FWHMeff(self, indict): 

363 for key in indict: 

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

365 self._M5Depth = None 

366 

367 @property 

368 def M5Depth(self): 

369 if self._M5Depth is None: 

370 self.calc_M5Depth() 

371 return self._M5Depth 

372 

373 def calc_M5Depth(self): 

374 self._M5Depth = {} 

375 for filtername in self._skybrightness: 

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

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

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

379 self._skybrightness[filtername][good], 

380 self._FWHMeff[filtername][good], 

381 self.exptime, 

382 self._airmass[good]) 

383 

384 def calc_solar_elongation(self): 

385 self._solar_elongation = _angularSeparation(self.ra, self.dec, self.sunRA, self.sunDec) 

386 

387 @property 

388 def solar_elongation(self): 

389 if self._solar_elongation is None: 

390 self.calc_solar_elongation() 

391 return self._solar_elongation 

392 

393 def calc_az_to_sun(self): 

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

395 

396 def calc_az_to_antisun(self): 

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

398 

399 @property 

400 def az_to_sun(self): 

401 if self._az_to_sun is None: 

402 self.calc_az_to_sun() 

403 return self._az_to_sun 

404 

405 @property 

406 def az_to_antisun(self): 

407 if self._az_to_antisun is None: 

408 self.calc_az_to_antisun() 

409 return self._az_to_antisun 

410 

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

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

413 if self.season_offset is not None: 

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

415 if ~kwargs_match: 

416 self.season_modulo = modulo 

417 self.season_max_season = max_season 

418 self.season_length = season_length 

419 self.season_floor = floor 

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

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

422 modulo=modulo, max_season=max_season, 

423 season_length=season_length, floor=floor) 

424 else: 

425 self._season = None 

426 

427 return self._season