323 inputs = butlerQC.get(inputRefs)
324 columns_ref, columns_target = (tuple(columns)
for columns
in self.config.get_columns_ordered_in())
325 skymap = inputs.pop(
"skymap")
if (self.config.refcat_sharding_type !=
'none')
or (
326 self.config.target_sharding_type !=
'none')
else None
327 is_refcat_per_tract = self.config.refcat_sharding_type ==
'tract'
328 is_target_per_tract = self.config.target_sharding_type ==
'tract'
330 if self.config.match_multiple_target:
331 if is_target_per_tract:
332 names_columns = [(
"cat_target", columns_target)]
335 catalogs = {
"cat_target": inputs[
"cat_target"].get(parameters={
'columns': columns_target})}
337 if is_refcat_per_tract:
338 names_columns.append((
"cat_ref", columns_ref))
340 catalogs[
"cat_ref"] = inputs[
"cat_ref"].get(parameters={
'columns': columns_ref})
342 for name, columns
in names_columns:
345 for idx, (tract, handle)
in enumerate(sorted(
346 (inputRef.dataId[
"tract"], inputHandle)
347 for inputRef, inputHandle
in zip(getattr(inputRefs, name), inputs[name], strict=
True)
349 handles.append(handle)
350 extra_values[idx] = {
"tract": tract}
351 catalogs[name] = TableVStack.vstack_handles(
353 extra_values=extra_values,
354 kwargs_get={
"parameters": {
"columns": columns}}
356 catalog_ref, catalog_target = catalogs[
"cat_ref"], catalogs[
"cat_target"]
358 catalog_ref, catalog_target = (
359 inputs[name].get(parameters={
'columns': columns})
360 for name, columns
in ((
'cat_ref', columns_ref), (
'cat_target', columns_target))
362 if self.config.match_multiple_target:
366 catalog_ref=catalog_ref,
367 catalog_target=catalog_target,
368 wcs=
None if (self.config.match_multiple_target
or (skymap
is None))
else (
369 skymap[butlerQC.quantum.dataId[
"tract"]].wcs),
372 if self.config.match_multiple_target:
375 for name, catalog_in, name_tract
in (
376 (
"cat_output_ref", catalog_ref, outputs.name_tract_ref_in),
377 (
"cat_output_target", catalog_target,
"tract"),
380 for outputRef
in getattr(outputRefs, name):
381 tract = outputRef.dataId[
"tract"]
382 catalog_out = getattr(outputs, name)
383 catalogs_out.append(catalog_out[catalog_in[name_tract] == tract])
384 outputs_new[name] = catalogs_out
386 if self.config.output_matched_catalog:
387 cat_matched = outputs.cat_output_matched
389 tract_target = cat_matched[outputs.name_tract_target]
390 patch_target = cat_matched[outputs.name_patch_target]
391 masked_target = tract_target.mask ==
True
393 tract_ref = cat_matched[outputs.name_tract_ref]
394 tract_out = np.array(tract_target)
395 tract_out[masked_target] = tract_ref[masked_target]
396 cat_matched[
"tract"] = tract_out
397 cat_matched[
"tract"].description = (f
"{outputs.name_tract_target} if available"
398 f
" else {outputs.name_tract_ref}")
399 patch_ref = cat_matched[outputs.name_patch_ref]
400 patch_out = np.array(patch_target)
402 patch_out[masked_target] = patch_ref[masked_target]
403 cat_matched[
"patch"] = patch_out
404 cat_matched[
"patch"].description = (f
"{outputs.name_patch_target} if available"
405 f
" else {outputs.name_patch_ref}")
407 for outputRef
in outputRefs.cat_output_matched:
408 tract = outputRef.dataId[
"tract"]
409 catalogs_out.append(cat_matched[tract_out == tract])
410 outputs_new[
"cat_output_matched"] = catalogs_out
412 outputs = pipeBase.Struct(
414 **{k: v
for k, v
in outputs.getDict().items()
if k
not in outputs_new},
417 butlerQC.put(outputs, outputRefs)
421 catalog_ref: pd.DataFrame | astropy.table.Table,
422 catalog_target: pd.DataFrame | astropy.table.Table,
423 wcs: afwGeom.SkyWcs =
None,
424 ) -> pipeBase.Struct:
425 """Match sources in a reference tract catalog with a target catalog.
429 catalog_ref : `pandas.DataFrame` | `astropy.table.Table`
430 A reference catalog to match objects/sources from.
431 catalog_target : `pandas.DataFrame` | `astropy.table.Table`
432 A target catalog to match reference objects/sources to.
433 wcs : `lsst.afw.image.SkyWcs`
434 A coordinate system to convert catalog positions to sky coordinates,
439 retStruct : `lsst.pipe.base.Struct`
440 A struct with output_ref and output_target attribute containing the
441 output matched catalogs. If self.config.output_matched_catalog is
442 True, cat_output_matched is also returned, along with the names of
443 the tract/patch columns, which may have been changed to avoid
446 output = self.match_tract_catalog.run(catalog_ref, catalog_target, wcs=wcs)
447 if output.exceptions:
448 self.log.warning(
'Exceptions: %s', output.exceptions)
449 retStruct = pipeBase.Struct(cat_output_ref=output.cat_output_ref,
450 cat_output_target=output.cat_output_target)
452 if self.config.output_matched_catalog:
456 name_tract_ref_in =
"tract"
457 diff_prefix_ref = self.config.diff_matched_catalog.value.column_matched_prefix_ref
458 diff_prefix_target = self.config.diff_matched_catalog.value.column_matched_prefix_target
459 name_tract_ref = f
"{diff_prefix_ref}tract"
460 name_tract_target = f
"{diff_prefix_target}tract"
461 name_patch_ref = f
"{diff_prefix_ref}patch"
462 name_patch_target = f
"{diff_prefix_target}patch"
464 if (name_tract_ref_in
in catalog_ref.colnames)
and (name_tract_ref == name_tract_target):
465 prefix_new =
"ref_" if diff_prefix_ref !=
"ref_" else "refcat_"
466 name_new = f
"{prefix_new}tract"
467 catalog_ref.rename_column(name_tract_ref, name_new)
468 name_tract_ref = name_new
469 name_tract_ref_in = name_new
470 if name_patch_ref
in catalog_ref.colnames:
471 name_new = f
"{prefix_new}patch"
472 catalog_ref.rename_column(name_patch_ref, name_new)
473 name_patch_ref = name_new
475 outputs_new = self.diff_matched_catalog.run(
476 catalog_ref=catalog_ref,
477 catalog_target=catalog_target,
478 catalog_match_ref=retStruct.cat_output_ref,
479 catalog_match_target=retStruct.cat_output_target,
481 retStruct = pipeBase.Struct(
482 **retStruct.getDict(),
483 cat_output_matched=outputs_new.cat_matched,
484 name_tract_ref=name_tract_ref,
485 name_tract_ref_in=name_tract_ref_in,
486 name_tract_target=name_tract_target,
487 name_patch_ref=name_patch_ref,
488 name_patch_target=name_patch_target,
494 """Add a tract column to catalogs that may be missing it.
503 The skymap info to use.
505 Whether to add a patch column as well.
508 if compute_tract_target := (
"tract" not in catalog_target.colnames):
509 if self.config.target_sharding_type !=
"none":
511 f
"Target catalog has no tract column with {self.config.target_sharding_type=} != 'none'"
513 if compute_tract_ref := (
"tract" not in catalog_ref.colnames):
514 if self.config.refcat_sharding_type !=
"none":
516 f
"Ref catalog has no tract column with {self.config.refcat_sharding_type=} != 'none'"
519 raise RuntimeError(
"; ".join(errors))
520 compute_patch_ref = add_patch
and (
"patch" not in catalog_ref.colnames)
521 compute_patch_target = add_patch
and (
"patch" not in catalog_target.colnames)
523 if compute_tract_target
or compute_tract_ref
or compute_patch_target
or compute_patch_ref:
524 if not self.config.diff_matched_catalog.coord_format.coords_spherical:
526 f
"Can't compute tract columns with unless"
527 f
" {self.config.diff_matched_catalog.coord_format.coords_spherical} == True"
529 ref_c, target_c = self.config.diff_matched_catalog.coord_format.format_catalogs(
530 catalog_ref=catalog_ref, catalog_target=catalog_target,
533 if compute_tract_target
or compute_patch_target:
535 target_c, catalog_target,
"target", compute_tract_target, compute_patch_target
537 if compute_tract_ref:
538 cats_add.append((ref_c, catalog_ref,
"ref", compute_tract_ref, compute_patch_ref))
539 for cat_c, catalog_add, name_c, compute_tract, compute_patch
in cats_add:
540 if (unit := getattr(catalog_add[cat_c.column_coord1],
"unit"))
is None:
541 unit = self.config.coord_unit
544 f
"Must specify coord_unit since {name_c} column={cat_c.column_coord1}"
549 unit = getattr(
lsst.geom, unit_dict[str(unit)])
550 coords = [
SpherePoint(ra, dec, unit)
for ra, dec
in zip(cat_c.coord1, cat_c.coord2)]
552 catalog_add[
"tract"] = np.array([skymap.findTract(coord).getId()
for coord
in coords])
554 tracts = catalog_add[
"tract"]
556 skymap[tract].findPatch(coord).getSequentialIndex()
557 for coord, tract
in zip(coords, tracts)
559 catalog_add[
"patch"] = patches