733 bbox=None):
734 """Calculate the shift in pixels of an exposure due to DCR.
735
736 Parameters
737 ----------
738 visitInfo : `lsst.afw.image.VisitInfo`
739 Metadata for the exposure.
740 wcs : `lsst.afw.geom.SkyWcs`
741 Coordinate system definition (wcs) for the exposure.
742 effectiveWavelength : `float`
743 The effective wavelengths of the current filter, in nanometers.
744 bandwidth : `float`
745 The bandwidth of the current filter, in nanometers.
746 dcrNumSubfilters : `int`
747 Number of sub-filters used to model chromatic effects within a band.
748 splitSubfilters : `bool`, optional
749 Calculate DCR for two evenly-spaced wavelengths in each subfilter,
750 instead of at the midpoint. Default: False
751 bbox : `lsst.afw.geom.Box2I`, optional
752 Bounding box for the region of interest for evaluating the local
753 pixelScale (defaults to the Sky Origin of the ``wcs`` provided if
754 ``bbox`` is None).
755
756 Returns
757 -------
758 dcrShift : `tuple` of two `float`
759 The 2D shift due to DCR, in pixels.
760 Uses numpy axes ordering (Y, X).
761 """
762 rotation = calculateImageParallacticAngle(visitInfo, wcs)
763 dcrShift = []
764 weight = [0.75, 0.25]
765 for wl0, wl1 in wavelengthGenerator(effectiveWavelength, bandwidth, dcrNumSubfilters):
766
767
768 diffRefractAmp0 = differentialRefraction(wavelength=wl0, wavelengthRef=effectiveWavelength,
769 elevation=visitInfo.getBoresightAzAlt().getLatitude(),
770 observatory=visitInfo.getObservatory(),
771 weather=visitInfo.getWeather())
772 diffRefractAmp1 = differentialRefraction(wavelength=wl1, wavelengthRef=effectiveWavelength,
773 elevation=visitInfo.getBoresightAzAlt().getLatitude(),
774 observatory=visitInfo.getObservatory(),
775 weather=visitInfo.getWeather())
776 if bbox is not None:
777 pixelScale = wcs.getPixelScale(bbox.getCenter()).asArcseconds()
778 else:
779 pixelScale = wcs.getPixelScale().asArcseconds()
780
781 if splitSubfilters:
782 diffRefractPix0 = diffRefractAmp0.asArcseconds()/pixelScale
783 diffRefractPix1 = diffRefractAmp1.asArcseconds()/pixelScale
784 diffRefractArr = [diffRefractPix0*weight[0] + diffRefractPix1*weight[1],
785 diffRefractPix0*weight[1] + diffRefractPix1*weight[0]]
786 shiftX = [diffRefractPix*np.sin(rotation.asRadians()) for diffRefractPix in diffRefractArr]
787 shiftY = [diffRefractPix*np.cos(rotation.asRadians()) for diffRefractPix in diffRefractArr]
788 dcrShift.append(((shiftY[0], shiftX[0]), (shiftY[1], shiftX[1])))
789 else:
790 diffRefractAmp = (diffRefractAmp0 + diffRefractAmp1)/2.
791 diffRefractPix = diffRefractAmp.asArcseconds()/pixelScale
792 shiftX = diffRefractPix*np.sin(rotation.asRadians())
793 shiftY = diffRefractPix*np.cos(rotation.asRadians())
794 dcrShift.append((shiftY, shiftX))
795 return dcrShift
796
797