105.3. Forced photometry¶
105.3. Forced Photometry¶
Data Release: Data Preview 1
Container Size: large
LSST Science Pipelines version: Release r29.1.1
Last verified to run: 2025-06-20
Repository: github.com/lsst/tutorial-notebooks
Learning objective: Measure sources from an image via forced photometry using IDs and coordinates.
LSST data products: deep_coadd
, object
table
Packages: lsst.pipe.tasks
, lsst.daf.butler
Credit: Originally developed by the Rubin Community Science team working with Erfan Nourbakhsh. Please consider acknowledging them if this notebook is used for the preparation of journal articles, software releases, or other notebooks.
Get Support: Everyone is encouraged to ask questions or raise issues in the Support Category of the Rubin Community Forum. Rubin staff will respond to all questions posted there.
1. Introduction¶
This notebook performs forced photometry using the Data Preview 1 (DP1) data products on an input image, given a set of input IDs and RA/Dec coordinates. The ForcedMeasurementDriverTask
serves as a convenience function, allowing users to measure sources without running a full-scale pipetask
command or dealing with additional pipeline setup. This task measures fluxes, shapes, and other properties at known source positions (e.g., from a pre-existing catalog) on a given image, without running source detection.
Related tutorials: Detect and measure sources
1.1. Import packages¶
Import numpy
, a fundamental package for scientific computing with arrays in Python
(numpy.org). From the lsst
package, we import Butler
to access the LSST data repository, which allows us to retrieve images and catalogs using data IDs. Also import ForcedMeasurementDriverConfig
and ForcedMeasurementDriverTask
, which are mid-level config and task classes used to set up and execute forced photometry.
import numpy as np
from lsst.daf.butler import Butler
from lsst.pipe.tasks.measurementDriver import (
ForcedMeasurementDriverConfig,
ForcedMeasurementDriverTask,
)
1.2. Define parameters and functions¶
Create an instance of the butler, and assert that it exists.
butler = Butler("dp1", collections="LSSTComCam/DP1")
assert butler is not None
2. Load an image and table¶
Identify coadded images overlapping a particular sky position and select one to run forced photometry on. Also load the corresponding object
table using the butler so the forced photometry can be run at the positions of already-detected objects.
ra = 53.076
dec = -28.110
band = 'r'
query = f"band='{band}' AND \
patch.region OVERLAPS POINT({ra}, {dec})"
coadd_img_refs = butler.query_datasets('deep_coadd',
where=query,
order_by='patch.id')
ref = coadd_img_refs[0]
exposure = butler.get("deep_coadd", dataId=ref.dataId)
objtable = butler.get("object",
dataId=ref.dataId,
storageClass="ArrowAstropy",
parameters={"columns": ["objectId", "coord_ra", "coord_dec", "tract",
"patch",
f"{band}_psfFlux", "refExtendedness"]},
)
The object
table contains measurements from the deep_coadd
images for a full tract. It is quite large, so cut it down to a only include the patch of interest to make it a manageable size.
Select objects from the same tract and patch as the loaded image.
pick = np.where((objtable["tract"] == ref.dataId["tract"])
& (objtable["patch"] == ref.dataId["patch"]))
print(f"Length of patch table: {len(pick[0])}")
Length of patch table: 11123
Grab the first 5 matching entries with just their objectId
and coordinates for testing. These are the only required inputs for the ForcedMeasurementDriverTask
.
table = objtable[pick][:5].copy()["objectId", "coord_ra", "coord_dec"]
table
objectId | coord_ra | coord_dec |
---|---|---|
int64 | float64 | float64 |
611254385447534870 | 53.22648161543073 | -28.186979527401327 |
611254385447534887 | 53.20215945266285 | -28.186654031188027 |
611254385447534890 | 53.18789858793983 | -28.186399452233857 |
611254385447534905 | 53.108281109881865 | -28.186148514289624 |
611254385447534911 | 53.12831647802782 | -28.18576147271307 |
3. Run the forced measurement driver¶
Configure the forced measurement driver to use base_PsfFlux
as the algorithm for the PSF flux slot, and base_TransformedCentroidFromCoord
for centroids transformed from the reference catalog. Set other config parameters to disable shape measurement, avoid replacing other detected footprints with noise, and enable aperture correction for the selected flux slot. These are simply examples of how to set configuration options, and are not required to run the task.
Note: base_PsfFlux
and base_TransformedCentroidFromCoord
are included by default in the measurement plugins, so there's no need to add them manually to config.measurement.plugins.names
. They’ll be picked up automatically.
config = ForcedMeasurementDriverConfig()
config.measurement.slots.psfFlux = "base_PsfFlux"
config.measurement.slots.centroid = "base_TransformedCentroidFromCoord"
config.measurement.slots.shape = None
config.measurement.doReplaceWithNoise = False
config.doApCorr = True
Create the forced photometry driver task using the configuration.
driver = ForcedMeasurementDriverTask(config=config)
Run the task using the input table of source positions and IDs, the deep_coadd
exposure image, and required parameters: column names for ID, RA, and Dec, plus a PSF footprint scaling factor (used to create synthetic footprints since detection is skipped in forced photometry).
result = driver.runFromAstropy(
table,
exposure,
id_column_name="objectId",
ra_column_name="coord_ra",
dec_column_name="coord_dec",
psf_footprint_scaling=3.0,
)
lsst.forcedMeasurementDriver INFO: Measuring 5 sources in a single band using 'ForcedMeasurementTask'
lsst.forcedMeasurementDriver.measurement INFO: Performing forced measurement on 5 sources
lsst.forcedMeasurementDriver INFO: Applying aperture corrections to a single band
lsst.forcedMeasurementDriver.applyApCorr INFO: Applying aperture corrections to 1 instFlux fields
lsst.forcedMeasurementDriver INFO: Finished processing for a single band; output catalog has 88 fields and 5 records
Examine the resulting Astropy table containing the measured sources. Each row corresponds to a record from the input table, with columns for source ID, RA, and Dec, along with additional measurement fields defined by the configuration.
result
coord_ra | coord_dec | parent | objectId | parentObjectId | deblend_nChild | base_TransformedCentroidFromCoord_x | slot_Centroid_x | base_TransformedCentroidFromCoord_y | slot_Centroid_y | base_CircularApertureFlux_3_0_instFlux | base_CircularApertureFlux_3_0_instFluxErr | base_CircularApertureFlux_3_0_flag | base_CircularApertureFlux_3_0_flag_apertureTruncated | base_CircularApertureFlux_3_0_flag_sincCoeffsTruncated | base_CircularApertureFlux_4_5_instFlux | base_CircularApertureFlux_4_5_instFluxErr | base_CircularApertureFlux_4_5_flag | base_CircularApertureFlux_4_5_flag_apertureTruncated | base_CircularApertureFlux_4_5_flag_sincCoeffsTruncated | base_CircularApertureFlux_6_0_instFlux | base_CircularApertureFlux_6_0_instFluxErr | base_CircularApertureFlux_6_0_flag | base_CircularApertureFlux_6_0_flag_apertureTruncated | base_CircularApertureFlux_6_0_flag_sincCoeffsTruncated | base_CircularApertureFlux_9_0_instFlux | base_CircularApertureFlux_9_0_instFluxErr | base_CircularApertureFlux_9_0_flag | base_CircularApertureFlux_9_0_flag_apertureTruncated | base_CircularApertureFlux_9_0_flag_sincCoeffsTruncated | base_CircularApertureFlux_12_0_instFlux | base_CircularApertureFlux_12_0_instFluxErr | base_CircularApertureFlux_12_0_flag | base_CircularApertureFlux_12_0_flag_apertureTruncated | base_CircularApertureFlux_17_0_instFlux | base_CircularApertureFlux_17_0_instFluxErr | base_CircularApertureFlux_17_0_flag | base_CircularApertureFlux_17_0_flag_apertureTruncated | base_CircularApertureFlux_25_0_instFlux | base_CircularApertureFlux_25_0_instFluxErr | base_CircularApertureFlux_25_0_flag | base_CircularApertureFlux_25_0_flag_apertureTruncated | base_CircularApertureFlux_35_0_instFlux | base_CircularApertureFlux_35_0_instFluxErr | base_CircularApertureFlux_35_0_flag | base_CircularApertureFlux_35_0_flag_apertureTruncated | base_CircularApertureFlux_50_0_instFlux | base_CircularApertureFlux_50_0_instFluxErr | base_CircularApertureFlux_50_0_flag | base_CircularApertureFlux_50_0_flag_apertureTruncated | base_CircularApertureFlux_70_0_instFlux | base_CircularApertureFlux_70_0_instFluxErr | base_CircularApertureFlux_70_0_flag | base_CircularApertureFlux_70_0_flag_apertureTruncated | base_PixelFlags_flag | base_PixelFlags_flag_offimage | base_PixelFlags_flag_edge | base_PixelFlags_flag_nodata | base_PixelFlags_flag_interpolated | base_PixelFlags_flag_saturated | base_PixelFlags_flag_cr | base_PixelFlags_flag_bad | base_PixelFlags_flag_suspect | base_PixelFlags_flag_edgeCenter | base_PixelFlags_flag_nodataCenter | base_PixelFlags_flag_interpolatedCenter | base_PixelFlags_flag_saturatedCenter | base_PixelFlags_flag_crCenter | base_PixelFlags_flag_badCenter | base_PixelFlags_flag_suspectCenter | base_PixelFlags_flag_edgeCenterAll | base_PixelFlags_flag_nodataCenterAll | base_PixelFlags_flag_interpolatedCenterAll | base_PixelFlags_flag_saturatedCenterAll | base_PixelFlags_flag_crCenterAll | base_PixelFlags_flag_badCenterAll | base_PixelFlags_flag_suspectCenterAll | base_PsfFlux_instFlux | slot_PsfFlux_instFlux | base_PsfFlux_instFluxErr | slot_PsfFlux_instFluxErr | base_PsfFlux_area | slot_PsfFlux_area | base_PsfFlux_chi2 | slot_PsfFlux_chi2 | base_PsfFlux_npixels | slot_PsfFlux_npixels | base_PsfFlux_flag | slot_PsfFlux_flag | base_PsfFlux_flag_noGoodPixels | slot_PsfFlux_flag_noGoodPixels | base_PsfFlux_flag_edge | slot_PsfFlux_flag_edge | base_InvalidPsf_flag | base_PsfFlux_apCorr | slot_PsfFlux_apCorr | base_PsfFlux_apCorrErr | slot_PsfFlux_apCorrErr | base_PsfFlux_flag_apCorr | slot_PsfFlux_flag_apCorr |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
rad | rad | pix | pix | pix | pix | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | ct | pix | pix | pix | pix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
float64 | float64 | int64 | int64 | int64 | int32 | float64 | float64 | float64 | float64 | float64 | float64 | bool | bool | bool | float64 | float64 | bool | bool | bool | float64 | float64 | bool | bool | bool | float64 | float64 | bool | bool | bool | float64 | float64 | bool | bool | float64 | float64 | bool | bool | float64 | float64 | bool | bool | float64 | float64 | bool | bool | float64 | float64 | bool | bool | float64 | float64 | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | bool | float64 | float64 | float64 | float64 | float32 | float32 | float32 | float32 | int32 | int32 | bool | bool | bool | bool | bool | bool | bool | float64 | float64 | float64 | float64 | bool | bool |
0.9289773534414965 | -0.4919555989453884 | 0 | 611254385447534870 | 0 | 0 | 12794.763762297036 | 12794.763762297036 | 3003.466269001161 | 3003.466269001161 | 508.3687438964844 | 6.665900707244873 | False | False | False | 770.1231079101562 | 9.93996524810791 | False | False | False | 955.5059814453125 | 13.295246124267578 | False | False | False | 1139.1279296875 | 19.969926834106445 | False | False | False | 1179.2709827423096 | 26.884258101689277 | False | False | 1260.2906643748283 | 38.04785087164237 | False | False | 1358.9937336146832 | 55.84397631440843 | False | False | 1718.5288484096527 | 78.17241098028421 | False | False | 3039.611535191536 | 111.60576319761655 | False | False | 6560.780028581619 | 156.39657352631951 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | 974.6182453061164 | 974.6182453061164 | 11.364497557955273 | 11.364497557955273 | 80.06211 | 80.06211 | 1259.3179 | 1259.3179 | 1225 | 1225 | False | False | False | False | False | False | False | 0.9912035362201694 | 0.9912035362201694 | 0.0 | 0.0 | False | False |
0.928552851619991 | -0.4919499179647635 | 0 | 611254385447534887 | 0 | 0 | 13180.666428726481 | 13180.666428726481 | 3009.729317068548 | 3009.729317068548 | 37.971256256103516 | 6.537532329559326 | False | False | False | 61.384830474853516 | 9.772468566894531 | False | False | False | 75.80436706542969 | 13.086181640625 | False | False | False | 104.4230728149414 | 19.707996368408203 | False | False | False | 184.8607995212078 | 26.587062346690335 | False | False | 322.8769664466381 | 37.6573457360617 | False | False | 1441.6865410208702 | 55.47213113715018 | False | False | 17948.585873931646 | 77.93020742295528 | False | False | 50428.610148727894 | 111.41405230536085 | False | False | 77395.19233214855 | 155.7412771018013 | False | False | False | False | False | False | True | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | 76.54020507067322 | 76.54020507067322 | 11.17328180985954 | 11.17328180985954 | 80.522316 | 80.522316 | 1164.1997 | 1164.1997 | 1225 | 1225 | False | False | False | False | False | False | False | 0.9915093377042407 | 0.9915093377042407 | 0.0 | 0.0 | False | False |
0.9283039525763928 | -0.491945474723807 | 0 | 611254385447534890 | 0 | 0 | 13406.93347842264 | 13406.93347842264 | 3014.5128488849914 | 3014.5128488849914 | 185.5901336669922 | 6.468040943145752 | False | False | False | 285.6594543457031 | 9.704038619995117 | False | False | False | 326.20098876953125 | 13.019852638244629 | False | False | False | 391.8002624511719 | 19.607486724853516 | False | False | False | 468.4096273779869 | 26.439470770047226 | False | False | 583.5716409087181 | 37.423573852395144 | False | False | 1091.416959285736 | 54.869677380003914 | False | False | 2269.0049473941326 | 76.87333089288077 | False | False | 5434.982126444578 | 109.90134999836206 | False | False | 68463.42139658332 | 154.48411469713955 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | 359.6370891157531 | 359.6370891157531 | 11.084436307905435 | 11.084436307905435 | 80.439285 | 80.439285 | 1244.6193 | 1244.6193 | 1225 | 1225 | False | False | False | False | False | False | False | 0.9920718805118075 | 0.9920718805118075 | 0.0 | 0.0 | False | False |
0.926914365442147 | -0.4919410950304619 | 0 | 611254385447534905 | 0 | 0 | 14670.188650312628 | 14670.188650312628 | 3019.6607777124336 | 3019.6607777124336 | 154.94583129882812 | 6.7749176025390625 | False | False | False | 244.2054901123047 | 10.124290466308594 | False | False | False | 292.4091796875 | 13.573420524597168 | False | False | False | 310.5824890136719 | 20.43981170654297 | False | False | False | 301.5161562561989 | 27.54426059444401 | False | False | 374.0983957648277 | 38.98112749475285 | False | False | 409.90519720315933 | 57.31104598182115 | False | False | 1141.3614585399628 | 80.26401248891636 | False | False | 2005.0532057285309 | 114.69537985286343 | False | False | 5913.335474818945 | 160.59163545944864 | False | False | False | False | False | False | True | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | 296.6360079792229 | 296.6360079792229 | 11.579363562317782 | 11.579363562317782 | 80.608665 | 80.608665 | 1239.9381 | 1239.9381 | 1225 | 1225 | False | False | False | False | False | False | False | 0.9906901634557377 | 0.9906901634557377 | 0.0 | 0.0 | False | False |
0.927264048583143 | -0.491934339880609 | 0 | 611254385447534911 | 0 | 0 | 14352.29335932013 | 14352.29335932013 | 3026.547898794748 | 3026.547898794748 | 83.66658782958984 | 6.598418712615967 | False | False | False | 145.59071350097656 | 9.870516777038574 | False | False | False | 179.47401428222656 | 13.228277206420898 | False | False | False | 223.35696411132812 | 19.953493118286133 | False | False | False | 237.26244297623634 | 26.952998369444664 | False | False | 253.3319989144802 | 38.17747256803523 | False | False | 523.4960869252682 | 56.23464481366406 | False | False | 1111.902500629425 | 78.79277245747345 | False | False | 8702.840077251196 | 112.62334327664705 | False | False | 17946.956145495176 | 157.77988235483815 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | 167.65966984640008 | 167.65966984640008 | 11.236996979299153 | 11.236996979299153 | 79.97352 | 79.97352 | 1176.7106 | 1176.7106 | 1225 | 1225 | False | False | False | False | False | False | False | 0.9917935953031872 | 0.9917935953031872 | 0.0 | 0.0 | False | False |
Note that the table now includes PSF fluxes (base_psfFlux_instFlux
) and aperture fluxes (e.g., base_CircularApertureFlux_3_0_instFlux
for a 3-pixel aperture), among many other columns.
This functionality is limited to a handful of measurement plugins (for example, model-fitting algorithms will not work with this task because they require more ancillary information than it is set up to receive). Nonetheless, it offers a quick and easy way to extract measurements at arbitrary positions.