DES SN 5YR Tutorial Part 5: Lambda Dependent corrections

f921a66b000846479bb5566f66f304c2 Dark Energy Survey Supernova Program

Tutorial Part 5 Lambda Dependent corrections Contact author: Jason Lee

Here, we demonstrate how we obtain our magnitude corrections for the SHAPE (DCR + \(\lambda\)-dependent seeing) and COORD (DCR only) effects starting from the flux ratio tables and the altitude offset vs. g-i magnitude for each exposure.

[1]:
import warnings
warnings.filterwarnings("ignore")
[2]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import astropy.units as u

from scipy.interpolate import RegularGridInterpolator
from astropy.coordinates import Angle, SkyCoord, AltAz, EarthLocation
from astropy.time import Time
from astropy.coordinates import Angle
from astropy.coordinates import AltAz, ICRS
from scipy.interpolate import interp1d
[3]:
earth_location = EarthLocation.of_site('ctio')
[4]:
DES_5YR_DCR_path = '../../../../DES-SN5YR/6_DCR_CORRECTIONS/'

1. SHAPE effect

(starting from the flux ratio (correct vs. uncorrected) tables)

Reading tables for SHAPE effect

flux ratio values table gives the flux ratio to when using the average color star as the PSF table columns = g-i values, AM_values, PSF FWHM values in arcseconds

[5]:
flux_ratio_values_g_table = np.load(DES_5YR_DCR_path + 'flux_ratio_values_g_table_DCR_shape.npy')
g_table_columns = np.load(DES_5YR_DCR_path + 'g_table_DCR_shape_xyz_columns.npy', allow_pickle=True)

flux_ratio_values_r_table = np.load(DES_5YR_DCR_path + 'flux_ratio_values_r_table_DCR_shape.npy')
r_table_columns = np.load(DES_5YR_DCR_path + 'r_table_DCR_shape_xyz_columns.npy', allow_pickle=True)

Using the GridInterpolator to 3D interpolate between the column values and flux ratio

[6]:
g_i_values_g, AM_values_g, psf_fwhms_g = g_table_columns
g_i_values_r, AM_values_r, psf_fwhms_r = r_table_columns
flux_interp_g_all = RegularGridInterpolator((g_i_values_g, AM_values_g, psf_fwhms_g), flux_ratio_values_g_table, bounds_error=False, fill_value = None)
flux_interp_r_all = RegularGridInterpolator((g_i_values_r, AM_values_r, psf_fwhms_r), flux_ratio_values_r_table, bounds_error=False, fill_value = None)

MCOR_220929 contains all the MAGCORs (both COORD and SHAPE) to be added to the uncorrected (SALT2 or SALT3) magnitudes

[7]:
MCOR_220929 = pd.read_csv(DES_5YR_DCR_path + 'DCRADJ_MAGCOR_v220929.csv')
MCOR_220929.head()
[7]:
Unnamed: 0 Unnamed: 0.1 CID EXPNUM CCDNUM MJD BAND TREST LAMREST SNR ... HA dRA dDEC CUTS ddRA ddDEC ADJOFF PSF_FWHM_ASEC MAGCOR_COORD MAGCOR_SHAPE
0 0 3627 1246281 255449 51 56614.0731 g 49.2 3613.5 1.7 ... b'-02:46:38.520' 0.017590 0.004682 True 0.038003 0.009945 0.039283 0.927 -0.002618 0.002402
1 1 3655 1246281 255888 51 56615.0961 g 49.9 3613.5 1.8 ... b'-02:09:31.400' 0.012814 0.003172 True 0.033227 0.008435 0.034281 0.997 -0.001724 0.001877
2 2 4066 1246281 238052 51 56559.1905 g 8.1 3613.5 5.0 ... b'-03:33:58.020' -0.022856 -0.007003 True -0.002442 -0.001740 0.002999 1.330 -0.000007 -0.004349
3 3 4080 1246281 238984 51 56563.2002 g 11.1 3613.5 9.0 ... b'-03:04:13.260' -0.017628 -0.004924 True 0.002786 0.000338 0.002806 1.344 -0.000006 -0.003640
4 4 4108 1246281 242477 51 56575.1914 g 20.1 3613.5 5.2 ... b'-02:29:35.140' 0.001841 0.000471 True 0.022254 0.005734 0.022981 0.925 -0.000900 0.001309

5 rows × 26 columns

SHAPE example for one candidate at a particular exposure

[8]:
ind = 0

