LSSTApplications  18.1.0
LSSTDataManagementBasePackage
simpleMapper.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 """Mapper and cameraGeom definition for extremely simple mock data.
24 
25 SimpleMapper inherits directly from Mapper, not CameraMapper. This means
26 we can avoid any problems with paf files at the expense of reimplementing
27 some parts of CameraMapper here. Jim is not sure this was the best
28 possible approach, but it gave him an opportunity to play around with
29 prototyping a future paf-free mapper class, and it does everything it
30 needs to do right now.
31 """
32 import os
33 import shutil
34 import re
35 
38 from lsst.afw.cameraGeom.testUtils import DetectorWrapper
39 import lsst.afw.image.utils as afwImageUtils
40 import lsst.afw.image as afwImage
41 
42 __all__ = ("SimpleMapper", "makeSimpleCamera", "makeDataRepo")
43 
44 
46  """Base class of a hierarchy used by SimpleMapper to defined different kinds of types of objects
47  to persist.
48 
49  PersistenceType objects are never instantiated; only the type objects are used (we needed a
50  simple singleton struct that could be inherited, which is exactly what a Python type is).
51  """
52  python = None
53  cpp = "ignored"
54  storage = None
55  ext = ""
56  suffixes = ()
57 
58  @classmethod
59  def makeButlerLocation(cls, path, dataId, mapper, suffix=None, storage=None):
60  """Method called by SimpleMapping to implement a map_ method."""
61  return lsst.daf.persistence.ButlerLocation(cls.python, cls.cpp, cls.storage, [path], dataId,
62  mapper=mapper,
63  storage=storage)
64 
65  def canStandardize(self, datasetType):
66  return False
67 
68 
70  """Persistence type for things that don't actually use daf_persistence.
71  """
72 
73  python = "lsst.daf.base.PropertySet" # something to import even when we don't need to
74 
75  @classmethod
76  def makeButlerLocation(cls, path, dataId, mapper, suffix=None, storage=None):
77  """Method called by SimpleMapping to implement a map_ method; overridden to not use the path."""
78  return lsst.daf.persistence.ButlerLocation(cls.python, cls.cpp, cls.storage, [], dataId,
79  mapper=mapper, storage=storage)
80 
81 
83  """Persistence type of Exposure images.
84  """
85 
86  python = "lsst.afw.image.ExposureF"
87  cpp = "ExposureF"
88  storage = "FitsStorage"
89  ext = ".fits"
90  suffixes = ("_sub",)
91 
92  @classmethod
93  def makeButlerLocation(cls, path, dataId, mapper, suffix=None, storage=None):
94  """Method called by SimpleMapping to implement a map_ method; overridden to support subimages."""
95  if suffix is None:
96  loc = super(ExposurePersistenceType, cls).makeButlerLocation(path, dataId, mapper, suffix=None,
97  storage=storage)
98  # Write options are never applicable for _sub, since that's only
99  # for read. None of the values aside from the "NONE"s matter, but
100  # writing explicit meaningless values for all of them to appease
101  # afw is the price we pay for trying to write a non-CameraMapper
102  # Mapper. It'll all get better with Gen3 (TM).
103  options = {
104  "compression.algorithm": "NONE",
105  "compression.columns": 0,
106  "compression.rows": 0,
107  "compression.quantizeLevel": 0.0,
108  "scaling.algorithm": "NONE",
109  "scaling.bzero": 0.0,
110  "scaling.bscale": 0.0,
111  "scaling.bitpix": 0,
112  "scaling.quantizeLevel": 0.0,
113  "scaling.quantizePad": 0.0,
114  "scaling.fuzz": False,
115  "scaling.seed": 0,
116  }
117  for prefix in ("image", "mask", "variance"):
118  for k, v in options.items():
119  loc.additionalData.set("{}.{}".format(prefix, k), v)
120  elif suffix == "_sub":
121  subId = dataId.copy()
122  bbox = subId.pop('bbox')
123  loc = super(ExposurePersistenceType, cls).makeButlerLocation(path, subId, mapper, suffix=None,
124  storage=storage)
125  loc.additionalData.set('llcX', bbox.getMinX())
126  loc.additionalData.set('llcY', bbox.getMinY())
127  loc.additionalData.set('width', bbox.getWidth())
128  loc.additionalData.set('height', bbox.getHeight())
129  if 'imageOrigin' in dataId:
130  loc.additionalData.set('imageOrigin',
131  dataId['imageOrigin'])
132  return loc
133 
134 
136  python = "lsst.skymap.BaseSkyMap"
137  storage = "PickleStorage"
138  ext = ".pickle"
139 
140 
142  python = "lsst.afw.table.BaseCatalog"
143  cpp = "BaseCatalog"
144  storage = "FitsCatalogStorage"
145  ext = ".fits"
146 
147 
149  python = "lsst.afw.table.SimpleCatalog"
150  cpp = "SimpleCatalog"
151 
152 
154  python = "lsst.afw.table.SourceCatalog"
155  cpp = "SourceCatalog"
156 
157 
159  python = "lsst.afw.table.ExposureCatalog"
160  cpp = "ExposureCatalog"
161 
162 
164  python = "lsst.afw.detection.PeakCatalog"
165  cpp = "PeakCatalog"
166 
167 
169  """Mapping object used to implement SimpleMapper, similar in intent to lsst.daf.peristence.Mapping.
170  """
171 
172  template = None
173  keys = {}
174 
175  def __init__(self, persistence, template=None, keys=None):
176  self.persistence = persistence
177  if template is not None:
178  self.template = template
179  if keys is not None:
180  self.keys = keys
181 
182  def map(self, dataset, root, dataId, mapper, suffix=None, storage=None):
183  if self.template is not None:
184  path = self.template.format(dataset=dataset, ext=self.persistence.ext, **dataId)
185  else:
186  path = None
187  return self.persistence.makeButlerLocation(path, dataId, suffix=suffix, mapper=mapper,
188  storage=storage)
189 
190 
192  """Mapping for dataset types that are organized the same way as raw data (i.e. by CCD)."""
193 
194  template = "{dataset}-{visit:04d}-{ccd:01d}{ext}"
195  keys = dict(visit=int, ccd=int)
196 
197  def query(self, dataset, index, level, format, dataId):
198  dictList = index[dataset][level]
199  results = [list(d.values()) for d in dictList[dataId.get(level, None)]]
200  return results
201 
202 
204  """Mapping for dataset types that are organized according to a SkyMap subdivision of the sky."""
205 
206  template = "{dataset}-{filter}-{tract:02d}-{patch}{ext}"
207  keys = dict(filter=str, tract=int, patch=str)
208 
209 
211  """Mapping for CoaddTempExp datasets."""
212 
213  template = "{dataset}-{tract:02d}-{patch}-{visit:04d}{ext}"
214  keys = dict(tract=int, patch=str, visit=int)
215 
216 
218  """Mapping for forced_src datasets."""
219 
220  template = "{dataset}-{tract:02d}-{visit:04d}-{ccd:01d}{ext}"
221  keys = dict(tract=int, ccd=int, visit=int)
222 
223 
225  """Metaclass for SimpleMapper that creates map_ and query_ methods for everything found in the
226  'mappings' class variable.
227  """
228 
229  @staticmethod
230  def _makeMapClosure(dataset, mapping, suffix=None):
231  def mapClosure(self, dataId, write=False):
232  return mapping.map(dataset, self.root, dataId, self, suffix=suffix, storage=self.storage)
233  return mapClosure
234 
235  @staticmethod
236  def _makeQueryClosure(dataset, mapping):
237  def queryClosure(self, level, format, dataId):
238  return mapping.query(dataset, self.index, level, format, dataId)
239  return queryClosure
240 
241  def __init__(cls, name, bases, dict_): # noqa allow "cls" instead of "self"
242  type.__init__(cls, name, bases, dict_)
243  cls.keyDict = dict()
244  for dataset, mapping in cls.mappings.items():
245  setattr(cls, "map_" + dataset, MapperMeta._makeMapClosure(dataset, mapping, suffix=None))
246  for suffix in mapping.persistence.suffixes:
247  setattr(cls, "map_" + dataset + suffix,
248  MapperMeta._makeMapClosure(dataset, mapping, suffix=suffix))
249  if hasattr(mapping, "query"):
250  setattr(cls, "query_" + dataset, MapperMeta._makeQueryClosure(dataset, mapping))
251  cls.keyDict.update(mapping.keys)
252 
253 
254 class SimpleMapper(lsst.daf.persistence.Mapper, metaclass=MapperMeta):
255  """
256  An extremely simple mapper for an imaginary camera for use in integration tests.
257 
258  As SimpleMapper does not inherit from obs.base.CameraMapper, it does not
259  use a policy file to set mappings or a registry; all the information is here
260  (in the map_* and query_* methods).
261 
262  The imaginary camera's raw data format has only 'visit' and 'ccd' keys, with
263  two CCDs per visit (by default).
264  """
265 
266  mappings = dict(
267  calexp=RawMapping(ExposurePersistenceType),
268  forced_src=ForcedSrcMapping(SourceCatalogPersistenceType),
269  forced_src_schema=SimpleMapping(SourceCatalogPersistenceType,
270  template="{dataset}{ext}", keys={}),
271  truth=SimpleMapping(SimpleCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
272  keys={"tract": int}),
273  simsrc=RawMapping(SimpleCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
274  keys={"tract": int}),
275  observations=SimpleMapping(ExposureCatalogPersistenceType, template="{dataset}-{tract:02d}{ext}",
276  keys={"tract": int}),
277  ccdExposureId=RawMapping(BypassPersistenceType),
278  ccdExposureId_bits=SimpleMapping(BypassPersistenceType),
279  deepCoaddId=SkyMapping(BypassPersistenceType),
280  deepCoaddId_bits=SimpleMapping(BypassPersistenceType),
281  deepMergedCoaddId=SkyMapping(BypassPersistenceType),
282  deepMergedCoaddId_bits=SimpleMapping(BypassPersistenceType),
283  deepCoadd_skyMap=SimpleMapping(SkyMapPersistenceType, template="{dataset}{ext}", keys={}),
284  deepCoadd=SkyMapping(ExposurePersistenceType),
285  deepCoaddPsfMatched=SkyMapping(ExposurePersistenceType),
286  deepCoadd_calexp=SkyMapping(ExposurePersistenceType),
287  deepCoadd_calexp_background=SkyMapping(CatalogPersistenceType),
288  deepCoadd_icSrc=SkyMapping(SourceCatalogPersistenceType),
289  deepCoadd_icSrc_schema=SimpleMapping(SourceCatalogPersistenceType,
290  template="{dataset}{ext}", keys={}),
291  deepCoadd_src=SkyMapping(SourceCatalogPersistenceType),
292  deepCoadd_src_schema=SimpleMapping(SourceCatalogPersistenceType,
293  template="{dataset}{ext}", keys={}),
294  deepCoadd_peak_schema=SimpleMapping(PeakCatalogPersistenceType,
295  template="{dataset}{ext}", keys={}),
296  deepCoadd_ref=SkyMapping(SourceCatalogPersistenceType),
297  deepCoadd_ref_schema=SimpleMapping(SourceCatalogPersistenceType,
298  template="{dataset}{ext}", keys={}),
299  deepCoadd_det=SkyMapping(SourceCatalogPersistenceType),
300  deepCoadd_det_schema=SimpleMapping(SourceCatalogPersistenceType,
301  template="{dataset}{ext}", keys={}),
302  deepCoadd_mergeDet=SkyMapping(SourceCatalogPersistenceType),
303  deepCoadd_mergeDet_schema=SimpleMapping(SourceCatalogPersistenceType,
304  template="{dataset}{ext}", keys={}),
305  deepCoadd_deblendedFlux=SkyMapping(SourceCatalogPersistenceType),
306  deepCoadd_deblendedFlux_schema=SimpleMapping(SourceCatalogPersistenceType,
307  template="{dataset}{ext}", keys={}),
308  deepCoadd_deblendedModel=SkyMapping(SourceCatalogPersistenceType),
309  deepCoadd_deblendedModel_schema=SimpleMapping(SourceCatalogPersistenceType,
310  template="{dataset}{ext}", keys={}),
311  deepCoadd_meas=SkyMapping(SourceCatalogPersistenceType),
312  deepCoadd_meas_schema=SimpleMapping(SourceCatalogPersistenceType,
313  template="{dataset}{ext}", keys={}),
314  deepCoadd_forced_src=SkyMapping(SourceCatalogPersistenceType),
315  deepCoadd_forced_src_schema=SimpleMapping(SourceCatalogPersistenceType,
316  template="{dataset}{ext}", keys={}),
317  deepCoadd_mock=SkyMapping(ExposurePersistenceType),
318  deepCoaddPsfMatched_mock=SkyMapping(ExposurePersistenceType),
319  deepCoadd_directWarp=TempExpMapping(ExposurePersistenceType),
320  deepCoadd_directWarp_mock=TempExpMapping(ExposurePersistenceType),
321  deepCoadd_psfMatchedWarp=TempExpMapping(ExposurePersistenceType),
322  deepCoadd_psfMatchedWarp_mock=TempExpMapping(ExposurePersistenceType),
323  )
324 
325  levels = dict(
326  visit=['ccd'],
327  ccd=[],
328  )
329 
330  def __init__(self, root, **kwargs):
331  self.storage = lsst.daf.persistence.Storage.makeFromURI(root)
332  super(SimpleMapper, self).__init__(**kwargs)
333  self.root = root
334  self.camera = makeSimpleCamera(nX=1, nY=2, sizeX=400, sizeY=200, gapX=2, gapY=2)
335  afwImageUtils.defineFilter('r', 619.42)
336  self.update()
337 
338  def getDefaultLevel(self):
339  return "ccd"
340 
341  def getKeys(self, datasetType, level):
342  if datasetType is None:
343  keyDict = self.keyDict
344  else:
345  keyDict = self.mappings[datasetType].keys
346  if level is not None and level in self.levels:
347  keyDict = dict(keyDict)
348  for l in self.levels[level]:
349  if l in keyDict:
350  del keyDict[l]
351  return keyDict
352 
353  def update(self):
354  filenames = os.listdir(self.root)
355  rawRegex = re.compile(r"(?P<dataset>\w+)-(?P<visit>\d+)-(?P<ccd>\d).*")
356  self.index = {}
357  for filename in filenames:
358  m = rawRegex.match(filename)
359  if not m:
360  continue
361  index = self.index.setdefault(m.group('dataset'), dict(ccd={None: []}, visit={None: []}))
362  visit = int(m.group('visit'))
363  ccd = int(m.group('ccd'))
364  d1 = dict(visit=visit, ccd=ccd)
365  d2 = dict(visit=visit)
366  index['ccd'].setdefault(visit, []).append(d1)
367  index['ccd'][None].append(d1)
368  index['visit'][visit] = [d2]
369  index['visit'][None].append(d1)
370 
371  def keys(self):
372  return self.keyDict
373 
374  def bypass_camera(self, datasetType, pythonType, location, dataId):
375  return self.camera
376 
377  def map_camera(self, dataId, write=False):
379  "lsst.afw.cameraGeom.Camera", "Camera", None, [], dataId, mapper=self, storage=self.storage
380  )
381 
382  def std_calexp(self, item, dataId):
383  detectorId = dataId["ccd"]
384  detector = self.camera[detectorId]
385  item.setDetector(detector)
386  item.setFilter(afwImage.Filter("r"))
387  return item
388 
389  def _computeCcdExposureId(self, dataId):
390  return int(dataId["visit"]) * 10 + int(dataId["ccd"])
391 
392  def _computeCoaddId(self, dataId):
393  # Note: for real IDs, we'd want to include filter here, but it doesn't actually matter
394  # for any of the tests we've done so far, which all assume filter='r'
395  tract = int(dataId['tract'])
396  if tract < 0 or tract >= 128:
397  raise RuntimeError('tract not in range [0,128)')
398  patchX, patchY = (int(c) for c in dataId['patch'].split(','))
399  for p in (patchX, patchY):
400  if p < 0 or p >= 2**13:
401  raise RuntimeError('patch component not in range [0, 8192)')
402  return (tract * 2**13 + patchX) * 2**13 + patchY
403 
404  @staticmethod
405  def splitCcdExposureId(ccdExposureId):
406  return dict(visit=(int(ccdExposureId) // 10), ccd=(int(ccdExposureId) % 10))
407 
408  def bypass_ccdExposureId(self, datasetType, pythonType, location, dataId):
409  return self._computeCcdExposureId(dataId)
410 
411  def bypass_ccdExposureId_bits(self, datasetType, pythonType, location, dataId):
412  return 32
413 
414  def bypass_deepCoaddId(self, datasetType, pythonType, location, dataId):
415  return self._computeCoaddId(dataId)
416 
417  def bypass_deepCoaddId_bits(self, datasetType, pythonType, location, dataId):
418  return 1 + 7 + 13*2 + 3
419 
420  def bypass_deepMergedCoaddId(self, datasetType, pythonType, location, dataId):
421  return self._computeCoaddId(dataId)
422 
423  def bypass_deepMergedCoaddId_bits(self, datasetType, pythonType, location, dataId):
424  return 1 + 7 + 13*2 + 3
425 
426 
427 def makeSimpleCamera(
428  nX, nY,
429  sizeX, sizeY,
430  gapX, gapY,
431  pixelSize=1.0,
432  plateScale=20.0,
433  radialDistortion=0.925,
434 ):
435  """Create a camera
436 
437  @param[in] nx: number of detectors in x
438  @param[in] ny: number of detectors in y
439  @param[in] sizeX: detector size in x (pixels)
440  @param[in] sizeY: detector size in y (pixels)
441  @param[in] gapX: gap between detectors in x (mm)
442  @param[in] gapY: gap between detectors in y (mm)
443  @param[in] pixelSize: pixel size (mm) (a float)
444  @param[in] plateScale: plate scale in arcsec/mm; 20.0 is for LSST
445  @param[in] radialDistortion: radial distortion, in mm/rad^2
446  (the r^3 coefficient of the radial distortion polynomial
447  that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm);
448  0.925 is the value Dave Monet measured for lsstSim data
449 
450  Each detector will have one amplifier (with no raw information).
451  """
452  pScaleRad = lsst.afw.geom.arcsecToRad(plateScale)
453  radialDistortCoeffs = [0.0, 1.0/pScaleRad, 0.0, radialDistortion/pScaleRad]
454  focalPlaneToFieldAngle = lsst.afw.geom.makeRadialTransform(radialDistortCoeffs)
455  nativeSys = lsst.afw.cameraGeom.FOCAL_PLANE
456  transforms = {
457  lsst.afw.cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle,
458  }
459  transformMap = lsst.afw.cameraGeom.TransformMap(nativeSys, transforms)
460 
461  detectorList = []
463  for iY in range(nY):
464  cY = (iY - 0.5 * (nY - 1)) * (pixelSize * sizeY + gapY)
465  for iX in range(nX):
466  cX = (iX - 0.5 * (nX - 1)) * (pixelSize * sizeY + gapX)
467  fpPos = lsst.afw.geom.Point2D(cX, cY)
468  detectorName = "detector %d,%d" % (iX, iY)
469  detectorId = len(detectorList) + 1
470  detectorList.append(DetectorWrapper(
471  name=detectorName,
472  id=detectorId,
473  serial=detectorName + " serial",
474  bbox=ccdBBox,
475  ampExtent=ccdBBox.getDimensions(),
476  numAmps=1,
477  pixelSize=lsst.afw.geom.Extent2D(pixelSize, pixelSize),
478  orientation=lsst.afw.cameraGeom.Orientation(fpPos),
479  plateScale=plateScale,
480  radialDistortion=radialDistortion,
481  ).detector)
482 
484  name="Simple Camera",
485  detectorList=detectorList,
486  transformMap=transformMap,
487  )
488 
489 
490 def makeDataRepo(root):
491  """
492  Create a data repository for SimpleMapper and return a butler for it.
493 
494  Clobbers anything already in the given path.
495  """
496  if os.path.exists(root):
497  shutil.rmtree(root)
498  os.makedirs(root)
499  with open(os.path.join(root, "_mapper"), "w") as f:
500  f.write("lsst.pipe.tasks.mocks.SimpleMapper\n")
501  return lsst.daf.persistence.Butler(root=root)
def map_camera(self, dataId, write=False)
def bypass_camera(self, datasetType, pythonType, location, dataId)
A registry of 2-dimensional coordinate transforms for a specific camera.
Definition: TransformMap.h:64
A collection of Detectors plus additional coordinate system support.
Definition: Camera.h:43
def __init__(self, persistence, template=None, keys=None)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
def makeSimpleCamera(nX, nY, sizeX, sizeY, gapX, gapY, pixelSize=1.0, plateScale=20.0, radialDistortion=0.925)
def bypass_ccdExposureId_bits(self, datasetType, pythonType, location, dataId)
Describe a detector&#39;s orientation in the focal plane.
Definition: Orientation.h:52
def map(self, dataset, root, dataId, mapper, suffix=None, storage=None)
def __init__(cls, name, bases, dict_)
table::Key< int > type
Definition: Detector.cc:167
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
def bypass_deepMergedCoaddId(self, datasetType, pythonType, location, dataId)
std::shared_ptr< TransformPoint2ToPoint2 > makeRadialTransform(std::vector< double > const &forwardCoeffs, std::vector< double > const &inverseCoeffs)
A purely radial polynomial distortion.
Holds an integer identifier for an LSST filter.
Definition: Filter.h:141
def makeButlerLocation(cls, path, dataId, mapper, suffix=None, storage=None)
Definition: simpleMapper.py:76
def bypass_deepCoaddId(self, datasetType, pythonType, location, dataId)
def bypass_ccdExposureId(self, datasetType, pythonType, location, dataId)
def bypass_deepMergedCoaddId_bits(self, datasetType, pythonType, location, dataId)
std::vector< SchemaItem< Flag > > * items
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def query(self, dataset, index, level, format, dataId)
def bypass_deepCoaddId_bits(self, datasetType, pythonType, location, dataId)
def makeButlerLocation(cls, path, dataId, mapper, suffix=None, storage=None)
Definition: simpleMapper.py:93
An integer coordinate rectangle.
Definition: Box.h:54
daf::base::PropertyList * list
Definition: fits.cc:885
def makeButlerLocation(cls, path, dataId, mapper, suffix=None, storage=None)
Definition: simpleMapper.py:59