Пример #1
0
    def setFocalPlanePos(self, pupilPos, focalPlanePos=None, beam=None):
        """Set the position of a spot on the focal plane.

        Compute new telescope, camera rotator and CBP positions
        and thus update beam info.

        Parameters
        ----------
        pupilPos : pair of `float`
            Position of the specified beam on the :ref:`telescope pupil
            <lsst.cbp.pupil_position>` (x, y mm)
        focalPlanePos : pair of `float` (optional).
            :ref:`Focal plane position <lsst.cbp.focal_plane>` of the spot
            formed by the specified beam (x, y mm); defaults to (0, 0).
        beam : `int` or `str` (optional)
            Name or index of beam; defaults to self.maskInfo.defaultBeam.
        """
        beam = self.maskInfo.asHoleName(beam)
        if focalPlanePos is None:
            focalPlanePos = Point2D(0, 0)
        else:
            focalPlanePos = Point2D(*focalPlanePos)
        focalFieldAngle = self._fieldAngleToFocalPlane.applyInverse(
            focalPlanePos)
        self.setFocalFieldAngle(pupilPos=pupilPos,
                                focalFieldAngle=focalFieldAngle,
                                beam=beam)
Пример #2
0
 def test_Box2D_repr(self):
     from lsst.geom import Box2D, Point2D, Extent2D
     print(repr(Box2D()))
     self.assertEqual(eval(repr(Box2D())), Box2D())
     self.assertEqual(
         eval(repr(Box2D(Point2D(1.0, 2.0), Extent2D(3.0, 4.0)))),
         Box2D(Point2D(1.0, 2.0), Extent2D(3.0, 4.0)))
Пример #3
0
 def setUp(self):
     # Test geometry:
     #
     # -100,99                99,99
     #     +--------------------+
     #     |AAAAAAAAAACCCCCDDDDD|    A == only in epoch A
     #     |AAAAAAAAAACCCCCDDDDD|    B == only in epoch B
     #     |AAAAAAAAAACCCCCDDDDD|    C == in both epoch A and epoch B
     #     |AAAAAAAAAACCCCCDDDDD|    D == in epoch A; in B's bbox but outside its ValidPolygon
     #     |AAAAAAAAAACCCCCDDDDD|
     #     |          BBBBBBBBBB|    All WCSs have the same CRVAL and CD.
     #     |          BBBBBBBBBB|
     #     |          BBBBBBBBBB|    Coadd has CRPIX=(0, 0)
     #     |          BBBBBBBBBB|    Epoch A has CRPIX=(0, -50)
     #     |          BBBBBBBBBB|    Epoch B has CRPIX=(-50, 0)
     #     +--------------------+
     # -100,-100             99,-100
     #
     self.rng = np.random.RandomState(50)
     crval = SpherePoint(45.0, 45.0, degrees)
     cdMatrix = makeCdMatrix(scale=5E-5 * degrees, flipX=True)
     self.wcsCoadd = makeSkyWcs(crpix=Point2D(0.0, 0.0),
                                crval=crval,
                                cdMatrix=cdMatrix)
     self.wcsA = makeSkyWcs(crpix=Point2D(0.0, -50.0),
                            crval=crval,
                            cdMatrix=cdMatrix)
     self.wcsB = makeSkyWcs(crpix=Point2D(-50.0, 0.0),
                            crval=crval,
                            cdMatrix=cdMatrix)
     self.bboxCoadd = Box2I(Point2I(-100, -100), Point2I(99, 99))
     self.bboxA = Box2I(Point2I(-100, -50), Point2I(99, 49))
     self.bboxB = Box2I(Point2I(-50, -100), Point2I(49, 99))
     self.polygonA = None
     polygonD = Polygon(Box2D(Box2I(Point2I(0, 0), Point2I(49, 99))))
     self.polygonB, = polygonD.symDifference(Polygon(Box2D(self.bboxB)))
     self.curveA = makeRandomTransmissionCurve(self.rng)
     self.curveB = makeRandomTransmissionCurve(self.rng)
     self.weightA = 0.6
     self.weightB = 0.2
     schema = ExposureTable.makeMinimalSchema()
     weightKey = schema.addField("weight",
                                 type=float,
                                 doc="relative weight of image in Coadd")
     catalog = ExposureCatalog(schema)
     recordA = catalog.addNew()
     recordA[weightKey] = self.weightA
     recordA.setWcs(self.wcsA)
     recordA.setValidPolygon(self.polygonA)
     recordA.setBBox(self.bboxA)
     recordA.setTransmissionCurve(self.curveA)
     recordB = catalog.addNew()
     recordB[weightKey] = self.weightB
     recordB.setWcs(self.wcsB)
     recordB.setValidPolygon(self.polygonB)
     recordB.setBBox(self.bboxB)
     recordB.setTransmissionCurve(self.curveB)
     self.curveCoadd = makeCoaddTransmissionCurve(self.wcsCoadd, catalog)
Пример #4
0
 def testFixed(self):
     pos1 = Point2D(1.0, 1.0)
     pos2 = Point2D(-1.0, -1.0)
     img1 = self.fixedPsf.computeKernelImage(pos1)
     # Although _doComputeKernelImage would return a different image here due
     # do the difference between pos1 and pos2, for the fixed Psf, the
     # caching mechanism intercepts instead and _doComputeKernelImage is
     # never called with position=pos2.  So img1 == img2.
     img2 = self.fixedPsf.computeKernelImage(pos2)
     self.assertFloatsEqual(img1.array, img2.array)
