260 input_handle_dict = butlerQC.get(inputRefs)
262 band = butlerQC.quantum.dataId[
'band']
263 visit = butlerQC.quantum.dataId[
'visit']
265 src_dict_temp = {handle.dataId[
'detector']: handle
266 for handle
in input_handle_dict[
'srcs']}
267 calexp_dict_temp = {handle.dataId[
'detector']: handle
268 for handle
in input_handle_dict[
'calexps']}
269 isolated_star_cat_dict_temp = {handle.dataId[
'tract']: handle
270 for handle
in input_handle_dict[
'isolated_star_cats']}
271 isolated_star_source_dict_temp = {handle.dataId[
'tract']: handle
272 for handle
in input_handle_dict[
'isolated_star_sources']}
275 src_dict = {detector: src_dict_temp[detector]
for
276 detector
in sorted(src_dict_temp.keys())}
277 calexp_dict = {detector: calexp_dict_temp[detector]
for
278 detector
in sorted(calexp_dict_temp.keys())}
279 isolated_star_cat_dict = {tract: isolated_star_cat_dict_temp[tract]
for
280 tract
in sorted(isolated_star_cat_dict_temp.keys())}
281 isolated_star_source_dict = {tract: isolated_star_source_dict_temp[tract]
for
282 tract
in sorted(isolated_star_source_dict_temp.keys())}
284 struct = self.
run(visit,
286 isolated_star_cat_dict,
287 isolated_star_source_dict,
291 butlerQC.put(struct.psf_ap_corr_cat,
292 outputRefs.finalized_psf_ap_corr_cat)
293 butlerQC.put(pd.DataFrame(struct.output_table),
294 outputRefs.finalized_src_table)
296 def run(self, visit, band, isolated_star_cat_dict, isolated_star_source_dict, src_dict, calexp_dict):
298 Run the FinalizeCharacterizationTask.
303 Visit number. Used in the output catalogs.
305 Band name. Used to select reserved stars.
306 isolated_star_cat_dict : `dict`
307 Per-tract dict of isolated star catalog handles.
308 isolated_star_source_dict : `dict`
309 Per-tract dict of isolated star source catalog handles.
311 Per-detector dict of src catalog handles.
313 Per-detector dict of calibrated exposure handles.
317 struct : `lsst.pipe.base.struct`
318 Struct with outputs for persistence.
323 Raised if the selector returns no good sources.
329 isolated_star_cat_dict,
330 isolated_star_source_dict
333 exposure_cat_schema = afwTable.ExposureTable.makeMinimalSchema()
334 exposure_cat_schema.addField(
'visit', type=
'L', doc=
'Visit number')
336 metadata = dafBase.PropertyList()
337 metadata.add(
"COMMENT",
"Catalog id is detector id, sorted.")
338 metadata.add(
"COMMENT",
"Only detectors with data have entries.")
340 psf_ap_corr_cat = afwTable.ExposureCatalog(exposure_cat_schema)
341 psf_ap_corr_cat.setMetadata(metadata)
343 measured_src_tables = []
344 measured_src_table =
None
346 for detector
in src_dict:
347 src = src_dict[detector].get()
348 exposure = calexp_dict[detector].get()
355 isolated_source_table
359 if measured_src
is not None:
360 record = psf_ap_corr_cat.addNew()
361 record[
'id'] = int(detector)
362 record[
'visit'] = visit
365 if ap_corr_map
is not None:
366 record.setApCorrMap(ap_corr_map)
368 measured_src[
'visit'][:] = visit
369 measured_src[
'detector'][:] = detector
371 measured_src_tables.append(measured_src.asAstropy().as_array())
373 if len(measured_src_tables) > 0:
374 measured_src_table = np.concatenate(measured_src_tables)
376 if measured_src_table
is None:
377 raise pipeBase.NoWorkFound(f
'No good sources found for any detectors in visit {visit}')
379 return pipeBase.Struct(psf_ap_corr_cat=psf_ap_corr_cat,
380 output_table=measured_src_table)
383 """Make the schema mapper from the input schema to the output schema.
387 input_schema : `lsst.afw.table.Schema`
392 mapper : `lsst.afw.table.SchemaMapper`
394 output_schema : `lsst.afw.table.Schema`
395 Output schema (with alias map)
397 mapper = afwTable.SchemaMapper(input_schema)
398 mapper.addMinimalSchema(afwTable.SourceTable.makeMinimalSchema())
399 mapper.addMapping(input_schema[
'slot_Centroid_x'].asKey())
400 mapper.addMapping(input_schema[
'slot_Centroid_y'].asKey())
403 aper_fields = input_schema.extract(
'base_CircularApertureFlux_*')
404 for field, item
in aper_fields.items():
405 mapper.addMapping(item.key)
408 apflux_fields = input_schema.extract(
'slot_ApFlux_*')
409 for field, item
in apflux_fields.items():
410 mapper.addMapping(item.key)
412 calibflux_fields = input_schema.extract(
'slot_CalibFlux_*')
413 for field, item
in calibflux_fields.items():
414 mapper.addMapping(item.key)
417 input_schema[self.config.source_selector.active.signalToNoise.fluxField].asKey(),
418 'calib_psf_selection_flux')
420 input_schema[self.config.source_selector.active.signalToNoise.errField].asKey(),
421 'calib_psf_selection_flux_err')
423 output_schema = mapper.getOutputSchema()
425 output_schema.addField(
426 'calib_psf_candidate',
428 doc=(
'set if the source was a candidate for PSF determination, '
429 'as determined from FinalizeCharacterizationTask.'),
431 output_schema.addField(
432 'calib_psf_reserved',
434 doc=(
'set if source was reserved from PSF determination by '
435 'FinalizeCharacterizationTask.'),
437 output_schema.addField(
440 doc=(
'set if source was used in the PSF determination by '
441 'FinalizeCharacterizationTask.'),
443 output_schema.addField(
446 doc=
'Visit number for the sources.',
448 output_schema.addField(
451 doc=
'Detector number for the sources.',
454 alias_map = input_schema.getAliasMap()
455 alias_map_output = afwTable.AliasMap()
456 alias_map_output.set(
'slot_Centroid', alias_map.get(
'slot_Centroid'))
457 alias_map_output.set(
'slot_ApFlux', alias_map.get(
'slot_ApFlux'))
458 alias_map_output.set(
'slot_CalibFlux', alias_map.get(
'slot_CalibFlux'))
460 output_schema.setAliasMap(alias_map_output)
462 return mapper, output_schema
490 Concatenate isolated star catalogs and make reserve selection.
495 Band name. Used to select reserved stars.
496 isolated_star_cat_dict : `dict`
497 Per-tract dict of isolated star catalog handles.
498 isolated_star_source_dict : `dict`
499 Per-tract dict of isolated star source catalog handles.
503 isolated_table : `np.ndarray` (N,)
504 Table of isolated stars, with indexes to isolated sources.
505 isolated_source_table : `np.ndarray` (M,)
506 Table of isolated sources, with indexes to isolated stars.
509 isolated_sources = []
510 merge_cat_counter = 0
511 merge_source_counter = 0
513 for tract
in isolated_star_cat_dict:
514 df_cat = isolated_star_cat_dict[tract].get()
515 table_cat = df_cat.to_records()
517 df_source = isolated_star_source_dict[tract].get(
518 parameters={
'columns': [self.config.id_column,
521 table_source = df_source.to_records()
524 (use_band,) = (table_cat[f
'nsource_{band}'] > 0).nonzero()
526 if len(use_band) == 0:
528 self.log.info(
"No sources found in %s band in tract %d.", band, tract)
533 obj_index = table_source[
'obj_index'][:]
534 a, b = esutil.numpy_util.match(use_band, obj_index)
537 table_source[
'obj_index'][b] = a
538 _, index_new = np.unique(a, return_index=
True)
539 table_cat[f
'source_cat_index_{band}'][use_band] = index_new
550 table_source = table_source[b]
551 table_cat = table_cat[use_band]
554 table_cat = np.lib.recfunctions.append_fields(
557 np.zeros(table_cat.size, dtype=bool),
560 table_source = np.lib.recfunctions.append_fields(
563 np.zeros(table_source.size, dtype=bool),
568 table_cat[
'reserved'][:] = self.reserve_selection.run(
570 extra=f
'{band}_{tract}',
572 table_source[
'reserved'][:] = table_cat[
'reserved'][table_source[
'obj_index']]
575 table_cat[f
'source_cat_index_{band}'] += merge_source_counter
576 table_source[
'obj_index'] += merge_cat_counter
578 isolated_tables.append(table_cat)
579 isolated_sources.append(table_source)
581 merge_cat_counter += len(table_cat)
582 merge_source_counter += len(table_source)
584 isolated_table = np.concatenate(isolated_tables)
585 isolated_source_table = np.concatenate(isolated_sources)
587 return isolated_table, isolated_source_table
590 """Compute psf model and aperture correction map for a single exposure.
595 Visit number (for logging).
597 Detector number (for logging).
598 exposure : `lsst.afw.image.ExposureF`
599 src : `lsst.afw.table.SourceCatalog`
600 isolated_source_table : `np.ndarray`
604 psf : `lsst.meas.algorithms.ImagePsf`
606 ap_corr_map : `lsst.afw.image.ApCorrMap`
607 Aperture correction map.
608 measured_src : `lsst.afw.table.SourceCatalog`
609 Updated source catalog with measurements, flags and aperture corrections.
612 good_src = self.source_selector.selectSources(src)
613 if sum(good_src.selected) == 0:
614 self.log.warning(
'No good sources remain after cuts for visit %d, detector %d',
616 return None,
None,
None
625 selected_src = afwTable.SourceCatalog(selection_schema)
626 selected_src.reserve(good_src.selected.sum())
627 selected_src.extend(src[good_src.selected], mapper=selection_mapper)
631 selected_src[
'calib_psf_candidate'] = np.zeros(len(selected_src), dtype=bool)
632 selected_src[
'calib_psf_used'] = np.zeros(len(selected_src), dtype=bool)
633 selected_src[
'calib_psf_reserved'] = np.zeros(len(selected_src), dtype=bool)
636 matched_src, matched_iso = esutil.numpy_util.match(
638 isolated_source_table[self.config.id_column]
641 matched_arr = np.zeros(len(selected_src), dtype=bool)
642 matched_arr[matched_src] =
True
643 selected_src[
'calib_psf_candidate'] = matched_arr
645 reserved_arr = np.zeros(len(selected_src), dtype=bool)
646 reserved_arr[matched_src] = isolated_source_table[
'reserved'][matched_iso]
647 selected_src[
'calib_psf_reserved'] = reserved_arr
649 selected_src = selected_src[selected_src[
'calib_psf_candidate']].copy(deep=
True)
652 measured_src = afwTable.SourceCatalog(self.
schema)
653 measured_src.reserve(len(selected_src))
657 measured_src[
'calib_psf_candidate'] = selected_src[
'calib_psf_candidate']
658 measured_src[
'calib_psf_reserved'] = selected_src[
'calib_psf_reserved']
662 psf_selection_result = self.make_psf_candidates.run(selected_src, exposure=exposure)
663 except Exception
as e:
664 self.log.warning(
'Failed to make psf candidates for visit %d, detector %d: %s',
666 return None,
None, measured_src
668 psf_cand_cat = psf_selection_result.goodStarCat
672 psf_determiner_list = [cand
for cand, use
673 in zip(psf_selection_result.psfCandidates,
674 ~psf_cand_cat[
'calib_psf_reserved'])
if use]
675 flag_key = psf_cand_cat.schema[
'calib_psf_used'].asKey()
677 psf, cell_set = self.psf_determiner.determinePsf(exposure,
681 except Exception
as e:
682 self.log.warning(
'Failed to determine psf for visit %d, detector %d: %s',
684 return None,
None, measured_src
691 matched_selected, matched_measured = esutil.numpy_util.match(
695 measured_used = np.zeros(len(measured_src), dtype=bool)
696 measured_used[matched_measured] = selected_src[
'calib_psf_used'][matched_selected]
697 measured_src[
'calib_psf_used'] = measured_used
701 self.measurement.run(measCat=measured_src, exposure=exposure)
702 except Exception
as e:
703 self.log.warning(
'Failed to make measurements for visit %d, detector %d: %s',
705 return psf,
None, measured_src
709 ap_corr_map = self.measure_ap_corr.run(exposure=exposure,
710 catalog=measured_src).apCorrMap
711 except Exception
as e:
712 self.log.warning(
'Failed to compute aperture corrections for visit %d, detector %d: %s',
714 return psf,
None, measured_src
716 self.apply_ap_corr.run(catalog=measured_src, apCorrMap=ap_corr_map)
718 return psf, ap_corr_map, measured_src