LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
testUtils.py
Go to the documentation of this file.
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/>.
21 
22 __all__ = ["DetectorWrapper", "CameraWrapper"]
23 
24 import os
25 
26 import numpy as np
27 
28 import lsst.utils
29 import lsst.geom
30 import lsst.afw.geom as afwGeom
31 from lsst.utils.tests import inTestCase
32 from ._cameraGeom import CameraSys, PIXELS, TAN_PIXELS, FIELD_ANGLE, FOCAL_PLANE, ACTUAL_PIXELS, Orientation
33 from ._cameraGeom import Amplifier, ReadoutCorner
34 from ._camera import Camera
35 from ._cameraGeom import DetectorType
36 from .cameraConfig import DetectorConfig, CameraConfig
37 from ._cameraFactory import makeCameraFromAmpLists
38 from ._makePixelToTanPixel import makePixelToTanPixel
39 from ._transformConfig import TransformMapConfig
40 
41 
43  """A Detector and the data used to construct it
44 
45  Intended for use with unit tests, thus saves a copy of all input parameters.
46  Does not support setting details of amplifiers.
47 
48  Parameters
49  ----------
50  name : `str` (optional)
51  Detector name.
52  id : `int` (optional)
53  Detector ID.
54  detType : `lsst.afw.cameraGeom.DetectorType` (optional)
55  Detector type.
56  serial : `str` (optional)
57  Serial "number".
58  bbox : `lsst.geom.Box2I` (optional)
59  Bounding box; defaults to (0, 0), (1024x1024).
60  numAmps : `int` (optional)
61  Number of amplifiers.
62  pixelSize : `lsst.geom.Point2D` (optional)
63  Pixel size (mm).
64  ampExtent : `lsst.geom.Extent2I` (optional)
65  Dimensions of amplifier image bbox.
66  orientation : `lsst.afw.cameraGeom.Orientation` (optional)
67  Orientation of CCC in focal plane.
68  plateScale : `float` (optional)
69  Plate scale in arcsec/mm; 20.0 is for LSST.
70  radialDistortion : `float` (optional)
71  Radial distortion, in mm/rad^2.
72  The r^3 coefficient of the radial distortion polynomial
73  that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm;
74  0.925 is the value Dave Monet measured for lsstSim data
75  crosstalk : `iterable` (optional)
76  Crosstalk coefficient matrix. If None, then no crosstalk correction
77  can be performed.
78  modFunc : `callable` (optional)
79  A function that can modify attributes just before constructing the
80  detector; modFunc receives one argument: a DetectorWrapper with all
81  attributes except detector set.
82  physicalType : `str` (optional)
83  The physical type of the device, e.g. CCD, E2V, HgCdTe
84  """
85 
86  def __init__(self,
87  name="detector 1",
88  id=1,
89  detType=DetectorType.SCIENCE,
90  serial="xkcd722",
91  bbox=None, # do not use mutable objects as defaults
92  numAmps=3,
93  pixelSize=(0.02, 0.02),
94  ampExtent=(5, 6),
95  orientation=Orientation(),
96  plateScale=20.0,
97  radialDistortion=0.925,
98  crosstalk=None,
99  modFunc=None,
100  physicalType="CCD",
101  cameraBuilder=None
102  ):
103  # note that (0., 0.) for the reference position is the center of the
104  # first pixel
105  self.namename = name
106  self.idid = int(id)
107  self.typetype = detType
108  self.serialserial = serial
109  if bbox is None:
110  bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(1024, 1048))
111  self.bboxbbox = bbox
112  self.pixelSizepixelSize = lsst.geom.Extent2D(*pixelSize)
113  self.ampExtentampExtent = lsst.geom.Extent2I(*ampExtent)
114  self.plateScaleplateScale = float(plateScale)
115  self.orientationorientation = orientation
116  self.radialDistortionradialDistortion = float(radialDistortion)
117 
118  # compute TAN_PIXELS transform
119  pScaleRad = lsst.geom.arcsecToRad(self.plateScaleplateScale)
120  radialDistortCoeffs = [0.0, 1.0/pScaleRad,
121  0.0, self.radialDistortionradialDistortion/pScaleRad]
122  focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
123  pixelToTanPixel = makePixelToTanPixel(
124  bbox=self.bboxbbox,
125  orientation=self.orientationorientation,
126  focalPlaneToField=focalPlaneToField,
127  pixelSizeMm=self.pixelSizepixelSize,
128  )
129  tanPixelSys = CameraSys(TAN_PIXELS, self.namename)
130  actualPixelSys = CameraSys(ACTUAL_PIXELS, self.namename)
131  self.transMaptransMap = {
132  FOCAL_PLANE: self.orientationorientation.makePixelFpTransform(self.pixelSizepixelSize),
133  tanPixelSys: pixelToTanPixel,
134  actualPixelSys: afwGeom.makeRadialTransform([0, 0.95, 0.01]),
135  }
136  if crosstalk is None:
137  crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)]
138  self.crosstalkcrosstalk = crosstalk
139  self.physicalTypephysicalType = physicalType
140  if cameraBuilder is None:
141  cameraBuilder = Camera.Builder("CameraForDetectorWrapper")
142  self.ampListampList = []
143  for i in range(numAmps):
144  ampBuilder = Amplifier.Builder()
145  ampName = f"amp {i + 1}"
146  ampBuilder.setName(ampName)
147  ampBuilder.setBBox(lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtentampExtent))
148  ampBuilder.setGain(1.71234e3)
149  ampBuilder.setReadNoise(0.521237e2)
150  ampBuilder.setReadoutCorner(ReadoutCorner.LL)
151  self.ampListampList.append(ampBuilder)
152  if modFunc:
153  modFunc(self)
154  detectorBuilder = cameraBuilder.add(self.namename, self.idid)
155  detectorBuilder.setType(self.typetype)
156  detectorBuilder.setSerial(self.serialserial)
157  detectorBuilder.setPhysicalType(self.physicalTypephysicalType)
158  detectorBuilder.setBBox(self.bboxbbox)
159  detectorBuilder.setOrientation(self.orientationorientation)
160  detectorBuilder.setPixelSize(self.pixelSizepixelSize)
161  detectorBuilder.setTransformFromPixelsTo(tanPixelSys, self.transMaptransMap[tanPixelSys])
162  detectorBuilder.setTransformFromPixelsTo(actualPixelSys, self.transMaptransMap[actualPixelSys])
163  detectorBuilder.setCrosstalk(np.array(self.crosstalkcrosstalk, dtype=np.float32))
164  for ampBuilder in self.ampListampList:
165  detectorBuilder.append(ampBuilder)
166  camera = cameraBuilder.finish()
167  self.detectordetector = camera[self.namename]
168 
169 
171  """A simple Camera and the data used to construct it
172 
173  Intended for use with unit tests, thus saves some interesting information.
174 
175  Parameters
176  ----------
177  plateScale : `float`
178  Plate scale in arcsec/mm; 20.0 is for LSST.
179  radialDistortion : `float`
180  Radial distortion, in mm/rad^2.
181  The r^3 coefficient of the radial distortion polynomial
182  that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm;
183  0.925 is the value Dave Monet measured for lsstSim data.
184  isLsstLike : `bool`.
185  Make repository products with one raw image per amplifier (True)
186  or with one raw image per detector (False).
187  """
188 
189  def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False):
190  afwDir = lsst.utils.getPackageDir("afw")
191  self._afwTestDataDir_afwTestDataDir = os.path.join(afwDir, "python", "lsst", "afw",
192  "cameraGeom", "testData")
193 
194  # Info to store for unit tests
195  self.plateScaleplateScale = float(plateScale)
196  self.radialDistortionradialDistortion = float(radialDistortion)
197  self.detectorNameListdetectorNameList = []
198  self.detectorIdListdetectorIdList = []
199  self.ampDataDictampDataDict = {} # ampData[Dict]: raw dictionaries of test data fields
200 
201  # ampList[Dict]: actual cameraGeom.Amplifier objects
202  self.camConfig, self.ampListDictampListDict = self.makeTestRepositoryItemsmakeTestRepositoryItems(
203  isLsstLike)
205  self.camConfig, self.ampListDictampListDict)
206 
207  @property
208  def nDetectors(self):
209  """Return the number of detectors"""
210  return len(self.detectorNameListdetectorNameList)
211 
212  def makeDetectorConfigs(self, detFile):
213  """Construct a list of DetectorConfig, one per detector
214  """
215  detectors = []
216  self.detectorNameListdetectorNameList = []
217  self.detectorIdListdetectorIdList = []
218  with open(detFile) as fh:
219  names = fh.readline().rstrip().lstrip("#").split("|")
220  for line in fh:
221  els = line.rstrip().split("|")
222  detectorProps = dict([(name, el)
223  for name, el in zip(names, els)])
224  detectors.append(detectorProps)
225  detectorConfigs = []
226  for i, detector in enumerate(detectors):
227  detectorId = (i + 1) * 10 # to avoid simple 0, 1, 2...
228  detectorName = detector['name']
229  detConfig = DetectorConfig()
230  detConfig.name = detectorName
231  detConfig.id = detectorId
232  detConfig.bbox_x0 = 0
233  detConfig.bbox_y0 = 0
234  detConfig.bbox_x1 = int(detector['npix_x']) - 1
235  detConfig.bbox_y1 = int(detector['npix_y']) - 1
236  detConfig.serial = str(detector['serial'])
237  detConfig.detectorType = int(detector['detectorType'])
238  detConfig.offset_x = float(detector['x'])
239  detConfig.offset_y = float(detector['y'])
240  detConfig.refpos_x = float(detector['refPixPos_x'])
241  detConfig.refpos_y = float(detector['refPixPos_y'])
242  detConfig.yawDeg = float(detector['yaw'])
243  detConfig.pitchDeg = float(detector['pitch'])
244  detConfig.rollDeg = float(detector['roll'])
245  detConfig.pixelSize_x = float(detector['pixelSize'])
246  detConfig.pixelSize_y = float(detector['pixelSize'])
247  detConfig.transposeDetector = False
248  detConfig.transformDict.nativeSys = PIXELS.getSysName()
249  detectorConfigs.append(detConfig)
250  self.detectorNameListdetectorNameList.append(detectorName)
251  self.detectorIdListdetectorIdList.append(detectorId)
252  return detectorConfigs
253 
254  def makeAmpLists(self, ampFile, isLsstLike=False):
255  """Construct a dict of list of Amplifer, one list per detector.
256 
257  Parameters
258  ----------
259  ampFile : `str`
260  Path to amplifier data file.
261  isLsstLike : `bool`
262  If True then there is one raw image per amplifier;
263  if False then there is one raw image per detector.
264  """
265  readoutMap = {
266  'LL': ReadoutCorner.LL,
267  'LR': ReadoutCorner.LR,
268  'UR': ReadoutCorner.UR,
269  'UL': ReadoutCorner.UL,
270  }
271  ampDataList = []
272  with open(ampFile) as fh:
273  names = fh.readline().rstrip().lstrip("#").split("|")
274  for line in fh:
275  els = line.rstrip().split("|")
276  ampProps = dict([(name, el) for name, el in zip(names, els)])
277  ampDataList.append(ampProps)
278  ampListDict = {}
279  self.ampDataDictampDataDict = {}
280  for ampData in ampDataList:
281  if ampData['ccd_name'] in ampListDict:
282  ampList = ampListDict[ampData['ccd_name']]
283  self.ampDataDictampDataDict[ampData['ccd_name']]['namps'] += 1
284  else:
285  ampList = []
286  ampListDict[ampData['ccd_name']] = ampList
287  self.ampDataDictampDataDict[ampData['ccd_name']] = {'namps': 1, 'linInfo': {}}
288  builder = Amplifier.Builder()
289  bbox = lsst.geom.Box2I(lsst.geom.Point2I(int(ampData['trimmed_xmin']),
290  int(ampData['trimmed_ymin'])),
291  lsst.geom.Point2I(int(ampData['trimmed_xmax']),
292  int(ampData['trimmed_ymax'])))
293  rawBbox = lsst.geom.Box2I(lsst.geom.Point2I(int(ampData['raw_xmin']),
294  int(ampData['raw_ymin'])),
295  lsst.geom.Point2I(int(ampData['raw_xmax']),
296  int(ampData['raw_ymax'])))
297  rawDataBbox = lsst.geom.Box2I(
298  lsst.geom.Point2I(int(ampData['raw_data_xmin']),
299  int(ampData['raw_data_ymin'])),
300  lsst.geom.Point2I(int(ampData['raw_data_xmax']),
301  int(ampData['raw_data_ymax'])))
302  rawHOverscanBbox = lsst.geom.Box2I(
303  lsst.geom.Point2I(int(ampData['hoscan_xmin']),
304  int(ampData['hoscan_ymin'])),
305  lsst.geom.Point2I(int(ampData['hoscan_xmax']),
306  int(ampData['hoscan_ymax'])))
307  rawVOverscanBbox = lsst.geom.Box2I(
308  lsst.geom.Point2I(int(ampData['voscan_xmin']),
309  int(ampData['voscan_ymin'])),
310  lsst.geom.Point2I(int(ampData['voscan_xmax']),
311  int(ampData['voscan_ymax'])))
312  rawPrescanBbox = lsst.geom.Box2I(
313  lsst.geom.Point2I(int(ampData['pscan_xmin']),
314  int(ampData['pscan_ymin'])),
315  lsst.geom.Point2I(int(ampData['pscan_xmax']),
316  int(ampData['pscan_ymax'])))
317  xoffset = int(ampData['x_offset'])
318  yoffset = int(ampData['y_offset'])
319  flipx = bool(int(ampData['flipx']))
320  flipy = bool(int(ampData['flipy']))
321  readcorner = 'LL'
322  if not isLsstLike:
323  offext = lsst.geom.Extent2I(xoffset, yoffset)
324  if flipx:
325  xExt = rawBbox.getDimensions().getX()
326  rawBbox.flipLR(xExt)
327  rawDataBbox.flipLR(xExt)
328  rawHOverscanBbox.flipLR(xExt)
329  rawVOverscanBbox.flipLR(xExt)
330  rawPrescanBbox.flipLR(xExt)
331  if flipy:
332  yExt = rawBbox.getDimensions().getY()
333  rawBbox.flipTB(yExt)
334  rawDataBbox.flipTB(yExt)
335  rawHOverscanBbox.flipTB(yExt)
336  rawVOverscanBbox.flipTB(yExt)
337  rawPrescanBbox.flipTB(yExt)
338  if not flipx and not flipy:
339  readcorner = 'LL'
340  elif flipx and not flipy:
341  readcorner = 'LR'
342  elif flipx and flipy:
343  readcorner = 'UR'
344  elif not flipx and flipy:
345  readcorner = 'UL'
346  else:
347  raise RuntimeError("Couldn't find read corner")
348 
349  flipx = False
350  flipy = False
351  rawBbox.shift(offext)
352  rawDataBbox.shift(offext)
353  rawHOverscanBbox.shift(offext)
354  rawVOverscanBbox.shift(offext)
355  rawPrescanBbox.shift(offext)
356  xoffset = 0
357  yoffset = 0
358  offset = lsst.geom.Extent2I(xoffset, yoffset)
359  builder.setBBox(bbox)
360  builder.setRawXYOffset(offset)
361  builder.setName(str(ampData['name']))
362  builder.setReadoutCorner(readoutMap[readcorner])
363  builder.setGain(float(ampData['gain']))
364  builder.setReadNoise(float(ampData['readnoise']))
365  linCoeffs = np.array([float(ampData['lin_coeffs']), ], dtype=float)
366  builder.setLinearityCoeffs(linCoeffs)
367  builder.setLinearityType(str(ampData['lin_type']))
368  builder.setRawFlipX(flipx)
369  builder.setRawFlipY(flipy)
370  builder.setRawBBox(rawBbox)
371  builder.setRawDataBBox(rawDataBbox)
372  builder.setRawHorizontalOverscanBBox(rawHOverscanBbox)
373  builder.setRawVerticalOverscanBBox(rawVOverscanBbox)
374  builder.setRawPrescanBBox(rawPrescanBbox)
375  builder.setLinearityThreshold(float(ampData['lin_thresh']))
376  builder.setLinearityMaximum(float(ampData['lin_max']))
377  builder.setLinearityUnits(str(ampData['lin_units']))
378  self.ampDataDictampDataDict[ampData['ccd_name']]['linInfo'][ampData['name']] = \
379  {'lincoeffs': linCoeffs, 'lintype': str(ampData['lin_type']),
380  'linthresh': float(ampData['lin_thresh']), 'linmax': float(ampData['lin_max']),
381  'linunits': str(ampData['lin_units'])}
382  ampList.append(builder)
383  return ampListDict
384 
385  def makeTestRepositoryItems(self, isLsstLike=False):
386  """Make camera config and amp catalog dictionary, using default
387  detector and amp files.
388 
389  Parameters
390  ----------
391  isLsstLike : `bool`
392  If True then there is one raw image per amplifier;
393  if False then there is one raw image per detector.
394  """
395  detFile = os.path.join(self._afwTestDataDir_afwTestDataDir, "testCameraDetectors.dat")
396  detectorConfigs = self.makeDetectorConfigsmakeDetectorConfigs(detFile)
397  ampFile = os.path.join(self._afwTestDataDir_afwTestDataDir, "testCameraAmps.dat")
398  ampListDict = self.makeAmpListsmakeAmpLists(ampFile, isLsstLike=isLsstLike)
399  camConfig = CameraConfig()
400  camConfig.name = "testCamera%s"%('LSST' if isLsstLike else 'SC')
401  camConfig.detectorList = dict((i, detConfig)
402  for i, detConfig in enumerate(detectorConfigs))
403  camConfig.plateScale = self.plateScaleplateScale
404  pScaleRad = lsst.geom.arcsecToRad(self.plateScaleplateScale)
405  radialDistortCoeffs = [0.0, 1.0/pScaleRad,
406  0.0, self.radialDistortionradialDistortion/pScaleRad]
407  tConfig = afwGeom.TransformConfig()
408  tConfig.transform.name = 'inverted'
409  radialClass = afwGeom.transformRegistry['radial']
410  tConfig.transform.active.transform.retarget(radialClass)
411  tConfig.transform.active.transform.coeffs = radialDistortCoeffs
412  tmc = TransformMapConfig()
413  tmc.nativeSys = FOCAL_PLANE.getSysName()
414  tmc.transforms = {FIELD_ANGLE.getSysName(): tConfig}
415  camConfig.transformDict = tmc
416  return camConfig, ampListDict
417 
418 
419 @inTestCase
420 def compare2DFunctions(self, func1, func2, minVal=-10, maxVal=None, nVal=5):
421  """Compare two Point2D(Point2D) functions by evaluating them over a
422  range of values.
423  """
424  if maxVal is None:
425  maxVal = -minVal
426  dVal = (maxVal - minVal) / (nVal - 1)
427  for xInd in range(nVal):
428  x = minVal + (xInd * dVal)
429  for yInd in range(nVal):
430  y = minVal + (yInd * dVal)
431  fromPoint = lsst.geom.Point2D(x, y)
432  res1 = func1(fromPoint)
433  res2 = func2(fromPoint)
434  self.assertPairsAlmostEqual(res1, res2)
435 
436 
437 @inTestCase
438 def assertTransformMapsEqual(self, map1, map2, **kwds):
439  """Compare two TransformMaps.
440  """
441  self.assertEqual(list(map1), list(map2)) # compares the sets of CameraSys
442  for sysFrom in map1:
443  for sysTo in map1:
444  with self.subTest(sysFrom=sysFrom, sysTo=sysTo):
445  transform1 = map1.getTransform(sysFrom, sysTo)
446  transform2 = map2.getTransform(sysFrom, sysTo)
447  self.compare2DFunctions(transform1.applyForward, transform2.applyForward, **kwds)
448  self.compare2DFunctions(transform1.applyInverse, transform2.applyInverse, **kwds)
449 
450 
451 @inTestCase
452 def assertAmplifiersEqual(self, amp1, amp2):
453  self.assertEqual(amp1.getName(), amp2.getName())
454  self.assertEqual(amp1.getBBox(), amp2.getBBox())
455  self.assertFloatsEqual(amp1.getGain(), amp2.getGain(), ignoreNaNs=True)
456  self.assertFloatsEqual(amp1.getReadNoise(), amp2.getReadNoise(), ignoreNaNs=True)
457  self.assertFloatsEqual(amp1.getSaturation(), amp2.getSaturation(), ignoreNaNs=True)
458  self.assertEqual(amp1.getReadoutCorner(), amp2.getReadoutCorner())
459  self.assertFloatsEqual(amp1.getSuspectLevel(), amp2.getSuspectLevel(), ignoreNaNs=True)
460  self.assertEqual(amp1.getLinearityCoeffs().shape, amp2.getLinearityCoeffs().shape)
461  self.assertFloatsEqual(amp1.getLinearityCoeffs(), amp2.getLinearityCoeffs(), ignoreNaNs=True)
462  self.assertEqual(amp1.getLinearityType(), amp2.getLinearityType())
463  self.assertFloatsEqual(amp1.getLinearityThreshold(), amp2.getLinearityThreshold(), ignoreNaNs=True)
464  self.assertFloatsEqual(amp1.getLinearityMaximum(), amp2.getLinearityMaximum(), ignoreNaNs=True)
465  self.assertEqual(amp1.getLinearityUnits(), amp2.getLinearityUnits())
466  self.assertEqual(amp1.getRawBBox(), amp2.getRawBBox())
467  self.assertEqual(amp1.getRawDataBBox(), amp2.getRawDataBBox())
468  self.assertEqual(amp1.getRawFlipX(), amp2.getRawFlipX())
469  self.assertEqual(amp1.getRawFlipY(), amp2.getRawFlipY())
470  self.assertEqual(amp1.getRawHorizontalOverscanBBox(), amp2.getRawHorizontalOverscanBBox())
471  self.assertEqual(amp1.getRawVerticalOverscanBBox(), amp2.getRawVerticalOverscanBBox())
472  self.assertEqual(amp1.getRawPrescanBBox(), amp2.getRawPrescanBBox())
473 
474 
475 @inTestCase
476 def assertDetectorsEqual(self, detector1, detector2, *, compareTransforms=True, **kwds):
477  """Compare two Detectors.
478  """
479  self.assertEqual(detector1.getName(), detector2.getName())
480  self.assertEqual(detector1.getId(), detector2.getId())
481  self.assertEqual(detector1.getSerial(), detector2.getSerial())
482  self.assertEqual(detector1.getPhysicalType(), detector2.getPhysicalType())
483  self.assertEqual(detector1.getBBox(), detector2.getBBox())
484  self.assertEqual(detector1.getPixelSize(), detector2.getPixelSize())
485  orientationIn = detector1.getOrientation()
486  orientationOut = detector2.getOrientation()
487  self.assertEqual(orientationIn.getFpPosition(), orientationOut.getFpPosition())
488  self.assertEqual(orientationIn.getReferencePoint(), orientationOut.getReferencePoint())
489  self.assertEqual(orientationIn.getYaw(), orientationOut.getYaw())
490  self.assertEqual(orientationIn.getPitch(), orientationOut.getPitch())
491  self.assertEqual(orientationIn.getRoll(), orientationOut.getRoll())
492  self.assertFloatsEqual(detector1.getCrosstalk(), detector2.getCrosstalk())
493  if compareTransforms:
494  self.assertTransformMapsEqual(detector1.getTransformMap(), detector2.getTransformMap(), **kwds)
495  self.assertEqual(len(detector1.getAmplifiers()), len(detector2.getAmplifiers()))
496  for amp1, amp2 in zip(detector1.getAmplifiers(), detector2.getAmplifiers()):
497  self.assertAmplifiersEqual(amp1, amp2)
498 
499 
500 @inTestCase
501 def assertDetectorCollectionsEqual(self, collection1, collection2, **kwds):
502  """Compare two DetectorCollections.
503  """
504  self.assertCountEqual(list(collection1.getNameIter()), list(collection2.getNameIter()))
505  for k in collection1.getNameIter():
506  self.assertDetectorsEqual(collection1[k], collection2[k], **kwds)
507 
508 
509 @inTestCase
510 def assertCamerasEqual(self, camera1, camera2, **kwds):
511  """Compare two Camers.
512  """
513  self.assertDetectorCollectionsEqual(camera1, camera2, **kwds)
514  self.assertTransformMapsEqual(camera1.getTransformMap(), camera2.getTransformMap())
515  self.assertEqual(camera1.getName(), camera2.getName())
516  self.assertEqual(camera1.getPupilFactoryName(), camera2.getPupilFactoryName())
A mutable Amplifier subclass class that can be used to incrementally construct or modify Amplifiers.
Definition: Amplifier.h:305
A helper class for creating and modifying cameras.
Definition: Camera.h:208
def makeTestRepositoryItems(self, isLsstLike=False)
Definition: testUtils.py:385
def makeAmpLists(self, ampFile, isLsstLike=False)
Definition: testUtils.py:254
def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False)
Definition: testUtils.py:189
def __init__(self, name="detector 1", id=1, detType=DetectorType.SCIENCE, serial="xkcd722", bbox=None, numAmps=3, pixelSize=(0.02, 0.02), ampExtent=(5, 6), orientation=Orientation(), plateScale=20.0, radialDistortion=0.925, crosstalk=None, modFunc=None, physicalType="CCD", cameraBuilder=None)
Definition: testUtils.py:102
An integer coordinate rectangle.
Definition: Box.h:55
daf::base::PropertyList * list
Definition: fits.cc:913
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 makeCameraFromAmpLists(cameraConfig, ampListDict, pupilFactoryClass=PupilFactory)
def makePixelToTanPixel(bbox, orientation, focalPlaneToField, pixelSizeMm)
def assertAmplifiersEqual(self, amp1, amp2)
Definition: testUtils.py:452
def compare2DFunctions(self, func1, func2, minVal=-10, maxVal=None, nVal=5)
Definition: testUtils.py:420
def assertTransformMapsEqual(self, map1, map2, **kwds)
Definition: testUtils.py:438
def assertCamerasEqual(self, camera1, camera2, **kwds)
Definition: testUtils.py:510
def assertDetectorCollectionsEqual(self, collection1, collection2, **kwds)
Definition: testUtils.py:501
def assertDetectorsEqual(self, detector1, detector2, *compareTransforms=True, **kwds)
Definition: testUtils.py:476
std::shared_ptr< TransformPoint2ToPoint2 > makeRadialTransform(std::vector< double > const &coeffs)
A purely radial polynomial distortion.
constexpr double arcsecToRad(double x) noexcept
Definition: Angle.h:55