pfsConfig
Overview
pfsConfig records the realized fiber configuration for a specific visit (exposure).
It is the observed counterpart to pfsDesign, capturing where each fiber
actually ended up on the focal plane (as opposed to where it was intended to be). The pfsConfig files are the primary source of all object-related information, e.g. RA/Dec, catalog ID, target type, fiber status, object fluxes (from public catalogs).
Filename format: pfsConfig_PFS_{visit}_{collection}.fits
Example from proposal S25A-000QF, visit 137292 on the Science Platform:
/shared/pfs/programs/S25A-000QF/2d/run26_June2026/pfsConfig/20260111/137292/
pfsConfig_PFS_137292_run26_June2026.fits
FITS structure:
| HDU | Name | Type | Description |
|---|---|---|---|
| #0 | PDU | Header | Actual telescope boresight RA/Dec (degrees) |
| #1 | CONFIG | Binary table | Per-fiber target and position data |
| #2 | PHOTOMETRY | Binary table | Per-fiber flux measurements in multiple filters |
CONFIG table columns (key fields):
| Column | Type | Description |
|---|---|---|
fiberId |
32-bit int | Fiber identifier (starts at 1) |
catId |
32-bit int | Source catalog identifier |
objId |
64-bit int | Unique object identifier |
ra, dec |
64-bit float | Target position (degrees) |
targetType |
32-bit int | Target class (e.g. SCIENCE, SKY, FLUXSTD,...) |
fiberStatus |
32-bit int | Fiber health (e.g. GOOD, BROKENFIBER, BLOCKED,...) |
pfiNominal |
2×32-bit float | Intended fiber position on the focal plane (mm) |
pfiCenter |
2×32-bit float | Actual measured fiber position on the focal plane (mm) |
proposalId |
string | Subaru proposal ID (e.g. S24B-001QN) |
obCode |
string | Observing Block code within a proposal |
PHOTOMETRY table columns (key fields):
| Column | Units | Description |
|---|---|---|
fiberFlux |
nJy | Flux within ~1 arcsec fiber aperture (seeing-corrected) |
psfFlux |
nJy | Flux from PSF fitting to infinite radius |
totalFlux |
nJy | Total flux (PSF for point sources; extended model for galaxies) |
filterName |
— | Filter name specifying the transmission curve |
The fluxes above are collected from public catalogs such as HSC SSP, PS1, GAIA.
Checking All Visits in a Collection
Here we show how to set up Butler and list all available visits in a collection. Simply specify under USER-DEFINED PARAMETERS the 2d DRP data repository location and collection name:
from lsst.daf.butler import Butler
# ==== USER-DEFINED PARAMETERS ====
repo = "/shared/pfs/programs/S25A-000QF/2d/" # path to the 2d DRP repository
collections = "run26_June2026" # collection name
# ==== QUERY ALL VISITS FROM BUTLER AND PRINT ALL VISITS ====
butler = Butler(repo, collections=collections)
all_visits = sorted({ref.dataId['visit'] for ref in butler.registry.queryDatasets('pfsMerged')})
print(f"Total visits: {len(all_visits)}")
print(f"Visits: {all_visits}")
Output:
Total visits: 426
Visits: [137067, 137068, 137070, 137071, 137073, 137074, 137076, 137077, 137277, 137278, 137280, 137281, 137283, 137284, 137286, 137287, 137289, 137290, 137292, 137293, 137295, 137296, 137298, 137299, 137301, 137302, 137308, 137309, 137311, 137312, 137314, 137315, 137317, 137318, 137321, 137322, 137326, 137327, 137329, 137330, 137332, 137333, 137335, 137336, 137338, 137339, 137341, 137342, 137344, 137345, 137347, 137348, 137350, 137351, 137353, 137354, 137421, 137422, 137423, 137424, 137426, 137427, 137428, 137429, 137431, 137432, 137433, 137434, 137436, 137437, 137438, 137439, 137441, 137442, 137443, 137444, 137446, 137447, 137448, 137449, 137452, 137453, 137454, 137455, 137457, 137458, 137459, 137460, 137462, 137463, 137464, 137465, 137467, 137468, 137469, 137470, 137472, 137473, 137474, 137475, 137477, 137478, 137479, 137480, 137482, 137483, 137484, 137485, 137487, 137488, 137489, 137490, 137492, 137493, 137494, 137495, 137497, 137498, 137499, 137500, 137558, 137559, 137560, 137561, 137563, 137564, 137565, 137566, 137568, 137569, 137570, 137571, 137573, 137574, 137575, 137576, 137583, 137584, 137585, 137586, 137588, 137589, 137590, 137591, 137593, 137594, 137595, 137596, 137598, 137599, 137600, 137601, 137603, 137604, 137605, 137606, 137608, 137609, 137610, 137611, 137613, 137614, 137615, 137616, 137618, 137619, 137620, 137621, 137623, 137624, 137625, 137626, 137628, 137629, 137630, 137631, 137703, 137704, 137705, 137706, 137708, 137709, 137710, 137711, 137713, 137714, 137715, 137716, 137718, 137719, 137720, 137721, 137723, 137724, 137725, 137726, 137728, 137729, 137730, 137733, 137734, 137735, 137736, 137738, 137739, 137740, 137741, 137743, 137744, 137745, 137746, 137748, 137749, 137750, 137751, 137753, 137754, 137755, 137756, 137758, 137759, 137760, 137761, 137763, 137764, 137765, 137766, 137768, 137769, 137770, 137771, 137773, 137774, 137775, 137776, 137778, 137779, 137780, 137781, 138223, 138224, 138226, 138227, 138229, 138230, 138232, 138233, 138235, 138236, 138238, 138239, 138241, 138242, 138245, 138246, 138248, 138249, 138251, 138252, 138254, 138255, 138257, 138258, 138260, 138261, 138263, 138264, 138266, 138267, 138269, 138270, 138272, 138273, 138399, 138400, 138403, 138404, 138406, 138407, 138409, 138410, 138415, 138416, 138418, 138419, 138421, 138422, 138424, 138425, 138427, 138428, 138430, 138431, 138433, 138434, 138436, 138437, 138439, 138440, 138442, 138443, 138445, 138446, 138448, 138449, 138588, 138589, 138592, 138593, 138596, 138597, 138600, 138601, 138604, 138605, 138608, 138609, 138612, 138613, 138616, 138617, 138620, 138621, 138624, 138625, 138628, 138629, 138631, 138632, 138635, 138636, 138639, 138640, 138643, 138644, 138647, 138648, 138651, 138652, 138655, 138656, 138659, 138660, 138807, 138808, 138811, 138812, 138815, 138816, 138819, 138820, 138823, 138824, 138827, 138828, 138831, 138832, 138835, 138836, 138839, 138840, 138843, 138844, 138847, 138848, 138851, 138852, 138855, 138856, 138859, 138860, 138863, 138864, 138867, 138868, 138871, 138872, 138875, 138876, 138988, 138989, 138990, 138991, 138999, 139000, 139003, 139004, 139007, 139008, 139013, 139014, 139017, 139018, 139028, 139029, 139442, 139443, 139445, 139446, 139448, 139449, 139451, 139452, 139530, 139531, 139532, 139533, 139535, 139536, 139537, 139538, 139540, 139541, 139542, 139543, 139545, 139546, 139547, 139553, 139554, 139555, 139556, 139722, 139723, 139725, 139726]
Viewing Fiber Distribution in a Visit
The following code shows the distribution of all fibers (SCIENCE, SKY, FLUX STANDARDS) on the focal plane for a given visit. As before, specify the 2d DRP data repository location and collection name, along with the visit number (or increment through visits in the collection using a simple index):
import numpy as np
import matplotlib.pyplot as plt
from lsst.daf.butler import Butler
from pfs.datamodel import TargetType
# ==== USER-DEFINED PARAMETERS ====
repo = "/shared/pfs/programs/S25A-000QF/2d/" # path to the 2d DRP repository
collections = "run26_June2026" # collection name
VISIT = 137292 # Inspects and plots fiber positions in specified visit
VISIT_INDEX = 0 # Used if VISIT is None. Increment through visits in collections (0 = first, 1 = second, etc.)
# ==== QUERY ALL FULLY-PROCESSED VISITS FROM BUTLER ====
butler = Butler(repo, collections=collections)
all_visits = sorted({ref.dataId['visit'] for ref in butler.registry.queryDatasets('pfsMerged')})
print(f"Total visits in Collections ({collections}): {len(all_visits)}")
if VISIT is not None:
if VISIT not in all_visits:
raise ValueError(f"Visit {VISIT} not found in collections '{collections}'")
visit = VISIT
print(f"Using specified visit={visit}")
else:
visit = all_visits[VISIT_INDEX]
print(f"Selected visit index {VISIT_INDEX}: visit={visit}")
# ==== LOAD FIBER CONFIGURATION FOR THE SELECTED VISIT ====
pfsConfig = butler.get('pfsConfig', dict(visit=visit))
sci = pfsConfig.select(targetType=TargetType.SCIENCE, fiberStatus=1)
sky = pfsConfig.select(targetType=TargetType.SKY, fiberStatus=1)
fluxstd = pfsConfig.select(targetType=TargetType.FLUXSTD, fiberStatus=1)
# ==== PLOT RA/DEC POSITIONS OF ALL FIBER TYPES FOR THE SELECTED VISIT ====
fig, ax = plt.subplots(figsize=(7, 7))
fig.subplots_adjust(top=0.90, bottom=0.10, left=0.12, right=0.97)
ax.scatter(sci.ra, sci.dec, s=6, marker='o', color='black', label=f'SCIENCE ({len(sci.ra)})', zorder=3)
ax.scatter(sky.ra, sky.dec, s=12, marker='^', color='limegreen', label=f'SKY ({len(sky.ra)})', zorder=2)
ax.scatter(fluxstd.ra, fluxstd.dec, s=12, marker='s', color='orangered', label=f'FLUXSTD ({len(fluxstd.ra)})', zorder=2)
ax.set_xlabel('RA [deg]')
ax.set_ylabel('Dec [deg]')
ax.set_title(f'Fiber Positions Visit={visit}\nCollections ({collections})')
ax.legend(loc='upper right', fancybox=True, framealpha=0.5)
ax.invert_xaxis()
ax.minorticks_on()
plt.savefig(f'pfsConfig_fiber_distribution_visit={visit}.png', dpi=150, bbox_inches='tight')
plt.show()
Output:
Total visits in Collections (run26_June2026): 426
Using specified visit=137292

