[35]:
# from astropy.table import Table, QTable, join, vstack
from astropy.table import Table, QTable
# from astropy.io import fits
from astropy import units as u
import os
# import matplotlib
import matplotlib.pyplot as plt
from scipy.integrate import trapezoid
# from scipy.ndimage import uniform_filter1d
# from scipy import interpolate
import numpy as np
# import pandas as pd
import tqdm
import pickle
from svnds import PATH_DATA
The numbers in this notebook is only for forecast!
Survey Plan & Design
Site
Chile, El Sauce Observatory
Surveys
Reference (RIS): 20,000 deg^2
Wide-Field (Wide)
Intensive Monitoring Survey (IMS)
Details
Moon phases
Weather loss
[36]:
###### Telescope
D = 50.8 # Aperture size [cm]
D_obscuration = 29.8 # Central Obscuration (diameter) [cm]
EFL = 1537.3 # Effective FL [mm]
Deff = np.sqrt(D**2 - D_obscuration**2)
###### Detector
array = 'CMOS' # detector array type - verified, websete
dQ_RN = 3. # [e], readout noise - estimated, websete
I_dark = 0.01 # [e/s], dark current - seems to be very low ???
pixel_size = 3.76 # [um], "pitch" - verified, websete
theta_pixel = pixel_size / EFL * 206.265 # [arcsec], pixel scale- estimated, websete
nxpix, nypix = 9576, 6388 # [pixels], detector format, approx. 9k x 6k - verified, websete
[37]:
survey_area_per_night = 113. # [deg^2]
hrs_per_night = 9. # hours per night
FOV_per_pointing = nxpix*nypix * theta_pixel**2 / 3600**2
T_exposure = hrs_per_night / (survey_area_per_night/FOV_per_pointing) * 3600 / 2
print(f'FOV = {FOV_per_pointing:10.3g} deg^2')
print(f'Exposure time = {T_exposure:10.3g} sec')
FOV = 1.2 deg^2
Exposure time = 172 sec
[38]:
fwhm_seeing = 1.5 # [arcsec]
FWHM0 = fwhm_seeing # analysis aperture size
Tsamp = 180. # individual exposure time [sec], 3min
# How many pixels does a point source occupy?
# Effective number of pixels for a Gaussian PSF with FWHM0
Npix_ptsrc = np.pi*(FWHM0/theta_pixel)**2
print(f'number of pixels for a point source = {Npix_ptsrc:.1f}')
number of pixels for a point source = 27.8
[39]:
# Constants
h = 6.626e-27 # erg/Hz
c = 3e10 # cm/s
path_skytable = os.path.join(PATH_DATA, "systematics/", "skytable.fits")
sky_tbl = Table.read(path_skytable)
wl_nm = sky_tbl['lam'] # nm
wl_um = wl_nm / 1e3 # micron
wl_cm = wl_um / 1e4 # cm
wl_am = wl_angstrom = wl_nm * 10 # angstrom
nu = 3e18 / wl_angstrom # Hz
I_lambda = sky_tbl['flux'] # [ph/s/m2/micron/arcsec2] photon reate
f_lambda = I_lambda * (h*c/wl_cm) / (1e2**2) / (1e4) # erg/s/cm2/A
f_nu = f_lambda * wl_angstrom * (wl_cm/c) / (1e-23 * 1e-6) # micro Jansky
#####
# with open('filters_corrected', 'rb') as fr:
# filters_corrected = pickle.load(fr)
Calculate 7DS sensitivity
[40]:
def synth_phot(wave, flux, wave_lvf, resp_lvf, tol=1e-3, return_photonrate = False):
"""
Quick synthetic photometry routine.
Parameters
----------
wave : `numpy.ndarray`
wavelength of input spectrum.
flux : `numpy.ndarray`
flux density of input spectrum in f_nu unit
if `return_countrate` = True, erg/s/cm2/Hz is assumed
wave_lvf : `numpy.ndarray`
wavelength of the response function
resp_lvf : `numpy.ndarray`
response function. assume that this is a QE.
tol : float, optional
Consider only wavelength range above this tolerence (peak * tol).
The default is 1e-3.
Returns
-------
synthethic flux density in the input unit
if return_photonrate = True, photon rates [ph/s/cm2]
"""
index_filt, = np.where(resp_lvf > resp_lvf.max()*tol)
index_flux, = np.where(np.logical_and( wave > wave_lvf[index_filt].min(),
wave < wave_lvf[index_filt].max() ))
wave_resamp = np.concatenate( (wave[index_flux], wave_lvf[index_filt]) )
wave_resamp.sort()
wave_resamp = np.unique(wave_resamp)
flux_resamp = np.interp(wave_resamp, wave, flux)
resp_resamp = np.interp(wave_resamp, wave_lvf, resp_lvf)
if return_photonrate:
h_planck = 6.626e-27 # erg/Hz
return trapezoid(resp_resamp / wave_resamp * flux_resamp, wave_resamp) / h_planck
return trapezoid(resp_resamp / wave_resamp * flux_resamp, wave_resamp) \
/ trapezoid(resp_resamp / wave_resamp, wave_resamp)
[41]:
def pointsrc_signal2noise(mag_src, Tsamp, wave_sys, resp_sys):
"""
Calculate SN for a point source
Input
mag_src: AB mag of the source, scalar
Tsamp: individual exposure time [sec], can be scalar or array
WARNING: !!! ALL VARIABLES ARE GLOBALLY DECLARED !!!
"""
Naper = Npix_ptsrc
#source & sky background
f_nu_src = f_nu*0 + 10**(-0.4*(mag_src + 48.6)) # erg/s/cm2/Hz
f_nu_sky = f_nu*(1e-23*1e-6) # erg/s/cm2/Hz/arcsec2
photon_rate_src = synth_phot(wl_um, f_nu_src, wave_sys, resp_sys, return_photonrate=True) # ph/s/cm2
photon_rate_sky = synth_phot(wl_um, f_nu_sky, wave_sys, resp_sys, return_photonrate=True) # ph/s/cm2/arcsec2
I_photo_src = photon_rate_src * (np.pi/4*Deff**2) # [e/s] per aperture (no aperture loss)
I_photo_sky = photon_rate_sky * (np.pi/4*Deff**2) * (theta_pixel**2) # [e/s] per pixel
Q_photo_src = I_photo_src * Tsamp
Q_photo_sky = I_photo_sky * Tsamp
Q_photo_dark = I_dark * Tsamp
SN = Q_photo_src / np.sqrt(Q_photo_src + Naper*Q_photo_sky + Naper*Q_photo_dark + Naper*dQ_RN**2)
return SN
[42]:
lambda_7ds = np.arange(4000., 9000., 125)
Tsamp = 180 * 364/14 * 0.5 * 7
with open('filters_corrected', 'rb') as fr:
filters_corrected = pickle.load(fr)
[43]:
unit_SB = u.nW/(u.m)**2/u.sr
unit_cntrate = u.electron / u.s
T_sens = (QTable(
names=('band', 'wavelength', 'I_photo_sky', 'mag_sky', 'mag_pts'),
dtype=(np.int16,float,float,float,float,) )
)
for key in T_sens.colnames:
T_sens[key].info.format = '.4g'
for iw, wl_cen in enumerate(lambda_7ds):
wl_cen = int(wl_cen)
wave_sys = filters_corrected['wave_' + str(wl_cen)]
resp_sys = filters_corrected['resp_' + str(wl_cen)]
# photon rate
photon_rate = synth_phot(wl_um, f_nu*(1e-23*1e-6), wave_sys, resp_sys, return_photonrate=True)
# SB
SB_sky = synth_phot(wl_um, f_nu*(1e-23*1e-6), wave_sys, resp_sys)
# sky photo-current or count rate [e/s]
I_photo = photon_rate * (np.pi/4*Deff**2) * (theta_pixel**2)
# noise in count per obs [e].
Q_photo = (I_photo+I_dark)*Tsamp
dQ_photo = np.sqrt(Q_photo)
# noise in count rate [e/s]
# read-noise (indistinguishable from signal) should be added
dI_photo = np.sqrt(dQ_photo**2 + dQ_RN**2)/Tsamp
# surface brightness limit [one pixel]
dSB_sky = (dI_photo/I_photo)*SB_sky
mag_sky = -2.5*np.log10(5*dSB_sky) - 48.60
# point source limit
dFnu = np.sqrt(Npix_ptsrc) * dSB_sky*(theta_pixel)**2
mag_pts = -2.5*np.log10(5*dFnu) - 48.60
# Add data to the table
T_sens.add_row([iw, wl_cen, I_photo, mag_sky, mag_pts])
[44]:
T_sens
[44]:
QTable length=40
| band | wavelength | I_photo_sky | mag_sky | mag_pts |
|---|---|---|---|---|
| int16 | float64 | float64 | float64 | float64 |
| 0 | 4000 | 0.1534 | 23.33 | 23.02 |
| 1 | 4125 | 0.2052 | 23.37 | 23.05 |
| 2 | 4250 | 0.2221 | 23.46 | 23.14 |
| 3 | 4375 | 0.2557 | 23.48 | 23.16 |
| 4 | 4500 | 0.2999 | 23.46 | 23.14 |
| 5 | 4625 | 0.3221 | 23.47 | 23.15 |
| 6 | 4750 | 0.3342 | 23.47 | 23.15 |
| 7 | 4875 | 0.3399 | 23.48 | 23.16 |
| 8 | 5000 | 0.3358 | 23.48 | 23.16 |
| 9 | 5125 | 0.3294 | 23.47 | 23.15 |
| ... | ... | ... | ... | ... |
| 30 | 7750 | 0.1262 | 21.92 | 21.6 |
| 31 | 7875 | 0.1438 | 21.77 | 21.45 |
| 32 | 8000 | 0.1081 | 21.76 | 21.44 |
| 33 | 8125 | 0.06286 | 21.85 | 21.53 |
| 34 | 8250 | 0.07501 | 21.62 | 21.3 |
| 35 | 8375 | 0.1058 | 21.34 | 21.02 |
| 36 | 8500 | 0.08193 | 21.34 | 21.03 |
| 37 | 8625 | 0.06379 | 21.33 | 21.02 |
| 38 | 8750 | 0.09328 | 21.03 | 20.71 |
| 39 | 8875 | 0.1067 | 20.82 | 20.5 |
[34]:
# path_save = os.path.join(PATH_DATA, "sensitivity/" 'SDS_WFS_7yr_eff_5sig.csv')
# T_sens.write(path_save, overwrite = True)
[ ]: