Skip to content
This repository was archived by the owner on May 19, 2025. It is now read-only.

Commit f39d6f2

Browse files
Merge pull request #86 from lsst/bugfix/SIM-2024-userPointsSlicer
Bugfix/sim 2024 user points slicer
2 parents d094a2d + 0398cdc commit f39d6f2

File tree

6 files changed

+247
-117
lines changed

6 files changed

+247
-117
lines changed

doc/source/modules.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
Python API documentation
2-
========================
1+
python
2+
======
33

44
.. toctree::
55
:maxdepth: 4
66

7-
lsst.sims.maf
7+
lsst

python/lsst/sims/maf/slicers/baseSlicer.py

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,25 @@ def help(cls, doc=False):
4141
class BaseSlicer(object):
4242
"""
4343
Base class for all slicers: sets required methods and implements common functionality.
44+
45+
After first construction, the slicer should be ready for setupSlicer to define slicePoints, which will
46+
let the slicer 'slice' data and generate plots.
47+
After init after a restore: everything necessary for using slicer for plotting or
48+
saving/restoring metric data should be present (although slicer does not need to be able to
49+
slice data again and generally will not be able to).
50+
51+
Parameters
52+
----------
53+
verbose: boolean, optional
54+
True/False flag to send extra output to screen.
55+
Default True.
56+
badval: int or float, optional
57+
The value the Slicer uses to fill masked metric data values
58+
Default -666.
4459
"""
4560
__metaclass__ = SlicerRegistry
4661

4762
def __init__(self, verbose=True, badval=-666):
48-
"""Instantiate the base slicer object.
49-
50-
After first init with a 'blank' slicer: slicer should be ready for setupSlicer to
51-
define slicePoints.
52-
After init after a restore: everything necessary for using slicer for plotting or
53-
saving/restoring metric data should be present (although slicer does not need to be able to
54-
slice data again and generally will not be able to).
55-
56-
The sliceMetric has a 'memo-ize' functionality that can save previous indexes & return
57-
metric data value calculated for same set of previous indexes, if desired.
58-
CacheSize = 0 effectively turns this off, otherwise cacheSize should be set by the slicer.
59-
(Most useful for healpix slicer, where many healpixels may have same set of LSST visits).
60-
61-
Minimum set of __init__ kwargs:
62-
verbose: True/False flag to send extra output to screen
63-
badval: the value the Slicer uses to fill masked metric data values
64-
"""
6563
self.verbose = verbose
6664
self.badval = badval
6765
# Set cacheSize : each slicer will be able to override if appropriate.
@@ -87,7 +85,6 @@ def __init__(self, verbose=True, badval=-666):
8785
if self.nslice is not None:
8886
self.spatialExtent = [0,self.nslice-1]
8987

