Coverage for python/lsst/sims/utils/ObservationMetaData.py : 15%

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
1from builtins import zip
2from builtins import object
3import numpy as np
4import numbers
5from .SpatialBounds import SpatialBounds
6from lsst.sims.utils import ModifiedJulianDate
7from lsst.sims.utils import Site
9__all__ = ["ObservationMetaData"]
12class ObservationMetaData(object):
13 """Observation Metadata
15 This class contains any metadata for a query which is associated with
16 a particular telescope pointing, including bounds in RA and DEC, and
17 the time of the observation.
19 **Parameters**
21 All parameters are optional. It is possible to instantiate an
22 ObservationMetaData that is empty of data.
24 * pointing[RA,Dec] float
25 The coordinates of the pointing (in degrees; in the International
26 Celestial Reference System)
28 * boundType characterizes the shape of the field of view. Current options
29 are 'box, and 'circle'
31 * boundLength is the characteristic length scale of the field of view in degrees.
33 If boundType is 'box', boundLength can be a float(in which case boundLength is
34 half the length of the side of each box) or boundLength can be a numpy array
35 in which case the first argument is half the width of the RA side of the box
36 and the second argument is half the Dec side of the box.
38 If boundType is 'circle,' this will be the radius of the circle.
40 The bound will be centered on the point (pointingRA, pointingDec), however,
41 because objects are stored at their mean RA, Dec in the LSST databases
42 (i.e. they are stored at values of RA, Dec which neglect proper motion), the
43 bounds applied to database queries will be made slightly larger so that queries
44 can be reasonably expected to return all of the objects within the desired field
45 of view once those corrections have been applied.
47 * mjd :
48 Either a float (in which case, it will be assumed to be in International
49 Atomic Time), or an instantiation of the ModifiedJulianDate class representing
50 the date of the observation
52 * bandpassName : a char (e.g. 'u') or list (e.g. ['u', 'g', 'z'])
53 denoting the bandpasses used for this particular observation
55 * site: an instantiation of the lsst.sims.utils.Site class characterizing
56 the site of the observatory.
58 * m5: float or list
59 this should be the 5-sigma limiting magnitude in the bandpass or
60 bandpasses specified in bandpassName. Ultimately, m5 will be stored
61 in a dict keyed to the bandpassName (or Names) you passed in, i.e.
62 you will be able to access m5 from outside of this class using, for
63 example:
65 myObservationMetaData.m5['u']
67 * skyBrightness: float the magnitude of the sky in the
68 filter specified by bandpassName
70 * seeing float or list
71 Analogous to m5, corresponds to the seeing in arcseconds in the bandpasses in
72 bandpassName
74 * rotSkyPos float
75 The orientation of the telescope in degrees.
76 This is used by the Astrometry mixins in sims_coordUtils.
78 The convention for rotSkyPos is as follows:
80 rotSkyPos = 0 means north is in the +y direction on the focal plane and east is +x
82 rotSkyPos = 90 means north is +x and east is -y
84 rotSkyPos = -90 means north is -x and east is +y
86 rotSkyPos = 180 means north is -y and east is -x
88 This should be consistent with PhoSim conventions.
90 **Examples**::
91 >>> data = ObservationMetaData(boundType='box', pointingRA=5.0, pointingDec=15.0,
92 boundLength=5.0)
94 """
95 def __init__(self, boundType=None, boundLength=None,
96 mjd=None, pointingRA=None, pointingDec=None, rotSkyPos=None,
97 bandpassName=None, site=Site(name='LSST'), m5=None, skyBrightness=None,
98 seeing=None):
100 self._bounds = None
101 self._boundType = boundType
102 self._bandpass = bandpassName
103 self._skyBrightness = skyBrightness
104 self._site = site
105 self._OpsimMetaData = None
107 if mjd is not None:
108 if isinstance(mjd, numbers.Number):
109 self._mjd = ModifiedJulianDate(TAI=mjd)
110 elif isinstance(mjd, ModifiedJulianDate):
111 self._mjd = mjd
112 else:
113 raise RuntimeError("You must pass either a float or a ModifiedJulianDate "
114 "as the kwarg mjd to ObservationMetaData")
115 else:
116 self._mjd = None
118 if rotSkyPos is not None:
119 self._rotSkyPos = np.radians(rotSkyPos)
120 else:
121 self._rotSkyPos = None
123 if pointingRA is not None:
124 self._pointingRA = np.radians(pointingRA)
125 else:
126 self._pointingRA = None
128 if pointingDec is not None:
129 self._pointingDec = np.radians(pointingDec)
130 else:
131 self._pointingDec = None
133 if boundLength is not None:
134 self._boundLength = np.radians(boundLength)
135 else:
136 self._boundLength = None
138 self._m5 = self._assignDictKeyedToBandpass(m5, 'm5')
140 self._seeing = self._assignDictKeyedToBandpass(seeing, 'seeing')
142 if self._bounds is None:
143 self._buildBounds()
145 @property
146 def summary(self):
147 mydict = {}
148 mydict['site'] = self.site
150 mydict['boundType'] = self.boundType
151 mydict['boundLength'] = self.boundLength
152 mydict['pointingRA'] = self.pointingRA
153 mydict['pointingDec'] = self.pointingDec
154 mydict['rotSkyPos'] = self.rotSkyPos
156 if self.mjd is None:
157 mydict['mjd'] = None
158 else:
159 mydict['mjd'] = self.mjd.TAI
161 mydict['bandpass'] = self.bandpass
162 mydict['skyBrightness'] = self.skyBrightness
163 # mydict['m5'] = self.m5
165 mydict['OpsimMetaData'] = self._OpsimMetaData
167 return mydict
169 def __ne__(self, other):
170 return not self.__eq__(other)
172 def __eq__(self, other):
174 if self.bounds != other.bounds:
175 return False
177 if self.pointingRA != other.pointingRA:
178 return False
180 if self.pointingDec != other.pointingDec:
181 return False
183 if self.rotSkyPos != other.rotSkyPos:
184 return False
186 if self.bandpass != other.bandpass:
187 return False
189 if self.seeing != other.seeing:
190 return False
192 if self.m5 != other.m5:
193 return False
195 if self.site != other.site:
196 return False
198 if self.mjd != other.mjd:
199 return False
201 if self.skyBrightness != other.skyBrightness:
202 return False
204 if self.OpsimMetaData != other.OpsimMetaData:
205 return False
207 return True
209 def _assignDictKeyedToBandpass(self, inputValue, inputName):
210 """
211 This method sets up a dict of either m5 or seeing values (or any other quantity
212 keyed to bandpassName). It reads in a list of values and associates them with
213 the list of bandpass names in self._bandpass.
215 Note: this method assumes that self._bandpass has already been set.
216 It will raise an exception of self._bandpass is None.
218 @param [in] inputValue is a single value or list of m5/seeing/etc. corresponding to
219 the bandpasses stored in self._bandpass
221 @param [in] inputName is the name of the paramter stored in inputValue
222 (for constructing helpful error message)
224 @param [out] returns a dict of inputValue values keed to self._bandpass
225 """
227 if inputValue is None:
228 return None
229 else:
230 bandpassIsList = False
231 inputIsList = False
233 if self._bandpass is None:
234 raise RuntimeError('You cannot set %s if you have not set ' % inputName +
235 'bandpass in ObservationMetaData')
237 if hasattr(self._bandpass, '__iter__') and not isinstance(self._bandpass, str):
238 bandpassIsList = True
240 if hasattr(inputValue, '__iter__') and not isinstance(inputValue, str):
241 inputIsList = True
243 if bandpassIsList and not inputIsList:
244 raise RuntimeError('You passed a list of bandpass names' +
245 'but did not pass a list of %s to ObservationMetaData' % inputName)
247 if inputIsList and not bandpassIsList:
248 raise RuntimeError('You passed a list of %s ' % inputName +
249 'but did not pass a list of bandpass names to ObservationMetaData')
251 if inputIsList:
252 if len(inputValue) != len(self._bandpass):
253 raise RuntimeError('The list of %s you passed to ObservationMetaData ' % inputName +
254 'has a different length than the list of bandpass names you passed')
256 # now build the dict
257 if bandpassIsList:
258 if len(inputValue) != len(self._bandpass):
259 raise RuntimeError('In ObservationMetaData you tried to assign bandpass ' +
260 'and %s with lists of different length' % inputName)
262 outputDict = {}
263 for b, m in zip(self._bandpass, inputValue):
264 outputDict[b] = m
265 else:
266 outputDict = {self._bandpass: inputValue}
268 return outputDict
270 def _buildBounds(self):
271 """
272 Set up the member variable self._bounds.
274 If self._boundType, self._boundLength, self._pointingRA, or
275 self._pointingDec are None, nothing will happen.
276 """
278 if self._boundType is None:
279 return
281 if self._boundLength is None:
282 return
284 if self._pointingRA is None or self._pointingDec is None:
285 return
287 self._bounds = SpatialBounds.getSpatialBounds(self._boundType, self._pointingRA, self._pointingDec,
288 self._boundLength)
290 @property
291 def pointingRA(self):
292 """
293 The RA of the telescope pointing in degrees
294 (in the International Celestial Reference System).
295 """
296 if self._pointingRA is not None:
297 return np.degrees(self._pointingRA)
298 else:
299 return None
301 @pointingRA.setter
302 def pointingRA(self, value):
303 self._pointingRA = np.radians(value)
304 self._buildBounds()
306 @property
307 def pointingDec(self):
308 """
309 The Dec of the telescope pointing in degrees
310 (in the International Celestial Reference System).
311 """
312 if self._pointingDec is not None:
313 return np.degrees(self._pointingDec)
314 else:
315 return None
317 @pointingDec.setter
318 def pointingDec(self, value):
319 self._pointingDec = np.radians(value)
320 self._buildBounds()
322 @property
323 def boundLength(self):
324 """
325 Either a list or a float indicating the size of the field
326 of view associated with this ObservationMetaData.
328 See the documentation in the SpatialBounds class for more
329 details (specifically, the 'length' paramter).
331 In degrees (Yes: the documentation in SpatialBounds says that
332 the length should be in radians. The present class converts
333 from degrees to radians before passing to SpatialBounds).
334 """
335 if self._boundLength is None:
336 return None
338 return np.degrees(self._boundLength)
340 @boundLength.setter
341 def boundLength(self, value):
342 self._boundLength = np.radians(value)
343 self._buildBounds()
345 @property
346 def boundType(self):
347 """
348 Tag indicating what sub-class of SpatialBounds should
349 be instantiated for this ObservationMetaData.
350 """
351 return self._boundType
353 @boundType.setter
354 def boundType(self, value):
355 self._boundType = value
356 self._buildBounds()
358 @property
359 def bounds(self):
360 """
361 Instantiation of a sub-class of SpatialBounds. This
362 is what actually construct the WHERE clause of the SQL
363 query associated with this ObservationMetaData.
364 """
365 return self._bounds
367 @property
368 def rotSkyPos(self):
369 """
370 The rotation of the telescope with respect to the sky in degrees.
371 It is a parameter you should get from OpSim.
372 """
373 if self._rotSkyPos is not None:
374 return np.degrees(self._rotSkyPos)
375 else:
376 return None
378 @rotSkyPos.setter
379 def rotSkyPos(self, value):
380 self._rotSkyPos = np.radians(value)
382 @property
383 def m5(self):
384 """
385 A dict of m5 (the 5-sigma limiting magnitude) values
386 associated with the bandpasses represented by this
387 ObservationMetaData.
388 """
389 return self._m5
391 @m5.setter
392 def m5(self, value):
393 self._m5 = self._assignDictKeyedToBandpass(value, 'm5')
395 @property
396 def seeing(self):
397 """
398 A dict of seeing values in arcseconds associated
399 with the bandpasses represented by this ObservationMetaData
400 """
401 return self._seeing
403 @seeing.setter
404 def seeing(self, value):
405 self._seeing = self._assignDictKeyedToBandpass(value, 'seeing')
407 @property
408 def site(self):
409 """
410 An instantiation of the Site class containing information about
411 the telescope site.
412 """
413 return self._site
415 @site.setter
416 def site(self, value):
417 self._site = value
419 @property
420 def mjd(self):
421 """
422 The MJD of the observation associated with this ObservationMetaData.
423 """
424 return self._mjd
426 @mjd.setter
427 def mjd(self, value):
428 """
429 Either a float or a ModifiedJulianDate. If a float, this setter
430 assumes that you are passing in International Atomic Time
431 """
432 if isinstance(value, float):
433 self._mjd = ModifiedJulianDate(TAI=value)
434 elif isinstance(value, ModifiedJulianDate):
435 self._mjd = value
436 else:
437 raise RuntimeError("You can only set mjd to either a float or a ModifiedJulianDate")
439 @property
440 def bandpass(self):
441 """
442 The bandpass associated with this ObservationMetaData.
443 Can be a list.
444 """
445 return self._bandpass
447 def setBandpassM5andSeeing(self, bandpassName=None, m5=None, seeing=None):
448 """
449 Set the bandpasses and associated 5-sigma limiting magnitudes
450 and seeing values for this ObservationMetaData.
452 @param [in] bandpassName is either a char or a list of chars denoting
453 the name of the bandpass associated with this ObservationMetaData.
455 @param [in] m5 is the 5-sigma-limiting magnitude(s) associated
456 with bandpassName
458 @param [in] seeing is the seeing(s) in arcseconds associated
459 with bandpassName
461 Nothing is returned. This method just sets member variables of
462 this ObservationMetaData.
463 """
465 self._bandpass = bandpassName
466 self._m5 = self._assignDictKeyedToBandpass(m5, 'm5')
467 self._seeing = self._assignDictKeyedToBandpass(seeing, 'seeing')
469 @property
470 def skyBrightness(self):
471 """
472 The sky brightness in mags per square arcsecond associated
473 with this ObservationMetaData.
474 """
475 return self._skyBrightness
477 @skyBrightness.setter
478 def skyBrightness(self, value):
479 self._skyBrightness = value
481 @property
482 def OpsimMetaData(self):
483 """
484 A dict of all of the columns taken from OpSim when constructing this
485 ObservationMetaData
486 """
487 return self._OpsimMetaData
489 @OpsimMetaData.setter
490 def OpsimMetaData(self, value):
491 if not isinstance(value, dict):
492 raise RuntimeError('OpsimMetaData must be a dict')
493 self._OpsimMetaData = value