| name | photerr |
| description | Guide to photerr, a Python library for photometric error modeling in astronomical imaging surveys (LSST/Rubin, Euclid, Roman). Covers ErrorModel/ErrorParams architecture, survey-specific models (LsstErrorModel, EuclidErrorModel, RomanErrorModel and variants), parameter customization (nYrObs, nVisYr, m5, theta, gamma, scale, sigmaSys), non-detection handling (ndMode, sigLim, ndFlag), extended-source aperture corrections (extendedType="auto"/"gaap"), and getLimitingMags. Use this skill whenever the user imports photerr; works with LsstErrorModel, EuclidErrorModel, RomanErrorModel, or ErrorModel; calls getLimitingMags; simulates photometric errors or survey depth; applies photometric noise to a magnitude catalog; handles non-detections or limiting magnitudes; models extended-source (galaxy) photometry; compares survey depths or error budgets across LSST, Euclid, or Roman; or asks how to add realistic photometric scatter to a simulated catalog. For RAIL pipeline stages that wrap photerr (LSSTErrorModel degradation stage inside rail.creation), use the rail skill instead. |
photerr — photometric error modeling
You're helping a user work with photerr, a library for simulating realistic photometric errors for LSST/Rubin, Euclid, and Roman surveys.
The package extends the Ivezic (2019) point-source error model to low-SNR regimes and extended sources, computing errors in flux space rather than magnitude space.
Install: pip install photerr (requires Python ≥ 3.10, NumPy ≥ 1.23, pandas ≥ 1.4)
Citation: Crenshaw et al. 2024, AJ, 168, 80
Core concepts
- Input: a pandas
DataFrame with columns named for each photometric band, containing true source magnitudes.
- Output: a
DataFrame with the same columns containing simulated observed magnitudes (true + noise), plus error columns.
- Noise is drawn in flux space; errors are Gaussian and include a systematic floor (
sigmaSys=0.005 mag by default).
- Non-detections are flagged with
np.inf by default (configurable).
Survey models
| Class | Survey | Default bands |
|---|
LsstErrorModel (= V2) | Rubin/LSST | u g r i z y |
LsstErrorModelV1 | Rubin/LSST (old) | u g r i z y |
EuclidErrorModel (= Wide) | Euclid wide | VIS Y J H |
EuclidDeepErrorModel | Euclid deep | VIS Y J H |
RomanErrorModel (= Medium) | Roman medium | Y J H |
RomanWideErrorModel | Roman wide | H |
RomanDeepErrorModel | Roman deep | Z Y J H F K W |
RomanUltraDeepErrorModel | Roman ultra-deep | Y J H |
from photerr import (
LsstErrorModel,
EuclidErrorModel, EuclidDeepErrorModel,
RomanErrorModel, RomanWideErrorModel, RomanDeepErrorModel,
)
Basic usage
import pandas as pd
from photerr import LsstErrorModel
catalog = pd.DataFrame({
"u": [23.5, 24.0], "g": [23.2, 24.1],
"r": [23.0, 23.9], "i": [22.9, 23.8],
"z": [22.8, 23.7], "y": [22.7, 23.6],
})
errModel = LsstErrorModel()
obs = errModel(catalog, random_state=42)
Key parameters
All parameters can be overridden at construction time as keyword args.
Dict values are per-band; a scalar applies to all bands.
| Parameter | Default (LSST) | Meaning |
|---|
nYrObs | 10 | Years of observation |
nVisYr | per-band | Mean visits per year |
m5 | computed | 5σ single-visit depth (mag) |
gamma | 0.039 | Ivezic (2019) band parameter |
theta | per-band | PSF FWHM (arcsec) |
airmass | per-band | Effective airmass |
km | per-band | Atmospheric extinction |
sigmaSys | 0.005 | Systematic error floor (mag) |
scale | 1.0 | Per-band error scaling factor |
sigLim | 0 | Detection threshold (σ); 0 = keep all |
ndMode | "flag" | Non-detection handling (see below) |
ndFlag | np.inf | Value for non-detected sources |
extendedType | "point" | Aperture type for galaxies |
decorrelate | True | Decorrelate inter-band errors |
highSNR | False | Use high-SNR Gaussian approx |
errLoc | "after" | "after": append band_err cols; "alone": return only errors |
absFlux | False | Work in absolute flux units instead of magnitudes |
majorCol | "major" | Catalog column name for galaxy semi-major axis (arcsec) |
minorCol | "minor" | Catalog column name for galaxy semi-minor axis (arcsec) |
renameDict | {} | Rename bands, e.g. {"u": "lsst_u"} |
Common customisations
errModel = LsstErrorModel(nYrObs=1)
errModel = LsstErrorModel(m5={"u": 23.5, "g": 24.0})
errModel = LsstErrorModel(scale={"u": 2.0, "y": 2.0})
errModel = LsstErrorModel(renameDict={"u": "lsst_u", "g": "lsst_g"})
Limiting magnitudes
m5 = errModel.getLimitingMags()
m1_single = errModel.getLimitingMags(nSigma=1, coadded=False)
m5_ext = errModel.getLimitingMags(aperture=1.0)
Non-detection handling
errModel = LsstErrorModel(sigLim=0, ndMode="flag")
errModel = LsstErrorModel(sigLim=1, ndMode="sigLim")
errModel = LsstErrorModel(sigLim=5, ndMode="flag")
Extended sources (galaxies)
The catalog must include galaxy half-light radii columns (major, minor in arcsec by default, overridable with majorCol/minorCol).
galaxy_catalog = pd.DataFrame({
"u": [24.0], "g": [23.8], "r": [23.5],
"i": [23.3], "z": [23.1], "y": [23.0],
"major": [0.5],
"minor": [0.4],
})
errModel = LsstErrorModel(extendedType="auto")
errModel = LsstErrorModel(extendedType="gaap")
obs = errModel(galaxy_catalog)
Multi-survey comparison
from photerr import LsstErrorModel, EuclidErrorModel, RomanDeepErrorModel
models = {
"LSST": LsstErrorModel(nYrObs=10),
"Euclid": EuclidErrorModel(),
"Roman deep": RomanDeepErrorModel(),
}
for name, m in models.items():
print(name, m.getLimitingMags())
Notes
errModel(catalog) returns a new DataFrame; it does not modify the input.
- Pass
random_state for reproducibility.
LsstErrorModel is an alias for LsstErrorModelV2; LsstErrorModelV1 uses the older single-visit depth calculation.
- The
ErrorModel and ErrorParams base classes are available for building custom survey models.