def testGainAndReadnoise(self): isrTask = IsrTask() detector = DetectorWrapper().detector raw = afwImage.ExposureF(detector.getBBox()) level = 10 readNoise = 1.5 raw.image.set(level) amp = detector[0] for gain in [-1, 0, 0.1, 1, np.NaN]: # Because amplifiers are immutable, we can't change the gain or # read noise in-place. Instead, we clone, and update the clone. testAmp = Amplifier.Builder() testAmp.assign(amp) testAmp.setReadNoise(readNoise) testAmp.setGain(gain) testAmp.finish() isrTask.updateVariance(raw, testAmp) if gain <= 0: # behave the same way as amp.setGain gain = 1 if math.isnan(gain): gain = 1 self.assertEqual(raw.variance[0, 0, afwImage.LOCAL], level / gain + readNoise**2)
def setUp(self): self.data = SimpleNamespace( name="Amp1", gain=1.2345, saturation=65535, readNoise=-0.523, linearityCoeffs=np.array([1.1, 2.2, 3.3, 4.4], dtype=float), linearityType="Polynomial", bbox=lsst.geom.Box2I(lsst.geom.Point2I(3, -2), lsst.geom.Extent2I(231, 320)), rawFlipX=True, rawFlipY=False, readoutCorner=ReadoutCorner.UL, rawBBox=lsst.geom.Box2I(lsst.geom.Point2I(-25, 2), lsst.geom.Extent2I(550, 629)), rawXYOffset=lsst.geom.Extent2I(-97, 253), rawDataBBox=lsst.geom.Box2I(lsst.geom.Point2I(-2, 29), lsst.geom.Extent2I(123, 307)), rawHorizontalOverscanBBox=lsst.geom.Box2I( lsst.geom.Point2I(150, 29), lsst.geom.Extent2I(25, 307), ), rawVerticalOverscanBBox=lsst.geom.Box2I( lsst.geom.Point2I(-2, 201), lsst.geom.Extent2I(123, 6), ), rawPrescanBBox=lsst.geom.Box2I( lsst.geom.Point2I(-20, 2), lsst.geom.Extent2I(5, 307), ), ) builder = Amplifier.Builder() builder.setBBox(self.data.bbox) builder.setName(self.data.name) builder.setGain(self.data.gain) builder.setSaturation(self.data.saturation) builder.setReadNoise(self.data.readNoise) builder.setReadoutCorner(self.data.readoutCorner) builder.setLinearityCoeffs(self.data.linearityCoeffs) builder.setLinearityType(self.data.linearityType) builder.setRawFlipX(self.data.rawFlipX) builder.setRawFlipY(self.data.rawFlipY) builder.setRawBBox(self.data.rawBBox) builder.setRawXYOffset(self.data.rawXYOffset) builder.setRawDataBBox(self.data.rawDataBBox) builder.setRawHorizontalOverscanBBox( self.data.rawHorizontalOverscanBBox) builder.setRawVerticalOverscanBBox(self.data.rawVerticalOverscanBBox) builder.setRawPrescanBBox(self.data.rawPrescanBBox) self.amplifier = builder.finish()
def _makeAmpBuilder(self): """Construct a trivial amplifier builder. The CBP code does not care about the details of the amplifier, so this builder is as simple as possible: one amplifier that covers the whole CCD, with no overscan, and semi-plausible valus for everything else. Returns ------- ampBuilder : `lsst.afw.cameraGeom.Amplifier.Builder` Amplifier builder. """ ampExtent = lsst.geom.Extent2I(self.detectorWidthPix, self.detectorHeightPix) ampBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), ampExtent) ampBuilder = Amplifier.Builder() ampBuilder.setName("TestAmp") ampBuilder.setBBox(ampBBox) ampBuilder.setGain(1.8) ampBuilder.setReadNoise(3.9) ampBuilder.setReadoutCorner(ReadoutCorner.LL) return ampBuilder
def testBasics(self): name = "Amp1" gain = 1.2345 saturation = 65535 readNoise = -0.523 linearityCoeffs = np.array([1.1, 2.2, 3.3, 4.4], dtype=float) linearityType = "Polynomial" bbox = lsst.geom.Box2I(lsst.geom.Point2I(3, -2), lsst.geom.Extent2I(231, 320)) rawFlipX = True rawFlipY = False readoutCorner = ReadoutCorner.UL rawBBox = lsst.geom.Box2I(lsst.geom.Point2I(-25, 2), lsst.geom.Extent2I(550, 629)) rawXYOffset = lsst.geom.Extent2I(-97, 253) rawDataBBox = lsst.geom.Box2I(lsst.geom.Point2I(-2, 29), lsst.geom.Extent2I(123, 307)) rawHorizontalOverscanBBox = lsst.geom.Box2I( lsst.geom.Point2I(150, 29), lsst.geom.Extent2I(25, 307)) rawVerticalOverscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(-2, 201), lsst.geom.Extent2I(123, 6)) rawPrescanBBox = lsst.geom.Box2I(lsst.geom.Point2I(-20, 2), lsst.geom.Extent2I(5, 307)) builder = Amplifier.Builder() builder.setBBox(bbox) builder.setName(name) builder.setGain(gain) builder.setSaturation(saturation) builder.setReadNoise(readNoise) builder.setReadoutCorner(readoutCorner) builder.setLinearityCoeffs(linearityCoeffs) builder.setLinearityType(linearityType) builder.setRawFlipX(rawFlipX) builder.setRawFlipY(rawFlipY) builder.setRawBBox(rawBBox) builder.setRawXYOffset(rawXYOffset) builder.setRawDataBBox(rawDataBBox) builder.setRawHorizontalOverscanBBox(rawHorizontalOverscanBBox) builder.setRawVerticalOverscanBBox(rawVerticalOverscanBBox) builder.setRawPrescanBBox(rawPrescanBBox) amplifier = builder.finish() self.assertEqual(name, amplifier.getName()) self.assertEqual(gain, amplifier.getGain()) self.assertEqual(saturation, amplifier.getSaturation()) self.assertEqual(readNoise, amplifier.getReadNoise()) self.assertEqual(readoutCorner, amplifier.getReadoutCorner()) self.assertEqual(list(linearityCoeffs), list(amplifier.getLinearityCoeffs())) self.assertEqual(linearityType, amplifier.getLinearityType()) self.assertEqual(bbox, amplifier.getBBox()) self.assertEqual(rawBBox, amplifier.getRawBBox()) self.assertEqual(rawDataBBox, amplifier.getRawDataBBox()) self.assertEqual(rawHorizontalOverscanBBox, amplifier.getRawHorizontalOverscanBBox()) self.assertEqual(rawVerticalOverscanBBox, amplifier.getRawVerticalOverscanBBox()) self.assertEqual(rawPrescanBBox, amplifier.getRawPrescanBBox()) self.assertEqual(rawHorizontalOverscanBBox, amplifier.getRawSerialOverscanBBox()) self.assertEqual(rawVerticalOverscanBBox, amplifier.getRawParallelOverscanBBox()) self.assertEqual(rawPrescanBBox, amplifier.getRawSerialPrescanBBox()) self.assertEqual(rawPrescanBBox, amplifier.getRawHorizontalPrescanBBox()) self.assertEqual(rawFlipX, amplifier.getRawFlipX()) self.assertEqual(rawFlipY, amplifier.getRawFlipY()) self.assertEqual(rawXYOffset, amplifier.getRawXYOffset()) # Test get/set methods for overscan/prescan alias names. # Change slightly, don't care about contiguity, make smaller. newHorizontalOverscanBBox = lsst.geom.Box2I( lsst.geom.Point2I(150, 29), lsst.geom.Extent2I(25, 306)) newVerticalOverscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(-2, 201), lsst.geom.Extent2I(123, 5)) newPrescanBBox = lsst.geom.Box2I(lsst.geom.Point2I(-20, 2), lsst.geom.Extent2I(4, 306)) builder.setRawSerialOverscanBBox(newHorizontalOverscanBBox) builder.setRawParallelOverscanBBox(newVerticalOverscanBBox) builder.setRawSerialPrescanBBox(newPrescanBBox) amplifier = builder.finish() self.assertEqual(newHorizontalOverscanBBox, amplifier.getRawHorizontalOverscanBBox()) self.assertEqual(newVerticalOverscanBBox, amplifier.getRawVerticalOverscanBBox()) self.assertEqual(newPrescanBBox, amplifier.getRawPrescanBBox()) newPrescanBBox2 = lsst.geom.Box2I(lsst.geom.Point2I(-20, 2), lsst.geom.Extent2I(5, 306)) builder.setRawHorizontalPrescanBBox(newPrescanBBox2) amplifier = builder.finish() self.assertEqual(newPrescanBBox2, amplifier.getRawPrescanBBox())
def makeAmplifierList(ccd): """Construct a list of AmplifierBuilder objects """ # Much of this will need to be filled in when we know it. assert len(ccd) > 0 amp = list(ccd['amplifiers'].values())[0] rawBBox = makeBBoxFromList(amp['rawBBox']) # total in file xRawExtent, yRawExtent = rawBBox.getDimensions() readCorners = {"LL": ReadoutCorner.LL, "LR": ReadoutCorner.LR, "UL": ReadoutCorner.UL, "UR": ReadoutCorner.UR} amplifierList = [] for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']): amplifier = Amplifier.Builder() amplifier.setName(name) ix, iy = amp['ixy'] perAmpData = amp['perAmpData'] if perAmpData: x0, y0 = 0, 0 # origin of data within each amp image else: x0, y0 = ix*xRawExtent, iy*yRawExtent rawDataBBox = makeBBoxFromList(amp['rawDataBBox']) # Photosensitive area xDataExtent, yDataExtent = rawDataBBox.getDimensions() amplifier.setBBox(geom.BoxI( geom.PointI(ix*xDataExtent, iy*yDataExtent), rawDataBBox.getDimensions())) rawBBox = makeBBoxFromList(amp['rawBBox']) rawBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawBBox(rawBBox) rawDataBBox = makeBBoxFromList(amp['rawDataBBox']) rawDataBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawDataBBox(rawDataBBox) rawSerialOverscanBBox = makeBBoxFromList(amp['rawSerialOverscanBBox']) rawSerialOverscanBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawHorizontalOverscanBBox(rawSerialOverscanBBox) rawParallelOverscanBBox = makeBBoxFromList(amp['rawParallelOverscanBBox']) rawParallelOverscanBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawVerticalOverscanBBox(rawParallelOverscanBBox) rawSerialPrescanBBox = makeBBoxFromList(amp['rawSerialPrescanBBox']) rawSerialPrescanBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawPrescanBBox(rawSerialPrescanBBox) if perAmpData: amplifier.setRawXYOffset(geom.Extent2I(ix*xRawExtent, iy*yRawExtent)) else: amplifier.setRawXYOffset(geom.Extent2I(0, 0)) amplifier.setReadoutCorner(readCorners[amp['readCorner']]) amplifier.setGain(amp['gain']) amplifier.setReadNoise(amp['readNoise']) amplifier.setSaturation(amp['saturation']) amplifier.setSuspectLevel(amp.get('suspect', np.nan)) # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD) flipX, flipY = amp.get("flipXY") amplifier.setRawFlipX(flipX) amplifier.setRawFlipY(flipY) # linearity placeholder stuff amplifier.setLinearityCoeffs([float(val) for val in amp['linearityCoeffs']]) amplifier.setLinearityType(amp['linearityType']) amplifier.setLinearityThreshold(float(amp['linearityThreshold'])) amplifier.setLinearityMaximum(float(amp['linearityMax'])) amplifier.setLinearityUnits("DN") amplifierList.append(amplifier) return amplifierList
def makeAmpTables(segmentsFile, gainFile): """ Read the segments file from a PhoSim release and produce the appropriate AmpInfo @param segmentsFile (str) full path to the segmentation file. @param gainFile (str) full path to the gain/saturation file. @return (dict) per amp dictionary of ampCatalogs """ gainDict = {} with open(gainFile) as fh: for l in fh: els = l.rstrip().split() gainDict[els[0]] = { 'gain': float(els[1]), 'saturation': int(els[2]) } returnDict = {} # TODO currently there is no linearity provided, but we should identify # how to get this information. linearityCoeffs = (0., 1., 0., 0.) linearityType = NullLinearityType readoutMap = { 'LL': ReadoutCorner.LL, 'LR': ReadoutCorner.LR, 'UR': ReadoutCorner.UR, 'UL': ReadoutCorner.UL } ampCatalog = [] detectorName = [ ] # set to a value that is an invalid dict key, to catch bugs correctY0 = False with open(segmentsFile) as fh: for l in fh: if l.startswith("#"): continue els = l.rstrip().split() if len(els) == 4: if len(ampCatalog) != 0: returnDict[detectorName] = ampCatalog detectorName = expandDetectorName(els[0]) numy = int(els[2]) ampCatalog = [] if len(els[0].split('_')) == 3: # wavefront sensor correctY0 = True else: correctY0 = False continue amplifier = Amplifier.Builder() name = els[0].split("_")[-1] name = '%s,%s' % (name[1], name[2]) # Because of the camera coordinate system, we choose an # image coordinate system that requires a -90 rotation to get # the correct pixel positions from the # phosim segments file y0 = numy - 1 - int(els[2]) y1 = numy - 1 - int(els[1]) # Another quirk of the phosim file is that one of the wavefront sensor # chips has an offset of 2000 pix in y. It's always the 'C1' chip. if correctY0: if y0 > 0: y1 -= y0 y0 = 0 x0 = int(els[3]) x1 = int(els[4]) try: saturation = gainDict[els[0]]['saturation'] gain = gainDict[els[0]]['gain'] except KeyError: # Set default if no gain exists saturation = 65535 gain = float(els[7]) readnoise = float(els[11]) bbox = geom.Box2I(geom.Point2I(x0, y0), geom.Point2I(x1, y1)) if int(els[5]) == -1: flipx = False else: flipx = True if int(els[6]) == 1: flipy = False else: flipy = True # Since the amps are stored in amp coordinates, the readout is the same # for all amps readCorner = readoutMap['LL'] ndatax = x1 - x0 + 1 ndatay = y1 - y0 + 1 # Because in versions v3.3.2 and earlier there was no overscan, we use the extended register # as the overscan region prescan = 1 hoverscan = 0 extended = 4 voverscan = 0 rawBBox = geom.Box2I( geom.Point2I(0, 0), geom.Extent2I(extended + ndatax + hoverscan, prescan + ndatay + voverscan)) rawDataBBox = geom.Box2I(geom.Point2I(extended, prescan), geom.Extent2I(ndatax, ndatay)) rawHorizontalOverscanBBox = geom.Box2I( geom.Point2I(0, prescan), geom.Extent2I(extended, ndatay)) rawVerticalOverscanBBox = geom.Box2I( geom.Point2I(extended, prescan + ndatay), geom.Extent2I(ndatax, voverscan)) rawPrescanBBox = geom.Box2I(geom.Point2I(extended, 0), geom.Extent2I(ndatax, prescan)) extraRawX = extended + hoverscan extraRawY = prescan + voverscan rawx0 = x0 + extraRawX * (x0 // ndatax) rawy0 = y0 + extraRawY * (y0 // ndatay) # Set the elements of the amplifier for this amp amplifier.setBBox(bbox) amplifier.setName(name) amplifier.setReadoutCorner(readCorner) amplifier.setGain(gain) amplifier.setSaturation(saturation) amplifier.setSuspectLevel(float("nan")) amplifier.setReadNoise(readnoise) amplifier.setLinearityCoeffs(linearityCoeffs) amplifier.setLinearityType(linearityType) # amplifier.setHasRawInfo(True) amplifier.setRawFlipX(flipx) amplifier.setRawFlipY(flipy) amplifier.setRawBBox(rawBBox) amplifier.setRawXYOffset(geom.Extent2I(rawx0, rawy0)) amplifier.setRawDataBBox(rawDataBBox) amplifier.setRawHorizontalOverscanBBox(rawHorizontalOverscanBBox) amplifier.setRawVerticalOverscanBBox(rawVerticalOverscanBBox) amplifier.setRawPrescanBBox(rawPrescanBBox) ampCatalog.append(amplifier) returnDict[detectorName] = ampCatalog return returnDict