778 bbox=None):
779 """Calculate the shift in pixels of an exposure due to DCR.
780
781 Parameters
782 ----------
783 visitInfo : `lsst.afw.image.VisitInfo`
784 Metadata for the exposure.
785 wcs : `lsst.afw.geom.SkyWcs`
786 Coordinate system definition (wcs) for the exposure.
787 effectiveWavelength : `float`
788 The effective wavelengths of the current filter, in nanometers.
789 bandwidth : `float`
790 The bandwidth of the current filter, in nanometers.
791 dcrNumSubfilters : `int`
792 Number of sub-filters used to model chromatic effects within a band.
793 splitSubfilters : `bool`, optional
794 Calculate DCR for two evenly-spaced wavelengths in each subfilter,
795 instead of at the midpoint. Default: False
796 bbox : `lsst.afw.geom.Box2I`, optional
797 Bounding box for the region of interest for evaluating the local
798 pixelScale (defaults to the Sky Origin of the ``wcs`` provided if
799 ``bbox`` is None).
800
801 Returns
802 -------
803 dcrShift : `tuple` of two `float`
804 The 2D shift due to DCR, in pixels.
805 Uses numpy axes ordering (Y, X).
806 """
807 rotation = calculateImageParallacticAngle(visitInfo, wcs)
808 dcrShift = []
809 weight = [0.75, 0.25]
810 for wl0, wl1 in wavelengthGenerator(effectiveWavelength, bandwidth, dcrNumSubfilters):
811
812
813 diffRefractAmp0 = differentialRefraction(wavelength=wl0, wavelengthRef=effectiveWavelength,
814 elevation=visitInfo.getBoresightAzAlt().getLatitude(),
815 observatory=visitInfo.getObservatory(),
816 weather=visitInfo.getWeather())
817 diffRefractAmp1 = differentialRefraction(wavelength=wl1, wavelengthRef=effectiveWavelength,
818 elevation=visitInfo.getBoresightAzAlt().getLatitude(),
819 observatory=visitInfo.getObservatory(),
820 weather=visitInfo.getWeather())
821 if bbox is not None:
822 pixelScale = wcs.getPixelScale(bbox.getCenter()).asArcseconds()
823 else:
824 pixelScale = wcs.getPixelScale().asArcseconds()
825
826 if splitSubfilters:
827 diffRefractPix0 = diffRefractAmp0.asArcseconds()/pixelScale
828 diffRefractPix1 = diffRefractAmp1.asArcseconds()/pixelScale
829 diffRefractArr = [diffRefractPix0*weight[0] + diffRefractPix1*weight[1],
830 diffRefractPix0*weight[1] + diffRefractPix1*weight[0]]
831 shiftX = [diffRefractPix*np.sin(rotation.asRadians()) for diffRefractPix in diffRefractArr]
832 shiftY = [diffRefractPix*np.cos(rotation.asRadians()) for diffRefractPix in diffRefractArr]
833 dcrShift.append(((shiftY[0], shiftX[0]), (shiftY[1], shiftX[1])))
834 else:
835 diffRefractAmp = (diffRefractAmp0 + diffRefractAmp1)/2.
836 diffRefractPix = diffRefractAmp.asArcseconds()/pixelScale
837 shiftX = diffRefractPix*np.sin(rotation.asRadians())
838 shiftY = diffRefractPix*np.cos(rotation.asRadians())
839 dcrShift.append((shiftY, shiftX))
840 return dcrShift
841
842