Пример #5
0
    def setPupilFieldAngle(self, pupilPos, pupilFieldAngle=None, beam=None):
        """Set the pupil field angle and pupil position of a beam.

        Compute new telescope, camera rotator and CBP positions
        and thus update beam info.

        This method is primarily intended for internal use,
        to support the other set methods. It is public so it can be
        unit-tested.

        Parameters
        ----------
        pupilPos : pair of `float`
            Position of the specified beam on the :ref:`telescope pupil
            <lsst.cbp.pupil_position>` (x, y mm).
        pupilFieldAngle : pair of `float` (optional)
            Pupil field angle of specified beam (x, y rad);
            defaults to (0, 0).
        beam : `int` or `str` (optional)
            Name or index of beam; defaults to self.maskInfo.defaultBeam.
        """
        beam = self.maskInfo.asHoleName(beam)
        if pupilFieldAngle is None:
            pupilFieldAngle = Point2D(0, 0)
        else:
            pupilFieldAngle = Point2D(*pupilFieldAngle)
        beamPosAtCtr = coordUtils.computeShiftedPlanePos(
            pupilPos, pupilFieldAngle, -self.config.telPupilOffset)
        beamVectorInCtrPupil = self._computeBeamVectorInCtrPupilFrame(
            beamPosAtCtr=beamPosAtCtr, pupilFieldAngle=pupilFieldAngle)
        cbpVectorInCtrPupil = self._computeCbpVectorInCtrPupilFrame(
            beamPosAtCtr=beamPosAtCtr,
            beamVectorInCtrPupil=beamVectorInCtrPupil)

        telAzAlt = coordUtils.computeAzAltFromBasePupil(
            vectorBase=self.config.cbpPosition,
            vectorPupil=cbpVectorInCtrPupil)

        beamVectorBase = coordUtils.convertVectorFromPupilToBase(
            vectorPupil=beamVectorInCtrPupil,
            pupilAzAlt=telAzAlt,
        )
        beamFieldAngleCbp = self._getBeamCbpFieldAngle(beam)
        beamUnitVectorCbpPupil = coordUtils.fieldAngleToVector(
            beamFieldAngleCbp, self.config.cbpFlipX)
        cbpAzAlt = coordUtils.computeAzAltFromBasePupil(
            vectorBase=-beamVectorBase,
            vectorPupil=beamUnitVectorCbpPupil,
        )

        self._telRot = self._computeCameraRotatorAngle(telAzAlt=telAzAlt,
                                                       cbpAzAlt=cbpAzAlt)
        self._telAzAlt = telAzAlt
        self._cbpAzAlt = cbpAzAlt
Пример #6
0
    def setUp(self):
        # Set up a Coadd with CoaddInputs tables that have blank filter
        # columns to be filled in by later test code.
        self.coadd = ExposureF(30, 90)
        # WCS is arbitrary, since it'll be the same for all images
        wcs = makeSkyWcs(crpix=Point2D(0, 0),
                         crval=SpherePoint(45.0, 45.0, degrees),
                         cdMatrix=makeCdMatrix(scale=0.17 * degrees))
        self.coadd.setWcs(wcs)
        schema = ExposureCatalog.Table.makeMinimalSchema()
        self.filterKey = schema.addField("filter", type=str, doc="", size=16)
        weightKey = schema.addField("weight", type=float, doc="")
        # First input image covers the first 2/3, second covers the last 2/3,
        # so they overlap in the middle 1/3.
        inputs = ExposureCatalog(schema)
        self.input1 = inputs.addNew()
        self.input1.setId(1)
        self.input1.setBBox(Box2I(Point2I(0, 0), Point2I(29, 59)))
        self.input1.setWcs(wcs)
        self.input1.set(weightKey, 2.0)
        self.input2 = inputs.addNew()
        self.input2.setId(2)
        self.input2.setBBox(Box2I(Point2I(0, 30), Point2I(29, 89)))
        self.input2.setWcs(wcs)
        self.input2.set(weightKey, 3.0)
        # Use the same catalog for visits and CCDs since the algorithm we're
        # testing only cares about CCDs.
        self.coadd.getInfo().setCoaddInputs(CoaddInputs(inputs, inputs))

        # Set up a catalog with centroids and a FilterFraction plugin.
        # We have one record in each region (first input only, both inputs,
        # second input only)
        schema = SourceCatalog.Table.makeMinimalSchema()
        centroidKey = Point2DKey.addFields(schema,
                                           "centroid",
                                           doc="position",
                                           unit="pixel")
        schema.getAliasMap().set("slot_Centroid", "centroid")
        self.plugin = FilterFractionPlugin(
            config=FilterFractionPlugin.ConfigClass(),
            schema=schema,
            name="subaru_FilterFraction",
            metadata=PropertyList())
        catalog = SourceCatalog(schema)
        self.record1 = catalog.addNew()
        self.record1.set(centroidKey, Point2D(14.0, 14.0))
        self.record12 = catalog.addNew()
        self.record12.set(centroidKey, Point2D(14.0, 44.0))
        self.record2 = catalog.addNew()
        self.record2.set(centroidKey, Point2D(14.0, 74.0))
