Effective PWV on a Black BodyΒΆ

The effective precipitable water vapor is defined as a rescaling of the precipitable water vapor (PWV) along the the line of sight such that changes in the brightness of an object become (roughly) linear. This notebook demonstrates the definition of effective PWV by plotting the PWV induced change in the magnitude of a Black Body.

[1]:
from pathlib import Path

import numpy as np
import snat_sim
import sncosmo
from astropy.modeling import models
from astropy import units as u
from matplotlib import pyplot as plt
from pwv_kpno.defaults import v1_transmission

WARNING: AstropyDeprecationWarning: The update_default_config function is deprecated and may be removed in a future version. [sncosmo]
[2]:
fig_dir = Path('.') / 'figs'
fig_dir.mkdir(exist_ok=True, parents=True)

[3]:
def black_body_sed(temp, wavelengths, pwv, res=5):
    """Simulate a black body spectrum with PWV effects

    Args:
        temp        (float): Temperature of the black body in Kelvin
        wavelengths (array): Wavelengths to simulate the SED at
        pwv         (float): PWV concentration to apply
        res         (float): Resolution to sample the atmosphere with

    Returns:
        An array of flux values
    """

    bb = models.BlackBody(temperature=temp * u.K)
    flux_without_atm = bb(wavelengths * u.AA)
    transmission = v1_transmission(pwv, wavelengths, res)
    return flux_without_atm * transmission


def calc_delta_bb_mag(pwv_vals, band, bb_temp=8000):
    """Calculate the PWV induced change in magnitude for a black body

    Args:
        pwv_vals (array): The pwv values to plot saturation for
        band  (Bandpass): SNCosmo bandpass
        bb_temp  (float): Temperature of the black body to use

    Returns:
        A list of delta magnitudes in the given band
    """

    wave = np.arange(band.minwave(), band.maxwave(), .05)
    base_sed = black_body_sed(bb_temp, wave, pwv=0)
    base_mag = -2.5 * np.log(np.trapz(y=base_sed * wave, x=wave))

    delta_mag = []
    for pwv in pwv_vals:
        sed_with_pwv = black_body_sed(bb_temp, wave, pwv)
        pwv_mag = -2.5 * np.log(np.trapz(y=sed_with_pwv * wave, x=wave))
        delta_mag.append(pwv_mag - base_mag)

    return delta_mag

[4]:
def plot_delta_bb_mag(pwv_los, pwv_los_labels, exp=0.6, norm_pwv= 2, fid_pwv=4):
    """Plot the PWV induced change in magnitude for a black body

    Plot relative to LOS and effective PWV

    Args:
        pwv_los        (array): PWV values along line of sight
        pwv_los_labels (array): Axis labels for line of sight PWV
        exp            (float): Exponent for calculating PWV_eff from PWV_los
        norm_pwv       (float): PWV value used to determine ``coef``
        fid_pwv        (float): Fiducial PWV used in the paper
    """

    coef = 1 / (norm_pwv ** exp)

    # Convert PWV line-of-sight into PWV effective
    pwv_eff = coef * (pwv_los ** exp)
    pwv_eff_labels = coef * (pwv_los_labels ** exp)

    # Create axes for plotting vs los and effective pwv
    fig, (left_ax, right_ax) = plt.subplots(1, 2, figsize=(10, 5), sharey=True)
    left_twin_y = left_ax.twiny()
    right_twin_y = right_ax.twiny()

    # Manually set axis limits to avoid wrestling with the auto scaling later on
    los_xlim = pwv_los_labels.min(), pwv_los_labels.max()

    left_twin_y.set_xlim(los_xlim)
    left_twin_y.set_xticks(pwv_los_labels)
    left_twin_y.set_xticklabels(pwv_los_labels)

    eff_xlim = pwv_eff_labels.min(), pwv_eff_labels.max()
    right_ax.set_xlim(eff_xlim)

    right_twin_y.set_xlim(eff_xlim)
    right_twin_y.set_xticks(pwv_eff_labels)
    right_twin_y.set_xticklabels(pwv_los_labels)

    # Plot delta mag for each band
    for band_abbrev in 'rizy':
        band_name = f'lsst_hardware_{band_abbrev}'
        band = sncosmo.get_bandpass(band_name)
        delta_mag = calc_delta_bb_mag(pwv_los, band)

        left_twin_y.plot(pwv_los, delta_mag)
        right_ax.plot(pwv_eff, delta_mag, label=f'LSST {band_abbrev}')

    # Plot reference lines for pwv values
    left_twin_y.axvline(norm_pwv, linestyle=':', color='k', alpha=.85, label='Norm pwv')
    left_twin_y.axvline(fid_pwv, linestyle='-.', color='k', alpha=.7, label='Fiducial pwv')
    right_twin_y.axvline(coef * (norm_pwv ** exp), linestyle=':', color='k', alpha=.85)
    right_twin_y.axvline(coef * (fid_pwv ** exp), linestyle='-.', color='k', alpha=.7)

    eff_ticks = np.arange(0, max(pwv_eff_labels) + 1, .5)
    los_ticks = (eff_ticks / coef) ** (1/exp)
    left_ax.set_xticks(los_ticks)
    left_ax.set_xticklabels(eff_ticks)
    left_ax.set_xlim(los_xlim)

    # A final bit of formatting
    left_ax.set_ylabel(r'$\Delta$ mag')
    left_ax.set_xlabel(r'PWV$_{eff}$')
    left_twin_y.set_xlabel(r'PWV$_{los}$')
    right_ax.set_xlabel(r'PWV$_{eff}$')
    right_twin_y.set_xlabel(r'PWV$_{los}$')
    left_twin_y.legend(loc='upper left', framealpha=1)
    right_ax.legend(loc='upper left', framealpha=1)

    left_ax.set_ylim(ymin=0)
    plt.tight_layout()

[5]:
# Sample PWV values more frequently at lower values for a smoother curve
pwv = np.concatenate([
    np.arange(0.01, .1, .01),
    np.arange(.1, 2, .1),
    np.arange(2, 10, .5),
    np.arange(10, 12.1, 1)
])

pwv_labels = np.arange(0, 13)
plot_delta_bb_mag(pwv, pwv_labels)
plt.ylim(0, .5)
plt.savefig(fig_dir / f'delta_bb_mag.png')
plt.show()

../_images/notebooks_pwv_eff_on_black_body_5_0.png
[ ]: