Coverage for python/lsst/pipe/tasks/fit_coadd_psf.py: 60%

52 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 11:03 +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__ = [ 

23 "CatalogExposurePsf", "CoaddPsfFitConfig", "CoaddPsfFitSubConfig", "CoaddPsfFitSubTask", 

24 "CoaddPsfFitTask", 

25] 

26 

27from .fit_multiband import CatalogExposure, CatalogExposureConfig 

28from lsst.meas.base import SkyMapIdGeneratorConfig 

29import lsst.pex.config as pexConfig 

30import lsst.pipe.base as pipeBase 

31import lsst.pipe.base.connectionTypes as cT 

32 

33from abc import ABC, abstractmethod 

34from pydantic.dataclasses import dataclass 

35 

36 

37@dataclass(frozen=True, kw_only=True, config=CatalogExposureConfig) 

38class CatalogExposurePsf(CatalogExposure): 

39 def get_catalog(self): 

40 return self.catalog 

41 

42 def get_psf_image(self, source): 

43 """Return the PSF image for this object.""" 

44 bbox = source.getFootprint().getBBox() 

45 center = bbox.getCenter() 

46 return self.exposure.getPsf().computeKernelImage(center).array 

47 

48 

49CoaddPsfFitBaseTemplates = { 

50 "name_coadd": "deep", 

51 "name_output_method": "multiprofit", 

52} 

53 

54 

55class CoaddPsfFitConnections( 

56 pipeBase.PipelineTaskConnections, 

57 dimensions=("tract", "patch", "band", "skymap"), 

58 defaultTemplates=CoaddPsfFitBaseTemplates, 

59): 

60 coadd = cT.Input( 

61 doc="Coadd image to fit a PSF model to", 

62 name="{name_coadd}Coadd_calexp", 

63 storageClass="ExposureF", 

64 dimensions=("tract", "patch", "band", "skymap"), 

65 ) 

66 cat_meas = cT.Input( 

67 doc="Deblended single-band source catalog", 

68 name="{name_coadd}Coadd_meas", 

69 storageClass="SourceCatalog", 

70 dimensions=("tract", "patch", "band", "skymap"), 

71 ) 

72 cat_output = cT.Output( 

73 doc="Output PSF fit parameter catalog", 

74 name="{name_coadd}Coadd_psfs_{name_output_method}", 

75 storageClass="ArrowTable", 

76 dimensions=("tract", "patch", "band", "skymap"), 

77 ) 

78 

79 

80class CoaddPsfFitSubConfig(pexConfig.Config): 

81 """Base config class for the CoaddPsfFitTask. 

82 

83 Implementing classes may add any necessary attributes. 

84 """ 

85 

86 

87class CoaddPsfFitSubTask(pipeBase.Task, ABC): 

88 """Interface for CoaddPsfFitTask subtasks to fit PSFs. 

89 

90 Parameters 

91 ---------- 

92 **kwargs 

93 Additional arguments to be passed to the `lsst.pipe.base.Task` 

94 constructor. 

95 """ 

96 ConfigClass = CoaddPsfFitSubConfig 

97 

98 def __init__(self, **kwargs): 

99 super().__init__(**kwargs) 

100 

101 @abstractmethod 

102 def run( 

103 self, catexp: CatalogExposurePsf 

104 ) -> pipeBase.Struct: 

105 """Fit PSF images at locations of sources in a single exposure. 

106 

107 Parameters 

108 ---------- 

109 catexp : `CatalogExposurePsf` 

110 An exposure to fit a model PSF at the position of all 

111 sources in the corresponding catalog. 

112 

113 Returns 

114 ------- 

115 retStruct : `lsst.pipe.base.Struct` 

116 A struct with a cat_output attribute containing the output 

117 measurement catalog. 

118 

119 Notes 

120 ----- 

121 Subclasses may have further requirements on the input parameters, 

122 including: 

123 - Passing only one catexp per band; 

124 - Catalogs containing HeavyFootprints with deblended images; 

125 - Fitting only a subset of the sources. 

126 If any requirements are not met, the subtask should fail as soon as 

127 possible. 

128 """ 

129 raise NotImplementedError() 

130 

131 

132class CoaddPsfFitConfig( 

133 pipeBase.PipelineTaskConfig, 

134 pipelineConnections=CoaddPsfFitConnections, 

135): 

136 """Configure a CoaddPsfFitTask, including a configurable fitting subtask. 

137 """ 

138 fit_coadd_psf = pexConfig.ConfigurableField( 

139 target=CoaddPsfFitSubTask, 

140 doc="Task to fit PSF models for a single coadd", 

141 ) 

142 idGenerator = SkyMapIdGeneratorConfig.make_field() 

143 

144 

145class CoaddPsfFitTask(pipeBase.PipelineTask): 

146 """Fit a PSF model at the location of sources in a coadd. 

147 

148 This task is intended to fit only a single PSF model at the 

149 centroid of all of the sources in a single coadd exposure. 

150 Subtasks may choose to filter which sources they fit, 

151 and may output whatever columns they desire in addition to 

152 the minimum of 'id'. 

153 """ 

154 ConfigClass = CoaddPsfFitConfig 

155 _DefaultName = "CoaddPsfFit" 

156 

157 def __init__(self, initInputs, **kwargs): 

158 super().__init__(initInputs=initInputs, **kwargs) 

159 self.makeSubtask("fit_coadd_psf") 

160 

161 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

162 inputs = butlerQC.get(inputRefs) 

163 id_tp = self.config.idGenerator.apply(butlerQC.quantum.dataId).catalog_id 

164 dataId = inputRefs.cat_meas.dataId 

165 for dataRef in (inputRefs.coadd,): 

166 if dataRef.dataId != dataId: 

167 raise RuntimeError(f'{dataRef=}.dataId != {inputRefs.cat_meas.dataId=}') 

168 

169 catexp = CatalogExposurePsf( 

170 catalog=inputs['cat_meas'], exposure=inputs['coadd'], dataId=dataId, id_tract_patch=id_tp, 

171 ) 

172 outputs = self.run(catexp=catexp) 

173 butlerQC.put(outputs, outputRefs) 

174 

175 def run(self, catexp: CatalogExposurePsf) -> pipeBase.Struct: 

176 """Fit a PSF model at the location of sources in a coadd. 

177 

178 Parameters 

179 ---------- 

180 catexp : `typing.List [CatalogExposurePsf]` 

181 A list of catalog-exposure pairs in a given band. 

182 

183 Returns 

184 ------- 

185 retStruct : `lsst.pipe.base.Struct` 

186 A struct with a cat_output attribute containing the output 

187 measurement catalog. 

188 

189 Notes 

190 ----- 

191 Subtasks may have further requirements; see `CoaddPsfFitSubTask.run`. 

192 """ 

193 cat_output = self.fit_coadd_psf.run(catexp).output 

194 retStruct = pipeBase.Struct(cat_output=cat_output) 

195 return retStruct