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)
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()
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
497 Concatenate isolated star catalogs and make reserve selection.
502 Band name. Used to select reserved stars.
503 isolated_star_cat_dict : `dict`
504 Per-tract dict of isolated star catalog handles.
505 isolated_star_source_dict : `dict`
506 Per-tract dict of isolated star source catalog handles.
510 isolated_table : `np.ndarray` (N,)
511 Table of isolated stars, with indexes to isolated sources.
512 Returns None if there are no usable isolated catalogs.
513 isolated_source_table : `np.ndarray` (M,)
514 Table of isolated sources, with indexes to isolated stars.
515 Returns None if there are no usable isolated catalogs.
518 isolated_sources = []
519 merge_cat_counter = 0
520 merge_source_counter = 0
522 for tract
in isolated_star_cat_dict:
523 astropy_cat = isolated_star_cat_dict[tract].get()
524 table_cat = np.asarray(astropy_cat)
526 astropy_source = isolated_star_source_dict[tract].get(
527 parameters={
'columns': [self.config.id_column,
'obj_index']}
529 table_source = np.asarray(astropy_source)
532 (use_band,) = (table_cat[f
'nsource_{band}'] > 0).nonzero()
534 if len(use_band) == 0:
536 self.log.info(
"No sources found in %s band in tract %d.", band, tract)
541 obj_index = table_source[
'obj_index'][:]
542 a, b = esutil.numpy_util.match(use_band, obj_index)
545 table_source[
'obj_index'][b] = a
546 _, index_new = np.unique(a, return_index=
True)
547 table_cat[f
'source_cat_index_{band}'][use_band] = index_new
558 table_source = table_source[b]
559 table_cat = table_cat[use_band]
562 table_cat = np.lib.recfunctions.append_fields(
565 np.zeros(table_cat.size, dtype=bool),
568 table_source = np.lib.recfunctions.append_fields(
571 np.zeros(table_source.size, dtype=bool),
576 table_cat[
'reserved'][:] = self.reserve_selection.run(
578 extra=f
'{band}_{tract}',
580 table_source[
'reserved'][:] = table_cat[
'reserved'][table_source[
'obj_index']]
583 table_cat[f
'source_cat_index_{band}'] += merge_source_counter
584 table_source[
'obj_index'] += merge_cat_counter
586 isolated_tables.append(table_cat)
587 isolated_sources.append(table_source)
589 merge_cat_counter += len(table_cat)
590 merge_source_counter += len(table_source)
592 if len(isolated_tables) > 0:
593 isolated_table = np.concatenate(isolated_tables)
594 isolated_source_table = np.concatenate(isolated_sources)
596 isolated_table =
None
597 isolated_source_table =
None
599 return isolated_table, isolated_source_table
625 isolated_source_table, fgcm_standard_star_cat):
626 """Compute psf model and aperture correction map for a single exposure.
631 Visit number (for logging).
633 Detector number (for logging).
634 exposure : `lsst.afw.image.ExposureF`
635 src : `lsst.afw.table.SourceCatalog`
636 isolated_source_table : `np.ndarray`
640 psf : `lsst.meas.algorithms.ImagePsf`
642 ap_corr_map : `lsst.afw.image.ApCorrMap`
643 Aperture correction map.
644 measured_src : `lsst.afw.table.SourceCatalog`
645 Updated source catalog with measurements, flags and aperture corrections.
648 footprints = SingleFrameMeasurementTask.getFootprintsFromCatalog(src)
651 good_src = self.source_selector.selectSources(src)
652 if sum(good_src.selected) == 0:
653 self.log.warning(
'No good sources remain after cuts for visit %d, detector %d',
655 return None,
None,
None
665 selected_src.reserve(good_src.selected.sum())
666 selected_src.extend(src[good_src.selected], mapper=selection_mapper)
670 selected_src[
'calib_psf_candidate'] = np.zeros(len(selected_src), dtype=bool)
671 selected_src[
'calib_psf_used'] = np.zeros(len(selected_src), dtype=bool)
672 selected_src[
'calib_psf_reserved'] = np.zeros(len(selected_src), dtype=bool)
675 matched_src, matched_iso = esutil.numpy_util.match(
677 isolated_source_table[self.config.id_column]
679 if len(matched_src) == 0:
681 "No candidates from matched isolate stars for visit=%s, detector=%s "
682 "(this is probably the result of an earlier astrometry failure).",
685 return None,
None,
None
687 matched_arr = np.zeros(len(selected_src), dtype=bool)
688 matched_arr[matched_src] =
True
689 selected_src[
'calib_psf_candidate'] = matched_arr
691 reserved_arr = np.zeros(len(selected_src), dtype=bool)
692 reserved_arr[matched_src] = isolated_source_table[
'reserved'][matched_iso]
693 selected_src[
'calib_psf_reserved'] = reserved_arr
695 selected_src = selected_src[selected_src[
'calib_psf_candidate']].copy(deep=
True)
699 measured_src.reserve(len(selected_src))
703 measured_src[
'calib_psf_candidate'] = selected_src[
'calib_psf_candidate']
704 measured_src[
'calib_psf_reserved'] = selected_src[
'calib_psf_reserved']
705 if exposure.filter.hasBandLabel():
706 band = exposure.filter.bandLabel
714 psf_selection_result = self.make_psf_candidates.run(selected_src, exposure=exposure)
715 _ = self.make_psf_candidates.run(measured_src, exposure=exposure)
716 except Exception
as e:
717 self.log.exception(
'Failed to make PSF candidates for visit %d, detector %d: %s',
719 return None,
None, measured_src
721 psf_cand_cat = psf_selection_result.goodStarCat
725 psf_determiner_list = [cand
for cand, use
726 in zip(psf_selection_result.psfCandidates,
727 ~psf_cand_cat[
'calib_psf_reserved'])
if use]
728 flag_key = psf_cand_cat.schema[
'calib_psf_used'].asKey()
735 except Exception
as e:
736 self.log.exception(
'Failed to determine PSF for visit %d, detector %d: %s',
738 return None,
None, measured_src
740 sigma = psf.computeShape(psf.getAveragePosition(), psf.getAverageColor()).getDeterminantRadius()
742 self.log.warning(
'Failed to determine psf for visit %d, detector %d: '
743 'Computed final PSF size is NAN.',
745 return None,
None, measured_src
752 matched_selected, matched_measured = esutil.numpy_util.match(
756 measured_used = np.zeros(len(measured_src), dtype=bool)
757 measured_used[matched_measured] = selected_src[
'calib_psf_used'][matched_selected]
758 measured_src[
'calib_psf_used'] = measured_used
763 self.measurement.run(measCat=measured_src, exposure=exposure, footprints=footprints)
764 except Exception
as e:
765 self.log.warning(
'Failed to make measurements for visit %d, detector %d: %s',
767 return psf,
None, measured_src
771 ap_corr_map = self.measure_ap_corr.run(exposure=exposure,
772 catalog=measured_src).apCorrMap
773 except Exception
as e:
774 self.log.warning(
'Failed to compute aperture corrections for visit %d, detector %d: %s',
776 return psf,
None, measured_src
779 ap_corr_map_input = exposure.apCorrMap
780 for key
in ap_corr_map_input:
781 if key
not in ap_corr_map:
782 ap_corr_map[key] = ap_corr_map_input[key]
784 self.apply_ap_corr.run(catalog=measured_src, apCorrMap=ap_corr_map)
786 return psf, ap_corr_map, measured_src