Пример #7
0
 def getCrpix(self, metadata):
     """Get CRPIX from metadata using the LSST convention: 0-based in parent coordinates
     """
     return Point2D(  # zero-based, hence the - 1
         metadata.getScalar("CRPIX1") + metadata.getScalar("CRVAL1A") - 1,
         metadata.getScalar("CRPIX2") + metadata.getScalar("CRVAL2A") - 1,
     )
Пример #8
0
 def testMakeSimpleWcsMetadata(self):
     crpix = Point2D(111.1, 222.2)
     crval = SpherePoint(45.6 * degrees, 12.3 * degrees)
     scale = 1 * arcseconds
     for orientation in (0 * degrees, 21 * degrees):
         cdMatrix = makeCdMatrix(scale=scale, orientation=orientation)
         for projection in ("TAN", "STG"):
             metadata = makeSimpleWcsMetadata(crpix=crpix,
                                              crval=crval,
                                              cdMatrix=cdMatrix,
                                              projection=projection)
             desiredLength = 11 if orientation == 0 * degrees else 13
             self.assertEqual(len(metadata.names()), desiredLength)
             self.assertEqual(metadata.getScalar("RADESYS"), "ICRS")
             self.assertFalse(metadata.exists("EQUINOX"))
             self.assertEqual(metadata.getScalar("CTYPE1"),
                              "RA---" + projection)
             self.assertEqual(metadata.getScalar("CTYPE2"),
                              "DEC--" + projection)
             for i in range(2):
                 self.assertAlmostEqual(metadata.getScalar(f"CRPIX{i + 1}"),
                                        crpix[i] + 1)
                 self.assertAlmostEqual(metadata.getScalar(f"CRVAL{i + 1}"),
                                        crval[i].asDegrees())
                 self.assertEqual(metadata.getScalar(f"CUNIT{i + 1}"),
                                  "deg")
             for i in range(2):
                 for j in range(2):
                     name = f"CD{i + 1}_{j + 1}"
                     if cdMatrix[i, j] != 0:
                         self.assertAlmostEqual(metadata.getScalar(name),
                                                cdMatrix[i, j])
                     else:
                         self.assertFalse(metadata.exists(name))
Пример #9
0
    def computePsfImage(self, position=None):
        """Get a multiband PSF image

        The PSF Kernel Image is computed for each band
        and combined into a (filter, y, x) array and stored
        as `self._psfImage`.
        The result is not cached, so if the same PSF is expected
        to be used multiple times it is a good idea to store the
        result in another variable.

        Parameters
        ----------
        position: `Point2D` or `tuple`
            Coordinates to evaluate the PSF. If `position` is `None`
            then `Psf.getAveragePosition()` is used.

        Returns
        -------
        self._psfImage: array
            The multiband PSF image.
        """
        psfs = []
        # Make the coordinates into a Point2D (if necessary)
        if not isinstance(position, Point2D) and position is not None:
            position = Point2D(position[0], position[1])
        for single in self.singles:
            if position is None:
                psfs.append(single.getPsf().computeImage().array)
            else:
                psfs.append(single.getPsf().computeImage(position).array)
        psfs = np.array(psfs)
        psfImage = MultibandImage(self.filters, array=psfs)
        return psfImage
Пример #10
0
    def testFilters(self):
        wavelengths = np.linspace(3000, 12000, 1000)
        point = Point2D(1000, -500)

        def check(curve, central, w1, w2):
            # check that throughput within w1 of central is strictly greater
            # than throughput outside w2 of central
            throughput = curve.sampleAt(point, wavelengths)
            mid = np.logical_and(wavelengths > central - w1,
                                 wavelengths < central + w1)
            outer = np.logical_or(wavelengths < central - w2,
                                  wavelengths > central + w2)
            self.assertGreater(throughput[mid].min(), throughput[outer].max())

        for curves in makeTransmissionCurves.getFilterTransmission().values():
            check(curves["NB0387"], 3870, 50, 100)
            check(curves["NB0816"], 8160, 50, 100)
            check(curves["NB0921"], 9210, 50, 100)
            check(curves["HSC-G"], 4730, 500, 1500)
            check(curves["HSC-R"], 6230, 500, 1500)
            check(curves["HSC-R2"], 6230, 500, 1500)
            check(curves["HSC-I"], 7750, 500, 1500)
            check(curves["HSC-I2"], 7750, 500, 1500)
            check(curves["HSC-Z"], 8923, 500, 1500)
            check(curves["HSC-Y"], 10030, 500, 1500)
