259 input_handle_dict = butlerQC.get(inputRefs)
261 band = butlerQC.quantum.dataId[
'band']
262 visit = butlerQC.quantum.dataId[
'visit']
264 src_dict_temp = {handle.dataId[
'detector']: handle
265 for handle
in input_handle_dict[
'srcs']}
266 calexp_dict_temp = {handle.dataId[
'detector']: handle
267 for handle
in input_handle_dict[
'calexps']}
268 isolated_star_cat_dict_temp = {handle.dataId[
'tract']: handle
269 for handle
in input_handle_dict[
'isolated_star_cats']}
270 isolated_star_source_dict_temp = {handle.dataId[
'tract']: handle
271 for handle
in input_handle_dict[
'isolated_star_sources']}
274 src_dict = {detector: src_dict_temp[detector]
for
275 detector
in sorted(src_dict_temp.keys())}
276 calexp_dict = {detector: calexp_dict_temp[detector]
for
277 detector
in sorted(calexp_dict_temp.keys())}
278 isolated_star_cat_dict = {tract: isolated_star_cat_dict_temp[tract]
for
279 tract
in sorted(isolated_star_cat_dict_temp.keys())}
280 isolated_star_source_dict = {tract: isolated_star_source_dict_temp[tract]
for
281 tract
in sorted(isolated_star_source_dict_temp.keys())}
283 struct = self.
run(visit,
285 isolated_star_cat_dict,
286 isolated_star_source_dict,
290 butlerQC.put(struct.psf_ap_corr_cat,
291 outputRefs.finalized_psf_ap_corr_cat)
292 butlerQC.put(pd.DataFrame(struct.output_table),
293 outputRefs.finalized_src_table)
295 def run(self, visit, band, isolated_star_cat_dict, isolated_star_source_dict, src_dict, calexp_dict):
297 Run the FinalizeCharacterizationTask.
302 Visit number. Used in the output catalogs.
304 Band name. Used to select reserved stars.
305 isolated_star_cat_dict : `dict`
306 Per-tract dict of isolated star catalog handles.
307 isolated_star_source_dict : `dict`
308 Per-tract dict of isolated star source catalog handles.
310 Per-detector dict of src catalog handles.
312 Per-detector dict of calibrated exposure handles.
316 struct : `lsst.pipe.base.struct`
317 Struct with outputs for persistence.
323 isolated_star_cat_dict,
324 isolated_star_source_dict
327 exposure_cat_schema = afwTable.ExposureTable.makeMinimalSchema()
328 exposure_cat_schema.addField(
'visit', type=
'L', doc=
'Visit number')
330 metadata = dafBase.PropertyList()
331 metadata.add(
"COMMENT",
"Catalog id is detector id, sorted.")
332 metadata.add(
"COMMENT",
"Only detectors with data have entries.")
334 psf_ap_corr_cat = afwTable.ExposureCatalog(exposure_cat_schema)
335 psf_ap_corr_cat.setMetadata(metadata)
337 measured_src_tables = []
339 for detector
in src_dict:
340 src = src_dict[detector].get()
341 exposure = calexp_dict[detector].get()
348 isolated_source_table
352 record = psf_ap_corr_cat.addNew()
353 record[
'id'] = int(detector)
354 record[
'visit'] = visit
357 if ap_corr_map
is not None:
358 record.setApCorrMap(ap_corr_map)
360 measured_src[
'visit'][:] = visit
361 measured_src[
'detector'][:] = detector
363 measured_src_tables.append(measured_src.asAstropy().as_array())
365 measured_src_table = np.concatenate(measured_src_tables)
367 return pipeBase.Struct(psf_ap_corr_cat=psf_ap_corr_cat,
368 output_table=measured_src_table)
371 """Make the schema mapper from the input schema to the output schema.
375 input_schema : `lsst.afw.table.Schema`
380 mapper : `lsst.afw.table.SchemaMapper`
382 output_schema : `lsst.afw.table.Schema`
383 Output schema (with alias map)
385 mapper = afwTable.SchemaMapper(input_schema)
386 mapper.addMinimalSchema(afwTable.SourceTable.makeMinimalSchema())
387 mapper.addMapping(input_schema[
'slot_Centroid_x'].asKey())
388 mapper.addMapping(input_schema[
'slot_Centroid_y'].asKey())
391 aper_fields = input_schema.extract(
'base_CircularApertureFlux_*')
392 for field, item
in aper_fields.items():
393 mapper.addMapping(item.key)
396 apflux_fields = input_schema.extract(
'slot_ApFlux_*')
397 for field, item
in apflux_fields.items():
398 mapper.addMapping(item.key)
400 calibflux_fields = input_schema.extract(
'slot_CalibFlux_*')
401 for field, item
in calibflux_fields.items():
402 mapper.addMapping(item.key)
405 input_schema[self.config.source_selector.active.signalToNoise.fluxField].asKey(),
406 'calib_psf_selection_flux')
408 input_schema[self.config.source_selector.active.signalToNoise.errField].asKey(),
409 'calib_psf_selection_flux_err')
411 output_schema = mapper.getOutputSchema()
413 output_schema.addField(
414 'calib_psf_candidate',
416 doc=(
'set if the source was a candidate for PSF determination, '
417 'as determined from FinalizeCharacterizationTask.'),
419 output_schema.addField(
420 'calib_psf_reserved',
422 doc=(
'set if source was reserved from PSF determination by '
423 'FinalizeCharacterizationTask.'),
425 output_schema.addField(
428 doc=(
'set if source was used in the PSF determination by '
429 'FinalizeCharacterizationTask.'),
431 output_schema.addField(
434 doc=
'Visit number for the sources.',
436 output_schema.addField(
439 doc=
'Detector number for the sources.',
442 alias_map = input_schema.getAliasMap()
443 alias_map_output = afwTable.AliasMap()
444 alias_map_output.set(
'slot_Centroid', alias_map.get(
'slot_Centroid'))
445 alias_map_output.set(
'slot_ApFlux', alias_map.get(
'slot_ApFlux'))
446 alias_map_output.set(
'slot_CalibFlux', alias_map.get(
'slot_CalibFlux'))
448 output_schema.setAliasMap(alias_map_output)
450 return mapper, output_schema
478 Concatenate isolated star catalogs and make reserve selection.
483 Band name. Used to select reserved stars.
484 isolated_star_cat_dict : `dict`
485 Per-tract dict of isolated star catalog handles.
486 isolated_star_source_dict : `dict`
487 Per-tract dict of isolated star source catalog handles.
491 isolated_table : `np.ndarray` (N,)
492 Table of isolated stars, with indexes to isolated sources.
493 isolated_source_table : `np.ndarray` (M,)
494 Table of isolated sources, with indexes to isolated stars.
497 isolated_sources = []
498 merge_cat_counter = 0
499 merge_source_counter = 0
501 for tract
in isolated_star_cat_dict:
502 df_cat = isolated_star_cat_dict[tract].get()
503 table_cat = df_cat.to_records()
505 df_source = isolated_star_source_dict[tract].get(
506 parameters={
'columns': [self.config.id_column,
509 table_source = df_source.to_records()
512 (use_band,) = (table_cat[f
'nsource_{band}'] > 0).nonzero()
514 if len(use_band) == 0:
516 self.log.info(
"No sources found in %s band in tract %d.", band, tract)
521 obj_index = table_source[
'obj_index'][:]
522 a, b = esutil.numpy_util.match(use_band, obj_index)
525 table_source[
'obj_index'][b] = a
526 _, index_new = np.unique(a, return_index=
True)
527 table_cat[f
'source_cat_index_{band}'][use_band] = index_new
538 table_source = table_source[b]
539 table_cat = table_cat[use_band]
542 table_cat = np.lib.recfunctions.append_fields(
545 np.zeros(table_cat.size, dtype=bool),
548 table_source = np.lib.recfunctions.append_fields(
551 np.zeros(table_source.size, dtype=bool),
556 table_cat[
'reserved'][:] = self.reserve_selection.run(
558 extra=f
'{band}_{tract}',
560 table_source[
'reserved'][:] = table_cat[
'reserved'][table_source[
'obj_index']]
563 table_cat[f
'source_cat_index_{band}'] += merge_source_counter
564 table_source[
'obj_index'] += merge_cat_counter
566 isolated_tables.append(table_cat)
567 isolated_sources.append(table_source)
569 merge_cat_counter += len(table_cat)
570 merge_source_counter += len(table_source)
572 isolated_table = np.concatenate(isolated_tables)
573 isolated_source_table = np.concatenate(isolated_sources)
575 return isolated_table, isolated_source_table
578 """Compute psf model and aperture correction map for a single exposure.
583 Visit number (for logging).
585 Detector number (for logging).
586 exposure : `lsst.afw.image.ExposureF`
587 src : `lsst.afw.table.SourceCatalog`
588 isolated_source_table : `np.ndarray`
592 psf : `lsst.meas.algorithms.ImagePsf`
594 ap_corr_map : `lsst.afw.image.ApCorrMap`
595 Aperture correction map.
596 measured_src : `lsst.afw.table.SourceCatalog`
597 Updated source catalog with measurements, flags and aperture corrections.
600 good_src = self.source_selector.selectSources(src)
609 selected_src = afwTable.SourceCatalog(selection_schema)
610 selected_src.reserve(good_src.selected.sum())
611 selected_src.extend(src[good_src.selected], mapper=selection_mapper)
615 selected_src[
'calib_psf_candidate'] = np.zeros(len(selected_src), dtype=bool)
616 selected_src[
'calib_psf_used'] = np.zeros(len(selected_src), dtype=bool)
617 selected_src[
'calib_psf_reserved'] = np.zeros(len(selected_src), dtype=bool)
620 matched_src, matched_iso = esutil.numpy_util.match(
622 isolated_source_table[self.config.id_column]
625 matched_arr = np.zeros(len(selected_src), dtype=bool)
626 matched_arr[matched_src] =
True
627 selected_src[
'calib_psf_candidate'] = matched_arr
629 reserved_arr = np.zeros(len(selected_src), dtype=bool)
630 reserved_arr[matched_src] = isolated_source_table[
'reserved'][matched_iso]
631 selected_src[
'calib_psf_reserved'] = reserved_arr
633 selected_src = selected_src[selected_src[
'calib_psf_candidate']].copy(deep=
True)
636 measured_src = afwTable.SourceCatalog(self.
schema)
637 measured_src.reserve(len(selected_src))
641 measured_src[
'calib_psf_candidate'] = selected_src[
'calib_psf_candidate']
642 measured_src[
'calib_psf_reserved'] = selected_src[
'calib_psf_reserved']
646 psf_selection_result = self.make_psf_candidates.run(selected_src, exposure=exposure)
647 except Exception
as e:
648 self.log.warning(
'Failed to make psf candidates for visit %d, detector %d: %s',
650 return None,
None, measured_src
652 psf_cand_cat = psf_selection_result.goodStarCat
656 psf_determiner_list = [cand
for cand, use
657 in zip(psf_selection_result.psfCandidates,
658 ~psf_cand_cat[
'calib_psf_reserved'])
if use]
659 flag_key = psf_cand_cat.schema[
'calib_psf_used'].asKey()
661 psf, cell_set = self.psf_determiner.determinePsf(exposure,
665 except Exception
as e:
666 self.log.warning(
'Failed to determine psf for visit %d, detector %d: %s',
668 return None,
None, measured_src
675 matched_selected, matched_measured = esutil.numpy_util.match(
679 measured_used = np.zeros(len(measured_src), dtype=bool)
680 measured_used[matched_measured] = selected_src[
'calib_psf_used'][matched_selected]
681 measured_src[
'calib_psf_used'] = measured_used
685 self.measurement.run(measCat=measured_src, exposure=exposure)
686 except Exception
as e:
687 self.log.warning(
'Failed to make measurements for visit %d, detector %d: %s',
689 return psf,
None, measured_src
693 ap_corr_map = self.measure_ap_corr.run(exposure=exposure,
694 catalog=measured_src).apCorrMap
695 except Exception
as e:
696 self.log.warning(
'Failed to compute aperture corrections for visit %d, detector %d: %s',
698 return psf,
None, measured_src
700 self.apply_ap_corr.run(catalog=measured_src, apCorrMap=ap_corr_map)
702 return psf, ap_corr_map, measured_src