22from __future__
import annotations
24__all__ = (
"RewriteCellCoaddConnections",
"RewriteCellCoaddTask",
"RewriteCellCoaddConfig")
26from typing
import Any, ClassVar
32import lsst.pipe.base.connectionTypes
as cT
36from lsst.cell_coadds
import MultipleCellCoadd
as LegacyMultipleCellCoadd
37from lsst.images
import Box, Mask, get_legacy_deep_coadd_mask_planes
38from lsst.images.cells
import CellCoadd
39from lsst.images.fields
import field_from_legacy_background
40from lsst.images.fits
import ExtensionKey, FitsOpaqueMetadata
43from lsst.pipe.base
import (
47 PipelineTaskConnections,
54 PipelineTaskConnections,
55 dimensions={
"patch",
"band"},
56 defaultTemplates={
"legacy_prefix":
"legacy_",
"future_prefix":
""},
58 legacy_cell_coadd = cT.Input(
59 "deep_coadd_cell_predetection",
60 storageClass=
"MultipleCellCoadd",
61 dimensions={
"patch",
"band"},
62 doc=
"The pre-detection cell coadd to convert.",
64 object_background = cT.Input(
65 "deep_coadd_background",
66 storageClass=
"Background",
67 dimensions={
"patch",
"band"},
68 doc=
"The background model used to generate the object catalog, to be subtracted from the coadd.",
71 object_mask = cT.Input(
72 "{legacy_prefix}deep_coadd.mask",
74 dimensions={
"patch",
"band"},
75 doc=
"The mask to use for the coadd, including the final DETECTED mask plane.",
78 alternate_background_coadd = cT.Input(
79 "pretty_coadd_extra_bg_subtracted",
80 storageClass=
"ExposureF",
81 dimensions={
"patch",
"band"},
82 doc=
"A coadd to subtract from ``legacy_cell_coadd`` and fit a background to.",
86 doc=
"Description of the skymap's tracts and patches.",
87 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
88 storageClass=
"SkyMap",
89 dimensions=(
"skymap",),
91 future_cell_coadd = cT.Output(
92 "{future_prefix}deep_coadd",
93 storageClass=
"CellCoadd",
94 dimensions={
"patch",
"band"},
95 doc=
"The output CellCoadd.",
98 config: RewriteCellCoaddConfig
100 def __init__(self, config: RewriteCellCoaddConfig |
None):
101 super().__init__(config=config)
102 if not self.config.do_alternate_background_fit:
103 del self.alternate_background_coadd
106class RewriteCellCoaddConfig(
108 pipelineConnections=RewriteCellCoaddConnections,
110 object_background_description = Field(
111 "Description of the object background, to be stored with the image.",
114 "Background subtracted from the image when generating the Object catalog. "
115 "This intentionally oversubtracts the background to reduce blending and ensure "
116 "scattered light is subtracted. "
117 "Restoring this background does not restore all original backgrounds, "
118 "as the coadd was built from background-subtracted visit images; in most "
119 "cases this background term is actually quite small."
122 do_alternate_background_fit = Field(
123 "Whether to fit an alternate background to the difference between "
124 "the alternate_background_coadd and the main coadd.",
128 alternate_background_fit = ConfigurableField(
129 target=SubtractBackgroundTask,
131 "Configuration for fitting the alternate background. "
132 "Ignored if do_alternate_background_fit is False"
135 alternate_background_name = Field(
136 "Name for the alternate background attached to the image. "
137 "ignored if do_alternate_background_fit is False.",
141 alternate_background_description = Field(
142 "Description for the alternate background attached to the image. "
143 "Ignored if do_alternate_background_fit is False.",
146 "An alternate background optimized for visually attractive RGB images. "
147 "'Subtracting' this background will generally *add* flux, correcting "
148 "for most oversubtraction problems, but leaving in scattered light and "
149 "some instrumental backgrounds in some cases. "
150 "Because this background is fit to a difference of two wholly different "
151 "coadds that may have different input images, it can also be pulled "
152 "up or down by bright variable or transient objects."
156 def setDefaults(self) -> None:
157 super().setDefaults()
158 self.alternate_background_fit.useApprox =
False
159 self.alternate_background_fit.binSize = 64
160 self.alternate_background_fit.ignoredPixelMask = [
"NO_DATA",
"SAT"]
163class RewriteCellCoaddTask(PipelineTask):
164 ConfigClass: ClassVar[type[RewriteCellCoaddConfig]] = RewriteCellCoaddConfig
165 config: RewriteCellCoaddConfig
166 _DefaultName =
"rewriteCellCoadd"
168 def __init__(self, *args: Any, **kwargs: Any) ->
None:
169 super().__init__(*args, **kwargs)
170 self.makeSubtask(
"alternate_background_fit")
174 legacy_cell_coadd: LegacyMultipleCellCoadd,
177 object_background: BackgroundList |
None =
None,
178 object_mask: LegacyMask |
None =
None,
179 alternate_background_coadd: LegacyExposure |
None =
None,
181 tract_info = sky_map[legacy_cell_coadd.identifiers.tract]
182 patch_bbox = tract_info[legacy_cell_coadd.identifiers.patch].getOuterBBox()
183 future_cell_coadd = CellCoadd.from_legacy(
185 tract_info=tract_info,
186 bbox=Box.from_legacy(patch_bbox),
188 if getattr(future_cell_coadd,
"_opaque_metadata",
None)
is None:
189 future_cell_coadd._opaque_metadata = FitsOpaqueMetadata()
190 primary_header = future_cell_coadd._opaque_metadata.headers.setdefault(
191 ExtensionKey(), astropy.io.fits.Header()
193 primary_header.set(
"INSTRUME",
"LSSTCam")
194 primary_header.set(
"ORIGIN",
"NSF-DOE Vera C. Rubin Observatory")
195 primary_header.set(
"TELESCOP",
"Rubin:Simonyi")
196 primary_header.set(
"DATE", astropy.time.Time.now().fits,
"UTC date this HDU was written.")
197 if object_mask
is not None:
198 future_cell_coadd.mask.clear()
199 future_cell_coadd.mask.update(
200 Mask.from_legacy(object_mask, plane_map=get_legacy_deep_coadd_mask_planes())
207 "No object mask found; clearing DETECTED and using other predetection mask planes."
209 future_cell_coadd.mask.clear(
"DETECTED")
211 if self.config.do_alternate_background_fit:
212 if alternate_background_coadd
is not None:
213 alt_bg_diff = future_cell_coadd.to_legacy_exposure(copy=
True)
214 alt_bg_diff.maskedImage -= alternate_background_coadd.getMaskedImage()
216 alt_bg_result = self.alternate_background_fit.run(alt_bg_diff, stats=
False)
217 except AlgorithmError:
218 self.log.exception(
"Could not fit alternate background to diff; rewriting without it.")
220 alt_bg = field_from_legacy_background(alt_bg_result.background, unit=astropy.units.nJy)
221 future_cell_coadd.backgrounds.add(
222 self.config.alternate_background_name,
224 self.config.alternate_background_description,
227 self.log.warning(
"No alternate background coadd found; rewriting without it.")
228 elif alternate_background_coadd
is not None:
230 "alternate_background_coadd provided but config.do_alternate_background_fit=False"
232 if object_background
is not None:
238 future_cell_coadd.image.array -= object_background.getImage().array
239 future_cell_coadd.backgrounds.add(
241 field_from_legacy_background(object_background, unit=astropy.units.nJy),
242 self.config.object_background_description,
247 "No object background model found; rewriting coadd without subtracting background."
249 return Struct(future_cell_coadd=future_cell_coadd)