Coverage for python/lsst/pipe/tasks/fit_coadd_psf.py: 58%
51 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-25 04:32 -0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-25 04:32 -0700
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/>.
22__all__ = [
23 "CoaddPsfFitConfig", "CoaddPsfFitSubConfig", "CoaddPsfFitSubTask", "CoaddPsfFitTask",
24]
26from .fit_multiband import CatalogExposure, CatalogExposureConfig
27from lsst.obs.base import ExposureIdInfo
28import lsst.pex.config as pexConfig
29import lsst.pipe.base as pipeBase
30import lsst.pipe.base.connectionTypes as cT
32from abc import ABC, abstractmethod
33from pydantic.dataclasses import dataclass
36@dataclass(frozen=True, kw_only=True, config=CatalogExposureConfig)
37class CatalogExposurePsf(CatalogExposure):
38 def get_catalog(self):
39 return self.catalog
41 def get_psf_image(self, source):
42 bbox = source.getFootprint().getBBox()
43 center = bbox.getCenter()
44 return self.exposure.getPsf().computeKernelImage(center).array
47CoaddPsfFitBaseTemplates = {
48 "name_coadd": "deep",
49 "name_output_method": "multiprofit",
50}
53class CoaddPsfFitConnections(
54 pipeBase.PipelineTaskConnections,
55 dimensions=("tract", "patch", "skymap"),
56 defaultTemplates=CoaddPsfFitBaseTemplates,
57):
58 coadd = cT.Input(
59 doc="Coadd image to fit a PSF model to",
60 name="{name_coadd}Coadd_calexp",
61 storageClass="ExposureF",
62 dimensions=("tract", "patch", "band", "skymap"),
63 )
64 cat_meas = cT.Input(
65 doc="Deblended single-band source catalog",
66 name="{name_coadd}Coadd_meas",
67 storageClass="SourceCatalog",
68 dimensions=("tract", "patch", "band", "skymap"),
69 )
70 cat_output = cT.Output(
71 doc="Output PSF fit parameter catalog",
72 name="{name_coadd}Coadd_psfs_{name_output_method}",
73 storageClass="ArrowTable",
74 dimensions=("tract", "patch", "band", "skymap"),
75 )
78class CoaddPsfFitSubConfig(pexConfig.Config):
79 """Base config class for the CoaddPsfFitTask.
81 Implementing classes may add any necessary attributes.
82 """
85class CoaddPsfFitSubTask(pipeBase.Task, ABC):
86 """Interface for CoaddPsfFitTask subtasks to fit PSFs.
88 Parameters
89 ----------
90 **kwargs
91 Additional arguments to be passed to the `lsst.pipe.base.Task`
92 constructor.
93 """
94 ConfigClass = CoaddPsfFitSubConfig
96 def __init__(self, **kwargs):
97 super().__init__(**kwargs)
99 @abstractmethod
100 def run(
101 self, catexp: CatalogExposurePsf
102 ) -> pipeBase.Struct:
103 """Fit PSF images at locations of sources in a single exposure.
105 Parameters
106 ----------
107 catexp : `CatalogExposurePsf`
108 An exposure to fit a model PSF at the position of all
109 sources in the corresponding catalog.
111 Returns
112 -------
113 retStruct : `lsst.pipe.base.Struct`
114 A struct with a cat_output attribute containing the output
115 measurement catalog.
117 Notes
118 -----
119 Subclasses may have further requirements on the input parameters,
120 including:
121 - Passing only one catexp per band;
122 - Catalogs containing HeavyFootprints with deblended images;
123 - Fitting only a subset of the sources.
124 If any requirements are not met, the subtask should fail as soon as
125 possible.
126 """
127 raise NotImplementedError()
130class CoaddPsfFitConfig(
131 pipeBase.PipelineTaskConfig,
132 pipelineConnections=CoaddPsfFitConnections,
133):
134 """Configure a CoaddPsfFitTask, including a configurable fitting subtask.
135 """
136 fit_coadd_psf = pexConfig.ConfigurableField(
137 target=CoaddPsfFitSubTask,
138 doc="Task to fit PSF models for a single coadd",
139 )
142class CoaddPsfFitTask(pipeBase.PipelineTask):
143 """Fit a PSF model at the location of sources in a coadd.
145 This task is intended to fit only a single PSF model at the
146 centroid of all of the sources in a single coadd exposure.
147 Subtasks may choose to filter which sources they fit,
148 and may output whatever columns they desire in addition to
149 the minimum of 'id'.
150 """
151 ConfigClass = CoaddPsfFitConfig
152 _DefaultName = "CoaddPsfFit"
154 def __init__(self, initInputs, **kwargs):
155 super().__init__(initInputs=initInputs, **kwargs)
156 self.makeSubtask("fit_coadd_psf")
158 def runQuantum(self, butlerQC, inputRefs, outputRefs):
159 inputs = butlerQC.get(inputRefs)
160 id_tp = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId, "tract_patch").expId
161 dataId = inputRefs.cat_meas.dataId
162 for dataRef in (inputRefs.coadd,):
163 if dataRef.dataId != dataId:
164 raise RuntimeError(f'{dataRef=}.dataId != {inputRefs.cat_meas.dataId=}')
166 catexp = CatalogExposurePsf(
167 catalog=inputs['cat_meas'], exposure=inputs['coadd'], dataId=dataId, id_tract_patch=id_tp,
168 )
169 outputs = self.run(catexp=catexp)
170 butlerQC.put(outputs, outputRefs)
172 def run(self, catexp: CatalogExposurePsf) -> pipeBase.Struct:
173 """Fit a PSF model at the location of sources in a coadd.
175 Parameters
176 ----------
177 catexp : `typing.List [CatalogExposurePsf]`
178 A list of catalog-exposure pairs in a given band.
180 Returns
181 -------
182 retStruct : `lsst.pipe.base.Struct`
183 A struct with a cat_output attribute containing the output
184 measurement catalog.
186 Notes
187 -----
188 Subtasks may have further requirements; see `CoaddPsfFitSubTask.run`.
189 """
190 cat_output = self.fit_coadd_psf.run(catexp).output
191 retStruct = pipeBase.Struct(cat_output=cat_output)
192 return retStruct