Photometric calibration: fitting a stellar spectrum — species 0.1.2 documentation

# Photometric calibration: fitting a stellar spectrum

The 2MASS photometry of PZ Tel A is fitted with a IRTF spectrum of a G8V type star. With the best-fit scaling parameter, synthetic photometry is calculated for the VLT/SPHERE H2 filter.

## Getting started

The required modules are imported, species is initiated, and an object of `Database` is created.

    [1]:

    import urllib.request
    import species
    from IPython.display import Image

    [2]:

    species.SpeciesInit()

    Initiating species v0.1.1... [DONE]
    Creating species_config.ini... [DONE]
    Database: /Users/tomasstolker/applications/species/docs/tutorials/species_database.hdf5
    Data folder: /Users/tomasstolker/applications/species/docs/tutorials/data
    Working folder: /Users/tomasstolker/applications/species/docs/tutorials
    Creating species_database.hdf5... [DONE]
    Creating data folder... [DONE]

    [2]:

    <species.core.setup.SpeciesInit at 0x12e51f710>

    [3]:

    database = species.Database()

The distance and certainty of PZ Tel are defined and a dictionary with the 2MASS magnitudes is created.

    [4]:

    distance = (47.13, 0.13)  # [pc]

    [5]:

    magnitudes = {'2MASS/2MASS.J':(6.856, 0.021),
                  '2MASS/2MASS.H':(6.486, 0.049),
                  '2MASS/2MASS.Ks':(6.366, 0.024)}

Also a list with the filter names is created.

    [6]:

    filters = list(magnitudes.keys())

## Adding an object and calibration spectrum

The distance and magnitudes are of PZ Tel A are stored in the database as object.

    [7]:

    database.add_object(object_name='PZ Tel A',
                        distance=distance,
                        app_mag=magnitudes,
                        spectrum=None)

    Adding filter: 2MASS/2MASS.J... [DONE]
    Downloading Vega spectrum (270 kB)... [DONE]
    Adding Vega spectrum... [DONE]
    Adding filter: 2MASS/2MASS.H... [DONE]
    Adding filter: 2MASS/2MASS.Ks... [DONE]
    Adding object: PZ Tel A... [DONE]

A G8V type spectrum is downloaded from the IRTF website.

    [8]:

    urllib.request.urlretrieve('http://irtfweb.ifa.hawaii.edu/~spex/IRTF_Spectral_Library/Data/G8V_HD75732.txt',
                               'data/G8V_HD75732.txt')

    [8]:

    'data/G8V_HD75732.txt'

And stored as calibration data in de database.

    [9]:

    database.add_calibration(filename='data/G8V_HD75732.txt',
                             tag='G8V_HD75732')

    Adding calibration spectrum: G8V_HD75732... [DONE]

## Fitting the stellar spectrum

The stellar spectrum is fitted to the 2MASS fluxes with a scaling parameter. The boundaries for the uniform prior is provided as dictionary.

    [10]:

    fit = species.FitSpectrum(object_name='PZ Tel A',
                              filters=None,
                              spectrum='G8V_HD75732',
                              bounds={'scaling':(0., 1e0)})

    Getting object: PZ Tel A... [DONE]

The posterior distribution are estimated with the MCMC ensemble sampler of emcee.

    [11]:

    fit.run_mcmc(nwalkers=200,
                 nsteps=1000,
                 guess={'scaling':5e-1},
                 tag='pztel')

    Running MCMC...

    100%|██████████| 1000/1000 [00:06<00:00, 152.69it/s]

    Mean acceptance fraction: 0.786
    Integrated autocorrelation time = [3706.75420287]

## Plotting the results

Let’s plot the evolution of the walkers.

    [12]:

    species.plot_walkers(tag='pztel',
                         nsteps=None,
                         offset=(-0.2, -0.08),
                         output='walkers.png')

    Plotting walkers: walkers.png... [DONE]

    [13]:

    Image('walkers.png')

    [13]:

And the posterior distribution of the scaling parameter.

    [14]:

    species.plot_posteriors(tag='pztel',
                            burnin=500,
                            offset=(-0.3, -0.10),
                            title_fmt='.4f',
                            output='posterior.png')

    Plotting posteriors: posterior.png... [DONE]

    [15]:

    Image('posterior.png')

    [15]:

For plotting the spectrum, the data of PZ Tel A are stored in an `ObjectBox`.

    [16]:

    objectbox = database.get_object(object_name='PZ Tel A',
                                    filters=None)

    Getting object: PZ Tel A... [DONE]

Next, 30 randomly selected samples from the posterior are used for creating comparison spectra.

    [17]:

    samples = database.get_mcmc_spectra(tag='pztel',
                                        burnin=500,
                                        random=30,
                                        wavel_range=(0.1, 50.0),
                                        spec_res=None)

    Getting MCMC spectra: 100%|██████████| 30/30 [00:00<00:00, 1247.19it/s]

And also the best-fit value is extracted as the median of the posterior distribution.

    [18]:

    median = database.get_median_sample(tag='pztel',
                                        burnin=500)

Let’s have a look at the value.

    [19]:

    print(median)

    {'scaling': 0.11991330981254578}

With this value, the best-fit spectrum is calculated.

    [20]:

    readcalib = species.ReadCalibration(tag='G8V_HD75732',
                                        filter_name=None)

    [21]:

    spectrum = readcalib.get_spectrum(model_param=median)

The best-fit value is also used for creating synthetic photometry in the 2MASS filters.

    [22]:

    synphot = species.multi_photometry(datatype='calibration',
                                       spectrum='G8V_HD75732',
                                       filters=filters,
                                       parameters=median)

    Calculating synthetic photometry... [DONE]

The difference between the data and the best-fit synthetic photometry is stored in a `ResidualsBox`.

    [23]:

    residuals = species.get_residuals(datatype='calibration',
                                      spectrum='G8V_HD75732',
                                      parameters=median,
                                      filters=filters,
                                      objectbox=objectbox,
                                      inc_phot=True,
                                      inc_spec=False)

    Calculating synthetic photometry... [DONE]
    Calculating residuals... [DONE]

Finally, all the boxes are provide as input to the `plot_spectrum` function.

    [24]:

    species.plot_spectrum(boxes=[samples, spectrum, objectbox, synphot],
                          filters=filters,
                          colors=['gray', 'black', ('black', None), 'black'],
                          residuals=residuals,
                          xlim=(1., 2.5),
                          ylim=(-1.5e-12, 1.1e-11),
                          scale=('linear', 'linear'),
                          offset=(-0.3, -0.08),
                          output='spectrum.png')

    Plotting spectrum: spectrum.png... [DONE]

    [25]:

    Image('spectrum.png')

    [25]:

## Photometric calibration

The magnitude of PZ Tel B can now be computed for any other filter within the wavelength coverage of the IRTF spectrum. This example calculates the flux and magnitude for the VLT/SPHERE H2 filter.

    [26]:

    readcalib = species.ReadCalibration(tag='G8V_HD75732',
                                        filter_name='Paranal/SPHERE.IRDIS_D_H23_2')

    Adding filter: Paranal/SPHERE.IRDIS_D_H23_2... [DONE]

    [27]:

    flux = readcalib.get_flux(model_param=median)
    print(f'Flux density [W m-2 micron-1] = {flux:.2e}')

    Flux density [W m-2 micron-1] = 3.30e-12

    [28]:

    app_mag, abs_mag = readcalib.get_magnitude(model_param=median, distance=distance[0])
    print(f'Apparent magnitude [mag]] = {app_mag:.2f}')
    print(f'Absolute magnitude [mag] = {abs_mag:.2f}')

    Apparent magnitude [mag]] = 6.50
    Absolute magnitude [mag] = 3.13