Пример #11
0
 def testMultiPlaneFitsReaders(self):
     """Run tests for MaskedImageFitsReader and ExposureFitsReader.
     """
     metadata = PropertyList()
     metadata.add("FIVE", 5)
     metadata.add("SIX", 6.0)
     wcs = makeSkyWcs(Point2D(2.5, 3.75),
                      SpherePoint(40.0 * degrees, 50.0 * degrees),
                      np.array([[1E-5, 0.0], [0.0, -1E-5]]))
     defineFilter("test_readers_filter", lambdaEff=470.0)
     calib = PhotoCalib(2.5E4)
     psf = GaussianPsf(21, 21, 8.0)
     polygon = Polygon(Box2D(self.bbox))
     apCorrMap = ApCorrMap()
     visitInfo = VisitInfo(exposureTime=5.0)
     transmissionCurve = TransmissionCurve.makeIdentity()
     coaddInputs = CoaddInputs(ExposureTable.makeMinimalSchema(),
                               ExposureTable.makeMinimalSchema())
     detector = DetectorWrapper().detector
     record = coaddInputs.ccds.addNew()
     record.setWcs(wcs)
     record.setPhotoCalib(calib)
     record.setPsf(psf)
     record.setValidPolygon(polygon)
     record.setApCorrMap(apCorrMap)
     record.setVisitInfo(visitInfo)
     record.setTransmissionCurve(transmissionCurve)
     record.setDetector(detector)
     for n, dtypeIn in enumerate(self.dtypes):
         with self.subTest(dtypeIn=dtypeIn):
             exposureIn = Exposure(self.bbox, dtype=dtypeIn)
             shape = exposureIn.image.array.shape
             exposureIn.image.array[:, :] = np.random.randint(low=1,
                                                              high=5,
                                                              size=shape)
             exposureIn.mask.array[:, :] = np.random.randint(low=1,
                                                             high=5,
                                                             size=shape)
             exposureIn.variance.array[:, :] = np.random.randint(low=1,
                                                                 high=5,
                                                                 size=shape)
             exposureIn.setMetadata(metadata)
             exposureIn.setWcs(wcs)
             exposureIn.setFilter(Filter("test_readers_filter"))
             exposureIn.setFilterLabel(
                 FilterLabel(physical="test_readers_filter"))
             exposureIn.setPhotoCalib(calib)
             exposureIn.setPsf(psf)
             exposureIn.getInfo().setValidPolygon(polygon)
             exposureIn.getInfo().setApCorrMap(apCorrMap)
             exposureIn.getInfo().setVisitInfo(visitInfo)
             exposureIn.getInfo().setTransmissionCurve(transmissionCurve)
             exposureIn.getInfo().setCoaddInputs(coaddInputs)
             exposureIn.setDetector(detector)
             with lsst.utils.tests.getTempFilePath(".fits") as fileName:
                 exposureIn.writeFits(fileName)
                 self.checkMaskedImageFitsReader(exposureIn, fileName,
                                                 self.dtypes[n:])
                 self.checkExposureFitsReader(exposureIn, fileName,
                                              self.dtypes[n:])
Пример #12
0
 def _createSkyWcsFromMetadata(self):
     # We need to know which direction the chip is "flipped" in order to
     # make a sensible WCS from the header metadata.
     wcs = makeSkyWcs(self.metadata, strip=True)
     dimensions = bboxFromMetadata(self.metadata).getDimensions()
     center = Point2D(dimensions/2.0)
     return wcs #makeFlippedWcs(wcs, self.FLIP_LR, self.FLIP_TB, center)
Пример #13
0
    def makeWcs(self, crvalOffset, affMatrix):
        """Apply a shift and affine transform to the WCS internal to this
        class.

        A new SkyWcs with these transforms applied is returns.

        Parameters
        ----------
        crval_shift : `numpy.ndarray`, (2,)
            Shift in radians to apply to the Wcs origin/crvals.
        aff_matrix : 'numpy.ndarray', (3, 3)
            Affine matrix to apply to the mapping/transform to add to the
            WCS.

        Returns
        -------
        outputWcs : `lsst.afw.geom.SkyWcs`
            Wcs with a final shift and affine transform applied.
        """
        # Create a WCS that only maps from IWC to Sky with the shifted
        # Sky origin position. This is simply the final undistorted tangent
        # plane to sky. The PIXELS to SKY map will be become our IWC to SKY
        # map and gives us our final shift position.
        iwcsToSkyWcs = makeSkyWcs(
            Point2D(0., 0.),
            self.origin.offset(crvalOffset[0] * degrees,
                               crvalOffset[1] * arcseconds),
            np.array([[1., 0.], [0., 1.]]))
        iwcToSkyMap = iwcsToSkyWcs.getFrameDict().getMapping("PIXELS", "SKY")

        # Append a simple affine Matrix transform to the current to the
        # second to last frame mapping. e.g. the one just before IWC to SKY.
        newMapping = self.lastMapBeforeSky.then(astshim.MatrixMap(affMatrix))

        # Create a new frame dict starting from the input_sky_wcs's first
        # frame. Append the correct mapping created above and our new on
        # sky location.
        outputFrameDict = astshim.FrameDict(
            self.frameDict.getFrame(self.frameMin))
        for frameIdx in self.frameIdxs:
            if frameIdx == self.mapFrom:
                outputFrameDict.addFrame(
                    self.mapFrom,
                    newMapping,
                    self.frameDict.getFrame(self.mapTo))
            elif frameIdx >= self.mapTo:
                continue
            else:
                outputFrameDict.addFrame(
                    frameIdx,
                    self.frameDict.getMapping(frameIdx, frameIdx + 1),
                    self.frameDict.getFrame(frameIdx + 1))
        # Append the final sky frame to the frame dict.
        outputFrameDict.addFrame(
            self.frameMax - 1,
            iwcToSkyMap,
            iwcsToSkyWcs.getFrameDict().getFrame("SKY"))

        return SkyWcs(outputFrameDict)
