116 source_table_handle_dict=None, finalized_source_table_handle_dict=None):
117 """Propagate flags from single-frame sources to coadd objects.
118
119 Flags are only propagated if a configurable percentage of the sources
120 are matched to the coadd objects. This task will match both "plain"
121 source flags and "finalized" source flags.
122
123 Parameters
124 ----------
125 coadd_object_cat : `lsst.afw.table.SourceCatalog`
126 Table of coadd objects.
127 ccd_inputs : `lsst.afw.table.ExposureCatalog`
128 Table of single-frame inputs to coadd.
129 source_table_handle_dict : `dict` [`int`: `lsst.daf.butler.DeferredDatasetHandle`]
130 Dict for sourceTable_visit handles (key is visit). May be None if
131 ``config.source_flags`` has no entries.
132 finalized_source_table_handle_dict : `dict` [`int`:
133 `lsst.daf.butler.DeferredDatasetHandle`]
134 Dict for finalized_src_table handles (key is visit). May be None if
135 ``config.finalized_source_flags`` has no entries.
136 """
137 if len(self.config.source_flags) == 0 and len(self.config.finalized_source_flags) == 0:
138 return
139
140 source_columns = self._get_source_table_column_names(
141 self.config.x_column,
142 self.config.y_column,
143 self.config.source_flags.keys()
144 )
145 finalized_columns = self._get_source_table_column_names(
146 self.config.finalized_x_column,
147 self.config.finalized_y_column,
148 self.config.finalized_source_flags.keys(),
149 )
150
151
152
153
154 num_overlaps = np.zeros(len(coadd_object_cat), dtype=np.int32)
155 for i, obj in enumerate(coadd_object_cat):
156 num_overlaps[i] = len(ccd_inputs.subsetContaining(obj.getCoord(), True))
157
158 visits = np.unique(ccd_inputs["visit"])
159
160 matcher = Matcher(np.rad2deg(coadd_object_cat["coord_ra"]),
161 np.rad2deg(coadd_object_cat["coord_dec"]))
162
163 source_flag_counts = {f: np.zeros(len(coadd_object_cat), dtype=np.int32)
164 for f in self.config.source_flags}
165 finalized_source_flag_counts = {f: np.zeros(len(coadd_object_cat), dtype=np.int32)
166 for f in self.config.finalized_source_flags}
167
168 handles_list = [source_table_handle_dict, finalized_source_table_handle_dict]
169 columns_list = [source_columns, finalized_columns]
170 counts_list = [source_flag_counts, finalized_source_flag_counts]
171 x_column_list = [self.config.x_column, self.config.finalized_x_column]
172 y_column_list = [self.config.y_column, self.config.finalized_y_column]
173 name_list = ["sources", "finalized_sources"]
174
175 for handle_dict, columns, flag_counts, x_col, y_col, name in zip(handles_list,
176 columns_list,
177 counts_list,
178 x_column_list,
179 y_column_list,
180 name_list):
181 if handle_dict is not None and len(columns) > 0:
182 for visit in visits:
183 if visit not in handle_dict:
184 self.log.info("Visit %d not in input handle dict for %s", visit, name)
185 continue
186 handle = handle_dict[visit]
187 df = handle.get(parameters={"columns": columns})
188
189
190 for row in ccd_inputs[ccd_inputs["visit"] == visit]:
191 detector = row["ccd"]
192 wcs = row.getWcs()
193 if wcs is None:
194 self.log.info("No WCS for visit %d detector %d, so can't match sources to "
195 "propagate flags. Skipping...", visit, detector)
196 continue
197
198 df_det = df[df["detector"] == detector]
199
200 if len(df_det) == 0:
201 continue
202
203 ra, dec = wcs.pixelToSkyArray(df_det[x_col].values,
204 df_det[y_col].values,
205 degrees=True)
206
207 try:
208
209
210
211 idx, i1, i2, d = matcher.query_radius(
212 ra,
213 dec,
214 self.config.match_radius/3600.,
215 return_indices=True
216 )
217 except IndexError:
218
219 self.log.info("Visit %d has no overlapping objects", visit)
220 continue
221
222 if len(i1) == 0:
223
224 self.log.info("Visit %d has no overlapping objects", visit)
225 continue
226
227 for flag in flag_counts:
228 flag_values = df_det[flag].values
229 flag_counts[flag][i1] += flag_values[i2].astype(np.int32)
230
231 for flag in source_flag_counts:
232 thresh = num_overlaps*self.config.source_flags[flag]
233 object_flag = (source_flag_counts[flag] > thresh)
234 coadd_object_cat[flag] = object_flag
235 self.log.info("Propagated %d sources with flag %s", object_flag.sum(), flag)
236
237 for flag in finalized_source_flag_counts:
238 thresh = num_overlaps*self.config.finalized_source_flags[flag]
239 object_flag = (finalized_source_flag_counts[flag] > thresh)
240 coadd_object_cat[flag] = object_flag
241 self.log.info("Propagated %d finalized sources with flag %s", object_flag.sum(), flag)
242