EX_g_i = MCOR_220929.iloc[ind]['MAG_MODEL_g'] - MCOR_220929.iloc[ind]['MAG_MODEL_i']
EX_AM = MCOR_220929.iloc[ind]['AIRMASS']
EX_PSF_FWHM = MCOR_220929.iloc[ind]['PSF_FWHM_ASEC']
[9]:
# Convert flux ratio to magnitude correction

EX_MAG_COR_SHAPE = -2.5/np.log(10) * (1. - flux_interp_g_all([EX_g_i, EX_AM, EX_PSF_FWHM]))
[10]:
# Compare with MAGCOR file

print(EX_MAG_COR_SHAPE, MCOR_220929.iloc[ind]['MAGCOR_SHAPE'])
[0.00240155] 0.0024015458236172

2. COORD effect

(starting from altitude shift vs. g-i magnitude)

We first start from reading tables for COORD

[11]:
coord_dictionary = pd.read_csv(DES_5YR_DCR_path + 'DCR_dictionary.csv')
mag_corr_vs_offset = np.loadtxt(DES_5YR_DCR_path + 'mmag_vs_fracpsf_Moffat3.txt')
[12]:
DCR_coord_table = pd.read_csv(DES_5YR_DCR_path + 'DCR_coord_table.csv')
[13]:
DES_bands = ['g', 'r', 'i', 'z']
[14]:
where_band = []
for i in range(len(DES_bands)):
    where_band.append(np.where(MCOR_220929['BAND'] == DES_bands[i][0]))

COORD example

[15]:
expnums = MCOR_220929['EXPNUM'].values

g_i_vals = MCOR_220929['MAG_MODEL_g'].values - MCOR_220929['MAG_MODEL_i'].values

Altitude slope and y intercept values for each exposure

[16]:
slope_y_int_vals = np.zeros([2, len(MCOR_220929)])

for i in range(len(slope_y_int_vals[0])):
    dict_ = coord_dictionary[coord_dictionary['EXPNUM'] == expnums[i]]
    slope_y_int_vals[0][i] = dict_['SLOPE_ALT']
    slope_y_int_vals[1][i] = dict_['YINT_ALT']

Convert to altitude shifts: see Figure 5 of Lee & Acevedo et. al (2023) https://arxiv.org/abs/2304.01858

[17]:
alt_shift_vals = g_i_vals * slope_y_int_vals[0] + slope_y_int_vals[1]
[18]:
def sky_to_alt_az_coords(SN_fid_pos, mjd, loc = earth_location):
    coord0 = SkyCoord(SN_fid_pos[0] * u.deg, SN_fid_pos[1] * u.deg)
    aa = AltAz(location=loc, obstime=Time(mjd, scale = 'utc', format = 'mjd', location = loc))
    alt, az = coord0.transform_to(aa).alt, coord0.transform_to(aa).az
    return alt, az
[19]:
def alt_az_to_sky_coords(alt, az, mjd, loc=earth_location):
    # Create AltAz object
    aa = AltAz(alt=alt*u.deg, az=az*u.deg, location=loc, obstime=Time(mjd, format='mjd'))

    # Transform AltAz to Equatorial coordinates (ICRS)
    equatorial_coords = aa.transform_to(ICRS())

    # Extract RA and DEC
    ra, dec = equatorial_coords.ra, equatorial_coords.dec

    return ra, dec
[20]:
nominal_RA_vals, nominal_DEC_vals = MCOR_220929['RADEG'].values, MCOR_220929['DECDEG'].values
mjds = MCOR_220929['MJD'].values

alt_deg, az_deg = sky_to_alt_az_coords([nominal_RA_vals, nominal_DEC_vals], mjds)
[21]:
lat = earth_location.lat.value

sinq = -np.cos(lat*u.deg)*np.sin(az_deg)/np.cos(nominal_DEC_vals*u.deg)
cosq = (np.sin(lat*u.deg)-np.sin(nominal_DEC_vals*u.deg)*np.sin(alt_deg))/(np.cos(nominal_DEC_vals*u.deg)*np.cos(alt_deg))
[22]:
# Convert altitude shifts to dRA, dDEC

dRA, dDEC = alt_shift_vals*sinq, alt_shift_vals*cosq
[23]:
# Checking dRA dDEC with csv file

plt.plot(dRA[np.where(MCOR_220929['BAND'] == 'g')[0]], MCOR_220929[MCOR_220929['BAND'] == 'g']['dRA'], '.')

