Coverage for python/lsst/afw/cameraGeom/_cameraFactory.py: 22%
53 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-23 02:38 -0700
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-23 02:38 -0700
1# This file is part of afw.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22__all__ = ["addDetectorBuilderFromConfig",
23 "makeCameraFromPath", "makeCameraFromAmpLists"]
25import os.path
26from lsst.afw.table import BaseCatalog
27from ._cameraGeom import FOCAL_PLANE, FIELD_ANGLE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS, CameraSys
28from ._cameraGeom import Amplifier
29from ._camera import Camera
30from ._makePixelToTanPixel import makePixelToTanPixel
31from .pupil import PupilFactory
33cameraSysList = [FIELD_ANGLE, FOCAL_PLANE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS]
34cameraSysMap = dict((sys.getSysName(), sys) for sys in cameraSysList)
37def addDetectorBuilderFromConfig(cameraBuilder, detectorConfig, amplifiers, focalPlaneToField):
38 """Build a dictionary of Detector constructor keyword arguments.
40 The returned dictionary can be passed as keyword arguments to the Detector
41 constructor, providing all required arguments. However, using these
42 arguments directly constructs a Detector with knowledge of only the
43 coordinate systems that are *directly* mapped to its own PIXELS coordinate
44 system. To construct Detectors with a shared TransformMap for the full
45 Camera, use makeCameraFromCatalogs or makeCameraFromPath instead of
46 calling this function or makeDetector directly.
48 Parameters
49 ----------
50 cameraBuilder : `lsst.afw.cameraGeonm.Camera.Builder`
51 Camera builder object to which the new Detector Builder
52 should be added.
53 detectorConfig : `lsst.pex.config.Config`
54 Configuration for this detector.
55 amplifiers : `list` [`~lsst.afw.cameraGeom.Amplifier`]
56 amplifier information for this detector
57 focalPlaneToField : `lsst.afw.geom.TransformPoint2ToPoint2`
58 FOCAL_PLANE to FIELD_ANGLE Transform
60 Returns
61 -------
62 detectorBuilder : `lsst.afw.cameraGeom.Detector.InCameraBuilder`
63 A builder object for a detector corresponding to the given config,
64 associated with the given camera builder object.
65 """
66 detectorBuilder = cameraBuilder.add(detectorConfig.name, detectorConfig.id)
67 detectorBuilder.fromConfig(detectorConfig)
69 for ampBuilder in amplifiers:
70 detectorBuilder.append(ampBuilder)
72 transforms = makeTransformDict(detectorConfig.transformDict.transforms)
74 # It seems the C++ code has always assumed that the "nativeSys" for
75 # detectors is PIXELS, despite the configs here giving the illusion of
76 # choice. We'll use PIXELS if the config value is None, and assert that
77 # the value is PIXELS otherwise. Note that we can't actually get rid of
78 # the nativeSys config option without breaking lots of on-disk camera
79 # configs.
80 detectorNativeSysPrefix = cameraSysMap.get(detectorConfig.transformDict.nativeSys, PIXELS)
81 assert detectorNativeSysPrefix == PIXELS, "Detectors with nativeSys != PIXELS are not supported."
83 for toSys, transform in transforms.items():
84 detectorBuilder.setTransformFromPixelsTo(toSys, transform)
85 tanPixSys = CameraSys(TAN_PIXELS, detectorConfig.name)
86 transforms[tanPixSys] = makePixelToTanPixel(
87 bbox=detectorBuilder.getBBox(),
88 orientation=detectorBuilder.getOrientation(),
89 focalPlaneToField=focalPlaneToField,
90 pixelSizeMm=detectorBuilder.getPixelSize(),
91 )
93 for toSys, transform in transforms.items():
94 detectorBuilder.setTransformFromPixelsTo(toSys, transform)
96 detectorBuilder.setCrosstalk(detectorConfig.getCrosstalk(len(amplifiers)))
98 return detectorBuilder
101def makeTransformDict(transformConfigDict):
102 """Make a dictionary of CameraSys: lsst.afw.geom.Transform from a config dict.
104 Parameters
105 ----------
106 transformConfigDict : value obtained from a `lsst.pex.config.ConfigDictField`
107 registry; keys are camera system names.
109 Returns
110 -------
111 transforms : `dict` [`CameraSys` or `CameraSysPrefix`, `lsst.afw.geom.Transform`]
112 A dict of CameraSys or CameraSysPrefix: lsst.afw.geom.Transform
113 """
114 resMap = dict()
115 if transformConfigDict is not None:
116 for key in transformConfigDict:
117 transform = transformConfigDict[key].transform.apply()
118 resMap[CameraSys(key)] = transform
119 return resMap
122def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc,
123 pupilFactoryClass=PupilFactory):
124 """Make a Camera instance from a directory of ampInfo files
126 The directory must contain one ampInfo fits file for each detector in cameraConfig.detectorList.
127 The name of each ampInfo file must be shortNameFunc(fullDetectorName) + ".fits".
129 Parameters
130 ----------
131 cameraConfig : `CameraConfig`
132 Config describing camera and its detectors.
133 ampInfoPath : `str`
134 Path to ampInfo data files.
135 shortNameFunc : callable
136 A function that converts a long detector name to a short one.
137 pupilFactoryClass : `type`, optional
138 Class to attach to camera; default is `lsst.afw.cameraGeom.PupilFactory`.
140 Returns
141 -------
142 camera : `lsst.afw.cameraGeom.Camera`
143 New Camera instance.
144 """
145 ampListDict = dict()
146 for detectorConfig in cameraConfig.detectorList.values():
147 shortName = shortNameFunc(detectorConfig.name)
148 ampCatPath = os.path.join(ampInfoPath, shortName + ".fits")
149 catalog = BaseCatalog.readFits(ampCatPath)
150 ampListDict[detectorConfig.name] = [Amplifier.Builder.fromRecord(record)
151 for record in catalog]
153 return makeCameraFromAmpLists(cameraConfig, ampListDict, pupilFactoryClass)
156def makeCameraFromAmpLists(cameraConfig, ampListDict,
157 pupilFactoryClass=PupilFactory):
158 """Construct a Camera instance from a dictionary of detector name: list of
159 Amplifier.Builder
161 Parameters
162 ----------
163 cameraConfig : `CameraConfig`
164 Config describing camera and its detectors.
165 ampListDict : `dict` [`str`, `list` [`Amplifier.Builder`]]
166 A dictionary of detector name: list of Amplifier.Builder
167 pupilFactoryClass : `type`, optional
168 Class to attach to camera; `lsst.default afw.cameraGeom.PupilFactory`.
170 Returns
171 -------
172 camera : `lsst.afw.cameraGeom.Camera`
173 New Camera instance.
174 """
175 nativeSys = cameraSysMap[cameraConfig.transformDict.nativeSys]
177 # nativeSys=FOCAL_PLANE seems is baked into the camera class definition,
178 # despite CameraConfig providing the illusion that it's configurable. Note
179 # that we can't actually get rid of the nativeSys config option without
180 # breaking lots of on-disk camera configs.
181 assert nativeSys == FOCAL_PLANE, "Cameras with nativeSys != FOCAL_PLANE are not supported."
183 cameraBuilder = Camera.Builder(cameraConfig.name)
184 cameraBuilder.setPupilFactoryClass(pupilFactoryClass)
186 transformDict = makeTransformDict(cameraConfig.transformDict.transforms)
187 focalPlaneToField = transformDict[FIELD_ANGLE]
189 for toSys, transform in transformDict.items():
190 cameraBuilder.setTransformFromFocalPlaneTo(toSys, transform)
192 for detectorConfig in cameraConfig.detectorList.values():
193 addDetectorBuilderFromConfig(
194 cameraBuilder,
195 detectorConfig=detectorConfig,
196 amplifiers=ampListDict[detectorConfig.name],
197 focalPlaneToField=focalPlaneToField,
198 )
200 return cameraBuilder.finish()