Skip to content

Commit

Permalink
Merge pull request #86 from lsst/bugfix/SIM-2024-userPointsSlicer
Browse files Browse the repository at this point in the history
Bugfix/sim 2024 user points slicer
  • Loading branch information
rhiannonlynne authored Jun 10, 2016
2 parents d094a2d + 0398cdc commit f39d6f2
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 117 deletions.
6 changes: 3 additions & 3 deletions doc/source/modules.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Python API documentation
========================
python
======

.. toctree::
:maxdepth: 4

lsst.sims.maf
lsst
88 changes: 60 additions & 28 deletions python/lsst/sims/maf/slicers/baseSlicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,25 @@ def help(cls, doc=False):
class BaseSlicer(object):
"""
Base class for all slicers: sets required methods and implements common functionality.
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).
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

def __init__(self, verbose=True, badval=-666):
"""Instantiate the base slicer object.
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).
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
"""
self.verbose = verbose
self.badval = badval
# Set cacheSize : each slicer will be able to override if appropriate.
Expand All @@ -87,7 +85,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.
"""
Expand All @@ -100,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()
Expand Down Expand Up @@ -167,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
Expand Down Expand Up @@ -208,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'):
Expand Down Expand Up @@ -307,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)
Expand Down
77 changes: 52 additions & 25 deletions python/lsst/sims/maf/slicers/baseSpatialSlicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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)

Expand All @@ -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
Expand Down
70 changes: 58 additions & 12 deletions python/lsst/sims/maf/slicers/healpixSlicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
Loading

0 comments on commit f39d6f2

Please sign in to comment.