353 """Make the schema mapper from the input schema to the output schema.
357 input_schema : `lsst.afw.table.Schema`
362 mapper : `lsst.afw.table.SchemaMapper`
364 output_schema : `lsst.afw.table.Schema`
365 Output schema (with alias map)
367 mapper = afwTable.SchemaMapper(input_schema)
368 mapper.addMinimalSchema(afwTable.SourceTable.makeMinimalSchema())
369 mapper.addMapping(input_schema[
'slot_Centroid_x'].asKey())
370 mapper.addMapping(input_schema[
'slot_Centroid_y'].asKey())
373 aper_fields = input_schema.extract(
'base_CircularApertureFlux_*')
374 for field, item
in aper_fields.items():
375 mapper.addMapping(item.key)
380 apflux_fields = input_schema.extract(
'slot_ApFlux_*')
381 for field, item
in apflux_fields.items():
382 mapper.addMapping(item.key)
384 calibflux_fields = input_schema.extract(
'slot_CalibFlux_*')
385 for field, item
in calibflux_fields.items():
386 mapper.addMapping(item.key)
389 input_schema[self.config.source_selector.active.signalToNoise.fluxField].asKey(),
390 'calib_psf_selection_flux')
392 input_schema[self.config.source_selector.active.signalToNoise.errField].asKey(),
393 'calib_psf_selection_flux_err')
395 output_schema = mapper.getOutputSchema()
397 output_schema.addField(
398 'calib_psf_candidate',
400 doc=(
'set if the source was a candidate for PSF determination, '
401 'as determined from FinalizeCharacterizationTask.'),
403 output_schema.addField(
404 'calib_psf_reserved',
406 doc=(
'set if source was reserved from PSF determination by '
407 'FinalizeCharacterizationTask.'),
409 output_schema.addField(
412 doc=(
'set if source was used in the PSF determination by '
413 'FinalizeCharacterizationTask.'),
415 output_schema.addField(
418 doc=
'Visit number for the sources.',
420 output_schema.addField(
423 doc=
'Detector number for the sources.',
425 output_schema.addField(
428 doc=
"Color used in PSF fit."
430 output_schema.addField(
434 doc=
"Color used in PSF fit."
436 output_schema.addField(
439 doc=
"Maximum value in the star image used to train PSF.",
443 alias_map = input_schema.getAliasMap()
444 alias_map_output = afwTable.AliasMap()
445 alias_map_output.set(
'slot_Centroid', alias_map.get(
'slot_Centroid'))
446 alias_map_output.set(
'slot_ApFlux', alias_map.get(
'slot_ApFlux'))
447 alias_map_output.set(
'slot_CalibFlux', alias_map.get(
'slot_CalibFlux'))
449 output_schema.setAliasMap(alias_map_output)
451 return mapper, output_schema
498 isolated_star_cat_dict,
499 isolated_star_source_dict,
504 Concatenate isolated star catalogs and make reserve selection.
509 Band name. Used to select reserved stars.
510 isolated_star_cat_dict : `dict`
511 Per-tract dict of isolated star catalog handles.
512 isolated_star_source_dict : `dict`
513 Per-tract dict of isolated star source catalog handles.
514 visit : `int`, optional
515 Visit to down-select sources.
516 detector : `int`, optional
517 Detector to down-select sources. Will only be used if visit
522 isolated_table : `astropy.table.Table` (N,)
523 Table of isolated stars, with indexes to isolated sources.
524 Returns None if there are no usable isolated catalogs.
525 isolated_source_table : `astropy.table.Table` (M,)
526 Table of isolated sources, with indexes to isolated stars.
527 Returns None if there are no usable isolated catalogs.
530 isolated_sources = []
531 merge_cat_counter = 0
532 merge_source_counter = 0
534 handle = isolated_star_cat_dict[list(isolated_star_cat_dict.keys())[0]]
535 all_source_columns = handle.get(component=
'columns')
536 source_columns = [self.config.id_column,
'obj_index']
538 if visit
is not None and visit
in all_source_columns:
539 source_columns.append(
'visit')
540 if detector
is not None:
541 source_columns.append(
'detector')
546 for tract
in isolated_star_cat_dict:
547 astropy_cat = isolated_star_cat_dict[tract].get()
548 astropy_source = isolated_star_source_dict[tract].get(
549 parameters={
'columns': source_columns}
553 sources_downselected =
False
554 if visit
is not None:
555 sources_downselected =
True
556 these_sources = (astropy_source[
'visit'] == visit)
558 if these_sources.sum() == 0:
559 self.log.info(
'No sources found for visit %d in tract %d.', visit, tract)
562 if detector
is not None:
563 these_sources &= (astropy_source[
'detector'] == detector)
564 if these_sources.sum() == 0:
566 'No sources found for visit %d, detector %d in tract %d.',
573 astropy_source = astropy_source[these_sources]
578 (use_band,) = (astropy_cat[f
'nsource_{band}'] > 0).nonzero()
580 if len(use_band) == 0:
582 self.log.info(
"No sources found in %s band in tract %d.", band, tract)
587 a, b = esutil.numpy_util.match(use_band, np.asarray(astropy_source[
'obj_index']))
590 astropy_source[
'obj_index'][b] = a
591 if sources_downselected:
600 astropy_cat[f
'source_cat_index_{band}'][use_band][a] = b
604 _, index_new = np.unique(a, return_index=
True)
605 astropy_cat[f
'source_cat_index_{band}'][use_band] = index_new
616 astropy_source = astropy_source[b]
617 astropy_cat = astropy_cat[use_band]
620 astropy_cat[
'reserved'] =
False
621 astropy_source[
'reserved'] =
False
624 astropy_cat[
'reserved'][:] = self.reserve_selection.run(
626 extra=f
'{band}_{tract}',
628 astropy_source[
'reserved'][:] = astropy_cat[
'reserved'][astropy_source[
'obj_index']]
631 astropy_cat[f
'source_cat_index_{band}'] += merge_source_counter
632 astropy_source[
'obj_index'] += merge_cat_counter
634 isolated_tables.append(astropy_cat)
635 isolated_sources.append(astropy_source)
637 merge_cat_counter += len(astropy_cat)
638 merge_source_counter += len(astropy_source)
640 if len(isolated_tables) > 0:
641 isolated_table = astropy.table.vstack(isolated_tables, metadata_conflicts=
'silent')
642 isolated_source_table = astropy.table.vstack(isolated_sources, metadata_conflicts=
'silent')
644 isolated_table =
None
645 isolated_source_table =
None
647 return isolated_table, isolated_source_table
673 isolated_source_table, fgcm_standard_star_cat):
674 """Compute psf model and aperture correction map for a single exposure.
679 Visit number (for logging).
681 Detector number (for logging).
682 exposure : `lsst.afw.image.ExposureF`
683 src : `lsst.afw.table.SourceCatalog`
684 isolated_source_table : `np.ndarray` or `astropy.table.Table`
685 fgcm_standard_star_cat : `np.ndarray`
689 psf : `lsst.meas.algorithms.ImagePsf`
691 ap_corr_map : `lsst.afw.image.ApCorrMap`
692 Aperture correction map.
693 measured_src : `lsst.afw.table.SourceCatalog`
694 Updated source catalog with measurements, flags and aperture corrections.
697 footprints = SingleFrameMeasurementTask.getFootprintsFromCatalog(src)
700 good_src = self.source_selector.selectSources(src)
701 if sum(good_src.selected) == 0:
702 self.log.warning(
'No good sources remain after cuts for visit %d, detector %d',
704 return None,
None,
None
713 selected_src = afwTable.SourceCatalog(selection_schema)
714 selected_src.reserve(good_src.selected.sum())
715 selected_src.extend(src[good_src.selected], mapper=selection_mapper)
719 selected_src[
'calib_psf_candidate'] = np.zeros(len(selected_src), dtype=bool)
720 selected_src[
'calib_psf_used'] = np.zeros(len(selected_src), dtype=bool)
721 selected_src[
'calib_psf_reserved'] = np.zeros(len(selected_src), dtype=bool)
724 matched_src, matched_iso = esutil.numpy_util.match(
726 np.asarray(isolated_source_table[self.config.id_column]),
728 if len(matched_src) == 0:
730 "No candidates from matched isolate stars for visit=%s, detector=%s "
731 "(this is probably the result of an earlier astrometry failure).",
734 return None,
None,
None
736 matched_arr = np.zeros(len(selected_src), dtype=bool)
737 matched_arr[matched_src] =
True
738 selected_src[
'calib_psf_candidate'] = matched_arr
740 reserved_arr = np.zeros(len(selected_src), dtype=bool)
741 reserved_arr[matched_src] = np.asarray(isolated_source_table[
'reserved'][matched_iso])
742 selected_src[
'calib_psf_reserved'] = reserved_arr
744 selected_src = selected_src[selected_src[
'calib_psf_candidate']].copy(deep=
True)
747 measured_src = afwTable.SourceCatalog(self.
schema)
748 measured_src.reserve(len(selected_src))
752 measured_src[
'calib_psf_candidate'] = selected_src[
'calib_psf_candidate']
753 measured_src[
'calib_psf_reserved'] = selected_src[
'calib_psf_reserved']
754 if exposure.filter.hasBandLabel():
755 band = exposure.filter.bandLabel
763 psf_selection_result = self.make_psf_candidates.run(selected_src, exposure=exposure)
764 _ = self.make_psf_candidates.run(measured_src, exposure=exposure)
765 except Exception
as e:
766 self.log.exception(
'Failed to make PSF candidates for visit %d, detector %d: %s',
768 return None,
None, measured_src
770 psf_cand_cat = psf_selection_result.goodStarCat
774 psf_determiner_list = [cand
for cand, use
775 in zip(psf_selection_result.psfCandidates,
776 ~psf_cand_cat[
'calib_psf_reserved'])
if use]
777 flag_key = psf_cand_cat.schema[
'calib_psf_used'].asKey()
784 except Exception
as e:
785 self.log.exception(
'Failed to determine PSF for visit %d, detector %d: %s',
787 return None,
None, measured_src
789 sigma = psf.computeShape(psf.getAveragePosition(), psf.getAverageColor()).getDeterminantRadius()
791 self.log.warning(
'Failed to determine psf for visit %d, detector %d: '
792 'Computed final PSF size is NAN.',
794 return None,
None, measured_src
801 matched_selected, matched_measured = esutil.numpy_util.match(
805 measured_used = np.zeros(len(measured_src), dtype=bool)
806 measured_used[matched_measured] = selected_src[
'calib_psf_used'][matched_selected]
807 measured_src[
'calib_psf_used'] = measured_used
812 self.measurement.run(measCat=measured_src, exposure=exposure, footprints=footprints)
813 except Exception
as e:
814 self.log.warning(
'Failed to make measurements for visit %d, detector %d: %s',
816 return psf,
None, measured_src
820 ap_corr_map = self.measure_ap_corr.run(exposure=exposure,
821 catalog=measured_src).apCorrMap
822 except Exception
as e:
823 self.log.warning(
'Failed to compute aperture corrections for visit %d, detector %d: %s',
825 return psf,
None, measured_src
828 ap_corr_map_input = exposure.apCorrMap
829 for key
in ap_corr_map_input:
830 if key
not in ap_corr_map:
831 ap_corr_map[key] = ap_corr_map_input[key]
833 self.apply_ap_corr.run(catalog=measured_src, apCorrMap=ap_corr_map)
835 return psf, ap_corr_map, measured_src