Spectral Standards and Photometric Calibration (sbpy.calib)

sbpy’s photometric calibration is based on spectra of the Sun and Vega. For example, they are used to convert between reflectance, cross-section, and magnitude, between Afρ and spectral flux density, and between Vega-based and other magnitude systems. sbpy has built-in spectra for each, and users may provide their own.

The spectrum of Bohlin (2014) is the default and only built-in spectrum for Vega. It is distributed with sbpy. Five solar spectra are built-in:

  • Castelli1996 - Castelli model from Colina et al. (1996).

  • E490_2014 - E490 (2014) standard.

  • E490_2014LR - A low resolution version of the E490 standard.

  • Kurucz1993 - Kurucz (1993) model.

  • calspec - R=5000, created by R. Bohlin from Kurucz Special Model

The E490 spectra are included with sbpy, and the others are downloaded as needed from MAST’s Spectral Atlas Files for Synphot Software (REFERENCE-ATLASES) or STScI’s CALSPEC Database.

Each star has a class for use within sbpy. The classes can be initialized with the default spectrum using from_default():

>>> from sbpy.calib import Sun
>>> sun = Sun.from_default()
>>> print(sun)
<Sun: E490-00a (2014) reference solar spectrum (Table 3)>

The names of the built-in sources are stored as an internal array. They can be discovered with show_builtin(), and used to initialize an object with from_builtin():

>>> from sbpy.calib import Sun
>>> Sun.show_builtin()
    name                                description
------------ -----------------------------------------------------------------
Castelli1996      Castelli model, scaled and presented by Colina et al. (1996)
   E490_2014                E490-00a (2014) reference solar spectrum (Table 3)
 E490_2014LR E490-00a (2014) low resolution reference solar spectrum (Table 4)
  Kurucz1993               Kurucz (1993) model, scaled by Colina et al. (1996)
     calspec            R=5000, created by R. Bohlin from Kurucz Special Model
>>> sun = Sun.from_builtin('E490_2014LR')
>>> print(sun)
<Sun: E490-00a (2014) low resolution reference solar spectrum (Table 4)>

Controlling the default spectra

The Vega and solar spectra in current use are respectively controlled with ScienceState objects named vega_spectrum and solar_spectrum:

>>> from sbpy.calib import Sun, solar_spectrum
>>> solar_spectrum.set('E490_2014LR')
<ScienceState solar_spectrum: <Sun: E490-00a (2014) low resolution reference solar spectrum (Table 4)>>
>>> # E490 low-resolution spectrum in effect for all of sbpy
>>> sun = Sun.from_default()
>>> print(sun)
<Sun: E490-00a (2014) low resolution reference solar spectrum (Table 4)>

vega_spectrum and solar_spectrum can also be used as a context manager to temporarily change the default spectrum:

>>> from sbpy.calib import Sun, solar_spectrum
>>> solar_spectrum.set('E490_2014')  # E490 in effect
<ScienceState solar_spectrum: <Sun: E490-00a (2014) reference solar spectrum (Table 3)>>
>>> with solar_spectrum.set('E490_2014LR'):
...   # E490 low-res in effect
...   print(Sun.from_default())
<Sun: E490-00a (2014) low resolution reference solar spectrum (Table 4)>
>>> # Back to module default.
>>> print(Sun.from_default())
<Sun: E490-00a (2014) reference solar spectrum (Table 3)>

Provide your own solar spectrum with the Sun class:

>>> from sbpy.calib import Sun, solar_spectrum
>>> with solar_spectrum.set(Sun.from_file('sun.txt')):  
...   # sun.txt in effect

See Sun for more information on ways to create solar spectra.

An example showing how to change the default Vega spectrum:

>>> from sbpy.calib import Vega, vega_spectrum
>>> print(Vega.from_default())     
<Vega: Dust-free template spectrum of Bohlin 2014>
>>> with vega_spectrum.set(Vega.from_file('vega.txt')):  
...   # vega.txt in effect

Photometric calibration (without spectra or synphot)

The ScienceState objects solar_fluxd and vega_fluxd control photometric calibration by filter name. These are completely independent of the spectroscopic calibration and can be used without the optional synphot package. The spectral flux densities (per unit wavelength) of the Sun and Vega are provided and enabled by default. Values and filters are from Willmer (2018):

