def show_projection(self, track, axes=True, max_theta=30., X_mark=None): """ Obtain the polar coordinates of a shower track relative to a telescope position in both horizontal and FoV coordinates systems and determine the fraction of the track within the telescope field of view. In addition, show the projection of the shower track as viewed by the telescope. Parameters ---------- track : Track object or Shower object. axes : bool, default True Show the axes of both coordinate systems of reference. max_theta : float, default 30 degrees Maximum offset angle in degrees relative to the telescope pointing direction. X_mark : float Reference slant depth in g/cm^2 of the shower track to be marked in the figure. If None, no mark is included. Returns ------- Projection object. (ax1, ax2) : PolarAxesSubplot objects. See also -------- Projection.show """ projection = sm.Projection(self, track) from ._tools import show_projection return projection, (show_projection(projection, None, False, axes, max_theta, X_mark))
def Projection(self, track): """ Obtain the coordinates of a shower track relative to the telescope position in both zenith and camera projection and determine the fraction of the track within the telescope field of view. Parameters ---------- track : Track object or Shower object. Returns ------- Projection object. See also -------- Projection.show """ return sm.Projection(self, track)
def Projection(self, telescope): """ Obtain the coordinates of a shower track relative to a telescope position in both zenith and camera projection and determine the fraction of the track within the telescope field of view. Parameters ---------- telescope : Telescope object. Returns ------- Projection object. (ax1, ax2) : AxesSubplot objects. See also -------- Projection.show """ return sm.Projection(telescope, self.track)
def show_projection(self, telescope, shower_size=True, axes=True, max_theta=30., X_mark='X_max'): """ Make a Projection object and show it. Parameters ---------- telescope : Telescope object. shower_size : book, default True Make the radii of the shower track points proportional to the shower size. axes : book, default True Show the axes of both frames of reference. max_theta : float, default 30 degrees Maximum offset angle in degrees relative to the telescope pointing direction. X_mark : float Reference slant depth in g/cm^2 of the shower track to be ma ked in the figure, default X_max. If X_mark=None, no mark is included. Returns ------- Projection object. (ax1, ax2) : PolarAxesSubpot objects. See also -------- Projection.show """ if X_mark == 'X_max': X_mark = self.X_max projection = sm.Projection(telescope, self.track) profile = self.profile from ._tools import show_projection return projection, (show_projection(projection, profile, shower_size, axes, max_theta, X_mark))
def Signal(telescope, shower, projection=None, atm_trans=True, tel_eff=True, **kwargs): """ Calculate the signal produced by a shower detected by a telescope. Parameters ---------- telescope : Telescope object. shower : Shower object. projection : Projection object If None, it will generated from telescope and shower. atm_trans : bool, default True Include the atmospheric transmision to transport photons. tel_eff : bool, default True Include the telescope efficiency to calculate the signal. If False, 100% efficiency is assumed for a given wavelenght interval. **kwargs {wvl_ini, wvl_fin, wvl_step} These parameters will modify the wavelenght interval when tel_eff==False. If None, the wavelength interval defined in the telescope is used. Returns ------- signal : Signal object. """ from .telescope import _Telescope from .shower import _Shower if not isinstance(telescope, _Telescope): if not isinstance(telescope, _Shower): raise ValueError('The input telescope is not valid') else: telescope, shower = (shower, telescope) if not isinstance(shower, _Shower): raise ValueError('The input shower is not valid') # This function is normally called from Event. If not, projection must be # generated. from .projection import _Projection if not isinstance(projection, _Projection): projection = sm.Projection(telescope, shower.track) atmosphere = shower.atmosphere track = shower.track fluorescence = shower.fluorescence cherenkov = shower.cherenkov signal = _Signal() signal.shower = shower signal.telescope = telescope signal.projection = projection signal.atmosphere = atmosphere signal.track = track signal.profile = shower.profile signal.fluorescence = fluorescence signal.cherenkov = cherenkov signal.atm_trans = atm_trans signal.tel_eff = tel_eff if tel_eff: # Wavelenght range to calculate the signal wvl_ini = telescope.wvl_ini wvl_fin = telescope.wvl_fin wvl_step = telescope.wvl_step wvl_cher = telescope.wvl_cher eff_fluo = telescope.eff_fluo eff_cher = telescope.eff_cher else: # User-defined wavalength range wvl_ini = kwargs.get('wvl_ini', telescope.wvl_ini) wvl_fin = kwargs.get('wvl_fin', telescope.wvl_fin) wvl_step = kwargs.get('wvl_step', telescope.wvl_step) wvl_cher = np.arange(wvl_ini, wvl_fin, wvl_step) signal.wvl_ini = wvl_ini signal.wvl_fin = wvl_fin signal.wvl_step = wvl_step # Only discretization points within the telescope field of view contributes # to the signal. In addition, the very begninning of the shower profile is # ignored to speed up calculations points = projection[projection.FoV & (signal.profile.s > 0.01)].index distance = np.array(projection.distance.loc[points]) theta = np.radians(projection.theta.loc[points]) alt = np.radians(projection.alt.loc[points]) # Solid angle fraction covered by the telescope area. Only discretization # points within the telescope field of view contributes to the signal collection = (telescope.area * np.cos(theta) / 4000000. / np.pi / distance**2) # Collection efficiency for the angular distribution of Cherenkov light # See F. Nerling et al., Astropart. Phys. 24(2006)241. beta = np.radians(projection.beta.loc[points]) theta_c = np.radians(cherenkov.theta_c.loc[points]) theta_cc = np.radians(cherenkov.theta_cc.loc[points]) a = np.array(cherenkov.a.loc[points]) b = np.array(cherenkov.b.loc[points]) collection_cher = collection * 2. / np.sin(beta) * ( a / theta_c * np.exp(-beta / theta_c) + b / theta_cc * np.exp(-beta / theta_cc)) # Relative fluorescence contribution from each shower point at each band # (between wvl_ini and wvl_fin). The atmospheric transmission is included # later rel_fluo = fluorescence.loc[points] if tel_eff: rel_fluo *= eff_fluo # 34 bands # Selection of bands within the wavelenght range rel_fluo = rel_fluo.loc[:, wvl_ini:wvl_fin] if atm_trans: # Atmospheric transmission at 350 nm. Only Rayleigh scattering is # considered X_vert = np.array(atmosphere.X_vert.loc[points]) rho = np.array(atmosphere.rho.loc[points]) thickness = np.array( atmosphere.h_to_Xv(atmosphere.h0 + telescope.z) - X_vert) thickness[thickness != 0] = (thickness[thickness != 0] / np.sin(alt[thickness != 0])) thickness[thickness == 0] = (100000. * distance[thickness == 0] * rho[thickness == 0]) # Only points within the telescope FoV, otherwise trans=0 trans = np.exp(-thickness / 1645.) # Relative fluorescence contribution including atmospheric transmission for wvl in rel_fluo: rel_fluo[wvl] *= trans**((350. / wvl)**4) # Wavelenght factor for Cherenkov contribution to signal from each # shower point wvl_factor = pd.DataFrame(index=points) for wvl in wvl_cher: wvl_factor[wvl] = trans**((350. / wvl)**4) / wvl**2 # wvl**2 -> (wvl**2 - wvl_step**2 / 4.) if tel_eff: wvl_factor *= eff_cher wvl_factor = wvl_factor.sum(axis=1) * wvl_step / (1. / 290. - 1. / 430.) elif tel_eff: # If atmospheric transmission is not included # The wavelength factor of Cherenkov signal is the same for all # shower points wvl_factor = eff_cher / wvl_cher**2 # wvl_cher**2 -> (wvl_cher**2 - wvl_step**2 / 4.) wvl_factor = wvl_factor.sum() * wvl_step / (1. / 290. - 1. / 430.) # If neither the atmospheric transmission nor the telescope efficiency are # included else: # The wavelength factor of Cherenkov signal only depends on the # integration wavelength interval wvl_factor = (1. / wvl_ini - 1. / wvl_fin) / (1. / 290. - 1. / 430.) # Number of photoelectrons due to fluorescence light emitted from each # shower point signal['Npe_fluo'] = rel_fluo.sum(axis=1) * collection # Number of photoelectrons due to fluorescence light within the FoV signal.Npe_fluo_sum = signal.Npe_fluo.sum() # Number of photoelectrons due to Cherenkov light emitted from each shower # point signal['Npe_cher'] = (cherenkov.N_ph.loc[points] * collection_cher * wvl_factor) # Number of photoelectrons due to Cherenkov light within the FoV signal.Npe_cher_sum = signal.Npe_cher.sum() # Total number of photoelectrons from both light components emitted at each # shower point signal['Npe_total'] = signal.sum(axis=1) # Total number of photoelectrons signal.Npe_total_sum = signal.Npe_cher_sum + signal.Npe_fluo_sum return signal
def Event(observatory, shower, atm_trans=True, tel_eff=True, **kwargs): """ Construct an Event object from a shower and an observatory. The Event objet contains the signal produced by the shower in each telescope of the observatory. Parameters ---------- observatory : Observatory object (may be a Grid object). shower : Shower object. atm_trans : bool, default True Include the atmospheric transmision to transport photons. tel_eff : bool, default True Include the telescope efficiency to calculate the signals. If False, 100% efficiency is assumed for a given wavelength interval. **kwargs {wvl_ini, wvl_fin, wvl_step} These parameters will be passed to the Signal constructor to modify the wavelength interval when tel_eff==False. If None, the wavelength interval defined in each telescope is used. Returns ------- event : Event object. """ from .observatory import _Observatory, _Grid from .telescope import _Telescope from .shower import _Shower if not isinstance(shower, _Shower): observatory, shower = (shower, observatory) if not isinstance(shower, _Shower): raise ValueError('The input shower is not valid') if isinstance(observatory, (_Telescope, _Observatory)): if isinstance(observatory, _Grid): event = _GridEvent() event.grid = observatory else: event = _Event() if isinstance(observatory, _Telescope): telescope = observatory event.observatory = _Observatory() event.observatory.append(telescope) else: event.observatory = observatory else: raise ValueError('The input observatory is not valid') event.shower = shower event.atmosphere = shower.atmosphere event.track = shower.track event.profile = shower.profile event.cherenkov = shower.cherenkov event.fluorescence = shower.fluorescence event.atm_trans = atm_trans event.tel_eff = tel_eff event.projections = [] event.signals = [] for telescope in observatory: projection = sm.Projection(telescope, event.track) event.projections.append(projection) signal = sm.Signal(telescope, shower, projection, atm_trans, tel_eff, **kwargs) event.signals.append(signal) event.images = None return event