Пример #14
0
 def __init__(self, cameraGeom, name, holePos, isOnPupil, isOnFocalPlane,
              focalPlanePos, pupilPos, focalFieldAngle, pupilFieldAngle):
     self._cameraGeom = cameraGeom
     self.name = name
     self.holePos = holePos
     self.isOnPupil = isOnPupil
     self.isOnFocalPlane = isOnFocalPlane
     self.focalPlanePos = Point2D(*focalPlanePos)
     self.focalFieldAngle = Point2D(*focalFieldAngle)
     self.pupilFieldAngle = Point2D(*pupilFieldAngle)
     self.pupilPos = Point2D(*pupilPos)
     fieldAngleToFocalPlane = self._cameraGeom.getTransform(
         FIELD_ANGLE, FOCAL_PLANE)
     self.focalPlanePos = fieldAngleToFocalPlane.applyForward(
         self.focalFieldAngle)
     self._isOnDetector = None
     self._detector = None
Пример #15
0
def add_psf_image(exposure, x, y, flux):

    psfImg = exposure.getPsf().computeImage(Point2D(x, y))
    psfImg *= flux

    subim = afwImage.ImageF(exposure.getMaskedImage().getImage(),
                            psfImg.getBBox(), afwImage.LOCAL)
    subim += psfImg.convertF()
Пример #16
0
 def testOptics(self):
     wavelengths = np.linspace(4000, 10000, 100)
     point = Point2D(1000, -500)
     for curve in makeTransmissionCurves.getOpticsTransmission().values():
         if curve is None:
             continue
         throughputs = curve.sampleAt(point, wavelengths)
         self.assertTrue((throughputs > 0.70).all())
Пример #17
0
    def checkExposureFitsReader(self, exposureIn, fileName, dtypesOut):
        """Test ExposureFitsReader.

        Parameters
        ----------
        exposureIn : `Exposure`
            Object originally saved, to compare against.
        fileName : `str`
            Name of the file the reader is reading.
        dtypesOut : sequence of `numpy.dype`
            Compatible image pixel types to try to read in.
        """
        reader = ExposureFitsReader(fileName)
        self.assertIn('EXPINFO_V',
                      reader.readMetadata().toDict(),
                      "metadata is automatically versioned")
        reader.readMetadata().remove('EXPINFO_V')
        self.assertEqual(exposureIn.getMetadata().toDict(),
                         reader.readMetadata().toDict())
        self.assertWcsAlmostEqualOverBBox(exposureIn.getWcs(),
                                          reader.readWcs(),
                                          self.bbox,
                                          maxDiffPix=0,
                                          maxDiffSky=0 * degrees)
        self.assertEqual(exposureIn.getFilter(), reader.readFilter())
        self.assertEqual(exposureIn.getPhotoCalib(), reader.readPhotoCalib())
        self.assertImagesEqual(exposureIn.getPsf().computeImage(),
                               reader.readPsf().computeImage())
        self.assertEqual(exposureIn.getInfo().getValidPolygon(),
                         reader.readValidPolygon())
        self.assertCountEqual(exposureIn.getInfo().getApCorrMap(),
                              reader.readApCorrMap())
        self.assertEqual(exposureIn.getInfo().getVisitInfo().getExposureTime(),
                         reader.readVisitInfo().getExposureTime())
        point = Point2D(2.3, 3.1)
        wavelengths = np.linspace(4000, 5000, 5)
        self.assertFloatsEqual(
            exposureIn.getInfo().getTransmissionCurve().sampleAt(
                point, wavelengths),
            reader.readTransmissionCurve().sampleAt(point, wavelengths))
        # Because we persisted the same instances, we should get back the same
        # instances for *archive* components, and hence equality comparisons
        # should work even if it just amounts to C++ pointer equality.
        record = reader.readCoaddInputs().ccds[0]
        self.assertEqual(record.getWcs(), reader.readWcs())
        self.assertEqual(record.getPsf(), reader.readPsf())
        self.assertEqual(record.getValidPolygon(), reader.readValidPolygon())
        self.assertEqual(record.getApCorrMap(), reader.readApCorrMap())
        self.assertEqual(record.getPhotoCalib(), reader.readPhotoCalib())
        self.assertEqual(record.getDetector(), reader.readDetector())
        self.checkMultiPlaneReader(
            reader,
            exposureIn,
            fileName,
            dtypesOut,
            compare=lambda a, b: self.assertMaskedImagesEqual(
                a.maskedImage, b.maskedImage))
Пример #18
0
def computeHolePositions(detectorNames, detectorPositions, cameraGeom,
                         cbpFlipX, cbpFocalLength):
    """Compute hole positions for a CBP mask.

    Given the desired locations of one or more spots on each detector,
    and assuming the telescope and CBP are pointing directly at each other,
    compute hole positions for a CBP mask.

    Parameters
    ----------
    detectorNames : `iterable` of `str`, or None,
        List of detector names; if None, use all detectors in ``cameraGeom``,
        sorted by name.
    detectorPositions : `iterable` of pair of `float`
        Detector x, y positions (pixels).
        Note that the center of a 1000x1000 pixel detector is (499.5, 499.5)
    cameraGeom : `lsst.afw.cameraGeom.Camera`
        Camera geometry.
    cbpFlipX : `bool`
        Is the CBP focal plane flipped?

    Returns
    -------
    holePositions : `list` of `tuple` of pair of `float`
        CBP mask x, y hole positions mm.

    Notes
    -----
    This code assumes that all detectors have approximately the same
    dimensions and orientation. This restriction should suffice for LSST
    because the two kinds of CCDs it uses have very similar dimensions.
    However, it will not do for HSC because that has very rectangular
    CCDs and some are 90 degrees from the others.
    """
    holePositions = []
    pixelPosList = [Point2D(*val) for val in detectorPositions]
    if detectorNames is None:
        detectorNames = sorted(list(cameraGeom.getNameIter()))
    for detectorName in detectorNames:
        detector = cameraGeom[detectorName]
        pixelSys = detector.makeCameraSys(PIXELS)
        pixelsToFieldAngle = cameraGeom.getTransform(pixelSys, FIELD_ANGLE)
        telFieldAngleList = pixelsToFieldAngle.applyForward(pixelPosList)
        for telFieldAngle in telFieldAngleList:
            # Compute hole positions for when the telescope and CBP
            # are pointing at each other;
            # thus CBP pupil vector = telescope pupil vector with z negated.
            # This is simply a shortcut for setting telAzAlt and cbpAzAlt,
            # then transforming vectors from tel pupil to base,
            # then from base to CBP pupil.
            telVector = fieldAngleToVector(telFieldAngle, False)
            cbpVector = (telVector[0], telVector[1], -telVector[2])
            cbpFieldAngle = vectorToFieldAngle(cbpVector, cbpFlipX)
            holePos = tuple(
                math.tan(ang) * cbpFocalLength for ang in cbpFieldAngle)
            holePositions.append(holePos)
    return holePositions
