334 catalog_ref: astropy.table.Table,
335 catalog_target: astropy.table.Table,
336 catalog_match_ref: astropy.table.Table,
337 catalog_match_target: astropy.table.Table,
338 wcs: afwGeom.SkyWcs =
None,
339 ) -> pipeBase.Struct:
340 """Load matched reference and target (measured) catalogs, measure summary statistics, and output
341 a combined matched catalog with columns from both inputs.
345 catalog_ref : `astropy.table.Table`
346 A reference catalog to diff objects/sources from.
347 catalog_target : `astropy.table.Table`
348 A target catalog to diff reference objects/sources to.
349 catalog_match_ref : `astropy.table.Table`
350 A catalog with match indices of target sources and selection flags
351 for each reference source.
352 catalog_match_target : `astropy.table.Table`
353 A catalog with selection flags for each target source.
354 wcs : `lsst.afw.image.SkyWcs`
355 A coordinate system to convert catalog positions to sky coordinates,
360 retStruct : `lsst.pipe.base.Struct`
361 A struct with output_ref and output_target attribute containing the
362 output matched catalogs.
365 config: DiffMatchedTractCatalogConfig = self.config
370 DatasetProvenance.strip_provenance_from_flat_dict(catalog_ref.meta)
371 DatasetProvenance.strip_provenance_from_flat_dict(catalog_target.meta)
372 DatasetProvenance.strip_provenance_from_flat_dict(catalog_match_ref.meta)
373 DatasetProvenance.strip_provenance_from_flat_dict(catalog_match_target.meta)
377 select_ref, select_target = (
378 (catalog[column]
if column
else np.ones(len(catalog), dtype=bool))
379 for catalog, column
in (
380 (catalog_match_ref, self.config.column_match_candidate_ref),
381 (catalog_match_target, self.config.column_match_candidate_target),
386 for column
in config.columns_target_select_true:
387 select_target &= catalog_target[column]
388 for column
in config.columns_target_select_false:
389 select_target &= ~catalog_target[column]
391 ref, target = config.coord_format.format_catalogs(
392 catalog_ref=catalog_ref, catalog_target=catalog_target,
393 select_ref=
None, select_target=select_target, wcs=wcs, radec_to_xy_func=radec_to_xy,
395 cat_ref = ref.catalog
396 cat_target = target.catalog
397 n_target = len(cat_target)
399 if not config.filter_on_match_candidate:
400 for cat_add, cat_match, column
in (
401 (cat_ref, catalog_match_ref, config.column_match_candidate_ref),
402 (cat_target, catalog_match_target, config.column_match_candidate_target),
404 if column
is not None:
405 cat_add[column] = cat_match[column]
407 match_row = catalog_match_ref[
'match_row']
408 matched_ref = match_row >= 0
409 matched_row = match_row[matched_ref]
410 matched_target = np.zeros(n_target, dtype=bool)
411 matched_target[matched_row] =
True
414 coord1_target_err, coord2_target_err = config.columns_target_coord_err
415 column_dist, column_dist_err =
'match_distance',
'match_distanceErr'
416 dist = np.full(n_target, np.nan)
418 target_match_c1, target_match_c2 = (
419 np.array(coord[matched_row])
for coord
in (target.coord1, target.coord2)
421 target_ref_c1, target_ref_c2 = (np.array(coord[matched_ref])
for coord
in (ref.coord1, ref.coord2))
423 dist_err = np.full(n_target, np.nan)
424 dist[matched_row] = sphdist(
425 target_match_c1, target_match_c2, target_ref_c1, target_ref_c2
426 )
if config.coord_format.coords_spherical
else np.hypot(
427 target_match_c1 - target_ref_c1, target_match_c2 - target_ref_c2,
429 cat_target_matched = cat_target[matched_row]
433 np.ma.getdata(cat_target_matched[c_err])
for c_err
in (coord1_target_err, coord2_target_err)
436 dist_err[matched_row] = sphdist(
437 target_match_c1, target_match_c2, target_match_c1 + c1_err, target_match_c2 + c2_err
438 )
if config.coord_format.coords_spherical
else np.hypot(c1_err, c2_err)
439 cat_target[column_dist], cat_target[column_dist_err] = dist, dist_err
442 cat_left = cat_target[matched_row]
443 cat_right = cat_ref[matched_ref]
444 if config.column_matched_prefix_target:
445 cat_left.rename_columns(
446 list(cat_left.columns),
447 new_names=[f
'{config.column_matched_prefix_target}{col}' for col
in cat_left.columns],
449 if config.column_matched_prefix_ref:
450 cat_right.rename_columns(
451 list(cat_right.columns),
452 new_names=[f
'{config.column_matched_prefix_ref}{col}' for col
in cat_right.columns],
454 cat_matched = astropy.table.hstack((cat_left, cat_right))
456 if config.include_unmatched:
460 cat_right = astropy.table.Table(
461 cat_ref[~matched_ref & select_ref]
463 cat_right.rename_columns(
465 [f
"{config.column_matched_prefix_ref}{col}" for col
in cat_right.colnames],
467 match_row_target = catalog_match_target[
'match_row']
468 cat_left = cat_target[~(match_row_target >= 0) & select_target]
469 cat_left.rename_columns(
471 [f
"{config.column_matched_prefix_target}{col}" for col
in cat_left.colnames],
476 cat_unmatched = astropy.table.vstack([cat_left, cat_right])
478 for columns_convert_base, prefix
in (
479 (config.columns_ref_mag_to_nJy, config.column_matched_prefix_ref),
480 (config.columns_target_mag_to_nJy,
""),
482 if columns_convert_base:
484 f
"{prefix}{k}": f
"{prefix}{v}" for k, v
in columns_convert_base.items()
485 }
if prefix
else columns_convert_base
486 to_convert = [cat_matched]
487 if config.include_unmatched:
488 to_convert.append(cat_unmatched)
489 for cat_convert
in to_convert:
490 cat_convert.rename_columns(
491 tuple(columns_convert.keys()),
492 tuple(columns_convert.values()),
494 for column_flux
in columns_convert.values():
495 cat_convert[column_flux] = u.ABmag.to(u.nJy, cat_convert[column_flux])
497 if config.include_unmatched:
499 cat_matched = astropy.table.vstack([cat_matched, cat_unmatched])
500 if (prefix_coord := config.prefix_best_coord)
is not None:
501 columns_coord_best = (
502 f
"{prefix_coord}{col_coord}" for col_coord
in (
503 (
"ra",
"dec")
if config.coord_format.coords_spherical
else (
"coord1",
"coord2")
506 for column_coord_best, column_coord_ref, column_coord_target
in zip(
508 (config.coord_format.column_ref_coord1, config.coord_format.column_ref_coord2),
509 (config.coord_format.column_target_coord1, config.coord_format.column_target_coord2),
511 column_full_ref = f
'{config.column_matched_prefix_ref}{column_coord_ref}'
512 column_full_target = f
'{config.column_matched_prefix_target}{column_coord_target}'
513 values = cat_matched[column_full_ref]
515 values_bad = np.ma.masked_invalid(values).mask
517 values = np.array(values)
518 values[values_bad] = cat_matched[column_full_target][values_bad]
519 cat_matched[column_coord_best] = values
520 cat_matched[column_coord_best].unit = unit
521 cat_matched[column_coord_best].description = (
522 f
"Best {column_coord_best} value from {column_full_ref} if available"
523 f
" else {column_full_target}"
526 retStruct = pipeBase.Struct(cat_matched=cat_matched)