specmangle applies a smooth, wavelength-dependent multiplicative correction to
an observed astronomical spectrum such that synthetic broadband photometry based
on that spectrum matches contemporaneous observations.
The package is deliberately narrow in scope, and uses existing Astropy-ecosystem data structures and APIs:
- spectra are
specutils.Spectrumobjects - photometric measurements are stored in
astropy.table.Tableobjects - passband profiles are retrieved from the SVO Filter Profile Service via
astroquery.svo_fps, or user-defined
Published releases are available on PyPI. The package can be installed by running
pip install specmangleFor development:
git clone https://github.com/cgobat/specmangle.git
cd specmangle
python -m pip install -e .import astropy.units as u
from astropy.nddata import StdDevUncertainty
from astropy.table import Table
from specutils import Spectrum
from specmangle import mangle
spectrum = Spectrum(
spectral_axis=wavelength * u.AA,
flux=flux * u.erg / u.s / u.cm**2 / u.AA,
uncertainty=StdDevUncertainty(
flux_error,
unit=u.erg / u.s / u.cm**2 / u.AA,
),
)
photometry = Table(
rows=[
("SLOAN/SDSS.g", 18.42, 0.03),
("SLOAN/SDSS.r", 18.17, 0.02),
("SLOAN/SDSS.i", 18.05, 0.04),
],
names=("band", "mag", "mag_err"),
)
result = mangle(spectrum, photometry)
mangled = result.spectrum
correction = result.correction
print(result.photometry)The input photometry table must contain:
band: passband identifier, normally an SVO FPS filter IDmag: AB magnitude
It may additionally contain:
mag_err: 1-sigma magnitude uncertainty. Masked values are treated as missing uncertainties.
Additional columns are preserved in result.photometry, which adds used,
coverage, skip_reason, synthetic_mag, synthetic_mag_err, and residual
columns. Bands that do not meet min_coverage are excluded from the fit and
reported explicitly in these diagnostic columns.
The correction is positive and multiplicative. It is represented by a natural cubic spline in log wavelength and log flux scale, with one free anchor at the pivot wavelength of each unique passband. The correction is held constant beyond the bluest and reddest anchors rather than extrapolating the spline.
The fit minimizes the difference between the mangled spectrum's synthetic AB magnitudes and the supplied photometry. When uncertainties are available, photometric uncertainties and synthetic-photometry uncertainties propagated from the spectrum are combined in quadrature.
A nonzero smoothness can be supplied to penalize changes in slope between
neighboring spline anchors:
result = mangle(spectrum, photometry, smoothness=0.1)The default is smoothness=0.0, so no additional regularization is imposed.
The fitted correction can be evaluated on any wavelength grid:
correction = result.correction_at(new_wavelength)The term "spectral mangling" is commonly used for smooth wavelength-dependent corrections that are applied to a spectrum to make it reproduce contemporaneous broadband photometry. The approach has roots in earlier spectral "warping" methods (e.g. Tonry et al. 2003), was formalized under the "mangling" nomenclature by Hsiao et al. (2007) and Conley et al. (2008), and was later applied directly to spectrophotometric calibration of observed core-collapse supernova spectra by Vincenzi et al. (2019).
specmangle supports the same uncertainty representations supported by
specutils for arithmetic propagation:
astropy.nddata.StdDevUncertainty;astropy.nddata.VarianceUncertainty;astropy.nddata.InverseVariance.
For synthetic photometry, uncertainties are converted to standard deviations and spectral samples are assumed to be statistically independent. Masked spectral samples are omitted.
The uncertainty stored on result.spectrum is the input spectral uncertainty
scaled by the best-fit mangling correction. Uncertainty in the correction
itself is returned separately as result.correction_uncertainty, because errors
in the fitted correction are correlated across wavelength.
By default, values in the band column are passed to the SVO Filter Profile
Service:
from specmangle import Bandpass
bandpass = Bandpass.from_svo("SLOAN/SDSS.g")Custom response curves can be supplied instead:
import astropy.units as u
from specmangle import Bandpass, mangle
custom = Bandpass(
filter_id="my/filter",
wavelength=filter_wavelength * u.AA,
transmission=filter_transmission,
detector_type="photon",
)
result = mangle(
spectrum,
photometry,
bandpasses={"my/filter": custom},
)The synthetic-photometry calculation distinguishes photon-counting and
energy-counting response curves. SVO's DetectorType metadata is used
automatically for SVO filters.
By default, at least 98% of the passband's AB-reference signal must fall within the wavelength range covered by the valid spectrum samples. This prevents a synthetic magnitude from silently relying on a substantially incomplete bandpass.
The threshold can be changed with min_coverage:
result = mangle(spectrum, photometry, min_coverage=0.95)For the common/general case of computing an AB magnitude directly from an SVO FPS identifier:
from specmangle import spectrum_to_magnitude
mag = spectrum_to_magnitude(spectrum, "SLOAN/SDSS.r")If the input Spectrum includes an uncertainty, the function returns
(mag, mag_err). Both values carry Astropy magnitude units. An already loaded
Bandpass can be supplied instead of an SVO identifier.
The lower-level synthetic-photometry result is also exposed when coverage and other diagnostics are useful:
from specmangle import Bandpass, synthetic_ab_magnitude
bandpass = Bandpass.from_svo("SLOAN/SDSS.r")
synthetic = synthetic_ab_magnitude(spectrum, bandpass)
print(synthetic.magnitude)
print(synthetic.uncertainty)
print(synthetic.coverage)mangle returns a MangleResult containing:
spectrum: the mangledspecutils.Spectrumcorrection: multiplicative correction evaluated at each input spectral samplecorrection_at(wavelength): evaluate the fitted correction at arbitrary wavelengthscorrection_uncertainty: estimated 1-sigma uncertainty in that correction, when identifiableanchor_wavelengths: spline-anchor pivot wavelengthslog_correction_parameters: fitted log-correction values at the anchorsparameter_covariance: fitted parameter covariance when absolute uncertainties are availablephotometry: the input table with synthetic-photometry diagnostics appendedbandpasses: the bandpasses used in the fitoptimizer: the underlyingscipy.optimize.OptimizeResult