From 0a06b7884fac4197eaa35527bb86b9e58cbe4cb9 Mon Sep 17 00:00:00 2001 From: Lynne Jones Date: Thu, 2 Jun 2016 15:59:18 -0700 Subject: [PATCH 1/4] Fixed bugs with userPointsSlicer. Can now read back data successfully, and compare two slicers. --- .../lsst/sims/maf/slicers/userPointsSlicer.py | 76 ++++++++++++++----- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/python/lsst/sims/maf/slicers/userPointsSlicer.py b/python/lsst/sims/maf/slicers/userPointsSlicer.py index 1edbfa36e..dea1895a2 100644 --- a/python/lsst/sims/maf/slicers/userPointsSlicer.py +++ b/python/lsst/sims/maf/slicers/userPointsSlicer.py @@ -4,23 +4,53 @@ __all__ = ['UserPointsSlicer'] -class UserPointsSlicer(BaseSpatialSlicer): - """Use spatial slicer on a user provided point """ - def __init__(self, ra, dec, verbose=True, lonCol='fieldRA', latCol='fieldDec', - badval=-666, leafsize=100, radius=1.75, - useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='expMJD', - chipNames=None): - """ - ra = list of ra points to use - dec = list of dec points to use - """ - super(UserPointsSlicer,self).__init__(verbose=verbose, - lonCol=lonCol, latCol=latCol, - badval=badval, radius=radius, leafsize=leafsize, - useCamera=useCamera, rotSkyPosColName=rotSkyPosColName, - mjdColName=mjdColName, chipNames=chipNames) +class UserPointsSlicer(BaseSpatialSlicer): + """A spatial slicer that evaluates pointings overlapping user-provided list of points. + Parameters + ---------- + ra : list or numpy.ndarray + User-selected RA points, in degrees. Stored (and used) internally in radians. + dec : list or numpy.ndarray + User-selected Dec points, in degrees. Stored internally in radians. + lonCol : str, optional + Name of the longitude (RA equivalent) column to use from the input data. + Default fieldRA + latCol : str, optional + Name of the latitude (Dec equivalent) column to use from the input data. + Default fieldDec + verbose : boolean, optional + Flag to indicate whether or not to write additional information to stdout during runtime. + Default True. + badval : float, optional + Bad value flag, relevant for plotting. Default -666. + leafsize : int, optional + Leafsize value for kdtree. Default 100. + radius : float, optional + Radius for matching in the kdtree. Equivalent to the radius of the FOV. Degrees. + Default 1.75. + useCamera : boolean, optional + Flag to indicate whether to use the LSST camera footprint or not. + Default False. + rotSkyPosColName : str, optional + Name of the rotSkyPos column in the input data. Only used if useCamera is True. + Describes the orientation of the camera orientation compared to the sky. + Default rotSkyPos. + mjdColName : str, optional + Name of the exposure time column. Only used if useCamera is True. + Default expMJD. + chipNames : array-like, optional + List of chips to accept, if useCamera is True. This lets users turn 'on' only a subset of chips. + Default 'all' - this uses all chips in the camera. + """ + def __init__(self, ra, dec, lonCol='fieldRA', latCol='fieldDec', verbose=True, + badval=-666, leafsize=100, radius=1.75, + useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='expMJD', chipNames='all'): + super(UserPointsSlicer, self).__init__(lonCol=lonCol, latCol=latCol, verbose=verbose, + badval=badval, radius=radius, leafsize=leafsize, + useCamera=useCamera, rotSkyPosColName=rotSkyPosColName, + mjdColName=mjdColName, chipNames=chipNames) # check that ra and dec are iterable, if not, they are probably naked numbers, wrap in list if not hasattr(ra, '__iter__'): ra = [ra] @@ -28,12 +58,19 @@ def __init__(self, ra, dec, verbose=True, lonCol='fieldRA', latCol='fieldDec', dec = [dec] if len(ra) != len(dec): raise ValueError('RA and Dec must be the same length') - self.nslice = np.size(ra) - self.shape = self.nslice - self.spatialExtent = [0,self.nslice-1] + ra = np.radians(ra) + dec = np.radians(dec) self.slicePoints['sid'] = np.arange(np.size(ra)) self.slicePoints['ra'] = np.array(ra) self.slicePoints['dec'] = np.array(dec) + self.nslice = np.size(ra) + self.shape = self.nslice + self.spatialExtent = [0, self.nslice - 1] + self.slicer_init = {'ra': ra, + 'dec': dec, + 'lonCol': lonCol, + 'latCol': latCol, + 'radius': radius} self.plotFuncs = [BaseSkyMap, BaseHistogram] def __eq__(self, otherSlicer): @@ -41,7 +78,8 @@ def __eq__(self, otherSlicer): result = False if isinstance(otherSlicer, UserPointsSlicer): if otherSlicer.nslice == self.nslice: - if np.all(otherSlicer.ra == self.ra) and np.all(otherSlicer.dec == self.dec): + if np.all(otherSlicer.slicePoints['ra'] == self.slicePoints['ra']) \ + and np.all(otherSlicer.slicePoints['dec'] == self.slicePoints['dec']): if (otherSlicer.lonCol == self.lonCol and otherSlicer.latCol == self.latCol): if otherSlicer.radius == self.radius: if otherSlicer.useCamera == self.useCamera: From 7db828a708db5c0dde9733163360bf8f7ac21b43 Mon Sep 17 00:00:00 2001 From: Lynne Jones Date: Thu, 2 Jun 2016 15:59:43 -0700 Subject: [PATCH 2/4] Updated documentation and flake8 for all slicers. Fixed unit test where it was failing with opsimFieldSlicer. --- python/lsst/sims/maf/slicers/baseSlicer.py | 38 ++++--- .../sims/maf/slicers/baseSpatialSlicer.py | 77 ++++++++----- python/lsst/sims/maf/slicers/healpixSlicer.py | 70 ++++++++++-- .../lsst/sims/maf/slicers/opsimFieldSlicer.py | 105 +++++++++++------- tests/test_io.py | 18 +-- 5 files changed, 204 insertions(+), 104 deletions(-) diff --git a/python/lsst/sims/maf/slicers/baseSlicer.py b/python/lsst/sims/maf/slicers/baseSlicer.py index 72abea705..891dd25f0 100644 --- a/python/lsst/sims/maf/slicers/baseSlicer.py +++ b/python/lsst/sims/maf/slicers/baseSlicer.py @@ -41,27 +41,30 @@ def help(cls, doc=False): class BaseSlicer(object): """ Base class for all slicers: sets required methods and implements common functionality. - """ - __metaclass__ = SlicerRegistry - def __init__(self, verbose=True, badval=-666): - """Instantiate the base slicer object. + After first construction, the slicer should be ready for setupSlicer to define slicePoints, which will + let the slicer 'slice' data and generate plots. + After init after a restore: everything necessary for using slicer for plotting or + saving/restoring metric data should be present (although slicer does not need to be able to + slice data again and generally will not be able to). - After first init with a 'blank' slicer: slicer should be ready for setupSlicer to - define slicePoints. - After init after a restore: everything necessary for using slicer for plotting or - saving/restoring metric data should be present (although slicer does not need to be able to - slice data again and generally will not be able to). + The sliceMetric has a 'memo-ize' functionality that can save previous indexes & return + metric data value calculated for same set of previous indexes, if desired. + CacheSize = 0 effectively turns this off, otherwise cacheSize should be set by the slicer. + (Most useful for healpix slicer, where many healpixels may have same set of LSST visits). - The sliceMetric has a 'memo-ize' functionality that can save previous indexes & return - metric data value calculated for same set of previous indexes, if desired. - CacheSize = 0 effectively turns this off, otherwise cacheSize should be set by the slicer. - (Most useful for healpix slicer, where many healpixels may have same set of LSST visits). + Parameters + ---------- + verbose: boolean, optional + True/False flag to send extra output to screen. + Default True. + badval: int or float, optional + The value the Slicer uses to fill masked metric data values + Default -666. + """ + __metaclass__ = SlicerRegistry - Minimum set of __init__ kwargs: - verbose: True/False flag to send extra output to screen - badval: the value the Slicer uses to fill masked metric data values - """ + def __init__(self, verbose=True, badval=-666): self.verbose = verbose self.badval = badval # Set cacheSize : each slicer will be able to override if appropriate. @@ -87,7 +90,6 @@ def __init__(self, verbose=True, badval=-666): if self.nslice is not None: self.spatialExtent = [0,self.nslice-1] - def _runMaps(self, maps): """Add map metadata to slicePoints. """ diff --git a/python/lsst/sims/maf/slicers/baseSpatialSlicer.py b/python/lsst/sims/maf/slicers/baseSpatialSlicer.py index cf830f12d..60f16be85 100644 --- a/python/lsst/sims/maf/slicers/baseSpatialSlicer.py +++ b/python/lsst/sims/maf/slicers/baseSpatialSlicer.py @@ -21,23 +21,44 @@ class BaseSpatialSlicer(BaseSlicer): - """Base slicer object, with added slicing functions for spatial slicer.""" - def __init__(self, verbose=True, - lonCol='fieldRA', latCol='fieldDec', + """Base spatial slicer object, contains additional functionality for spatial slicing, + including setting up and traversing a kdtree containing the simulated data points. + + Parameters + ---------- + lonCol : str, optional + Name of the longitude (RA equivalent) column to use from the input data. + Default fieldRA + latCol : str, optional + Name of the latitude (Dec equivalent) column to use from the input data. + Default fieldDec + verbose : boolean, optional + Flag to indicate whether or not to write additional information to stdout during runtime. + Default True. + badval : float, optional + Bad value flag, relevant for plotting. Default -666. + leafsize : int, optional + Leafsize value for kdtree. Default 100. + radius : float, optional + Radius for matching in the kdtree. Equivalent to the radius of the FOV. Degrees. + Default 1.75. + useCamera : boolean, optional + Flag to indicate whether to use the LSST camera footprint or not. + Default False. + rotSkyPosColName : str, optional + Name of the rotSkyPos column in the input data. Only used if useCamera is True. + Describes the orientation of the camera orientation compared to the sky. + Default rotSkyPos. + mjdColName : str, optional + Name of the exposure time column. Only used if useCamera is True. + Default expMJD. + chipNames : array-like, optional + List of chips to accept, if useCamera is True. This lets users turn 'on' only a subset of chips. + Default 'all' - this uses all chips in the camera. + """ + def __init__(self, lonCol='fieldRA', latCol='fieldDec', verbose=True, badval=-666, leafsize=100, radius=1.75, - useCamera=False, chipNames='all', rotSkyPosColName='rotSkyPos', mjdColName='expMJD'): - """ - Instantiate the base spatial slicer object. - lonCol = ra, latCol = dec, typically. - 'leafsize' is the number of RA/Dec pointings in each leaf node of KDtree - 'radius' (in degrees) is distance at which matches between - the simData KDtree - and slicePoint RA/Dec values will be produced - useCamera = boolean. False means all observations that fall in the radius are assumed to be observed - True means the observations are checked to make sure they fall on a chip. - chipNames = list of raft/chip names to include. By default, all chips are included. This way, - one can select only a subset of chips/rafts. - """ + useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='expMJD', chipNames='all'): super(BaseSpatialSlicer, self).__init__(verbose=verbose, badval=badval) self.lonCol = lonCol self.latCol = latCol @@ -55,7 +76,7 @@ def __init__(self, verbose=True, self.leafsize = leafsize self.useCamera = useCamera self.chipsToUse = chipNames - # RA and Dec are required slicePoint info for any spatial slicer. + # RA and Dec are required slicePoint info for any spatial slicer. Slicepoint RA/Dec are in radians. self.slicePoints['sid'] = None self.slicePoints['ra'] = None self.slicePoints['dec'] = None @@ -64,15 +85,21 @@ def __init__(self, verbose=True, self.plotFuncs = [BaseHistogram, BaseSkyMap] def setupSlicer(self, simData, maps=None): - """Use simData[self.lonCol] and simData[self.latCol] - (in radians) to set up KDTree. - - maps = list of map objects (such as dust extinction) that will run to build up - additional metadata at each slicePoint (available to metrics via slicePoint dictionary). + """Use simData[self.lonCol] and simData[self.latCol] (in radians) to set up KDTree. + + Parameters + ----------- + simData : numpy.recarray + The simulated data, including the location of each pointing. + maps : list of lsst.sims.maf.maps objects, optional + List of maps (such as dust extinction) that will run to build up additional metadata at each + slicePoint. This additional metadata is available to metrics via the slicePoint dictionary. + Default None. """ if maps is not None: if self.cacheSize != 0 and len(maps) > 0: - warnings.warn('Warning: Loading maps but cache on. Should probably set useCache=False in slicer.') + warnings.warn('Warning: Loading maps but cache on.' + 'Should probably set useCache=False in slicer.') self._runMaps(maps) self._setRad(self.radius) if self.useCamera: @@ -92,7 +119,8 @@ def _sliceSimData(islice): indices = self.sliceLookup[islice] slicePoint['chipNames'] = self.chipNames[islice] else: - sx, sy, sz = self._treexyz(self.slicePoints['ra'][islice], self.slicePoints['dec'][islice]) + sx, sy, sz = self._treexyz(self.slicePoints['ra'][islice], + self.slicePoints['dec'][islice]) # Query against tree. indices = self.opsimtree.query_ball_point((sx, sy, sz), self.rad) @@ -114,7 +142,6 @@ def _sliceSimData(islice): def _setupLSSTCamera(self): """If we want to include the camera chip gaps, etc""" - mapper = LsstSimMapper() self.camera = mapper.camera self.epoch = 2000.0 diff --git a/python/lsst/sims/maf/slicers/healpixSlicer.py b/python/lsst/sims/maf/slicers/healpixSlicer.py index 54161c689..4c0adcb36 100644 --- a/python/lsst/sims/maf/slicers/healpixSlicer.py +++ b/python/lsst/sims/maf/slicers/healpixSlicer.py @@ -13,16 +13,62 @@ __all__ = ['HealpixSlicer'] + class HealpixSlicer(BaseSpatialSlicer): - """Healpix spatial slicer.""" - def __init__(self, nside=128, lonCol ='fieldRA' , - latCol='fieldDec', verbose=True, - useCache=True, radius=1.75, leafsize=100, - useCamera=False, chipNames='all', rotSkyPosColName='rotSkyPos', mjdColName='expMJD'): + """ + A spatial slicer that evaluates pointings on a healpix-based grid. + + Parameters + ---------- + nside : int, optional + The nside parameter of the healpix grid. Must be a power of 2. + Default 128. + lonCol : str, optional + Name of the longitude (RA equivalent) column to use from the input data. + Default fieldRA + latCol : str, optional + Name of the latitude (Dec equivalent) column to use from the input data. + Default fieldDec + verbose : boolean, optional + Flag to indicate whether or not to write additional information to stdout during runtime. + Default True. + badval : float, optional + Bad value flag, relevant for plotting. Default the hp.UNSEEN value (in order to properly flag + bad data points for plotting with the healpix plotting routines). This should not be changed. + useCache : boolean + Flag allowing the user to indicate whether or not to cache (and reuse) metric results + calculated with the same set of simulated data pointings. + This can be safely set to True for slicers not using maps and will result in increased speed. + When calculating metric results using maps, the metadata at each individual ra/dec point may + influence the metric results and so useCache should be set to False. + Default True. + leafsize : int, optional + Leafsize value for kdtree. Default 100. + radius : float, optional + Radius for matching in the kdtree. Equivalent to the radius of the FOV. Degrees. + Default 1.75. + useCamera : boolean, optional + Flag to indicate whether to use the LSST camera footprint or not. + Default False. + rotSkyPosColName : str, optional + Name of the rotSkyPos column in the input data. Only used if useCamera is True. + Describes the orientation of the camera orientation compared to the sky. + Default rotSkyPos. + mjdColName : str, optional + Name of the exposure time column. Only used if useCamera is True. + Default expMJD. + chipNames : array-like, optional + List of chips to accept, if useCamera is True. This lets users turn 'on' only a subset of chips. + Default 'all' - this uses all chips in the camera. + """ + def __init__(self, nside=128, lonCol ='fieldRA', + latCol='fieldDec', verbose=True, badval=hp.UNSEEN, + useCache=True, leafsize=100, radius=1.75, + useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='expMJD', chipNames='all'): """Instantiate and set up healpix slicer object.""" super(HealpixSlicer, self).__init__(verbose=verbose, lonCol=lonCol, latCol=latCol, - badval=hp.UNSEEN, radius=radius, leafsize=leafsize, + badval=badval, radius=radius, leafsize=leafsize, useCamera=useCamera, rotSkyPosColName=rotSkyPosColName, mjdColName=mjdColName, chipNames=chipNames) # Valid values of nside are powers of 2. @@ -35,17 +81,17 @@ def __init__(self, nside=128, lonCol ='fieldRA' , self.nside = int(nside) self.pixArea = hp.nside2pixarea(self.nside) self.nslice = hp.nside2npix(self.nside) - self.spatialExtent = [0,self.nslice-1] + self.spatialExtent = [0, self.nslice-1] self.shape = self.nslice if self.verbose: - print 'Healpix slicer using NSIDE=%d, '%(self.nside) + \ - 'approximate resolution %f arcminutes'%(hp.nside2resol(self.nside,arcmin=True)) + print 'Healpix slicer using NSIDE=%d, ' % (self.nside) + \ + 'approximate resolution %f arcminutes' % (hp.nside2resol(self.nside, arcmin=True)) # Set variables so slicer can be re-constructed - self.slicer_init = {'nside':nside, 'lonCol':lonCol, 'latCol':latCol, - 'radius':radius} + self.slicer_init = {'nside': nside, 'lonCol': lonCol, 'latCol': latCol, + 'radius': radius} if useCache: # useCache set the size of the cache for the memoize function in sliceMetric. - binRes = hp.nside2resol(nside) # Pixel size in radians + binRes = hp.nside2resol(nside) # Pixel size in radians # Set the cache size to be ~2x the circumference self.cacheSize = int(np.round(4.*np.pi/binRes)) # Set up slicePoint metadata. diff --git a/python/lsst/sims/maf/slicers/opsimFieldSlicer.py b/python/lsst/sims/maf/slicers/opsimFieldSlicer.py index 7090d6c06..e1c45d6a2 100644 --- a/python/lsst/sims/maf/slicers/opsimFieldSlicer.py +++ b/python/lsst/sims/maf/slicers/opsimFieldSlicer.py @@ -9,29 +9,49 @@ __all__ = ['OpsimFieldSlicer'] -class OpsimFieldSlicer(BaseSpatialSlicer): - """Index-based slicer, matched ID's between simData and fieldData. - - Slicer uses fieldData RA and Dec values to do sky map plotting, but could be used more - generally for any kind of data slicing where the match is based on a simple ID value. - - Note that this slicer uses the fieldID of the opsim fields to generate spatial matches, - thus this slicer is not suitable for use in evaluating dithering or high resolution metrics - (use the healpix slicer instead for those use-cases). """ - def __init__(self, verbose=True, simDataFieldIDColName='fieldID', +class OpsimFieldSlicer(BaseSpatialSlicer): + """A spatial slicer that evaluates pointings based on matched IDs between the simData and fieldData. + + Note that this slicer uses the fieldID of the simulated data fields to generate the spatial matches. + Thus, it is not suitable for use in evaluating dithering or high resolution metrics + (use the HealpixSlicer instead for those use-cases). + + When the slicer is set up, it takes two arrays: fieldData and simData. FieldData is a numpy.recarray + containing the information about the fields - this is the basis for slicing. + The simData is a numpy.recarray that holds the information about the pointings - this is the data that + is matched against the fieldData. + + Parameters + ---------- + simDataFieldIDColName : str, optional + Name of the column in simData for the fieldID + Default fieldID. + simDataFieldRaColName : str, optional + Name of the column in simData for the fieldRA. + Default fieldRA. + simDataFieldDecColName : str, optional + Name of the column in simData for the fieldDec. + Default fieldDec. + fieldIDColName : str, optional + Name of the column in the fieldData for the fieldID (to match with simData). + Default fieldID. + fieldRAColName : str, optional + Name of the column in the fieldData for the fieldRA (used for plotting). + Default fieldDec. + fieldDecColName : str, optional + Name of the column in the fieldData for the fieldDec (used for plotting). + Default fieldRA. + verbose : boolean, optional + Flag to indicate whether or not to write additional information to stdout during runtime. + Default True. + badval : float, optional + Bad value flag, relevant for plotting. Default -666. + """ + def __init__(self, simDataFieldIDColName='fieldID', simDataFieldRaColName='fieldRA', simDataFieldDecColName='fieldDec', fieldIDColName='fieldID', fieldRaColName='fieldRA', fieldDecColName='fieldDec', - badval=-666): - """Instantiate opsim field slicer (an index-based slicer that can do spatial plots). - - simDataFieldIDColName = the column name in simData for the field ID - simDataFieldRaColName = the column name in simData for the field RA - simDataFieldDecColName = the column name in simData for the field Dec - fieldIDcolName = the column name in the fieldData for the field ID (to match with simData) - fieldRaColName = the column name in the fieldData for the field RA (for plotting only) - fieldDecColName = the column name in the fieldData for the field Dec (for plotting only). - """ + verbose=True, badval=-666): super(OpsimFieldSlicer, self).__init__(verbose=verbose, badval=badval) self.fieldID = None self.simDataFieldIDColName = simDataFieldIDColName @@ -39,26 +59,32 @@ def __init__(self, verbose=True, simDataFieldIDColName='fieldID', self.fieldRaColName = fieldRaColName self.fieldDecColName = fieldDecColName self.columnsNeeded = [simDataFieldIDColName, simDataFieldRaColName, simDataFieldDecColName] - while '' in self.columnsNeeded: self.columnsNeeded.remove('') + while '' in self.columnsNeeded: + self.columnsNeeded.remove('') self.fieldColumnsNeeded = [fieldIDColName, fieldRaColName, fieldDecColName] - self.slicer_init={'simDataFieldIDColName':simDataFieldIDColName, - 'simDataFieldRaColName':simDataFieldRaColName, - 'simDataFieldDecColName':simDataFieldDecColName, - 'fieldIDColName':fieldIDColName, - 'fieldRaColName':fieldRaColName, - 'fieldDecColName':fieldDecColName, 'badval':badval} + self.slicer_init = {'simDataFieldIDColName': simDataFieldIDColName, + 'simDataFieldRaColName': simDataFieldRaColName, + 'simDataFieldDecColName': simDataFieldDecColName, + 'fieldIDColName': fieldIDColName, + 'fieldRaColName': fieldRaColName, + 'fieldDecColName': fieldDecColName, 'badval': badval} self.plotFuncs = [BaseSkyMap, OpsimHistogram] self.needsFields = True - def setupSlicer(self, simData, fieldData, maps=None): """Set up opsim field slicer object. - simData = numpy rec array with simulation pointing history, - fieldData = numpy rec array with the field information (ID, RA, Dec), - Values for the column names are set during 'init'. + Parameters + ----------- + simData : numpy.recarray + Contains the simulation pointing history. + fieldData : numpy.recarray + Contains the field information (ID, Ra, Dec) about how to slice the simData. + For example, only fields in the fieldData table will be matched against the simData. + maps : list of lsst.sims.maf.maps objects, optional + Maps to run and provide additional metadata at each slicePoint. Default None. """ - if hasattr(self,'slicePoints'): + if hasattr(self, 'slicePoints'): warning_msg = 'Warning: this OpsimFieldSlicer was already set up once. ' warning_msg += 'Re-setting up an OpsimFieldSlicer can change the field information. ' warning_msg += 'Rerun metrics if this was intentional. ' @@ -78,34 +104,33 @@ def setupSlicer(self, simData, fieldData, maps=None): self.right = np.searchsorted(simFieldsSorted, self.slicePoints['sid'], 'right') self.spatialExtent = [simData[self.simDataFieldIDColName].min(), - simData[self.simDataFieldIDColName].max()] + simData[self.simDataFieldIDColName].max()] self.shape = self.nslice @wraps(self._sliceSimData) - def _sliceSimData(islice): idxs = self.simIdxs[self.left[islice]:self.right[islice]] # Build dict for slicePoint info - slicePoint={} + slicePoint = {} for key in self.slicePoints.keys(): - if (np.shape(self.slicePoints[key])[0] == self.nslice) & (key is not 'bins') & (key is not 'binCol'): + if (np.shape(self.slicePoints[key])[0] == self.nslice) & \ + (key is not 'bins') & (key is not 'binCol'): slicePoint[key] = self.slicePoints[key][islice] else: slicePoint[key] = self.slicePoints[key] - return {'idxs':idxs, 'slicePoint':slicePoint} + return {'idxs': idxs, 'slicePoint': slicePoint} setattr(self, '_sliceSimData', _sliceSimData) def __eq__(self, otherSlicer): """Evaluate if two grids are equivalent.""" - result = False if isinstance(otherSlicer, OpsimFieldSlicer): if np.all(otherSlicer.shape == self.shape): # Check if one or both slicers have been setup if (self.slicePoints['ra'] is not None) or (otherSlicer.slicePoints['ra'] is not None): if (np.array_equal(self.slicePoints['ra'], otherSlicer.slicePoints['ra']) & - np.array_equal(self.slicePoints['dec'], otherSlicer.slicePoints['dec']) & - np.array_equal(self.slicePoints['sid'], otherSlicer.slicePoints['sid'])): + np.array_equal(self.slicePoints['dec'], otherSlicer.slicePoints['dec']) & + np.array_equal(self.slicePoints['sid'], otherSlicer.slicePoints['sid'])): result = True # If they have not been setup, check that they have same fields elif ((otherSlicer.fieldIDColName == self.fieldIDColName) & diff --git a/tests/test_io.py b/tests/test_io.py index e361db6be..a46230ca0 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -85,25 +85,25 @@ def test_oneDSlicer(self): assert(getattr(slicer,att) == getattr(slicerBack,att)) def test_opsimFieldSlicer(self): - slicer=slicers.OpsimFieldSlicer(np.arange(100)) - names=['fieldRA','fieldDec','fieldID',] - dt = ['float','float','int'] + slicer=slicers.OpsimFieldSlicer() + names = ['fieldRA','fieldDec','fieldID'] + dt = ['float', 'float', 'int'] metricValues = np.random.rand(100) - fieldData = np.zeros(100, dtype=zip(names,dt)) + fieldData = np.zeros(100, dtype=zip(names, dt)) fieldData['fieldRA'] = np.random.rand(100) fieldData['fieldDec'] = np.random.rand(100) fieldData['fieldID'] = np.arange(100) - names=['data1','data2','fieldID',] - simData = np.zeros(100, dtype=zip(names,dt)) + names=['data1','data2','fieldID'] + simData = np.zeros(100, dtype=zip(names, dt)) simData['data1'] = np.random.rand(100) simData['fieldID'] = np.arange(100) - slicer.setupSlicer(simData,fieldData) + slicer.setupSlicer(simData, fieldData) filename = 'opsimslicer_test.npz' self.filenames.append(filename) slicer.writeData(filename, metricValues) - metricBack, slicerBack,header = self.baseslicer.readData(filename) + metricBack, slicerBack, header = self.baseslicer.readData(filename) assert(slicer == slicerBack) - np.testing.assert_almost_equal(metricBack,metricValues) + np.testing.assert_almost_equal(metricBack, metricValues) attr2check = ['nslice', 'columnsNeeded', 'lonCol', 'latCol','simDataFieldIDColName'] for att in attr2check: if type(getattr(slicer,att)).__name__ == 'dict': From 2e9d0da5cf5c80cc6b9be2dbc1cbd755157db704 Mon Sep 17 00:00:00 2001 From: Lynne Jones Date: Thu, 2 Jun 2016 17:07:37 -0700 Subject: [PATCH 3/4] Added documentation modules.rst file --- doc/source/modules.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/modules.rst b/doc/source/modules.rst index 6740af354..86e3a213d 100644 --- a/doc/source/modules.rst +++ b/doc/source/modules.rst @@ -1,7 +1,7 @@ -Python API documentation -======================== +python +====== .. toctree:: :maxdepth: 4 - lsst.sims.maf + lsst From 0398cdc01797541ae749198a2b517a89f2201b70 Mon Sep 17 00:00:00 2001 From: Lynne Jones Date: Thu, 2 Jun 2016 17:07:55 -0700 Subject: [PATCH 4/4] Fixed documentation for baseSlicer. --- python/lsst/sims/maf/slicers/baseSlicer.py | 60 ++++++++++++++++------ 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/python/lsst/sims/maf/slicers/baseSlicer.py b/python/lsst/sims/maf/slicers/baseSlicer.py index 891dd25f0..52339154a 100644 --- a/python/lsst/sims/maf/slicers/baseSlicer.py +++ b/python/lsst/sims/maf/slicers/baseSlicer.py @@ -48,11 +48,6 @@ class BaseSlicer(object): saving/restoring metric data should be present (although slicer does not need to be able to slice data again and generally will not be able to). - The sliceMetric has a 'memo-ize' functionality that can save previous indexes & return - metric data value calculated for same set of previous indexes, if desired. - CacheSize = 0 effectively turns this off, otherwise cacheSize should be set by the slicer. - (Most useful for healpix slicer, where many healpixels may have same set of LSST visits). - Parameters ---------- verbose: boolean, optional @@ -102,6 +97,13 @@ def setupSlicer(self, simData, maps=None): Set up internal parameters necessary for slicer to slice data and generates indexes on simData. Also sets _sliceSimData for a particular slicer. + + Parameters + ----------- + simData : np.recarray + The simulated data to be sliced. + maps : list of lsst.sims.maf.maps objects, optional. + Maps to apply at each slicePoint, to add to the slicePoint metadata. Default None. """ # Typically args will be simData, but opsimFieldSlicer also uses fieldData. raise NotImplementedError() @@ -169,8 +171,12 @@ def writeData(self, outfilename, metricValues, metricName='', """ Save metric values along with the information required to re-build the slicer. - outfilename: the output file - metricValues: the metric values to save to disk + Parameters + ----------- + outfilename : str + The output file name. + metricValues : np.ma.MaskedArray or np.ndarray + The metric values to save to disk. """ header = {} header['metricName']=metricName @@ -210,14 +216,29 @@ def outputJSON(self, metricValues, metricName='', """ Send metric data to JSON streaming API, along with a little bit of metadata. - Output is - header dictionary with 'metricName/metadata/simDataName/slicerName' and plot labels from plotDict (if provided). - then data for plot: - if oneDSlicer, it's [ [bin_left_edge, value], [bin_left_edge, value]..]. - if a spatial slicer, it's [ [lon, lat, value], [lon, lat, value] ..]. - - This method will only work for non-complex metrics (i.e. metrics where the metric value is a float or int), + This method will only work for metrics where the metricDtype is float or int, as JSON will not interpret more complex data properly. These values can't be plotted anyway though. + + Parameters + ----------- + metricValues : np.ma.MaskedArray or np.ndarray + The metric values. + metricName : str, optional + The name of the metric. Default ''. + simDataName : str, optional + The name of the simulated data source. Default ''. + metadata : str, optional + The metadata about this metric. Default ''. + plotDict : dict, optional. + The plotDict for this metric bundle. Default None. + + Returns + -------- + StringIO + StringIO object containing a header dictionary with metricName/metadata/simDataName/slicerName, + and plot labels from plotDict, and metric values/data for plot. + if oneDSlicer, the data is [ [bin_left_edge, value], [bin_left_edge, value]..]. + if a spatial slicer, the data is [ [lon, lat, value], [lon, lat, value] ..]. """ # Bail if this is not a good data type for JSON. if not (metricValues.dtype == 'float') or (metricValues.dtype == 'int'): @@ -309,7 +330,16 @@ def readData(self, infilename): """ Read metric data from disk, along with the info to rebuild the slicer (minus new slicing capability). - infilename: the filename containing the metric data. + Parameters + ----------- + infilename: str + The filename containing the metric data. + + Returns + ------- + np.ma.MaskedArray, lsst.sims.maf.slicer, dict + MetricValues stored in data file, the slicer basis for those metric values, and a dictionary + containing header information (runName, metadata, etc.). """ import lsst.sims.maf.slicers as slicers restored = np.load(infilename)