Coverage for python/lsst/source/injection/inject_visit.py: 43%
41 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-03 19:38 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-03 19:38 +0000
1# This file is part of source_injection.
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/>.
22from __future__ import annotations
24__all__ = ["VisitInjectConnections", "VisitInjectConfig", "VisitInjectTask"]
26from typing import cast
28from lsst.pex.config import Field
29from lsst.pipe.base.connectionTypes import Input, Output
31from .inject_base import BaseInjectConfig, BaseInjectConnections, BaseInjectTask
34class VisitInjectConnections( # type: ignore [call-arg]
35 BaseInjectConnections,
36 dimensions=("instrument", "visit", "detector"),
37):
38 """Visit-level connections for source injection tasks."""
40 visit_summary = Input(
41 doc="A visit summary table containing PSF, PhotoCalib and WCS information.",
42 name="finalVisitSummary",
43 storageClass="ExposureCatalog",
44 dimensions=("visit",),
45 deferLoad=True,
46 )
47 input_exposure = Input(
48 doc="Exposure to inject synthetic sources into.",
49 name="calexp",
50 storageClass="ExposureF",
51 dimensions=("instrument", "visit", "detector"),
52 )
53 output_exposure = Output(
54 doc="Injected Exposure.",
55 name="{injected_prefix}calexp",
56 storageClass="ExposureF",
57 dimensions=("instrument", "visit", "detector"),
58 )
59 output_catalog = Output(
60 doc="Catalog of injected sources.",
61 name="{injected_prefix}calexp_catalog",
62 storageClass="ArrowAstropy",
63 dimensions=("instrument", "visit", "detector"),
64 )
66 def __init__(self, *, config=None):
67 config = cast(VisitInjectConfig, config)
69 super().__init__(config=config)
70 if not config.external_psf and not config.external_photo_calib and not config.external_wcs:
71 self.inputs.remove("visit_summary")
74class VisitInjectConfig( # type: ignore [call-arg]
75 BaseInjectConfig,
76 pipelineConnections=VisitInjectConnections,
77):
78 """Visit-level configuration for source injection tasks."""
80 # Calibrated data options.
81 external_psf = Field[bool](
82 doc="If True, use the PSF model from a visit summary table. "
83 "If False (default), use the PSF model attached to the input exposure.",
84 dtype=bool,
85 default=False,
86 )
87 external_photo_calib = Field[bool](
88 doc="If True, use the photometric calibration from a visit summary table. "
89 "If False (default), use the photometric calibration attached to the input exposure.",
90 dtype=bool,
91 default=False,
92 )
93 external_wcs = Field[bool](
94 doc="If True, use the astrometric calibration from a visit summary table. "
95 "If False (default), use the astrometric calibration attached to the input exposure.",
96 dtype=bool,
97 default=False,
98 )
101class VisitInjectTask(BaseInjectTask):
102 """Visit-level class for injecting sources into images."""
104 _DefaultName = "visitInjectTask"
105 ConfigClass = VisitInjectConfig
107 def runQuantum(self, butler_quantum_context, input_refs, output_refs):
108 inputs = butler_quantum_context.get(input_refs)
109 detector_id = inputs["input_exposure"].getDetector().getId()
111 try:
112 visit_summary = inputs["visit_summary"].get()
113 except KeyError:
114 # Use internal PSF, PhotoCalib and WCS.
115 inputs["psf"] = inputs["input_exposure"].getPsf()
116 inputs["photo_calib"] = inputs["input_exposure"].getPhotoCalib()
117 inputs["wcs"] = inputs["input_exposure"].getWcs()
118 else:
119 # Use external PSF, PhotoCalib and WCS.
120 detector_summary = visit_summary.find(detector_id)
121 if detector_summary:
122 inputs["psf"] = detector_summary.getPsf()
123 inputs["photo_calib"] = detector_summary.getPhotoCalib()
124 inputs["wcs"] = detector_summary.getWcs()
125 else:
126 raise RuntimeError(f"No record for detector {detector_id} found in visit summary table.")
128 input_keys = ["injection_catalogs", "input_exposure", "sky_map", "psf", "photo_calib", "wcs"]
129 outputs = self.run(**{key: value for (key, value) in inputs.items() if key in input_keys})
130 butler_quantum_context.put(outputs, output_refs)