22__all__ = [
"PropagateSourceFlagsConfig",
"PropagateSourceFlagsTask"]
26from smatch.matcher
import Matcher
33 """Configuration for propagating source flags to coadd objects."""
34 source_flags = pexConfig.DictField(
38 "calib_astrometry_used": 0.2,
39 "calib_photometry_used": 0.2,
40 "calib_photometry_reserved": 0.2
42 doc=(
"Source flags to propagate, with the threshold of relative occurrence "
43 "(valid range: [0-1]). Coadd object will have flag set if fraction "
44 "of input visits in which it is flagged is greater than the threshold."),
46 finalized_source_flags = pexConfig.DictField(
50 "calib_psf_candidate": 0.2,
51 "calib_psf_used": 0.2,
52 "calib_psf_reserved": 0.2
54 doc=(
"Finalized source flags to propagate, with the threshold of relative "
55 "occurrence (valid range: [0-1]). Coadd object will have flag set if "
56 "fraction of input visits in which it is flagged is greater than the "
59 x_column = pexConfig.Field(
60 doc=
"Name of column with source x position (sourceTable_visit).",
64 y_column = pexConfig.Field(
65 doc=
"Name of column with source y position (sourceTable_visit).",
69 finalized_x_column = pexConfig.Field(
70 doc=
"Name of column with source x position (finalized_src_table).",
72 default=
"slot_Centroid_x",
74 finalized_y_column = pexConfig.Field(
75 doc=
"Name of column with source y position (finalized_src_table).",
77 default=
"slot_Centroid_y",
79 match_radius = pexConfig.Field(
82 doc=
"Source matching radius (arcsec)"
91 raise ValueError(f
"The set of source_flags {source_flags} must not overlap "
92 f
"with the finalized_source_flags {finalized_source_flags}")
96 """Task to propagate source flags to coadd objects.
98 Flagged sources may come from a mix of two different types of source catalogs.
99 The source_table catalogs
from ``CalibrateTask`` contain flags
for the first
100 round of astromety/photometry/psf fits.
101 The finalized_source_table catalogs
from ``FinalizeCalibrationTask`` contain
102 flags
from the second round of psf fitting.
104 ConfigClass = PropagateSourceFlagsConfig
107 pipeBase.Task.__init__(self, **kwargs)
110 for f
in self.config.source_flags:
111 self.
schema.addField(f, type=
"Flag", doc=
"Propagated from sources")
112 for f
in self.config.finalized_source_flags:
113 self.
schema.addField(f, type=
"Flag", doc=
"Propagated from finalized sources")
115 def run(self, coadd_object_cat, ccd_inputs,
116 source_table_handle_dict=None, finalized_source_table_handle_dict=None):
117 """Propagate flags from single-frame sources to coadd objects.
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.
126 Table of coadd objects.
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.
137 if len(self.config.source_flags) == 0
and len(self.config.finalized_source_flags) == 0:
141 self.config.x_column,
142 self.config.y_column,
143 self.config.source_flags.keys()
146 self.config.finalized_x_column,
147 self.config.finalized_y_column,
148 self.config.finalized_source_flags.keys(),
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))
158 visits = np.unique(ccd_inputs[
"visit"])
160 matcher = Matcher(np.rad2deg(coadd_object_cat[
"coord_ra"]),
161 np.rad2deg(coadd_object_cat[
"coord_dec"]))
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}
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"]
175 for handle_dict, columns, flag_counts, x_col, y_col, name
in zip(handles_list,
181 if handle_dict
is not None and len(columns) > 0:
183 if visit
not in handle_dict:
184 self.log.info(
"Visit %d not in input handle dict for %s", visit, name)
186 handle = handle_dict[visit]
187 df = handle.get(parameters={
"columns": columns})
190 for row
in ccd_inputs[ccd_inputs[
"visit"] == visit]:
191 detector = row[
"ccd"]
194 df_det = df[df[
"detector"] == detector]
199 ra, dec = wcs.pixelToSkyArray(df_det[x_col].values,
200 df_det[y_col].values,
207 idx, i1, i2, d = matcher.query_radius(
210 self.config.match_radius/3600.,
215 self.log.info(
"Visit %d has no overlapping objects", visit)
220 self.log.info(
"Visit %d has no overlapping objects", visit)
223 for flag
in flag_counts:
224 flag_values = df_det[flag].values
225 flag_counts[flag][i1] += flag_values[i2].astype(np.int32)
227 for flag
in source_flag_counts:
228 thresh = num_overlaps*self.config.source_flags[flag]
229 object_flag = (source_flag_counts[flag] > thresh)
230 coadd_object_cat[flag] = object_flag
231 self.log.info(
"Propagated %d sources with flag %s", object_flag.sum(), flag)
233 for flag
in finalized_source_flag_counts:
234 thresh = num_overlaps*self.config.finalized_source_flags[flag]
235 object_flag = (finalized_source_flag_counts[flag] > thresh)
236 coadd_object_cat[flag] = object_flag
237 self.log.info(
"Propagated %d finalized sources with flag %s", object_flag.sum(), flag)
239 def _get_source_table_column_names(self, x_column, y_column, flags):
240 """Get the list of source table columns from the config.
245 Name of column with x centroid.
247 Name of column
with y centroid.
248 flags : `list` [`str`]
249 List of flags to retrieve.
253 columns : [`list`] [`str`]
256 columns = ["visit",
"detector",
258 columns.extend(flags)
def __init__(self, schema, **kwargs)
def _get_source_table_column_names(self, x_column, y_column, flags)