plt.plot(np.arange(-0.2, 0.2, 0.01), np.arange(-0.2, 0.2, 0.01))
[23]:
[<matplotlib.lines.Line2D at 0x16a0e82c0>]
../_images/tutorial_05-LambdaDependentCorrections_35_1.png
[24]:
MCOR_220929['CID'].unique()
[24]:
array([1246281, 1246430, 1246527, ..., 1959025, 1960031, 1974019])
[25]:
cid, band = 1246281, 'g'
where_cid_band = np.where((MCOR_220929['CID'] == cid) & (MCOR_220929['BAND'] == band))[0]
SNR_where_cid_band = MCOR_220929['SNR'][where_cid_band]
[26]:
# COORD shift in RA, DEC with respect to the SNR^2 weighted average

ddRA_cid_band, ddDEC_cid_band = dRA[where_cid_band] - np.average(dRA[where_cid_band], weights = SNR_where_cid_band**2), dDEC[where_cid_band] - np.average(dDEC[where_cid_band], weights = SNR_where_cid_band**2)
[27]:
# Check ddRA, ddDEC with csv file

plt.plot(ddRA_cid_band, MCOR_220929['ddRA'][where_cid_band], '.')
plt.plot(np.arange(-0.2, 0.2, 0.01), np.arange(-0.2, 0.2, 0.01))
plt.xlim(ddRA_cid_band.min()*1.1, ddRA_cid_band.max()*1.1)
plt.ylim(MCOR_220929['ddRA'][where_cid_band].min()*1.1, MCOR_220929['ddRA'][where_cid_band].max()*1.1)

[27]:
(-0.00738635890821312, 0.0551164154097413)
../_images/tutorial_05-LambdaDependentCorrections_39_1.png
[28]:
# Total OFFSET

ADJOFF_cid_band = np.sqrt(ddRA_cid_band**2 + ddDEC_cid_band**2)
print(ADJOFF_cid_band, MCOR_220929['ADJOFF'][where_cid_band].values)
[0.03932834 0.03434253 0.00299094 0.00280766 0.02298685 0.03114068
 0.01049225 0.03709824 0.03020881 0.05214196 0.00630405 0.00941435
 0.01432433 0.00558399 0.00694969] [0.0392828  0.03428121 0.00299867 0.0028061  0.02298113 0.03108189
 0.01050769 0.03708267 0.03018175 0.05217067 0.00628068 0.00938201
 0.01433384 0.00562435 0.00692192]
[29]:
# Convert to offset in multiples of PSF FWHM (Figure 4 of Lee & Acevedo et al. (2023))

OFF_PSF_FWHM_cid_band = ADJOFF_cid_band/MCOR_220929['PSF_FWHM_ASEC'][where_cid_band].values
[30]:
# Interpolate mag correction by offset

coord_off, magoff = mag_corr_vs_offset[0], mag_corr_vs_offset[1]
magoff_interp = interp1d(coord_off, magoff)
[31]:
# Figure 4 for the Moffat profile

plt.plot(mag_corr_vs_offset[0], mag_corr_vs_offset[1])
plt.plot(coord_off, magoff_interp(coord_off), ls = '--')
plt.xlim(0, 0.10)
plt.ylim(0, 15)
[31]:
(0.0, 15.0)
../_images/tutorial_05-LambdaDependentCorrections_43_1.png
[32]:
# Compare with csv file

plt.plot(magoff_interp(OFF_PSF_FWHM_cid_band)*1e-3, MCOR_220929['MAGCOR_COORD'][where_cid_band].values, '.')
plt.plot(np.arange(0, 0.002, 0.00001), -np.arange(0, 0.002, 0.00001))

print(magoff_interp(OFF_PSF_FWHM_cid_band)*1e-3/MCOR_220929['MAGCOR_COORD'][where_cid_band].values)

plt.xlim(0, magoff_interp(OFF_PSF_FWHM_cid_band).max()*1e-3)
plt.ylim(MCOR_220929['MAGCOR_COORD'][where_cid_band].values.min(), 0)
[-0.82161476 -0.82300294 -0.91907314 -0.94210969 -0.82027624 -0.82292534
 -0.8261532  -0.82059541 -0.82148971 -0.81899311 -0.84834005 -0.83548809
 -0.822037   -0.83689179 -0.84754167]
[32]:
(-0.002618313475896, 0.0)
../_images/tutorial_05-LambdaDependentCorrections_44_2.png