Source code for deshima_sensitivity.instruments

# standard library
from typing import List, Union, Tuple


# dependent packages
import numpy as np
from .physics import c, e, h, rad_trans


# type aliases
ArrayLike = Union[np.ndarray, List[float], List[int], float, int]


# constants
Delta_Al = 188.0 * 10**-6 * e  # gap energy of Al
eta_pb = 0.4  # Pair breaking efficiency
eta_Al_ohmic_850 = 0.9975  # Ohmic loss of an Al surface at 850 GHz.
# Shitov+, ISSTT2008. https://www.nrao.edu/meetings/isstt/papers/2008/2008263266.pdf


# main functions
[docs]def D2HPBW(F: ArrayLike) -> ArrayLike: """Get half-power beam width of DESHIMA 2.0 at given frequency (frequencies). Parameters ---------- F Frequency. Units: Hz. Returns ------- hpbw Half-power beam width. Units: radian. """ return 29.0 * 240.0 / (F / 1e9) * np.pi / 180.0 / 60.0 / 60.0
[docs]def eta_mb_ruze(F: ArrayLike, LFlimit: float, sigma: float) -> ArrayLike: """Get main-beam efficiency by Ruze's equation. Parameters ---------- F Frequency. Units: Hz. LFlimit Main-beam efficiency at 0 Hz. sigma Surface error. Units: m. Returns ------- eta_mb Main-beam efficiency. Units: None. """ return LFlimit * np.exp(-((4.0 * np.pi * sigma * F / c) ** 2.0))
[docs]def photon_NEP_kid( F_int: ArrayLike, P_kid_binned: np.ndarray, W_F_int: ArrayLike ) -> ArrayLike: """NEP of the KID, with respect to the absorbed power. Parameters ----------- F_int Integration frequencies of the signal responsible for loading. Units: Hz. P_kid_binned m x n matrix of the power absorbed by the KID m: the number of integration bins. n: the number of filter channels. Units: W. W_F_int Integration bandwidth, with respect to the power that sets the loading. Units: Hz. Returns ------- NEP_kid Noise-equivalent power of the KID. Notes ----- Pkid/(W_F * h * F) gives the occupation number. """ # photon_term = 2 * Pkid * (h*F + Pkid/W_F) poisson_term = np.sum(2 * P_kid_binned * h * F_int, axis=1) bunching_term = np.sum(2 * P_kid_binned * P_kid_binned / W_F_int, axis=1) r_term = 4 * Delta_Al * np.sum(P_kid_binned, axis=1) / eta_pb return np.sqrt(poisson_term + bunching_term + r_term)
[docs]def window_trans( F: ArrayLike, psd_in: ArrayLike, psd_cabin: ArrayLike, psd_co: ArrayLike, thickness: ArrayLike = 8.0e-3, tandelta: float = 4.805e-4, tan2delta: float = 1.0e-8, neffHDPE: float = 1.52, window_AR: bool = True, ) -> Tuple[ArrayLike, ArrayLike]: """Calculates the window transmission. Parameters ---------- F Frequency. Units: Hz. psd_in PSD of the incoming signal. Units : W / Hz. psd_cabin Johnson-Nyquist PSD of telescope cabin temperature. Units : W / Hz. psd_co Johnson-Nyquist PSD of cold-optics temperature. Units : W / Hz. thickness Thickness of the HDPE window. Units: m. tandelta Values from Stephen. "# 2.893e-8 %% tan delta, measured Biorat. I use 1e-8 as this fits the tail of the data better". tan2delta Values from Stephen. "# 2.893e-8 %% tan delta, measured Biorat. I use 1e-8 as this fits the tail of the data better". neffHDPE Refractive index of HDPE. Set to 1 to remove reflections. Units : None. window_AR Whether the window is supposed to be coated by Ar (True) or not (False). Returns ------- psd_after_2nd_refl PSD looking into the window from the cold optics. eta_window Transmission of the window. Units: None. """ # Parameters to calcualte the window (HDPE), data from Stephen # reflection. ((1-neffHDPE)/(1+neffHDPE))^2. Set to 0 for Ar coated. if window_AR: HDPErefl = 0.0 else: HDPErefl = ((1 - neffHDPE) / (1 + neffHDPE)) ** 2 eta_HDPE = np.exp( -thickness * 2 * np.pi * neffHDPE * (tandelta * F / c + tan2delta * (F / c) ** 2) ) # most of the reflected power sees the cold. psd_after_1st_refl = rad_trans(psd_in, psd_co, 1.0 - HDPErefl) psd_before_2nd_refl = rad_trans(psd_after_1st_refl, psd_cabin, eta_HDPE) # the reflected power sees the cold. psd_after_2nd_refl = rad_trans(psd_before_2nd_refl, psd_co, 1.0 - HDPErefl) eta_window = (1.0 - HDPErefl) ** 2 * eta_HDPE return psd_after_2nd_refl, eta_window