LSSTApplications  16.0-11-g09ed895+2,16.0-11-g12e47bd,16.0-11-g9bb73b2+6,16.0-12-g5c924a4+6,16.0-14-g9a974b3+1,16.0-15-g1417920+1,16.0-15-gdd5ca33+1,16.0-16-gf0259e2,16.0-17-g31abd91+7,16.0-17-g7d7456e+7,16.0-17-ga3d2e9f+13,16.0-18-ga4d4bcb+1,16.0-18-gd06566c+1,16.0-2-g0febb12+21,16.0-2-g9d5294e+69,16.0-2-ga8830df+6,16.0-20-g21842373+7,16.0-24-g3eae5ec,16.0-28-gfc9ea6c+4,16.0-29-ge8801f9,16.0-3-ge00e371+34,16.0-4-g18f3627+13,16.0-4-g5f3a788+20,16.0-4-ga3eb747+10,16.0-4-gabf74b7+29,16.0-4-gb13d127+6,16.0-49-g42e581f7+6,16.0-5-g27fb78a+7,16.0-5-g6a53317+34,16.0-5-gb3f8a4b+87,16.0-6-g9321be7+4,16.0-6-gcbc7b31+42,16.0-6-gf49912c+29,16.0-7-gd2eeba5+51,16.0-71-ge89f8615e,16.0-8-g21fd5fe+29,16.0-8-g3a9f023+20,16.0-8-g4734f7a+1,16.0-8-g5858431+3,16.0-9-gf5c1f43+8,master-gd73dc1d098+1,w.2019.01
LSSTDataManagementBasePackage
cameraFactory.py
Go to the documentation of this file.
1 import os.path
2 import lsst.geom
3 from lsst.afw.table import AmpInfoCatalog
4 from .cameraGeomLib import FOCAL_PLANE, FIELD_ANGLE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS, CameraSys, \
5  Detector, DetectorType, Orientation, TransformMap
6 from .camera import Camera
7 from .makePixelToTanPixel import makePixelToTanPixel
8 from .pupil import PupilFactory
9 
10 __all__ = ["makeCameraFromPath", "makeCameraFromCatalogs",
11  "makeDetector", "copyDetector"]
12 
13 cameraSysList = [FIELD_ANGLE, FOCAL_PLANE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS]
14 cameraSysMap = dict((sys.getSysName(), sys) for sys in cameraSysList)
15 
16 
17 def makeDetectorData(detectorConfig, ampInfoCatalog, focalPlaneToField):
18  """Build a dictionary of Detector constructor keyword arguments.
19 
20  The returned dictionary can be passed as keyword arguments to the Detector
21  constructor, providing all required arguments. However, using these
22  arguments directly constructs a Detector with knowledge of only the
23  coordinate systems that are *directly* mapped to its own PIXELS coordinate
24  system. To construct Detectors with a shared TransformMap for the full
25  Camera, use makeCameraFromCatalogs or makeCameraFromPath instead of
26  calling this function or makeDetector directly.
27 
28  Parameters
29  ----------
30  detectorConfig : `lsst.pex.config.Config`
31  Configuration for this detector.
32  ampInfoCatalog : `lsst.afw.table.AmpInfoCatalog`
33  amplifier information for this detector
34  focalPlaneToField : `lsst.afw.geom.TransformPoint2ToPoint2`
35  FOCAL_PLANE to FIELD_ANGLE Transform
36 
37  Returns
38  -------
39  data : `dict`
40  Contains the following keys: name, id, type, serial, bbox, orientation,
41  pixelSize, transforms, ampInfoCatalog, and optionally crosstalk.
42  The transforms key is a dictionary whose values are Transforms that map
43  the Detector's PIXEL coordinate system to the CameraSys in the key.
44  """
45 
46  data = dict(
47  name=detectorConfig.name,
48  id=detectorConfig.id,
49  type=DetectorType(detectorConfig.detectorType),
50  serial=detectorConfig.serial,
51  ampInfoCatalog=ampInfoCatalog,
52  orientation=makeOrientation(detectorConfig),
53  pixelSize=lsst.geom.Extent2D(detectorConfig.pixelSize_x, detectorConfig.pixelSize_y),
54  bbox=lsst.geom.Box2I(
55  minimum=lsst.geom.Point2I(detectorConfig.bbox_x0, detectorConfig.bbox_y0),
56  maximum=lsst.geom.Point2I(detectorConfig.bbox_x1, detectorConfig.bbox_y1),
57  ),
58  )
59 
60  transforms = makeTransformDict(detectorConfig.transformDict.transforms)
61  transforms[FOCAL_PLANE] = data["orientation"].makePixelFpTransform(data["pixelSize"])
62 
63  tanPixSys = CameraSys(TAN_PIXELS, detectorConfig.name)
64  transforms[tanPixSys] = makePixelToTanPixel(
65  bbox=data["bbox"],
66  orientation=data["orientation"],
67  focalPlaneToField=focalPlaneToField,
68  pixelSizeMm=data["pixelSize"],
69  )
70 
71  data["transforms"] = transforms
72 
73  crosstalk = detectorConfig.getCrosstalk(len(ampInfoCatalog))
74  if crosstalk is not None:
75  data["crosstalk"] = crosstalk
76 
77  return data
78 
79 
80 def makeDetector(detectorConfig, ampInfoCatalog, focalPlaneToField):
81  """Make a Detector instance from a detector config and amp info catalog
82 
83  Parameters
84  ----------
85  detectorConfig : `lsst.pex.config.Config`
86  Configuration for this detector.
87  ampInfoCatalog : `lsst.afw.table.AmpInfoCatalog`
88  amplifier information for this detector
89  focalPlaneToField : `lsst.afw.geom.TransformPoint2ToPoint2`
90  FOCAL_PLANE to FIELD_ANGLE Transform
91 
92  Returns
93  -------
94  detector : `lsst.afw.cameraGeom.Detector`
95  New Detector instance.
96  """
97  data = makeDetectorData(detectorConfig, ampInfoCatalog, focalPlaneToField)
98  return Detector(**data)
99 
100 
101 def copyDetector(detector, ampInfoCatalog=None):
102  """Return a copy of a Detector with possibly-updated amplifier information.
103 
104  No deep copies are made; the input transformDict is used unmodified
105 
106  Parameters
107  ----------
108  detector : `lsst.afw.cameraGeom.Detector`
109  The Detector to clone
110  ampInfoCatalog The ampInfoCatalog to use; default use original
111 
112  Returns
113  -------
114  detector : `lsst.afw.cameraGeom.Detector`
115  New Detector instance.
116  """
117  if ampInfoCatalog is None:
118  ampInfoCatalog = detector.getAmpInfoCatalog()
119 
120  return Detector(detector.getName(), detector.getId(), detector.getType(),
121  detector.getSerial(), detector.getBBox(),
122  ampInfoCatalog, detector.getOrientation(), detector.getPixelSize(),
123  detector.getTransformMap(), detector.getCrosstalk())
124 
125 
126 def makeOrientation(detectorConfig):
127  """Make an Orientation instance from a detector config
128 
129  Parameters
130  ----------
131  detectorConfig : `lsst.pex.config.Config`
132  Configuration for this detector.
133 
134  Returns
135  -------
136  orientation : `lsst.afw.cameraGeom.Orientation`
137  Location and rotation of the Detector.
138  """
139  offset = lsst.geom.Point2D(detectorConfig.offset_x, detectorConfig.offset_y)
140  refPos = lsst.geom.Point2D(detectorConfig.refpos_x, detectorConfig.refpos_y)
141  yaw = lsst.geom.Angle(detectorConfig.yawDeg, lsst.geom.degrees)
142  pitch = lsst.geom.Angle(detectorConfig.pitchDeg, lsst.geom.degrees)
143  roll = lsst.geom.Angle(detectorConfig.rollDeg, lsst.geom.degrees)
144  return Orientation(offset, refPos, yaw, pitch, roll)
145 
146 
147 def makeTransformDict(transformConfigDict):
148  """Make a dictionary of CameraSys: lsst.afw.geom.Transform from a config dict.
149 
150  Parameters
151  ----------
152  transformConfigDict : value obtained from a `lsst.pex.config.ConfigDictField`
153  registry; keys are camera system names.
154 
155  Returns
156  -------
157  transforms : `dict`
158  A dict of CameraSys or CameraSysPrefix: lsst.afw.geom.Transform
159  """
160  resMap = dict()
161  if transformConfigDict is not None:
162  for key in transformConfigDict:
163  transform = transformConfigDict[key].transform.apply()
164  resMap[CameraSys(key)] = transform
165  return resMap
166 
167 
168 def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc,
169  pupilFactoryClass=PupilFactory):
170  """Make a Camera instance from a directory of ampInfo files
171 
172  The directory must contain one ampInfo fits file for each detector in cameraConfig.detectorList.
173  The name of each ampInfo file must be shortNameFunc(fullDetectorName) + ".fits".
174 
175  Parameters
176  ----------
177  cameraConfig : `CameraConfig`
178  Config describing camera and its detectors.
179  ampInfoPath : `str`
180  Path to ampInfo data files.
181  shortNameFunc : callable
182  A function that converts a long detector name to a short one.
183  pupilFactoryClass : `type`, optional
184  Class to attach to camera; default is `lsst.afw.cameraGeom.PupilFactory`.
185 
186  Returns
187  -------
188  camera : `lsst.afw.cameraGeom.Camera`
189  New Camera instance.
190  """
191  ampInfoCatDict = dict()
192  for detectorConfig in cameraConfig.detectorList.values():
193  shortName = shortNameFunc(detectorConfig.name)
194  ampCatPath = os.path.join(ampInfoPath, shortName + ".fits")
195  ampInfoCatalog = AmpInfoCatalog.readFits(ampCatPath)
196  ampInfoCatDict[detectorConfig.name] = ampInfoCatalog
197 
198  return makeCameraFromCatalogs(cameraConfig, ampInfoCatDict, pupilFactoryClass)
199 
200 
201 def makeCameraFromCatalogs(cameraConfig, ampInfoCatDict,
202  pupilFactoryClass=PupilFactory):
203  """Construct a Camera instance from a dictionary of detector name: AmpInfoCatalog
204 
205  Parameters
206  ----------
207  cameraConfig : `CameraConfig`
208  Config describing camera and its detectors.
209  ampInfoCatDict : `dict`
210  A dictionary of detector name: AmpInfoCatalog
211  pupilFactoryClass : `type`, optional
212  Class to attach to camera; `lsst.default afw.cameraGeom.PupilFactory`.
213 
214  Returns
215  -------
216  camera : `lsst.afw.cameraGeom.Camera`
217  New Camera instance.
218  """
219  nativeSys = cameraSysMap[cameraConfig.transformDict.nativeSys]
220 
221  # nativeSys=FOCAL_PLANE seems to be assumed in various places in this file
222  # (e.g. the definition of TAN_PIXELS), despite CameraConfig providing the
223  # illusion that it's configurable.
224  # Note that we can't actually get rid of the nativeSys config option
225  # without breaking lots of on-disk camera configs.
226  assert nativeSys == FOCAL_PLANE, "Cameras with nativeSys != FOCAL_PLANE are not supported."
227 
228  transformDict = makeTransformDict(cameraConfig.transformDict.transforms)
229  focalPlaneToField = transformDict[FIELD_ANGLE]
230  transformMapBuilder = TransformMap.Builder(nativeSys)
231  transformMapBuilder.connect(transformDict)
232 
233  # First pass: build a list of all Detector ctor kwargs, minus the
234  # transformMap (which needs information from all Detectors).
235  detectorData = []
236  for detectorConfig in cameraConfig.detectorList.values():
237 
238  # Get kwargs that could be used to construct each Detector
239  # if we didn't care about giving each of them access to
240  # all of the transforms.
241  thisDetectorData = makeDetectorData(
242  detectorConfig=detectorConfig,
243  ampInfoCatalog=ampInfoCatDict[detectorConfig.name],
244  focalPlaneToField=focalPlaneToField,
245  )
246 
247  # Pull the transforms dictionary out of the data dict; we'll replace
248  # it with a TransformMap argument later.
249  thisDetectorTransforms = thisDetectorData.pop("transforms")
250 
251  # Save the rest of the Detector data dictionary for later
252  detectorData.append(thisDetectorData)
253 
254  # For reasons I don't understand, some obs_ packages (e.g. HSC) set
255  # nativeSys to None for their detectors (which doesn't seem to be
256  # permitted by the config class!), but they really mean PIXELS. For
257  # backwards compatibility we use that as the default...
258  detectorNativeSysPrefix = cameraSysMap.get(detectorConfig.transformDict.nativeSys, PIXELS)
259 
260  # ...well, actually, it seems that we've always assumed down in C++
261  # that the answer is always PIXELS without ever checking that it is.
262  # So let's assert that it is, since there are hints all over this file
263  # (e.g. the definition of TAN_PIXELS) that other parts of the codebase
264  # have regularly made that assumption as well. Note that we can't
265  # actually get rid of the nativeSys config option without breaking
266  # lots of on-disk camera configs.
267  assert detectorNativeSysPrefix == PIXELS, "Detectors with nativeSys != PIXELS are not supported."
268  detectorNativeSys = CameraSys(detectorNativeSysPrefix, detectorConfig.name)
269 
270  # Add this detector's transform dict to the shared TransformMapBuilder
271  transformMapBuilder.connect(detectorNativeSys, thisDetectorTransforms)
272 
273  # Now that we've collected all of the Transforms, we can finally build the
274  # (immutable) TransformMap.
275  transformMap = transformMapBuilder.build()
276 
277  # Second pass through the detectorConfigs: actually make Detector instances
278  detectorList = [Detector(transformMap=transformMap, **kw) for kw in detectorData]
279 
280  return Camera(cameraConfig.name, detectorList, transformMap, pupilFactoryClass)
def copyDetector(detector, ampInfoCatalog=None)
def makeDetectorData(detectorConfig, ampInfoCatalog, focalPlaneToField)
def makeCameraFromCatalogs(cameraConfig, ampInfoCatDict, pupilFactoryClass=PupilFactory)
A class representing an angle.
Definition: Angle.h:127
Describe a detector's orientation in the focal plane.
Definition: Orientation.h:52
Helper class used to incrementally construct TransformMap instances.
Definition: TransformMap.h:239
def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc, pupilFactoryClass=PupilFactory)
Information about a CCD or other imaging detector.
Definition: Detector.h:61
def makePixelToTanPixel(bbox, orientation, focalPlaneToField, pixelSizeMm)
Make a Transform whose forward direction converts PIXELS to TAN_PIXELS for one detector.
def makeTransformDict(transformConfigDict)
def makeDetector(detectorConfig, ampInfoCatalog, focalPlaneToField)
An integer coordinate rectangle.
Definition: Box.h:54