Coverage for python/lsst/sims/maf/slicers/baseSpatialSlicer.py : 14%

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 range
3# The base class for all spatial slicers.
4# Slicers are 'data slicers' at heart; spatial slicers slice data by RA/Dec and
5# return the relevant indices in the simData to the metric.
6# The primary things added here are the methods to slice the data (for any spatial slicer)
7# as this uses a KD-tree built on spatial (RA/Dec type) indexes.
9import warnings
10import numpy as np
11from functools import wraps
12from lsst.sims.maf.plots.spatialPlotters import BaseHistogram, BaseSkyMap
14# For the footprint generation and conversion between galactic/equatorial coordinates.
15from lsst.obs.lsstSim import LsstSimMapper
16from lsst.sims.coordUtils import _chipNameFromRaDec
17import lsst.sims.utils as simsUtils
19from .baseSlicer import BaseSlicer
21__all__ = ['BaseSpatialSlicer']
24class BaseSpatialSlicer(BaseSlicer):
25 """Base spatial slicer object, contains additional functionality for spatial slicing,
26 including setting up and traversing a kdtree containing the simulated data points.
28 Parameters
29 ----------
30 lonCol : str, optional
31 Name of the longitude (RA equivalent) column to use from the input data.
32 Default fieldRA
33 latCol : str, optional
34 Name of the latitude (Dec equivalent) column to use from the input data.
35 Default fieldDec
36 latLonDeg : boolean, optional
37 Flag indicating whether lat and lon values from input data are in degrees (True) or radians (False).
38 Default True.
39 verbose : boolean, optional
40 Flag to indicate whether or not to write additional information to stdout during runtime.
41 Default True.
42 badval : float, optional
43 Bad value flag, relevant for plotting. Default -666.
44 leafsize : int, optional
45 Leafsize value for kdtree. Default 100.
46 radius : float, optional
47 Radius for matching in the kdtree. Equivalent to the radius of the FOV. Degrees.
48 Default 1.75.
49 useCamera : boolean, optional
50 Flag to indicate whether to use the LSST camera footprint or not.
51 Default False.
52 rotSkyPosColName : str, optional
53 Name of the rotSkyPos column in the input data. Only used if useCamera is True.
54 Describes the orientation of the camera orientation compared to the sky.
55 Default rotSkyPos.
56 mjdColName : str, optional
57 Name of the exposure time column. Only used if useCamera is True.
58 Default observationStartMJD.
59 chipNames : array-like, optional
60 List of chips to accept, if useCamera is True. This lets users turn 'on' only a subset of chips.
61 Default 'all' - this uses all chips in the camera.
62 """
63 def __init__(self, lonCol='fieldRA', latCol='fieldDec', latLonDeg=True,
64 verbose=True, badval=-666, leafsize=100, radius=1.75,
65 useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='observationStartMJD',
66 chipNames='all'):
67 super(BaseSpatialSlicer, self).__init__(verbose=verbose, badval=badval)
68 self.lonCol = lonCol
69 self.latCol = latCol
70 self.latLonDeg = latLonDeg
71 self.rotSkyPosColName = rotSkyPosColName
72 self.mjdColName = mjdColName
73 self.columnsNeeded = [lonCol, latCol]
74 self.useCamera = useCamera
75 if useCamera:
76 self.columnsNeeded.append(rotSkyPosColName)
77 self.columnsNeeded.append(mjdColName)
78 self.slicer_init = {'lonCol': lonCol, 'latCol': latCol,
79 'radius': radius, 'badval': badval,
80 'useCamera': useCamera}
81 self.radius = radius
82 self.leafsize = leafsize
83 self.useCamera = useCamera
84 self.chipsToUse = chipNames
85 # RA and Dec are required slicePoint info for any spatial slicer. Slicepoint RA/Dec are in radians.
86 self.slicePoints['sid'] = None
87 self.slicePoints['ra'] = None
88 self.slicePoints['dec'] = None
89 self.nslice = None
90 self.shape = None
91 self.plotFuncs = [BaseHistogram, BaseSkyMap]
93 def setupSlicer(self, simData, maps=None):
94 """Use simData[self.lonCol] and simData[self.latCol] (in radians) to set up KDTree.
96 Parameters
97 -----------
98 simData : numpy.recarray
99 The simulated data, including the location of each pointing.
100 maps : list of lsst.sims.maf.maps objects, optional
101 List of maps (such as dust extinction) that will run to build up additional metadata at each
102 slicePoint. This additional metadata is available to metrics via the slicePoint dictionary.
103 Default None.
104 """
105 if maps is not None:
106 if self.cacheSize != 0 and len(maps) > 0:
107 warnings.warn('Warning: Loading maps but cache on.'
108 'Should probably set useCache=False in slicer.')
109 self._runMaps(maps)
110 self._setRad(self.radius)
111 if self.useCamera:
112 self._setupLSSTCamera()
113 self._presliceFootprint(simData)
114 else:
115 if self.latLonDeg:
116 self._buildTree(np.radians(simData[self.lonCol]),
117 np.radians(simData[self.latCol]), self.leafsize)
118 else:
119 self._buildTree(simData[self.lonCol], simData[self.latCol], self.leafsize)
121 @wraps(self._sliceSimData)
122 def _sliceSimData(islice):
123 """Return indexes for relevant opsim data at slicepoint
124 (slicepoint=lonCol/latCol value .. usually ra/dec)."""
126 # Build dict for slicePoint info
127 slicePoint = {}
128 if self.useCamera:
129 indices = self.sliceLookup[islice]
130 slicePoint['chipNames'] = self.chipNames[islice]
131 else:
132 sx, sy, sz = simsUtils._xyz_from_ra_dec(self.slicePoints['ra'][islice],
133 self.slicePoints['dec'][islice])
134 # Query against tree.
135 indices = self.opsimtree.query_ball_point((sx, sy, sz), self.rad)
137 # Loop through all the slicePoint keys. If the first dimension of slicepoint[key] has
138 # the same shape as the slicer, assume it is information per slicepoint.
139 # Otherwise, pass the whole slicePoint[key] information. Useful for stellar LF maps
140 # where we want to pass only the relevant LF and the bins that go with it.
141 for key in self.slicePoints:
142 if len(np.shape(self.slicePoints[key])) == 0:
143 keyShape = 0
144 else:
145 keyShape = np.shape(self.slicePoints[key])[0]
146 if (keyShape == self.nslice):
147 slicePoint[key] = self.slicePoints[key][islice]
148 else:
149 slicePoint[key] = self.slicePoints[key]
150 return {'idxs': indices, 'slicePoint': slicePoint}
151 setattr(self, '_sliceSimData', _sliceSimData)
153 def _setupLSSTCamera(self):
154 """If we want to include the camera chip gaps, etc"""
155 mapper = LsstSimMapper()
156 self.camera = mapper.camera
157 self.epoch = 2000.0
159 def _presliceFootprint(self, simData):
160 """Loop over each pointing and find which sky points are observed """
161 # Now to make a list of lists for looking up the relevant observations at each slicepoint
162 self.sliceLookup = [[] for dummy in range(self.nslice)]
163 self.chipNames = [[] for dummy in range(self.nslice)]
164 # Make a kdtree for the _slicepoints_
165 # Using scipy 0.16 or later
166 self._buildTree(self.slicePoints['ra'], self.slicePoints['dec'], leafsize=self.leafsize)
168 # Loop over each unique pointing position
169 if self.latLonDeg:
170 lat = np.radians(simData[self.latCol])
171 lon = np.radians(simData[self.lonCol])
172 else:
173 lat = simData[self.latCol]
174 lon = simData[self.lonCol]
175 for ind, ra, dec, rotSkyPos, mjd in zip(np.arange(simData.size), lon, lat,
176 simData[self.rotSkyPosColName], simData[self.mjdColName]):
177 dx, dy, dz = simsUtils._xyz_from_ra_dec(ra, dec)
178 # Find healpixels inside the FoV
179 hpIndices = np.array(self.opsimtree.query_ball_point((dx, dy, dz), self.rad))
180 if hpIndices.size > 0:
181 obs_metadata = simsUtils.ObservationMetaData(pointingRA=np.degrees(ra),
182 pointingDec=np.degrees(dec),
183 rotSkyPos=np.degrees(rotSkyPos),
184 mjd=mjd)
186 chipNames = _chipNameFromRaDec(self.slicePoints['ra'][hpIndices],
187 self.slicePoints['dec'][hpIndices],
188 epoch=self.epoch,
189 camera=self.camera, obs_metadata=obs_metadata)
190 # If we are using only a subset of chips
191 if self.chipsToUse != 'all':
192 checkedChipNames = [chipName in self.chipsToUse for chipName in chipNames]
193 good = np.where(checkedChipNames)[0]
194 chipNames = chipNames[good]
195 hpIndices = hpIndices[good]
196 # Find the healpixels that fell on a chip for this pointing
197 good = np.where(chipNames != [None])[0]
198 hpOnChip = hpIndices[good]
199 for i, chipName in zip(hpOnChip, chipNames[good]):
200 self.sliceLookup[i].append(ind)
201 self.chipNames[i].append(chipName)
203 if self.verbose:
204 "Created lookup table after checking for chip gaps."
206 def _buildTree(self, simDataRa, simDataDec, leafsize=100):
207 """Build KD tree on simDataRA/Dec using utility function from mafUtils.
209 simDataRA, simDataDec = RA and Dec values (in radians).
210 leafsize = the number of Ra/Dec pointings in each leaf node."""
211 self.opsimtree = simsUtils._buildTree(simDataRa,
212 simDataDec,
213 leafsize)
215 def _setRad(self, radius=1.75):
216 """Set radius (in degrees) for kdtree search using utility function from mafUtils."""
217 self.rad = simsUtils.xyz_angular_radius(radius)