>>> from sbpy.calib import Sun, solar_fluxd, vega_fluxd
>>> import sbpy.units as sbu

.. doctest-requires:: astropy>=5.3

>>> from sbpy.calib import Sun, solar_fluxd, vega_fluxd
>>> import sbpy.units as sbu
>>> solar_fluxd.set('Willmer2018')   
>>> sun = Sun(None)
>>> print(sun.observe('PS1 r'))    
167.49428760264365 erg / (Angstrom s cm2)
>>> vega_fluxd.set('Willmer2018')   
>>> print(sun.observe('PS1 r', unit=sbu.VEGAmag))    
-27.05 mag(VEGA)

Use solar_fluxd.get('Willmer2018') to discover all built-in values.

Users wanting to calibrate data with their own flux densities may do so. For example, set the V-band apparent magnitude of the Sun to that in Colina et al. (1996). Observations through the 'V' filter will use the specified value:

>>> solar_fluxd.set({'V': -26.75 * sbu.VEGAmag})  
>>> sun = Sun.from_default()
>>> print(sun.observe('V'))
-26.75 mag(VEGA)

Some sbpy calculations will require the effective wavelength or the pivot wavelength. These are optional parameters that may be specified with solar_fluxd and vega_fluxd:

>>> import astropy.units as u
>>> from sbpy.calib import vega_fluxd, Vega
>>> # values from Willmer (2018)
>>> vega_fluxd.set({
...     'V': 3674.73 * u.Jy,
...     'V(lambda eff)': 5476 * u.AA
... })    
<ScienceState vega_fluxd: {'V': <Quantity 3674.73 Jy>, 'V(lambda eff)': <Quantity 5476. Angstrom>}>
>>> vega = Vega.from_default()
>>> print(vega.observe('V'))
3674.73 Jy
>>> print(vega.observe('V', unit='erg/(s cm2 AA)'))
Traceback (most recent call last):
UnitConversionError: 'Jy' (spectral flux density) and 'erg / (Angstrom s cm2)' (spectral flux density wav) are not convertible  Is "V(lambda pivot)" required and was it provided?
>>> vega_fluxd.set({
...     'V': 3674.73 * u.Jy,
...     'V(lambda eff)': 5476 * u.AA,
...     'V(lambda pivot)': 5511 * u.AA
... })    
<ScienceState vega_fluxd: {'V': <Quantity 3674.73 Jy>, 'V(lambda eff)': <Quantity 5476. Angstrom>, 'V(lambda pivot)': <Quantity 5511. Angstrom>}>
>>> print(vega.observe('V', unit='erg/(s cm2 AA)'))   
3.62701e-9 erg / (Angstrom s cm2)

Observe the Sun

sbpy can simulate observations of comets and asteroids through spectrometers and filter bandpasses. To support this functionality, the Sun and Vega classes have the observe() method that returns simulated flux densities. Users may request observations through filter bandpasses, or at a set of wavelengths (the default is to rebin the source spectrum).

Get the default solar spectrum, observe it through the Johnson V-band filter (distributed with sbpy), returning the result as a Vega-based magnitude in the Johnson-Morgan system:

>>> from sbpy.calib import Sun
>>> from sbpy.photometry import bandpass
>>> from sbpy.units import JMmag
>>> sun = Sun.from_default()
>>> bp = bandpass('Johnson V')
>>> fluxd = sun.observe(bp, unit=JMmag)
>>> print(fluxd)    
-26.744715028702647 mag(JM)

Binning versus interpolation with observe()

If a user requests a series of wavelengths or frequencies with a Quantity object, the default for observe() is to rebin the source spectrum using the requested values as bin centers. This behavior is appropriate when the source spectrum is at a higher spectral resolution than the requested wavelengths. This is because synphot assumes source spectra are continuous functions, rather than observations taken through a spectrometer (binned data).

When the requested spectral resolution is comparable to the spectral resolution of the source, rebinning may result in errors at the percent-level or more. Instead, use the interpolate=True parameter for observe.

Compare interpolation and rebinning for the E490 low-resolution solar spectrum, using the stored wavelengths of the spectrum. Initialize a Sun object with the low-resolution spectrum.

>>> import numpy as np
>>> from sbpy.calib import Sun
>>> sun = Sun.from_builtin('E490_2014LR')

Inspect a sub-set of the data for this example.