List all Visits (Exposures) for an Object
If you have the objId of a specific object you have observed, the following code prints a summary of all visits that contain that object, including its catalog ID and magnitude.
import numpy as np
from astropy.io import fits
from lsst.daf.butler import Butler
from pfs.datamodel import TargetType
# ==== USER-DEFINED PARAMETERS ====
repo = "/shared/pfs/programs/S25A-000QF/2d/" # path to the 2d DRP repository
collections = "run26_June2026" # collection name
objid = 89100543080260387 # input object id
# ==== GET ALL PFSCONFIG FILE REFERENCES FROM BUTLER ====
butler = Butler(repo, collections=collections)
all_visits = sorted({ref.dataId['visit'] for ref in butler.registry.queryDatasets('pfsMerged')})
all_refs = list(butler.registry.queryDatasets('pfsConfig',
where=f"visit IN ({','.join(str(v) for v in all_visits)})"))
# ==== SCAN ALL PFSCONFIG FILES TO FIND WHICH VISITS CONTAIN THE OBJECT ====
objid_visits = []
info_ref = None
for ref in all_refs:
uri = butler.getURI(ref)
with fits.open(uri.path) as hdul:
objids = hdul[1].data['objId']
if objid in objids:
objid_visits.append(ref.dataId['visit'])
if info_ref is None:
info_ref = ref
if not objid_visits:
raise ValueError(f"objId {objid} not found in any pfsConfig in collections '{collections}'")
objid_visits = sorted(set(objid_visits))
print(f"ObjId={objid} found in {len(objid_visits)} visit(s): {objid_visits}")
# ==== LOAD OBJECT DETAILS FROM THE FIRST MATCHING VISIT ====
pfsConfig = butler.get('pfsConfig', dict(visit=objid_visits[0]))
sci = pfsConfig.select(targetType=TargetType.SCIENCE, fiberStatus=1)
sci_mask = sci.objId == objid
idx = np.where(sci_mask)[0][0]
filter_names = list(sci.filterNames[0])
first_filter = filter_names[0]
total_flux = np.array([list(f) for f in sci.totalFlux], dtype=float)
psf_flux = np.array([list(f) for f in sci.psfFlux], dtype=float)
flux = np.where((total_flux > 0) & np.isfinite(total_flux), total_flux, psf_flux)
with np.errstate(divide='ignore', invalid='ignore'):
mag = np.where(flux > 0, -2.5 * np.log10(flux) + 31.4, np.nan)
print(f"\nObject info (from visit {objid_visits[0]}):")
print(f" ObjId = {int(sci.objId[idx])}")
print(f" CatId = {int(sci.catId[idx])}")
print(f" Spectrograph = {int(sci.spectrograph[idx])}")
print(f" Mag ({first_filter}) = {mag[idx, 0]:.3f} AB")
Output:
ObjId=89100543080260387 found in 2 visit(s): [137292, 137293]
Object info (from visit 137292):
ObjId = 89100543080260387
CatId = 10094
Spectrograph = 3
Mag (g_ps1) = 20.816 AB