Examples

Basic K-correction Calculation

The way kcorrect works is that the user initializes an object of the Kcorrect class for the specific set of response functions for the photometry they are interested in. This object takes a bit of time to calculate, because it precalculates the projections of the K-correction templates onto the response function on a grid of redshifts.

Given redshifts and photometry in units of maggies (and their inverse variance), the object’s fit_coeffs method fits the templates to the photometry.

These coefficients can then be used to calculate the kcorrections with the kcorrect method.

Below is an example run on a single object, for which redshift is a scalar, and maggies and ivar are 1-dimensional arrays. We can input the arrays as ndarrays or as lists.

import kcorrect.kcorrect

responses = ['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0']
kc = kcorrect.kcorrect.Kcorrect(responses=responses)

redshift = 0.0826
maggies = [27.26e-9, 73.98e-9, 132.56e-9, 198.52e-9, 238.05e-9]
ivar = [1.528e+18, 5.23e+18, 2.429e+18, 1.042e+18, 0.1653e+18]

# "coeffs" is a [5]-array with coefficients multiplying each template
coeffs = kc.fit_coeffs(redshift=redshift, maggies=maggies, ivar=ivar)

# "k" is a [5]-array with the K-corrections in magnitude units
k = kc.kcorrect(redshift=redshift, coeffs=coeffs)

For multiple objects (and optionally for single objects), redshift is a 1-dimensional array, whereas maggies and ivar are 2-dimensional arrays. Again, these can be input as lists too, as shown below.

import kcorrect.kcorrect

responses = ['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0']
kc = kcorrect.kcorrect.Kcorrect(responses=responses)

redshift = [0.0826, 0.111]
maggies = [[27.26e-9, 73.98e-9, 132.56e-9, 198.52e-9],
                 [4.022e-9, 29.36e-9, 98.230e-9, 155.63e-9]]
ivar = [[1.528e+18, 5.23e+18, 2.429e+18, 1.042e+18],
              [2.360e+18, 9.87e+18, 3.006e+18, 1.122e+18]]


# "coeffs" is a [2,5]-array coefficients multiplying each template
coeffs = kc.fit_coeffs(redshift=redshift, maggies=maggies, ivar=ivar)

# "k" is a [2,4]-array with the K-corrections in magnitude units
k = kc.kcorrect(redshift=redshift, coeffs=coeffs)

Absolute magnitudes

The absmag method returns absolute magnitudes.

The code returns:

\[m_Q = m_R - {\rm DM}(z) - K_{QR}(z),\]

for those bands for which the observed maggies are positive and have a positive inverse variance. For other bands, the returned absolute magnitude is determined using the reconstructed maggies (see next section).

absmag = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs)

If one of the maggies is zero or negative, the corresponding absolute magnitude is returned as -9999. In the code below, the u-band absolute magnitude of the first object should have this value.

maggies = [[-3.26e-9, 73.98e-9, 132.56e-9, 198.52e-9],
           [4.022e-9, 29.36e-9, 98.230e-9, 155.63e-9]]
coeffs = kc.fit_coeffs(redshift=redshift, maggies=maggies, ivar=ivar)
absmag = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs)

In these cases, the method can calculate the one-sigma absolute magnitude limit based on the inverse variance, if it is not zero, by setting the limit keyword to True. This returns the one-sigma detection limit for all objects and bands (even the detected ones).

absmag, absmag_limit = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs, limit=True)

If one of the input inverse variances is zero or negative, the corresponding absolute magnitude and (if it is requested) the limit are returned as -9999.

ivar = [[0., 0., 2.429e+18, 1.042e+18],
                    [4.022e-9, 29.36e-9, 98.230e-9, 155.63e-9]]
coeffs = kc.fit_coeffs(redshift=redshift, maggies=maggies, ivar=ivar)
absmag, absmag_limit = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs, limit=True)

There is also an option to return the reconstructed absolute magnitude from the full SED fit. This will differ somewhat from the absolute magnitude generated through applying the K-correction, of course, because the SED fit is not a perfect fit to the data. It may be a useful quantity in assessing the goodness of fit (though the reconstructed maggies are a more direct comparison to the input data, see below). It also may be useful as a guess of the absolute magnitude for missing bands.

absmag, absmag_reconstruct = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs, reconstruct=True)

Reconstructing spectra and fluxes

The model expressed by the coefficients is a full SED. The Kcorrect object has an attribute templates that is an instance of the Template class.

Each template is in units of \({\rm ~erg} {\rm ~s}^{-1} {\rm ~cm}^{-2} {\rm ~A}^{-1} {\rm ~}M_\odot^{-1}\) as observed for a galaxy at 10pc distance. The coefficients are in units of “the solar masses corresponding to a galaxy at 10pc” (see next section).