>>> wave = sun.wave[430:435]
>>> S = sun.fluxd[430:435]
>>> print(wave)    
[5495. 5505. 5515. 5525. 5535.] Angstrom
>>> print(S)       
[1895. 1862. 1871. 1846. 1882.] W / (um m2)

Interpolate with observe() and compare to the original values.

>>> wave = sun.wave[430:435]
>>> S = sun.fluxd[430:435]

>>> S_interp = sun.observe(wave, interpolate=True)
>>> np.allclose(S.value, S_interp.value)

Re-bin with observe using the same wavelengths as band centers.

>>> S_rebin = sun.observe(wave)
>>> np.allclose(S.value, S_rebin.value)

Inspect the differences.

>>> print((S_rebin - S) / (S_rebin + S) * 2)    
[-0.00429693  0.00281266 -0.00227604  0.00412338 -0.00132301]

Plot solar spectra

Solar spectra in Sun objects can be plotted at the native resolution of the data, or rebinned. Plot the solar spectrum at the native resolution, and at a resolution of ~25:

>>> import astropy.units as u
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from sbpy.calib import Sun
>>> # Create an array of wavelengths at R~25
>>> wrange = 0.3, 0.8  # wavelength range
>>> d = 1 + 1 / 25
>>> n = int(np.ceil(np.log(wrange[1] / wrange[0]) / np.log(d)))
>>> wave_binned = wrange[0] * d**np.arange(n) * u.um
>>> # Get the default solar spectrum, and rebin it
>>> sun = Sun.from_default()
>>> fluxd_binned = sun.observe(wave_binned, unit='W / (m2 um)')
>>> # Plot
>>> plt.plot(sun.wave.to('um'), sun.fluxd.to('W/(m2 um)'),
...          drawstyle='steps-mid', color='#1f77b4',
...          label='Native resolution')
>>> plt.plot(wave_binned, fluxd_binned, drawstyle='steps-mid',
...          color='#ff7f0e', label='R~25')
>>> plt.setp(plt.gca(), xlim=wrange, xlabel='Wavelength (μm)',
...          ylabel='Flux density (W/(m2 μm)')
>>> plt.legend()
>>> plt.tight_layout()

(Source code, png, hires.png, pdf)



sbpy photometric calibration

Most functionality requires the synphot package.

Users may use any of the built-in spectra or photometry for the Sun or Vega, or provide their own.

Solar calibration

The E490 spectra are included in the sbpy distribution. The others are downloaded and cached as needed:

E490_2014 - E490-00a (2014) standard spectrum (ASTM 2014).
E490_2014LR - Low resolution version of E490 (ASTM 2014).
Kurucz1993 - Kurucz (1993) model scaled by Colina et al. (1996).
Castelli1996 - Catelli model spectrum from Colina et al. (1996).

Filter photometry from Willmer (2018).

Vega calibration

The spectrum of Bohlin (2014) is included with sbpy.

Filter photometry from Willmer (2018).


ASTM 2014, E490-00a(2014) Standard Solar Constant and Zero Air Mass Solar Spectral Irradiance Tables, ASTM International, West Conshohocken, PA. doi:10.1520/E0490

Bohlin, R. C. 2014, Astronomical Journal, 147, 127.

Colina et al. 1996, Astronomical Journal 112, 307.

Kurucz 1993, Smithsonian Astropys. Obs. CD-ROM No. 13.

Willmer 2018, ApJS 236, 47.



Attempted to look up filter in, e.g., solar_fluxd, but not present.

SpectralStandard(source[, description, bibcode])

Abstract base class for SBPy spectral standards.

Sun(source[, description, bibcode])

Solar spectrum.


SpectralStandard was initialized without a source, but it was accessed.

Vega(source[, description, bibcode])

Vega spectrum.


Get/set the sbpy solar flux density.


Get/set the sbpy default solar spectrum.


Get/set the sbpy Vega flux density.


Get/set the sbpy default Vega spectrum.

Class Inheritance Diagram

Inheritance diagram of sbpy.calib.core.FilterLookupError, sbpy.calib.core.SpectralStandard, sbpy.calib.core.Sun, sbpy.calib.core.UndefinedSourceError, sbpy.calib.core.Vega, sbpy.calib.core.solar_fluxd, sbpy.calib.core.solar_spectrum, sbpy.calib.core.vega_fluxd, sbpy.calib.core.vega_spectrum