90-
9188
def _runMaps(self, maps):
9289
"""Add map metadata to slicePoints.
9390
"""
@@ -100,6 +97,13 @@ def setupSlicer(self, simData, maps=None):
10097
10198
Set up internal parameters necessary for slicer to slice data and generates indexes on simData.
10299
Also sets _sliceSimData for a particular slicer.
100+
101+
Parameters
102+
-----------
103+
simData : np.recarray
104+
The simulated data to be sliced.
105+
maps : list of lsst.sims.maf.maps objects, optional.
106+
Maps to apply at each slicePoint, to add to the slicePoint metadata. Default None.
103107
"""
104108
# Typically args will be simData, but opsimFieldSlicer also uses fieldData.
105109
raise NotImplementedError()
@@ -167,8 +171,12 @@ def writeData(self, outfilename, metricValues, metricName='',
167171
"""
168172
Save metric values along with the information required to re-build the slicer.
169173
170-
outfilename: the output file
171-
metricValues: the metric values to save to disk
174+
Parameters
175+
-----------
176+
outfilename : str
177+
The output file name.
178+
metricValues : np.ma.MaskedArray or np.ndarray
179+
The metric values to save to disk.
172180
"""
173181
header = {}
174182
header['metricName']=metricName
@@ -208,14 +216,29 @@ def outputJSON(self, metricValues, metricName='',
208216
"""
209217
Send metric data to JSON streaming API, along with a little bit of metadata.
210218
211-
Output is
212-
header dictionary with 'metricName/metadata/simDataName/slicerName' and plot labels from plotDict (if provided).
213-
then data for plot:
214-
if oneDSlicer, it's [ [bin_left_edge, value], [bin_left_edge, value]..].
215-
if a spatial slicer, it's [ [lon, lat, value], [lon, lat, value] ..].
216-
217-
This method will only work for non-complex metrics (i.e. metrics where the metric value is a float or int),
219+
This method will only work for metrics where the metricDtype is float or int,
218220
as JSON will not interpret more complex data properly. These values can't be plotted anyway though.
221+
222+
Parameters
223+
-----------
224+
metricValues : np.ma.MaskedArray or np.ndarray
225+
The metric values.
226+
metricName : str, optional
227+
The name of the metric. Default ''.
228+
simDataName : str, optional
229+
The name of the simulated data source. Default ''.
230+
metadata : str, optional
231+
The metadata about this metric. Default ''.
232+
plotDict : dict, optional.
233+
The plotDict for this metric bundle. Default None.
234+
235+
Returns
236+
--------
237+
StringIO
238+
StringIO object containing a header dictionary with metricName/metadata/simDataName/slicerName,
239+
and plot labels from plotDict, and metric values/data for plot.
240+
if oneDSlicer, the data is [ [bin_left_edge, value], [bin_left_edge, value]..].
241+
if a spatial slicer, the data is [ [lon, lat, value], [lon, lat, value] ..].
219242
"""
220243
# Bail if this is not a good data type for JSON.
221244
if not (metricValues.dtype == 'float') or (metricValues.dtype == 'int'):
@@ -307,7 +330,16 @@ def readData(self, infilename):
307330
"""
308331
Read metric data from disk, along with the info to rebuild the slicer (minus new slicing capability).
309332
310-
infilename: the filename containing the metric data.
333+
Parameters
334+
-----------
335+
infilename: str
336+
The filename containing the metric data.
337+
338+
Returns
339+
-------
340+
np.ma.MaskedArray, lsst.sims.maf.slicer, dict
341+
MetricValues stored in data file, the slicer basis for those metric values, and a dictionary
342+
containing header information (runName, metadata, etc.).
311343
"""
312344
import lsst.sims.maf.slicers as slicers
313345
restored = np.load(infilename)

python/lsst/sims/maf/slicers/baseSpatialSlicer.py

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,44 @@
2121

2222

2323
class BaseSpatialSlicer(BaseSlicer):
24-
"""Base slicer object, with added slicing functions for spatial slicer."""
25-
def __init__(self, verbose=True,
26-
lonCol='fieldRA', latCol='fieldDec',
24+
"""Base spatial slicer object, contains additional functionality for spatial slicing,
25+
including setting up and traversing a kdtree containing the simulated data points.
26+
27+
Parameters
28+
----------
29+
lonCol : str, optional
30+
Name of the longitude (RA equivalent) column to use from the input data.
31+
Default fieldRA
32+
latCol : str, optional
33+
Name of the latitude (Dec equivalent) column to use from the input data.
34+
Default fieldDec
35+
verbose : boolean, optional
36+
Flag to indicate whether or not to write additional information to stdout during runtime.
37+
Default True.
38+
badval : float, optional
39+
Bad value flag, relevant for plotting. Default -666.
40+
leafsize : int, optional
41+
Leafsize value for kdtree. Default 100.
42+
radius : float, optional
43+
Radius for matching in the kdtree. Equivalent to the radius of the FOV. Degrees.
44+
Default 1.75.
45+
useCamera : boolean, optional
46+
Flag to indicate whether to use the LSST camera footprint or not.
47+
Default False.
48+
rotSkyPosColName : str, optional
49+
Name of the rotSkyPos column in the input data. Only used if useCamera is True.
50+
Describes the orientation of the camera orientation compared to the sky.
51+
Default rotSkyPos.
52+
mjdColName : str, optional
53+
Name of the exposure time column. Only used if useCamera is True.
54+
Default expMJD.
55+
chipNames : array-like, optional
56+
List of chips to accept, if useCamera is True. This lets users turn 'on' only a subset of chips.
57+
Default 'all' - this uses all chips in the camera.
58+
"""
59+
def __init__(self, lonCol='fieldRA', latCol='fieldDec', verbose=True,
2760
badval=-666, leafsize=100, radius=1.75,
28-
useCamera=False, chipNames='all', rotSkyPosColName='rotSkyPos', mjdColName='expMJD'):
29-
"""
30-
Instantiate the base spatial slicer object.
31-
lonCol = ra, latCol = dec, typically.
32-
'leafsize' is the number of RA/Dec pointings in each leaf node of KDtree
33-
'radius' (in degrees) is distance at which matches between
34-
the simData KDtree
35-
and slicePoint RA/Dec values will be produced
36-
useCamera = boolean. False means all observations that fall in the radius are assumed to be observed
37-
True means the observations are checked to make sure they fall on a chip.
38-
chipNames = list of raft/chip names to include. By default, all chips are included. This way,
39-
one can select only a subset of chips/rafts.
40-
"""
61+
useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='expMJD', chipNames='all'):
4162
super(BaseSpatialSlicer, self).__init__(verbose=verbose, badval=badval)
4263
self.lonCol = lonCol
4364
self.latCol = latCol
@@ -55,7 +76,7 @@ def __init__(self, verbose=True,
5576
self.leafsize = leafsize
5677
self.useCamera = useCamera
5778
self.chipsToUse = chipNames
58-
# RA and Dec are required slicePoint info for any spatial slicer.
79+
# RA and Dec are required slicePoint info for any spatial slicer. Slicepoint RA/Dec are in radians.
5980
self.slicePoints['sid'] = None
6081
self.slicePoints['ra'] = None
6182
self.slicePoints['dec'] = None
@@ -64,15 +85,21 @@ def __init__(self, verbose=True,
6485
self.plotFuncs = [BaseHistogram, BaseSkyMap]
6586

6687
def setupSlicer(self, simData, maps=None):
67-
"""Use simData[self.lonCol] and simData[self.latCol]
68-
(in radians) to set up KDTree.
69-
70-
maps = list of map objects (such as dust extinction) that will run to build up
71-
additional metadata at each slicePoint (available to metrics via slicePoint dictionary).
88+
"""Use simData[self.lonCol] and simData[self.latCol] (in radians) to set up KDTree.
89+
90+
Parameters
91+
-----------
92+
simData : numpy.recarray
93+
The simulated data, including the location of each pointing.
94+
maps : list of lsst.sims.maf.maps objects, optional
95+
List of maps (such as dust extinction) that will run to build up additional metadata at each
96+
slicePoint. This additional metadata is available to metrics via the slicePoint dictionary.
97+
Default None.
7298
"""
7399
if maps is not None:
74100
if self.cacheSize != 0 and len(maps) > 0:
75-
warnings.warn('Warning: Loading maps but cache on. Should probably set useCache=False in slicer.')
101+
warnings.warn('Warning: Loading maps but cache on.'
102+
'Should probably set useCache=False in slicer.')
76103
self._runMaps(maps)
77104
self._setRad(self.radius)
78105
if self.useCamera:
@@ -92,7 +119,8 @@ def _sliceSimData(islice):
92119
indices = self.sliceLookup[islice]
93120
slicePoint['chipNames'] = self.chipNames[islice]
94121
else:
95-
sx, sy, sz = self._treexyz(self.slicePoints['ra'][islice], self.slicePoints['dec'][islice])
122+
sx, sy, sz = self._treexyz(self.slicePoints['ra'][islice],
123+
self.slicePoints['dec'][islice])
96124
# Query against tree.
97125
indices = self.opsimtree.query_ball_point((sx, sy, sz), self.rad)
98126

