Coverage for python/lsst/sims/featureScheduler/features/conditions.py : 18%

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
6__all__ = ['Conditions']
9class Conditions(object):
10 """
11 Class to hold telemetry information
13 If the incoming value is a healpix map, we use a setter to ensure the
14 resolution matches.
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)
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.
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.
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)
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.
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)
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
198 # Upcomming scheduled observations
199 self.scheduled_observations = np.array([], dtype=float)
201 # Attribute to hold the current observing queue
202 self.queue = None
204 # Moon
205 self.moonAlt = None
206 self.moonAz = None
207 self.moonRA = None
208 self.moonDec = None
209 self.moonPhase = None
211 # Sun
212 self.sunAlt = None
213 self.sunAz = None
214 self.sunRA = None
215 self.sunDec = None
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
227 self.planet_positions = None
229 # Current telescope pointing
230 self.telRA = None
231 self.telDec = None
232 self.telAlt = None
233 self.telAz = None
235 # Full sky cloud map
236 self._cloud_map = None
237 self._HA = None
239 # XXX--document
240 self.bulk_cloud = None
242 self.rotTelPos = None
244 self.targets_of_opportunity = None
246 self._season = None
248 self.season_modulo = None
249 self.season_max_season = None
250 self.season_length = 365.25
251 self.season_floor = True
253 @property
254 def lmst(self):
255 return self._lmst
257 @lmst.setter
258 def lmst(self, value):
259 self._lmst = value
260 self._HA = None
262 @property
263 def HA(self):
264 if self._HA is None:
265 self.calc_HA()
266 return self._HA
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
272 @property
273 def cloud_map(self):
274 return self._cloud_map
276 @cloud_map.setter
277 def cloud_map(self, value):
278 self._cloud_map = match_hp_resolution(value, nside_out=self.nside)
280 @property
281 def slewtime(self):
282 return self._slewtime
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)
292 @property
293 def airmass(self):
294 return self._airmass
296 @airmass.setter
297 def airmass(self, value):
298 self._airmass = match_hp_resolution(value, nside_out=self.nside)
299 self._M5Depth = None
301 @property
302 def pa(self):
303 if self._pa is None:
304 self.calc_pa()
305 return self._pa
307 def calc_pa(self):
308 self._pa = _approx_altaz2pa(self.alt, self.az, self.site.latitude_rad)
310 @property
311 def alt(self):
312 if self._alt is None:
313 self.calc_altAz()
314 return self._alt
316 @property
317 def az(self):
318 if self._az is None:
319 self.calc_altAz()
320 return self._az
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)
327 @property
328 def mjd(self):
329 return self._mjd
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
345 @property
346 def skybrightness(self):
347 return self._skybrightness
349 @skybrightness.setter
350 def skybrightness(self, indict):
351 for key in indict:
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
357 @property
358 def FWHMeff(self):
359 return self._FWHMeff
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
367 @property
368 def M5Depth(self):
369 if self._M5Depth is None:
370 self.calc_M5Depth()
371 return self._M5Depth
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])
384 def calc_solar_elongation(self):
385 self._solar_elongation = _angularSeparation(self.ra, self.dec, self.sunRA, self.sunDec)
387 @property
388 def solar_elongation(self):
389 if self._solar_elongation is None:
390 self.calc_solar_elongation()
391 return self._solar_elongation
393 def calc_az_to_sun(self):
394 self._az_to_sun = smallest_signed_angle(self.ra, self.sunRA)
396 def calc_az_to_antisun(self):
397 self._az_to_antisun = smallest_signed_angle(self.ra+np.pi, self.sunRA)
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
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
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
427 return self._season