Пример #19
0
 def testAtmosphere(self):
     wavelengths = np.linspace(4000, 12000, 100)
     point = Point2D(1000, -500)
     for curve in makeTransmissionCurves.getAtmosphereTransmission().values():
         if curve is None:
             continue
         throughputs = curve.sampleAt(point, wavelengths)
         ohAbsorption = np.logical_and(wavelengths > 11315, wavelengths < 11397)
         midR = np.logical_and(wavelengths > 6000, wavelengths < 6300)
         self.assertTrue((throughputs[ohAbsorption] < throughputs[midR]).all())
Пример #20
0
 def detectorPos(self):
     """The position of the spot on the detector, or (nan, nan)
     if `isOnDetector` is False.
     """
     if not self.isOnDetector:
         return Point2D(np.nan, np.nan)
     pixelSys = self._detector.makeCameraSys(PIXELS)
     focalPlaneToPixels = self._cameraGeom.getTransform(
         FOCAL_PLANE, pixelSys)
     return focalPlaneToPixels.applyForward(self.focalPlanePos)
Пример #21
0
 def testVectorization(self):
     x1 = np.random.randn(4, 3)
     y1 = np.random.randn(4, 3)
     x2, y2 = self.transform(x1, y1)
     self.assertEqual(x1.shape, x2.shape)
     self.assertEqual(x2.shape, y2.shape)
     for i in range(4):
         for j in range(3):
             x3, y3 = self.transform(Point2D(x1[i, j], y1[i, j]))
             self.assertAlmostEqual(x3, x2[i, j], 15)
             self.assertAlmostEqual(y3, y2[i, j], 15)
Пример #22
0
 def testSensors(self):
     wavelengths = np.linspace(4000, 12000, 100)
     point = Point2D(200, 10)
     for sensors in makeTransmissionCurves.getSensorTransmission().values():
         for i in range(112):
             curve = sensors[i]
             throughputs = curve.sampleAt(point, wavelengths)
             siliconTransparent = wavelengths > 11000
             self.assertTrue((throughputs[siliconTransparent] < 0.01).all())
             midR = np.logical_and(wavelengths > 6000, wavelengths < 6300)
             self.assertTrue((throughputs[midR] > 0.9).all())
Пример #23
0
def make_dm_wcs(galsim_wcs):
    """
    convert galsim wcs to stack wcs

    Parameters
    ----------
    galsim_wcs: galsim WCS
        Should be TAN or TAN-SIP

    Returns
    -------
    DM Stack sky wcs
    """

    if galsim_wcs.wcs_type == 'TAN':
        crpix = galsim_wcs.crpix
        # DM uses 0 offset, galsim uses FITS 1 offset
        stack_crpix = Point2D(crpix[0] - 1, crpix[1] - 1)
        cd_matrix = galsim_wcs.cd

        crval = geom.SpherePoint(
            galsim_wcs.center.ra / coord.radians,
            galsim_wcs.center.dec / coord.radians,
            geom.radians,
        )
        stack_wcs = makeSkyWcs(
            crpix=stack_crpix,
            crval=crval,
            cdMatrix=cd_matrix,
        )
    elif galsim_wcs.wcs_type == 'TAN-SIP':

        # No currently supported
        # this works with the 1-offset assumption from galsim
        #
        # this is not used if the lower bounds are 1, but the extra keywords
        # GS_{X,Y}MIN are set which we will remove below

        fake_bounds = galsim.BoundsI(1, 10, 1, 10)
        hdr = {}
        galsim_wcs.writeToFitsHeader(hdr, fake_bounds)

        del hdr["GS_XMIN"]
        del hdr["GS_YMIN"]

        metadata = PropertyList()

        for key, value in hdr.items():
            metadata.set(key, value)

        stack_wcs = makeSkyWcs(metadata)

    return stack_wcs
