21__all__ = [
"PropagateSourceFlagsConfig",
"PropagateSourceFlagsTask"]
25from smatch.matcher
import Matcher
32 """Configuration for propagating source flags to coadd objects."""
33 source_flags = pexConfig.DictField(
37 "calib_astrometry_used": 0.2,
38 "calib_photometry_used": 0.2,
39 "calib_photometry_reserved": 0.2
41 doc=(
"Source flags to propagate, with the threshold of relative occurrence "
42 "(valid range: [0-1]). Coadd object will have flag set if fraction "
43 "of input visits in which it is flagged is greater than the threshold."),
45 finalized_source_flags = pexConfig.DictField(
49 "calib_psf_candidate": 0.2,
50 "calib_psf_used": 0.2,
51 "calib_psf_reserved": 0.2
53 doc=(
"Finalized source flags to propagate, with the threshold of relative "
54 "occurrence (valid range: [0-1]). Coadd object will have flag set if "
55 "fraction of input visits in which it is flagged is greater than the "
58 x_column = pexConfig.Field(
59 doc=
"Name of column with source x position (sourceTable_visit).",
63 y_column = pexConfig.Field(
64 doc=
"Name of column with source y position (sourceTable_visit).",
68 finalized_x_column = pexConfig.Field(
69 doc=
"Name of column with source x position (finalized_src_table).",
71 default=
"slot_Centroid_x",
73 finalized_y_column = pexConfig.Field(
74 doc=
"Name of column with source y position (finalized_src_table).",
76 default=
"slot_Centroid_y",
78 match_radius = pexConfig.Field(
81 doc=
"Source matching radius (arcsec)"
90 raise ValueError(f
"The set of source_flags {source_flags} must not overlap "
91 f
"with the finalized_source_flags {finalized_source_flags}")
95 """Task to propagate source flags to coadd objects.
97 Flagged sources may come from a mix of two different types of source catalogs.
98 The source_table catalogs
from ``CalibrateTask`` contain flags
for the first
99 round of astromety/photometry/psf fits.
100 The finalized_source_table catalogs
from ``FinalizeCalibrationTask`` contain
101 flags
from the second round of psf fitting.
103 ConfigClass = PropagateSourceFlagsConfig
106 pipeBase.Task.__init__(self, **kwargs)
109 for f
in self.config.source_flags:
110 self.
schemaschema.addField(f, type=
"Flag", doc=
"Propagated from sources")
111 for f
in self.config.finalized_source_flags:
112 self.
schemaschema.addField(f, type=
"Flag", doc=
"Propagated from finalized sources")
114 def run(self, coadd_object_cat, ccd_inputs,
115 source_table_handle_dict=None, finalized_source_table_handle_dict=None):
116 """Propagate flags from single-frame sources to coadd objects.
118 Flags are only propagated if a configurable percentage of the sources
119 are matched to the coadd objects. This task will match both
"plain"
120 source flags
and "finalized" source flags.
125 Table of coadd objects.
127 Table of single-frame inputs to coadd.
128 source_table_handle_dict : `dict` [`int`: `lsst.daf.butler.DeferredDatasetHandle`]
129 Dict
for sourceTable_visit handles (key
is visit). May be
None if
130 ``config.source_flags`` has no entries.
131 finalized_source_table_handle_dict : `dict` [`int`:
132 `lsst.daf.butler.DeferredDatasetHandle`]
133 Dict
for finalized_src_table handles (key
is visit). May be
None if
134 ``config.finalized_source_flags`` has no entries.
136 if len(self.config.source_flags) == 0
and len(self.config.finalized_source_flags) == 0:
140 self.config.x_column,
141 self.config.y_column,
142 self.config.source_flags.keys()
145 self.config.finalized_x_column,
146 self.config.finalized_y_column,
147 self.config.finalized_source_flags.keys(),
153 num_overlaps = np.zeros(len(coadd_object_cat), dtype=np.int32)
154 for i, obj
in enumerate(coadd_object_cat):
155 num_overlaps[i] = len(ccd_inputs.subsetContaining(obj.getCoord(),
True))
157 visits = np.unique(ccd_inputs[
"visit"])
159 matcher = Matcher(np.rad2deg(coadd_object_cat[
"coord_ra"]),
160 np.rad2deg(coadd_object_cat[
"coord_dec"]))
162 source_flag_counts = {f: np.zeros(len(coadd_object_cat), dtype=np.int32)
163 for f
in self.config.source_flags}
164 finalized_source_flag_counts = {f: np.zeros(len(coadd_object_cat), dtype=np.int32)
165 for f
in self.config.finalized_source_flags}
167 handles_list = [source_table_handle_dict, finalized_source_table_handle_dict]
168 columns_list = [source_columns, finalized_columns]
169 counts_list = [source_flag_counts, finalized_source_flag_counts]
170 x_column_list = [self.config.x_column, self.config.finalized_x_column]
171 y_column_list = [self.config.y_column, self.config.finalized_y_column]
172 name_list = [
"sources",
"finalized_sources"]
174 for handle_dict, columns, flag_counts, x_col, y_col, name
in zip(handles_list,
180 if handle_dict
is not None and len(columns) > 0:
182 if visit
not in handle_dict:
183 self.log.
info(
"Visit %d not in input handle dict for %s", visit, name)
185 handle = handle_dict[visit]
186 df = handle.get(parameters={
"columns": columns})
189 for row
in ccd_inputs[ccd_inputs[
"visit"] == visit]:
190 detector = row[
"ccd"]
193 df_det = df[df[
"detector"] == detector]
198 ra, dec = wcs.pixelToSkyArray(df_det[x_col].values,
199 df_det[y_col].values,
206 idx, i1, i2, d = matcher.query_radius(
209 self.config.match_radius/3600.,
214 self.log.
info(
"Visit %d has no overlapping objects", visit)
219 self.log.
info(
"Visit %d has no overlapping objects", visit)
222 for flag
in flag_counts:
223 flag_values = df_det[flag].values
224 flag_counts[flag][i1] += flag_values[i2].astype(np.int32)
226 for flag
in source_flag_counts:
227 thresh = num_overlaps*self.config.source_flags[flag]
228 object_flag = (source_flag_counts[flag] > thresh)
229 coadd_object_cat[flag] = object_flag
230 self.log.
info(
"Propagated %d sources with flag %s", object_flag.sum(), flag)
232 for flag
in finalized_source_flag_counts:
233 thresh = num_overlaps*self.config.finalized_source_flags[flag]
234 object_flag = (finalized_source_flag_counts[flag] > thresh)
235 coadd_object_cat[flag] = object_flag
236 self.log.
info(
"Propagated %d finalized sources with flag %s", object_flag.sum(), flag)
238 def _get_source_table_column_names(self, x_column, y_column, flags):
239 """Get the list of source table columns from the config.
244 Name of column with x centroid.
246 Name of column
with y centroid.
247 flags : `list` [`str`]
248 List of flags to retrieve.
252 columns : [`list`] [`str`]
255 columns = ["visit",
"detector",
257 columns.extend(flags)
Custom catalog class for ExposureRecord/Table.
def __init__(self, schema, **kwargs)
def _get_source_table_column_names(self, x_column, y_column, flags)
def run(self, coadd_object_cat, ccd_inputs, source_table_handle_dict=None, finalized_source_table_handle_dict=None)
daf::base::PropertySet * set