343 catalog_ref: astropy.table.Table,
344 catalog_target: astropy.table.Table,
345 catalog_match_ref: astropy.table.Table,
346 catalog_match_target: astropy.table.Table,
348 ) -> pipeBase.Struct:
349 """Load matched reference and target (measured) catalogs, measure summary statistics, and output
350 a combined matched catalog with columns from both inputs.
354 catalog_ref : `astropy.table.Table`
355 A reference catalog to diff objects/sources from.
356 catalog_target : `astropy.table.Table`
357 A target catalog to diff reference objects/sources to.
358 catalog_match_ref : `astropy.table.Table`
359 A catalog with match indices of target sources and selection flags
360 for each reference source.
361 catalog_match_target : `astropy.table.Table`
362 A catalog with selection flags for each target source.
363 wcs : `lsst.afw.image.SkyWcs`
364 A coordinate system to convert catalog positions to sky coordinates,
369 retStruct : `lsst.pipe.base.Struct`
370 A struct with output_ref and output_target attribute containing the
371 output matched catalogs.
374 config: DiffMatchedTractCatalogConfig = self.config
379 DatasetProvenance.strip_provenance_from_flat_dict(catalog_ref.meta)
380 DatasetProvenance.strip_provenance_from_flat_dict(catalog_target.meta)
381 DatasetProvenance.strip_provenance_from_flat_dict(catalog_match_ref.meta)
382 DatasetProvenance.strip_provenance_from_flat_dict(catalog_match_target.meta)
386 select_ref, select_target = (
387 (catalog[column]
if column
else np.ones(len(catalog), dtype=bool))
388 for catalog, column
in (
389 (catalog_match_ref, self.config.column_match_candidate_ref),
390 (catalog_match_target, self.config.column_match_candidate_target),
395 for column
in config.columns_target_select_true:
396 select_target &= catalog_target[column]
397 for column
in config.columns_target_select_false:
398 select_target &= ~catalog_target[column]
400 ref, target = config.coord_format.format_catalogs(
401 catalog_ref=catalog_ref, catalog_target=catalog_target,
402 select_ref=
None, select_target=select_target, wcs=wcs, radec_to_xy_func=radec_to_xy,
404 cat_ref = ref.catalog
405 cat_target = target.catalog
406 n_target = len(cat_target)
408 if not config.filter_on_match_candidate:
409 for cat_add, cat_match, column
in (
410 (cat_ref, catalog_match_ref, config.column_match_candidate_ref),
411 (cat_target, catalog_match_target, config.column_match_candidate_target),
413 if column
is not None:
414 cat_add[column] = cat_match[column]
416 match_row = catalog_match_ref[
'match_row']
417 matched_ref = match_row >= 0
418 matched_row = match_row[matched_ref]
419 matched_target = np.zeros(n_target, dtype=bool)
420 matched_target[matched_row] =
True
423 coord1_target_err, coord2_target_err = config.columns_target_coord_err
424 column_dist, column_dist_err =
'match_distance',
'match_distanceErr'
425 dist = np.full(n_target, np.nan)
427 target_match_c1, target_match_c2 = (coord[matched_row]
for coord
in (target.coord1, target.coord2))
428 target_ref_c1, target_ref_c2 = (coord[matched_ref]
for coord
in (ref.coord1, ref.coord2))
430 dist_err = np.full(n_target, np.nan)
431 dist[matched_row] = sphdist(
432 target_match_c1, target_match_c2, target_ref_c1, target_ref_c2
433 )
if config.coord_format.coords_spherical
else np.hypot(
434 target_match_c1 - target_ref_c1, target_match_c2 - target_ref_c2,
436 cat_target_matched = cat_target[matched_row]
440 np.ma.getdata(cat_target_matched[c_err])
for c_err
in (coord1_target_err, coord2_target_err)
443 dist_err[matched_row] = sphdist(
444 target_match_c1, target_match_c2, target_match_c1 + c1_err, target_match_c2 + c2_err
445 )
if config.coord_format.coords_spherical
else np.hypot(c1_err, c2_err)
446 cat_target[column_dist], cat_target[column_dist_err] = dist, dist_err
449 cat_left = cat_target[matched_row]
450 cat_right = cat_ref[matched_ref]
451 if config.column_matched_prefix_target:
452 cat_left.rename_columns(
453 list(cat_left.columns),
454 new_names=[f
'{config.column_matched_prefix_target}{col}' for col
in cat_left.columns],
456 if config.column_matched_prefix_ref:
457 cat_right.rename_columns(
458 list(cat_right.columns),
459 new_names=[f
'{config.column_matched_prefix_ref}{col}' for col
in cat_right.columns],
461 cat_matched = astropy.table.hstack((cat_left, cat_right))
463 if config.include_unmatched:
467 cat_right = astropy.table.Table(
468 cat_ref[~matched_ref & select_ref]
470 cat_right.rename_columns(
472 [f
"{config.column_matched_prefix_ref}{col}" for col
in cat_right.colnames],
474 match_row_target = catalog_match_target[
'match_row']
475 cat_left = cat_target[~(match_row_target >= 0) & select_target]
476 cat_left.rename_columns(
478 [f
"{config.column_matched_prefix_target}{col}" for col
in cat_left.colnames],
483 cat_unmatched = astropy.table.vstack([cat_left, cat_right])
485 for columns_convert_base, prefix
in (
486 (config.columns_ref_mag_to_nJy, config.column_matched_prefix_ref),
487 (config.columns_target_mag_to_nJy,
""),
489 if columns_convert_base:
491 f
"{prefix}{k}": f
"{prefix}{v}" for k, v
in columns_convert_base.items()
492 }
if prefix
else columns_convert_base
493 to_convert = [cat_matched]
494 if config.include_unmatched:
495 to_convert.append(cat_unmatched)
496 for cat_convert
in to_convert:
497 cat_convert.rename_columns(
498 tuple(columns_convert.keys()),
499 tuple(columns_convert.values()),
501 for column_flux
in columns_convert.values():
502 cat_convert[column_flux] = u.ABmag.to(u.nJy, cat_convert[column_flux])
504 if config.include_unmatched:
506 cat_matched = astropy.table.vstack([cat_matched, cat_unmatched])
507 if (prefix_coord := config.prefix_best_coord)
is not None:
508 columns_coord_best = (
509 f
"{prefix_coord}{col_coord}" for col_coord
in (
510 (
"ra",
"dec")
if config.coord_format.coords_spherical
else (
"coord1",
"coord2")
513 for column_coord_best, column_coord_ref, column_coord_target
in zip(
515 (config.coord_format.column_ref_coord1, config.coord_format.column_ref_coord2),
516 (config.coord_format.column_target_coord1, config.coord_format.column_target_coord2),
518 column_full_ref = f
'{config.column_matched_prefix_ref}{column_coord_ref}'
519 column_full_target = f
'{config.column_matched_prefix_target}{column_coord_target}'
520 values = cat_matched[column_full_ref]
522 values_bad = np.ma.masked_invalid(values).mask
524 values = np.array(values)
525 values[values_bad] = cat_matched[column_full_target][values_bad]
526 cat_matched[column_coord_best] = values
527 cat_matched[column_coord_best].unit = unit
528 cat_matched[column_coord_best].description = (
529 f
"Best {columns_coord_best} value from {column_full_ref} if available"
530 f
" else {column_full_target}"
533 retStruct = pipeBase.Struct(cat_matched=cat_matched)