Пример #24
0
    def _computeCameraRotatorAngle(self, telAzAlt, cbpAzAlt):
        """Compute the internal camera rotator angle needed for a given
        telescope and CBP pointing.

        Parameters
        ----------
        telAzAlt : `lsst.geom.SpherePoint`
            Telescope internal azimuth and altitude.
        cbpAzAlt : `lsst.geom.SpherePoint`
            CBP internal azimuth and altitude.

        Returns
        -------
        rotatorAangle : `lsst.geom.Angle`
            Internal camera rotator angle.
        """
        # Compute focal plane position, ignoring self._telRot,
        # for two holes separated by x in the CBP equidistant from the center.
        # Compute the angle that would make the spots line up
        # with the x axis in the focal plane.
        ctrHolePos = Point2D(0, 0)
        holeDelta = Extent2D(*coordUtils.getFlippedPos(
            self._holeDelta, flipX=self.config.cbpFlipX))
        holePos1 = ctrHolePos - holeDelta
        holePos2 = ctrHolePos + holeDelta
        pupilUnitVector1 = self._computeTelPupilUnitVectorFromHolePos(
            holePos1, telAzAlt=telAzAlt, cbpAzAlt=cbpAzAlt)
        pupilUnitVector2 = self._computeTelPupilUnitVectorFromHolePos(
            holePos2, telAzAlt=telAzAlt, cbpAzAlt=cbpAzAlt)
        # Rotation is done in a right-handed system, regardless of telFlipX
        pupilFieldAngle1 = coordUtils.vectorToFieldAngle(pupilUnitVector1,
                                                         flipX=False)
        pupilFieldAngle2 = coordUtils.vectorToFieldAngle(pupilUnitVector2,
                                                         flipX=False)
        focalPlane1 = self._fieldAngleToFocalPlane.applyForward(
            Point2D(*pupilFieldAngle1))
        focalPlane2 = self._fieldAngleToFocalPlane.applyForward(
            Point2D(*pupilFieldAngle2))
        deltaFocalPlane = np.subtract(focalPlane2, focalPlane1)
        return -math.atan2(deltaFocalPlane[1], deltaFocalPlane[0]) * radians
Пример #25
0
 def interp_tester(self, curve_class, args, detector):
     curve = curve_class(*args)
     w = 3500*u.angstrom
     xs = np.linspace(0, 1023, 33)
     ys = np.linspace(0, 1023, 33)
     val_map = {'A': 0.9329662, 'B': 0.7463730}
     # Does interpolation work
     for x, y in zip(xs, ys):
         point = Point2D(x, y)
         if detector:
             amp = cgUtils.findAmp(detector, Point2I(point))
             value = val_map[amp.getName()]
         else:
             value = 0.9329662
         interp_val = curve.evaluate(detector, point, w)
         self.assertAlmostEqual(interp_val.value, value, places=5)
         self.assertEqual(interp_val.unit, u.percent)
     # Does interpolation work with arrays
     w_arr = np.linspace(320, 430, 70)*u.nm
     out_arr = curve.evaluate(detector, point, w_arr)
     self.assertEqual(len(w_arr), len(out_arr))
     # Does interpolation with different units work as expected
     point = Point2D(500., 500.)
     val1 = curve.evaluate(detector, point, w)
     new_w = w.to(u.mm)
     val2 = curve.evaluate(detector, point, new_w)
     self.assertEqual(val1.value, val2.value)
     # Does out of band interpolation do something reasonable
     # Default is to clamp to 0 outside the bounds.
     w = 0.*u.angstrom
     interp_val = curve.evaluate(detector, point, w)
     self.assertEqual(interp_val, 0.*u.percent)
     # interpolation with non-quantity should raise
     with self.assertRaises(ValueError):
         interp_val = curve.evaluate(detector, point, w.value)
     # Does interpolation fail with non-length unit
     with self.assertRaises(ValueError):
         w = 0.*u.Kelvin
         interp_val = curve.evaluate(detector, point, w)
Пример #26
0
def extractCtorArgs(md):
    wcs = makeSkyWcs(makePropertyListFromDict(md))
    kwds = {
        "pixelToIwc": getPixelToIntermediateWorldCoords(wcs),
        "bbox":
        Box2D(Box2I(Point2I(0, 0), Extent2I(md["NAXES1"], md["NAXES2"]))),
        "crpix":
        Point2D(md["CRPIX1"] - 1.0,
                md["CRPIX2"] - 1.0),  # -1 for LSST vs. FITS conventions
        "cd": np.array([[md["CD1_1"], md["CD1_2"]], [md["CD2_1"],
                                                     md["CD2_2"]]]),
    }
    return kwds
def _computePsfImage(self, position=None):
    """Get a multiband PSF image
    The PSF Kernel Image is computed for each band
    and combined into a (filter, y, x) array and stored
    as `self._psfImage`.
    The result is not cached, so if the same PSF is expected
    to be used multiple times it is a good idea to store the
    result in another variable.
    Note: this is a temporary fix during the deblender sprint.
    In the future this function will replace the current method
    in `afw.MultibandExposure.computePsfImage` (DM-19789).
    Parameters
    ----------
    position : `Point2D` or `tuple`
        Coordinates to evaluate the PSF. If `position` is `None`
        then `Psf.getAveragePosition()` is used.
    Returns
    -------
    self._psfImage: array
        The multiband PSF image.
    """
    psfs = []
    # Make the coordinates into a Point2D (if necessary)
    if not isinstance(position, Point2D) and position is not None:
        position = Point2D(position[0], position[1])

    for bidx, single in enumerate(self.singles):
        try:
            if position is None:
                psf = single.getPsf().computeImage()
                psfs.append(psf)
            else:
                psf = single.getPsf().computeKernelImage(position)
                psfs.append(psf)
        except InvalidParameterError:
            # This band failed to compute the PSF due to incomplete data
            # at that location. This is unlikely to be a problem for Rubin,
            # however the edges of some HSC COSMOS fields contain incomplete
            # data in some bands, so we track this error to distinguish it
            # from unknown errors.
            msg = "Failed to compute PSF at {} in band {}"
            raise IncompleteDataError(msg.format(position, self.filters[bidx]))

    left = np.min([psf.getBBox().getMinX() for psf in psfs])
    bottom = np.min([psf.getBBox().getMinY() for psf in psfs])
    right = np.max([psf.getBBox().getMaxX() for psf in psfs])
    top = np.max([psf.getBBox().getMaxY() for psf in psfs])
    bbox = Box2I(Point2I(left, bottom), Point2I(right, top))
    psfs = [afwImage.utils.projectImage(psf, bbox) for psf in psfs]
    psfImage = afwImage.MultibandImage.fromImages(self.filters, psfs)
    return psfImage