@@ -114,7 +142,6 @@ def _sliceSimData(islice):
114142

115143
def _setupLSSTCamera(self):
116144
"""If we want to include the camera chip gaps, etc"""
117-
118145
mapper = LsstSimMapper()
119146
self.camera = mapper.camera
120147
self.epoch = 2000.0

python/lsst/sims/maf/slicers/healpixSlicer.py

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,62 @@
1313

1414
__all__ = ['HealpixSlicer']
1515

16+
1617
class HealpixSlicer(BaseSpatialSlicer):
17-
"""Healpix spatial slicer."""
18-
def __init__(self, nside=128, lonCol ='fieldRA' ,
19-
latCol='fieldDec', verbose=True,
20-
useCache=True, radius=1.75, leafsize=100,
21-
useCamera=False, chipNames='all', rotSkyPosColName='rotSkyPos', mjdColName='expMJD'):
18+
"""
19+
A spatial slicer that evaluates pointings on a healpix-based grid.
20+
21+
Parameters
22+
----------
23+
nside : int, optional
24+
The nside parameter of the healpix grid. Must be a power of 2.
25+
Default 128.
26+
lonCol : str, optional
27+
Name of the longitude (RA equivalent) column to use from the input data.
28+
Default fieldRA
29+
latCol : str, optional
30+
Name of the latitude (Dec equivalent) column to use from the input data.
31+
Default fieldDec
32+
verbose : boolean, optional
33+
Flag to indicate whether or not to write additional information to stdout during runtime.
34+
Default True.
35+
badval : float, optional
36+
Bad value flag, relevant for plotting. Default the hp.UNSEEN value (in order to properly flag
37+
bad data points for plotting with the healpix plotting routines). This should not be changed.
38+
useCache : boolean
39+
Flag allowing the user to indicate whether or not to cache (and reuse) metric results
40+
calculated with the same set of simulated data pointings.
41+
This can be safely set to True for slicers not using maps and will result in increased speed.
42+
When calculating metric results using maps, the metadata at each individual ra/dec point may
43+
influence the metric results and so useCache should be set to False.
44+
Default True.
45+
leafsize : int, optional
46+
Leafsize value for kdtree. Default 100.
47+
radius : float, optional
48+
Radius for matching in the kdtree. Equivalent to the radius of the FOV. Degrees.
49+
Default 1.75.
50+
useCamera : boolean, optional
51+
Flag to indicate whether to use the LSST camera footprint or not.
52+
Default False.
53+
rotSkyPosColName : str, optional
54+
Name of the rotSkyPos column in the input data. Only used if useCamera is True.
55+
Describes the orientation of the camera orientation compared to the sky.
56+
Default rotSkyPos.
57+
mjdColName : str, optional
58+
Name of the exposure time column. Only used if useCamera is True.
59+
Default expMJD.
60+
chipNames : array-like, optional
61+
List of chips to accept, if useCamera is True. This lets users turn 'on' only a subset of chips.
62+
Default 'all' - this uses all chips in the camera.
63+
"""
64+
def __init__(self, nside=128, lonCol ='fieldRA',
65+
latCol='fieldDec', verbose=True, badval=hp.UNSEEN,
66+
useCache=True, leafsize=100, radius=1.75,
67+
useCamera=False, rotSkyPosColName='rotSkyPos', mjdColName='expMJD', chipNames='all'):
2268
"""Instantiate and set up healpix slicer object."""
2369
super(HealpixSlicer, self).__init__(verbose=verbose,
2470
lonCol=lonCol, latCol=latCol,
25-
badval=hp.UNSEEN, radius=radius, leafsize=leafsize,
71+
badval=badval, radius=radius, leafsize=leafsize,
2672
useCamera=useCamera, rotSkyPosColName=rotSkyPosColName,
2773
mjdColName=mjdColName, chipNames=chipNames)
2874
# Valid values of nside are powers of 2.
@@ -35,17 +81,17 @@ def __init__(self, nside=128, lonCol ='fieldRA' ,
3581
self.nside = int(nside)
3682
self.pixArea = hp.nside2pixarea(self.nside)
3783
self.nslice = hp.nside2npix(self.nside)
38-
self.spatialExtent = [0,self.nslice-1]
84+
self.spatialExtent = [0, self.nslice-1]
3985
self.shape = self.nslice
4086
if self.verbose:
41-
print 'Healpix slicer using NSIDE=%d, '%(self.nside) + \
42-
'approximate resolution %f arcminutes'%(hp.nside2resol(self.nside,arcmin=True))
87+
print 'Healpix slicer using NSIDE=%d, ' % (self.nside) + \
88+
'approximate resolution %f arcminutes' % (hp.nside2resol(self.nside, arcmin=True))
4389
# Set variables so slicer can be re-constructed
44-
self.slicer_init = {'nside':nside, 'lonCol':lonCol, 'latCol':latCol,
45-
'radius':radius}
90+
self.slicer_init = {'nside': nside, 'lonCol': lonCol, 'latCol': latCol,
91+
'radius': radius}
4692
if useCache:
4793
# useCache set the size of the cache for the memoize function in sliceMetric.
48-
binRes = hp.nside2resol(nside) # Pixel size in radians
94+
binRes = hp.nside2resol(nside) # Pixel size in radians
4995
# Set the cache size to be ~2x the circumference
5096
self.cacheSize = int(np.round(4.*np.pi/binRes))
5197
# Set up slicePoint metadata.

0 commit comments

Comments
 (0)