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 self.log.info(
"No WCS for visit %d detector %d, so can't match sources to "
195 "propagate flags. Skipping...", visit, detector)
198 df_det = df[df[
"detector"] == detector]
203 ra, dec = wcs.pixelToSkyArray(df_det[x_col].values,
204 df_det[y_col].values,
211 idx, i1, i2, d = matcher.query_radius(
214 self.config.match_radius/3600.,
219 self.log.info(
"Visit %d has no overlapping objects", visit)
224 self.log.info(
"Visit %d has no overlapping objects", visit)
227 for flag
in flag_counts:
228 flag_values = df_det[flag].values
229 flag_counts[flag][i1] += flag_values[i2].astype(np.int32)
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)
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)
243 def _get_source_table_column_names(self, x_column, y_column, flags):
244 """Get the list of source table columns from the config.
249 Name of column with x centroid.
251 Name of column
with y centroid.
252 flags : `list` [`str`]
253 List of flags to retrieve.
257 columns : [`list`] [`str`]
260 columns = ["visit",
"detector",
262 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)
daf::base::PropertySet * set