Пример #28
0
    def setUp(self):
        xy0 = Point2I(12345, 67890)  # xy0 for image
        dims = Extent2I(2345, 2345)  # Dimensions of image
        box = Box2I(xy0, dims)  # Bounding box of image
        sigma = 3.21  # PSF sigma
        buffer = 4.0  # Buffer for star centers around edge
        nSigmaForKernel = 5.0  # Number of PSF sigmas for kernel
        sky = 12345.6  # Sky level
        numStars = 100  # Number of stars
        noise = np.sqrt(sky) * np.pi * sigma**2  # Poisson noise per PSF
        faint = 1.0 * noise  # Faintest level for star fluxes
        bright = 100.0 * noise  # Brightest level for star fluxes
        starBox = Box2I(box)  # Area on image in which we can put star centers
        starBox.grow(-int(buffer * sigma))
        scale = 1.0e-5 * degrees  # Pixel scale

        np.random.seed(12345)
        stars = [(xx, yy, ff, sigma) for xx, yy, ff in zip(
            np.random.uniform(starBox.getMinX(), starBox.getMaxX(), numStars),
            np.random.uniform(starBox.getMinY(), starBox.getMaxY(), numStars),
            np.linspace(faint, bright, numStars))]
        self.exposure = plantSources(box, 2 * int(nSigmaForKernel * sigma) + 1,
                                     sky, stars, True)
        self.exposure.setWcs(
            makeSkyWcs(crpix=Point2D(0, 0),
                       crval=SpherePoint(0, 0, degrees),
                       cdMatrix=makeCdMatrix(scale=scale)))

        # Make a large area of extra background; we should be robust against it
        # Unfortunately, some tuning is required here to get something challenging but not impossible:
        # * A very large box will cause failures because the "extra" and the "normal" are reversed.
        # * A small box will not be challenging because it's simple to clip out.
        # * A large value will cause failures because it produces large edges in background-subtrction that
        #     broaden flux distributions.
        # * A small value will not be challenging because it has little effect.
        extraBox = Box2I(xy0 + Extent2I(345, 456),
                         Extent2I(1234, 1234))  # Box for extra background
        extraValue = 0.5 * noise  # Extra background value to add in
        self.exposure.image[extraBox, PARENT] += extraValue

        self.config = DynamicDetectionTask.ConfigClass()
        self.config.skyObjects.nSources = 300
        self.config.reEstimateBackground = False
        self.config.doTempWideBackground = True
        self.config.thresholdType = "pixel_stdev"

        # Relative tolerance for tweak factor
        # Not sure why this isn't smaller; maybe due to use of Poisson instead of Gaussian noise?
        self.rtol = 0.1
Пример #29
0
    def checkOrientation(self, maxDiff=50*arcseconds):
        """Check that the orientation of the focal plane is correct

        The definition of correct orientation is that two points to the
        left and right of the CBP center (by a buried delta)
        line up in the focal plane. We'll just use +/-1 for our delta
        """
        ctrHolePos = Point2D(0, 0)
        dx = -1 if self.cco.config.cbpFlipX else 1
        holePos1 = ctrHolePos[0] + dx, ctrHolePos[1]
        holePos2 = ctrHolePos[0] + dx, ctrHolePos[1]
        beamInfo1 = self.cco.getBeamInfo(beam="virtual1", holePos=holePos1)
        beamInfo2 = self.cco.getBeamInfo(beam="virtual2", holePos=holePos2)
        deltaFocalPlane = np.subtract(beamInfo2.focalPlanePos, beamInfo1.focalPlanePos)
        orientError = -math.atan2(deltaFocalPlane[1], deltaFocalPlane[0])*radians
        self.assertAnglesAlmostEqual(orientError, 0*radians)
Пример #30
0
 def __init__(self, name, defaultHole, holePositions, holeNames=None):
     self.name = name
     if holeNames is not None:
         if len(holePositions) != len(holeNames):
             raise ValueError(
                 "Number of hole positions = {} != Number of hole names = {}"
                 .format(len(holePositions), len(holeNames)))
     else:
         holeNames = [str(i) for i in range(len(holePositions))]
     self._holePosDict = OrderedDict()
     for holeName, holePos in zip(holeNames, holePositions):
         self._holePosDict[holeName] = Point2D(*holePos)
     # parse "defaultBeam" after "holes", so we can check the default
     if defaultHole is None:
         raise ValueError("defaultHole cannot be None")
     self.defaultBeam = self.asHoleName(defaultHole)