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.
322 Raised if the selector returns no good sources.
328 isolated_star_cat_dict,
329 isolated_star_source_dict
332 exposure_cat_schema = afwTable.ExposureTable.makeMinimalSchema()
333 exposure_cat_schema.addField(
'visit', type=
'L', doc=
'Visit number')
335 metadata = dafBase.PropertyList()
336 metadata.add(
"COMMENT",
"Catalog id is detector id, sorted.")
337 metadata.add(
"COMMENT",
"Only detectors with data have entries.")
339 psf_ap_corr_cat = afwTable.ExposureCatalog(exposure_cat_schema)
340 psf_ap_corr_cat.setMetadata(metadata)
342 measured_src_tables = []
343 measured_src_table =
None
345 for detector
in src_dict:
346 src = src_dict[detector].get()
347 exposure = calexp_dict[detector].get()
354 isolated_source_table
358 if measured_src
is not None:
359 record = psf_ap_corr_cat.addNew()
360 record[
'id'] = int(detector)
361 record[
'visit'] = visit
364 if ap_corr_map
is not None:
365 record.setApCorrMap(ap_corr_map)
367 measured_src[
'visit'][:] = visit
368 measured_src[
'detector'][:] = detector
370 measured_src_tables.append(measured_src.asAstropy().as_array())
372 if len(measured_src_tables) > 0:
373 measured_src_table = np.concatenate(measured_src_tables)
375 if measured_src_table
is None:
376 raise pipeBase.NoWorkFound(f
'No good sources found for any detectors in visit {visit}')
378 return pipeBase.Struct(psf_ap_corr_cat=psf_ap_corr_cat,
379 output_table=measured_src_table)
382 """Make the schema mapper from the input schema to the output schema.
386 input_schema : `lsst.afw.table.Schema`
391 mapper : `lsst.afw.table.SchemaMapper`
393 output_schema : `lsst.afw.table.Schema`
394 Output schema (with alias map)
396 mapper = afwTable.SchemaMapper(input_schema)
397 mapper.addMinimalSchema(afwTable.SourceTable.makeMinimalSchema())
398 mapper.addMapping(input_schema[
'slot_Centroid_x'].asKey())
399 mapper.addMapping(input_schema[
'slot_Centroid_y'].asKey())
402 aper_fields = input_schema.extract(
'base_CircularApertureFlux_*')
403 for field, item
in aper_fields.items():
404 mapper.addMapping(item.key)
407 apflux_fields = input_schema.extract(
'slot_ApFlux_*')
408 for field, item
in apflux_fields.items():
409 mapper.addMapping(item.key)
411 calibflux_fields = input_schema.extract(
'slot_CalibFlux_*')
412 for field, item
in calibflux_fields.items():
413 mapper.addMapping(item.key)
416 input_schema[self.config.source_selector.active.signalToNoise.fluxField].asKey(),
417 'calib_psf_selection_flux')
419 input_schema[self.config.source_selector.active.signalToNoise.errField].asKey(),
420 'calib_psf_selection_flux_err')
422 output_schema = mapper.getOutputSchema()
424 output_schema.addField(
425 'calib_psf_candidate',
427 doc=(
'set if the source was a candidate for PSF determination, '
428 'as determined from FinalizeCharacterizationTask.'),
430 output_schema.addField(
431 'calib_psf_reserved',
433 doc=(
'set if source was reserved from PSF determination by '
434 'FinalizeCharacterizationTask.'),
436 output_schema.addField(
439 doc=(
'set if source was used in the PSF determination by '
440 'FinalizeCharacterizationTask.'),
442 output_schema.addField(
445 doc=
'Visit number for the sources.',
447 output_schema.addField(
450 doc=
'Detector number for the sources.',
453 alias_map = input_schema.getAliasMap()
454 alias_map_output = afwTable.AliasMap()
455 alias_map_output.set(
'slot_Centroid', alias_map.get(
'slot_Centroid'))
456 alias_map_output.set(
'slot_ApFlux', alias_map.get(
'slot_ApFlux'))
457 alias_map_output.set(
'slot_CalibFlux', alias_map.get(
'slot_CalibFlux'))
459 output_schema.setAliasMap(alias_map_output)
461 return mapper, output_schema
489 Concatenate isolated star catalogs and make reserve selection.
494 Band name. Used to select reserved stars.
495 isolated_star_cat_dict : `dict`
496 Per-tract dict of isolated star catalog handles.
497 isolated_star_source_dict : `dict`
498 Per-tract dict of isolated star source catalog handles.
502 isolated_table : `np.ndarray` (N,)
503 Table of isolated stars, with indexes to isolated sources.
504 isolated_source_table : `np.ndarray` (M,)
505 Table of isolated sources, with indexes to isolated stars.
508 isolated_sources = []
509 merge_cat_counter = 0
510 merge_source_counter = 0
512 for tract
in isolated_star_cat_dict:
513 df_cat = isolated_star_cat_dict[tract].get()
514 table_cat = df_cat.to_records()
516 df_source = isolated_star_source_dict[tract].get(
517 parameters={
'columns': [self.config.id_column,
520 table_source = df_source.to_records()
523 (use_band,) = (table_cat[f
'nsource_{band}'] > 0).nonzero()
525 if len(use_band) == 0:
527 self.log.info(
"No sources found in %s band in tract %d.", band, tract)
532 obj_index = table_source[
'obj_index'][:]
533 a, b = esutil.numpy_util.match(use_band, obj_index)
536 table_source[
'obj_index'][b] = a
537 _, index_new = np.unique(a, return_index=
True)
538 table_cat[f
'source_cat_index_{band}'][use_band] = index_new
549 table_source = table_source[b]
550 table_cat = table_cat[use_band]
553 table_cat = np.lib.recfunctions.append_fields(
556 np.zeros(table_cat.size, dtype=bool),
559 table_source = np.lib.recfunctions.append_fields(
562 np.zeros(table_source.size, dtype=bool),
567 table_cat[
'reserved'][:] = self.reserve_selection.run(
569 extra=f
'{band}_{tract}',
571 table_source[
'reserved'][:] = table_cat[
'reserved'][table_source[
'obj_index']]
574 table_cat[f
'source_cat_index_{band}'] += merge_source_counter
575 table_source[
'obj_index'] += merge_cat_counter
577 isolated_tables.append(table_cat)
578 isolated_sources.append(table_source)
580 merge_cat_counter += len(table_cat)
581 merge_source_counter += len(table_source)
583 isolated_table = np.concatenate(isolated_tables)
584 isolated_source_table = np.concatenate(isolated_sources)
586 return isolated_table, isolated_source_table
589 """Compute psf model and aperture correction map for a single exposure.
594 Visit number (for logging).
596 Detector number (for logging).
597 exposure : `lsst.afw.image.ExposureF`
598 src : `lsst.afw.table.SourceCatalog`
599 isolated_source_table : `np.ndarray`
603 psf : `lsst.meas.algorithms.ImagePsf`
605 ap_corr_map : `lsst.afw.image.ApCorrMap`
606 Aperture correction map.
607 measured_src : `lsst.afw.table.SourceCatalog`
608 Updated source catalog with measurements, flags and aperture corrections.
611 good_src = self.source_selector.selectSources(src)
612 if sum(good_src.selected) == 0:
613 self.log.warning(
'No good sources remain after cuts for visit %d, detector %d',
615 return None,
None,
None
624 selected_src = afwTable.SourceCatalog(selection_schema)
625 selected_src.reserve(good_src.selected.sum())
626 selected_src.extend(src[good_src.selected], mapper=selection_mapper)
630 selected_src[
'calib_psf_candidate'] = np.zeros(len(selected_src), dtype=bool)
631 selected_src[
'calib_psf_used'] = np.zeros(len(selected_src), dtype=bool)
632 selected_src[
'calib_psf_reserved'] = np.zeros(len(selected_src), dtype=bool)
635 matched_src, matched_iso = esutil.numpy_util.match(
637 isolated_source_table[self.config.id_column]
640 matched_arr = np.zeros(len(selected_src), dtype=bool)
641 matched_arr[matched_src] =
True
642 selected_src[
'calib_psf_candidate'] = matched_arr
644 reserved_arr = np.zeros(len(selected_src), dtype=bool)
645 reserved_arr[matched_src] = isolated_source_table[
'reserved'][matched_iso]
646 selected_src[
'calib_psf_reserved'] = reserved_arr
648 selected_src = selected_src[selected_src[
'calib_psf_candidate']].copy(deep=
True)
651 measured_src = afwTable.SourceCatalog(self.
schema)
652 measured_src.reserve(len(selected_src))
656 measured_src[
'calib_psf_candidate'] = selected_src[
'calib_psf_candidate']
657 measured_src[
'calib_psf_reserved'] = selected_src[
'calib_psf_reserved']
661 psf_selection_result = self.make_psf_candidates.run(selected_src, exposure=exposure)
662 except Exception
as e:
663 self.log.warning(
'Failed to make psf candidates for visit %d, detector %d: %s',
665 return None,
None, measured_src
667 psf_cand_cat = psf_selection_result.goodStarCat
671 psf_determiner_list = [cand
for cand, use
672 in zip(psf_selection_result.psfCandidates,
673 ~psf_cand_cat[
'calib_psf_reserved'])
if use]
674 flag_key = psf_cand_cat.schema[
'calib_psf_used'].asKey()
676 psf, cell_set = self.psf_determiner.determinePsf(exposure,
680 except Exception
as e:
681 self.log.warning(
'Failed to determine psf for visit %d, detector %d: %s',
683 return None,
None, measured_src
690 matched_selected, matched_measured = esutil.numpy_util.match(
694 measured_used = np.zeros(len(measured_src), dtype=bool)
695 measured_used[matched_measured] = selected_src[
'calib_psf_used'][matched_selected]
696 measured_src[
'calib_psf_used'] = measured_used
700 self.measurement.run(measCat=measured_src, exposure=exposure)
701 except Exception
as e:
702 self.log.warning(
'Failed to make measurements for visit %d, detector %d: %s',
704 return psf,
None, measured_src
708 ap_corr_map = self.measure_ap_corr.run(exposure=exposure,
709 catalog=measured_src).apCorrMap
710 except Exception
as e:
711 self.log.warning(
'Failed to compute aperture corrections for visit %d, detector %d: %s',
713 return psf,
None, measured_src
715 self.apply_ap_corr.run(catalog=measured_src, apCorrMap=ap_corr_map)
717 return psf, ap_corr_map, measured_src