Coverage for python/lsst/pipe/tasks/warpAndPsfMatch.py: 31%

38 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-23 10:48 +0000

1# This file is part of pipe_tasks. 

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__ = ["WarpAndPsfMatchTask"] 

23 

24import lsst.pex.config as pexConfig 

25import lsst.afw.math as afwMath 

26import lsst.geom as geom 

27import lsst.afw.geom as afwGeom 

28import lsst.pipe.base as pipeBase 

29from lsst.ip.diffim import ModelPsfMatchTask 

30from lsst.meas.algorithms import WarpedPsf 

31 

32 

33class WarpAndPsfMatchConfig(pexConfig.Config): 

34 """Config for WarpAndPsfMatchTask 

35 """ 

36 psfMatch = pexConfig.ConfigurableField( 

37 target=ModelPsfMatchTask, 

38 doc="PSF matching model to model task", 

39 ) 

40 warp = pexConfig.ConfigField( 

41 dtype=afwMath.Warper.ConfigClass, 

42 doc="warper configuration", 

43 ) 

44 

45 

46class WarpAndPsfMatchTask(pipeBase.Task): 

47 """A task to warp and PSF-match an exposure 

48 """ 

49 ConfigClass = WarpAndPsfMatchConfig 

50 

51 def __init__(self, *args, **kwargs): 

52 pipeBase.Task.__init__(self, *args, **kwargs) 

53 self.makeSubtask("psfMatch") 

54 self.warper = afwMath.Warper.fromConfig(self.config.warp) 

55 

56 def run(self, exposure, wcs, modelPsf=None, maxBBox=None, destBBox=None, 

57 makeDirect=True, makePsfMatched=False): 

58 """Warp and optionally PSF-match exposure 

59 

60 Parameters 

61 ---------- 

62 exposure : :cpp:class: `lsst::afw::image::Exposure` 

63 Exposure to preprocess. 

64 wcs : :cpp:class:`lsst::afw::image::Wcs` 

65 Desired WCS of temporary images. 

66 modelPsf : :cpp:class: `lsst::meas::algorithms::KernelPsf` or None 

67 Target PSF to which to match. 

68 maxBBox : :cpp:class:`lsst::afw::geom::Box2I` or None 

69 Maximum allowed parent bbox of warped exposure. 

70 If None then the warped exposure will be just big enough to contain all warped pixels; 

71 if provided then the warped exposure may be smaller, and so missing some warped pixels; 

72 ignored if destBBox is not None. 

73 destBBox: :cpp:class: `lsst::afw::geom::Box2I` or None 

74 Exact parent bbox of warped exposure. 

75 If None then maxBBox is used to determine the bbox, otherwise maxBBox is ignored. 

76 makeDirect : bool 

77 Return an exposure that has been only warped? 

78 makePsfMatched : bool 

79 Return an exposure that has been warped and PSF-matched? 

80 

81 Returns 

82 ------- 

83 An lsst.pipe.base.Struct with the following fields: 

84 

85 direct : :cpp:class:`lsst::afw::image::Exposure` 

86 warped exposure 

87 psfMatched : :cpp:class: `lsst::afw::image::Exposure` 

88 warped and psf-Matched temporary exposure 

89 """ 

90 if makePsfMatched and modelPsf is None: 

91 raise RuntimeError("makePsfMatched=True, but no model PSF was provided") 

92 

93 if not makePsfMatched and not makeDirect: 

94 self.log.warning("Neither makeDirect nor makePsfMatched requested") 

95 

96 # Warp PSF before overwriting exposure 

97 xyTransform = afwGeom.makeWcsPairTransform(exposure.getWcs(), wcs) 

98 psfWarped = WarpedPsf(exposure.getPsf(), xyTransform) 

99 

100 if makePsfMatched and maxBBox is not None: 

101 # grow warped region to provide sufficient area for PSF-matching 

102 pixToGrow = 2 * max(self.psfMatch.kConfig.sizeCellX, 

103 self.psfMatch.kConfig.sizeCellY) 

104 # replace with copy 

105 maxBBox = geom.Box2I(maxBBox) 

106 maxBBox.grow(pixToGrow) 

107 

108 with self.timer("warp"): 

109 exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox) 

110 exposure.setPsf(psfWarped) 

111 

112 if makePsfMatched: 

113 try: 

114 exposurePsfMatched = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure 

115 except Exception as e: 

116 exposurePsfMatched = None 

117 self.log.info("Cannot PSF-Match: %s", e) 

118 

119 return pipeBase.Struct( 

120 direct=exposure if makeDirect else None, 

121 psfMatched=exposurePsfMatched if makePsfMatched else None 

122 )