23 "CatalogExposure",
"MultibandFitConfig",
"MultibandFitSubConfig",
"MultibandFitSubTask",
27 from abc
import ABC, abstractmethod
28 from dataclasses
import dataclass, field
31 import lsst.daf.butler
as dafButler
35 import lsst.pipe.base.connectionTypes
as cT
36 from typing
import Dict, Iterable, List, Optional, Set
39 @dataclass(frozen=True)
41 """A class to store a catalog, exposure, and metadata for a given dataId.
43 This class is intended to store an exposure and an associated measurement
44 catalog. There are no checks to ensure this, so repurpose responsibly.
48 return self.dataId[
'band']
51 def calib(self) -> Optional[afwImage.PhotoCalib]:
52 return None if self.exposure
is None else self.exposure.getPhotoCalib()
56 dataId: dafButler.DataCoordinate
57 id_tract_patch: Optional[int] = 0
58 metadata: Dict =
field(default_factory=dict)
61 if 'band' not in self.dataId:
62 raise ValueError(f
'dataId={self.dataId} must have a band')
65 multibandFitBaseTemplates = {
66 "name_input_coadd":
"deep",
67 "name_output_coadd":
"deep",
68 "name_output_cat":
"fit",
73 pipeBase.PipelineTaskConnections,
74 dimensions=(
"tract",
"patch",
"skymap"),
75 defaultTemplates=multibandFitBaseTemplates,
78 doc=
"Reference multiband source catalog",
79 name=
"{name_input_coadd}Coadd_ref",
80 storageClass=
"SourceCatalog",
81 dimensions=(
"tract",
"patch",
"skymap"),
84 doc=
"Deblended single-band source catalogs",
85 name=
"{name_input_coadd}Coadd_meas",
86 storageClass=
"SourceCatalog",
88 dimensions=(
"tract",
"patch",
"band",
"skymap"),
91 doc=
"Exposures on which to run fits",
92 name=
"{name_input_coadd}Coadd_calexp",
93 storageClass=
"ExposureF",
95 dimensions=(
"tract",
"patch",
"band",
"skymap"),
97 cat_output = cT.Output(
98 doc=
"Measurement multi-band catalog",
99 name=
"{name_output_coadd}Coadd_{name_output_cat}",
100 storageClass=
"SourceCatalog",
101 dimensions=(
"tract",
"patch",
"skymap"),
103 cat_ref_schema = cT.InitInput(
104 doc=
"Schema associated with a ref source catalog",
105 storageClass=
"SourceCatalog",
106 name=
"{name_input_coadd}Coadd_ref_schema",
108 cat_output_schema = cT.InitOutput(
109 doc=
"Output of the schema used in deblending task",
110 name=
"{name_output_coadd}Coadd_{name_output_cat}_schema",
111 storageClass=
"SourceCatalog"
115 """Validates the `lsst.daf.butler.DatasetRef` bands against the
116 subtask's list of bands to fit and drops unnecessary bands.
121 Dictionary whose keys are an input (regular or prerequisite)
122 connection name and whose values are a tuple of the connection
123 instance and a collection of associated `DatasetRef` objects.
124 The exact type of the nested collections is unspecified; it can be
125 assumed to be multi-pass iterable and support `len` and ``in``, but
126 it should not be mutated in place. In contrast, the outer
127 dictionaries are guaranteed to be temporary copies that are true
128 `dict` instances, and hence may be modified and even returned; this
129 is especially useful for delegating to `super` (see notes below).
131 Mapping of output datasets, with the same structure as ``inputs``.
133 Label for this task in the pipeline (should be used in all
134 diagnostic messages).
135 data_id : `lsst.daf.butler.DataCoordinate`
136 Data ID for this quantum in the pipeline (should be used in all
137 diagnostic messages).
141 adjusted_inputs : `Mapping`
142 Mapping of the same form as ``inputs`` with updated containers of
143 input `DatasetRef` objects. All inputs involving the 'band'
144 dimension are adjusted to put them in consistent order and remove
146 adjusted_outputs : `Mapping`
147 Mapping of updated output datasets; always empty for this task.
151 lsst.pipe.base.NoWorkFound
152 Raised if there are not enough of the right bands to run the task
156 bands_fit, bands_read_only = self.config.get_band_sets()
157 bands_needed = bands_fit.union(bands_read_only)
160 for connection_name, (connection, dataset_refs)
in inputs.items():
162 if 'band' in connection.dimensions:
163 datasets_by_band = {dref.dataId[
'band']: dref
for dref
in dataset_refs}
164 if not bands_needed.issubset(datasets_by_band.keys()):
165 raise pipeBase.NoWorkFound(
166 f
'DatasetRefs={dataset_refs} have data with bands in the'
167 f
' set={set(datasets_by_band.keys())},'
168 f
' which is not a superset of the required bands={bands_needed} defined by'
169 f
' {self.config.__class__}.fit_multiband='
170 f
'{self.config.fit_multiband._value.__class__}\'s attributes'
171 f
' bands_fit={bands_fit} and bands_read_only()={bands_read_only}.'
172 f
' Add the required bands={bands_needed.difference(datasets_by_band.keys())}.'
176 adjusted_inputs[connection_name] = (
178 [datasets_by_band[band]
for band
in bands_needed]
182 inputs.update(adjusted_inputs)
184 return adjusted_inputs, {}
188 """Config class for the MultibandFitTask to define methods returning
189 values that depend on multiple config settings.
193 """Return the set of bands that the Task needs to read (e.g. for
194 defining priors) but not necessarily fit.
198 The set of such bands.
204 """An abstract interface for subtasks of MultibandFitTask to perform
205 multiband fitting of deblended sources.
209 schema : `lsst.afw.table.Schema`
210 The input schema for the reference source catalog, used to initialize
213 Additional arguments to be passed to the `lsst.pipe.base.Task`
216 ConfigClass = MultibandFitSubConfig
218 def __init__(self, schema: afwTable.Schema, **kwargs):
224 ) -> pipeBase.Struct:
225 """Fit sources from a reference catalog using data from multiple
226 exposures in the same patch.
230 catexps : `typing.List [CatalogExposure]`
231 A list of catalog-exposure pairs in a given band.
232 cat_ref : `lsst.afw.table.SourceCatalog`
233 A reference source catalog to fit.
237 retStruct : `lsst.pipe.base.Struct`
238 A struct with a cat_output attribute containing the output
243 Subclasses may have further requirements on the input parameters,
245 - Passing only one catexp per band;
246 - Catalogs containing HeavyFootprints with deblended images;
247 - Fitting only a subset of the sources.
248 If any requirements are not met, the subtask should fail as soon as
251 raise NotImplementedError()
256 raise NotImplementedError()
260 pipeBase.PipelineTaskConfig,
261 pipelineConnections=MultibandFitConnections,
263 """Configure a MultibandFitTask, including a configurable fitting subtask.
265 fit_multiband = pexConfig.ConfigurableField(
266 target=MultibandFitSubTask,
267 doc=
"Task to fit sources using multiple bands",
271 """Get the set of bands required by the fit_multiband subtask.
276 The set of bands that the subtask will fit.
277 bands_read_only : `set`
278 The set of bands that the subtask will only read data
279 (measurement catalog and exposure) for.
283 except AttributeError:
284 raise RuntimeError(f
'{__class__}.fit_multiband must have bands_fit attribute')
from None
285 bands_read_only = self.
fit_multibandfit_multiband.bands_read_only()
286 return set(bands_fit),
set(bands_read_only)
290 """Fit deblended exposures in multiple bands simultaneously.
292 It is generally assumed but not enforced (except optionally by the
293 configurable `fit_multiband` subtask) that there is only one exposure
294 per band, presumably a coadd.
296 ConfigClass = MultibandFitConfig
297 _DefaultName =
"multibandFit"
300 super().
__init__(initInputs=initInputs, **kwargs)
301 self.makeSubtask(
"fit_multiband", schema=initInputs[
"cat_ref_schema"].schema)
305 inputs = butlerQC.get(inputRefs)
306 id_tp = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId,
"tract_patch").expId
307 input_refs_objs = [(inputRefs.cats_meas, inputs[
'cats_meas']), (inputRefs.coadds, inputs[
'coadds'])]
309 {dRef.dataId: obj
for dRef, obj
in zip(refs, objs)}
310 for refs, objs
in input_refs_objs
312 dataIds =
set(cats).union(
set(exps))
315 catalog=cats.get(dataId), exposure=exps.get(dataId), dataId=dataId, id_tract_patch=id_tp,
317 for dataId
in dataIds
319 outputs = self.
runrun(catexps=catexps, cat_ref=inputs[
'cat_ref'])
320 butlerQC.put(outputs, outputRefs)
323 raise RuntimeError(f
'{__class__}.config.fit_multiband.run schema != initOutput schema:'
324 f
' {outputs.cat_output.schema} vs {self.cat_output_schema.schema}')
327 """Fit sources from a reference catalog using data from multiple
328 exposures in the same region (patch).
332 catexps : `typing.List [CatalogExposure]`
333 A list of catalog-exposure pairs in a given band.
334 cat_ref : `lsst.afw.table.SourceCatalog`
335 A reference source catalog to fit.
339 retStruct : `lsst.pipe.base.Struct`
340 A struct with a cat_output attribute containing the output
345 Subtasks may have further requirements; see `MultibandFitSubTask.run`.
347 cat_output = self.fit_multiband.
run(catexps, cat_ref).output
348 retStruct = pipeBase.Struct(cat_output=cat_output)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Optional[afwImage.PhotoCalib] calib(self)
def adjustQuantum(self, inputs, outputs, label, data_id)
Set bands_read_only(self)
pipeBase.Struct run(self, Iterable[CatalogExposure] catexps, afwTable.SourceCatalog cat_ref)
afwTable.Schema schema(self)
def __init__(self, afwTable.Schema schema, **kwargs)
pipeBase.Struct run(self, List[CatalogExposure] catexps, afwTable.SourceCatalog cat_ref)
def runQuantum(self, butlerQC, inputRefs, outputRefs)
def __init__(self, initInputs, **kwargs)
daf::base::PropertySet * set
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.