It is straightforward to use the templates to reconstruct what that SED looks like. If you have found coefficients as in the first example above, the following code will return the SED in \({\rm ~erg} {\rm ~s}^{-1} {\rm ~cm}^{-2} {\rm ~A}^{-1}\). The templates have to be shifted to the observed frame (conserving bolometric flux) to get the correct observed spectrum.

import matplotlib.pyplot as plt
import numpy as np

wave = kc.templates.restframe_wave * (1. + redshift)
spec = coeffs.dot(kc.templates.restframe_flux) / (1. + redshift)

plt.plot(np.log10(wave), np.log10(spec))
plt.xlabel('$\\log_{10} wavelength$')
plt.ylabel('$\\log_{10} flux$ (erg s$^{-1}$ cm$^{-2}$ Ang$^{-1}$')
plt.xlim([3., 4.5])
plt.ylim([-18., -14.])
plt.show()

We can also reconstruct the fluxes from the model (of course we can, because the K-correction determination must do so!). This is useful to do to compare the best fit SED to the observations. In the case here you should find agreement within a few percent between maggies and rmaggies.

rmaggies = kc.reconstruct(redshift=redshift, coeffs=coeffs)

Derived parameters (i.e. stellar mass)

The derived method returns some derived parameters. These parameters are described in the kcorrect paper, but it is important to take them with a grain of salt.

The only one worth taking at all seriously is mremain and mtol, the surviving stellar mass and mass-to-light ratios in the stellar population fit. But this parameter is shown to disagree with other estimates, with a trend of a few tenths of dex across stellar mass (kcorrect declining). While I don’t know if any stellar mass indicator from broad band photometry is great, the one in kcorrect is particularly simple (and also doesn’t come with any error bar).

Importantly, the mass-to-light ratios are in the output bandpasses (and you have to specify band_shift if you want shifted output bandpasses).

Like the absolute magnitudes, the stellar masses use the cosmo attribute of the Kcorrect object, which by default is the Planck18 cosmology from astropy.

derived = kc.derived(redshift=redshift, coeffs=coeffs)

# This has one entry per object
stellar_mass = derived['mtol']

# This has one entry per object per output bandpass
mtol = derived['mtol']

Changing the output responses

As one gets to higher redshift, the K-corrections from a given observed frame bandpass to its rest frame counterpart become more and more dependent on the SED model being correct.

One approach to dealing with this is to define a set of output rest frame bandpasses that are shifted versions of your input bandpasses, where the shift is the typical redshift of your sample. This minimizes the internal error in your sample, at the expense of calculating quantities that are less likely to be comparable to catalogs in the literature. This option can be utilized by just specifying band_shift for the kcorrect, absmag, or derived methods when you use them:

absmag = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs, band_shift=0.1)

A second approach is to define a set of output rest frame bandpasses that correspond closely to the effective rest frame wavelength of the observed bandpass for galaxies at the typical observed redshift in your sample. For example, at \(z\sim 0.7\) the observed SDSS \(r\), \(i\), and \(z\) bands are close in wavelength to the rest frame \(U\), \(B\), and \(V\) bands. So if we observed galaxies in the SDSS bands at those redshifts, we could find the K-corrections to \(UBV\). To do so, we have to instantiate a Kcorrect object that can do so using the response_map and response_out arguments:

import kcorrect.kcorrect

responses_in = ['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0']
responses_out = ['bessell_U', 'bessell_B', 'bessell_V']
responses_map = ['sdss_r0', 'sdss_i0', 'sdss_z0']
kc = kcorrect.kcorrect.Kcorrect(responses=responses_in,
                                responses_out=responses_out,
                                responses_map=responses_map)

# These are the ugriz observations (made up!)
redshift = 0.72
maggies = [27.26e-9, 73.98e-9, 132.56e-9, 198.52e-9, 238.05e-9]
ivar = [1.528e+18, 5.23e+18, 2.429e+18, 1.042e+18, 0.1653e+18]

# "coeffs" is a [5]-array with coefficients multiplying each template
coeffs = kc.fit_coeffs(redshift=redshift, maggies=maggies, ivar=ivar)

# "k" is a [3]-array with the K-corrections in magnitude units,
# from riz to UBV.
k = kc.kcorrect(redshift=redshift, coeffs=coeffs)

# "absmag" is also a [3]-array resuling from applying K-corrections
# and the distance modulus
absmag = kc.absmag(redshift=redshift, maggies=maggies, ivar=ivar, coeffs=coeffs)