Esempio n. 1
0
def analyze_muon_event(event):
    """
    Generic muon event analyzer.

    Parameters
    ----------
    event : ctapipe dl1 event container


    Returns
    -------
    muonringparam, muonintensityparam : MuonRingParameter
    and MuonIntensityParameter container event

    """

    names = [
        'LST_LST_LSTCam', 'MST_MST_NectarCam', 'MST_MST_FlashCam',
        'MST_SCT_SCTCam', 'SST_1M_DigiCam', 'SST_GCT_CHEC',
        'SST_ASTRI_ASTRICam', 'SST_ASTRI_CHEC'
    ]
    tail_cuts = [(5, 7), (5, 7), (10, 12), (5, 7), (5, 7), (5, 7), (5, 7),
                 (5, 7)]  # 10, 12?
    impact = [(0.2, 0.9), (0.1, 0.95), (0.2, 0.9), (0.2, 0.9), (0.1, 0.95),
              (0.1, 0.95), (0.1, 0.95), (0.1, 0.95)] * u.m
    ringwidth = [(0.04, 0.08), (0.02, 0.1), (0.01, 0.1), (0.02, 0.1),
                 (0.01, 0.5), (0.02, 0.2), (0.02, 0.2), (0.02, 0.2)] * u.deg
    total_pix = [1855., 1855., 1764., 11328., 1296., 2048., 2368., 2048]
    # 8% (or 6%) as limit
    min_pix = [148., 148., 141., 680., 104., 164., 142., 164]
    # Need to either convert from the pixel area in m^2 or check the camera specs
    ang_pixel_width = [0.1, 0.2, 0.18, 0.067, 0.24, 0.2, 0.17, 0.2, 0.163
                       ] * u.deg
    # Found from TDRs (or the pixel area)
    hole_rad = [
        0.308 * u.m, 0.244 * u.m, 0.244 * u.m, 4.3866 * u.m, 0.160 * u.m,
        0.130 * u.m, 0.171 * u.m, 0.171 * u.m
    ]  # Assuming approximately spherical hole
    cam_rad = [2.26, 3.96, 3.87, 4., 4.45, 2.86, 5.25, 2.86] * u.deg
    # Above found from the field of view calculation
    sec_rad = [
        0. * u.m, 0. * u.m, 0. * u.m, 2.7 * u.m, 0. * u.m, 1. * u.m, 1.8 * u.m,
        1.8 * u.m
    ]
    sct = [False, False, False, True, False, True, True, True]
    # Added cleaning here. All these options should go to an input card
    cleaning = True

    muon_cuts = {
        'Name': names,
        'tail_cuts': tail_cuts,
        'Impact': impact,
        'RingWidth': ringwidth,
        'total_pix': total_pix,
        'min_pix': min_pix,
        'CamRad': cam_rad,
        'SecRad': sec_rad,
        'SCT': sct,
        'AngPixW': ang_pixel_width,
        'HoleRad': hole_rad
    }
    logger.debug(muon_cuts)

    muonringlist = []  # [None] * len(event.dl0.tels_with_data)
    muonintensitylist = []  # [None] * len(event.dl0.tels_with_data)
    tellist = []
    muon_event_param = {
        'TelIds': tellist,
        'MuonRingParams': muonringlist,
        'MuonIntensityParams': muonintensitylist
    }

    for telid in event.dl0.tels_with_data:

        logger.debug("Analysing muon event for tel %d", telid)
        image = event.dl1.tel[telid].image

        # Get geometry
        teldes = event.inst.subarray.tel[telid]
        geom = teldes.camera
        x, y = geom.pix_x, geom.pix_y

        dict_index = muon_cuts['Name'].index(str(teldes))
        logger.debug('found an index of %d for camera %d', dict_index,
                     geom.cam_id)

        tailcuts = muon_cuts['tail_cuts'][dict_index]
        logger.debug("Tailcuts are %s", tailcuts)

        clean_mask = tailcuts_clean(geom,
                                    image,
                                    picture_thresh=tailcuts[0],
                                    boundary_thresh=tailcuts[1])

        # TODO: correct this hack for values over 90
        altval = event.mcheader.run_array_direction[1]
        if altval > Angle(90, unit=u.deg):
            warnings.warn('Altitude over 90 degrees')
            altval = Angle(90, unit=u.deg)

        telescope_pointing = SkyCoord(alt=altval,
                                      az=event.mcheader.run_array_direction[0],
                                      frame=AltAz())
        camera_coord = SkyCoord(
            x=x,
            y=y,
            frame=CameraFrame(
                focal_length=teldes.optics.equivalent_focal_length,
                rotation=geom.pix_rotation,
                telescope_pointing=telescope_pointing,
            ))

        nom_coord = camera_coord.transform_to(
            NominalFrame(origin=telescope_pointing))
        x = nom_coord.delta_az.to(u.deg)
        y = nom_coord.delta_alt.to(u.deg)

        if (cleaning):
            img = image * clean_mask
        else:
            img = image

        muonring = ChaudhuriKunduRingFitter(None)

        logger.debug("img: %s mask: %s, x=%s y= %s", np.sum(image),
                     np.sum(clean_mask), x, y)

        if not sum(img):  # Nothing left after tail cuts
            continue

        muonringparam = muonring.fit(x, y, image * clean_mask)

        dist = np.sqrt(
            np.power(x - muonringparam.ring_center_x, 2) +
            np.power(y - muonringparam.ring_center_y, 2))
        ring_dist = np.abs(dist - muonringparam.ring_radius)

        muonringparam = muonring.fit(
            x, y, img * (ring_dist < muonringparam.ring_radius * 0.4))

        dist = np.sqrt(
            np.power(x - muonringparam.ring_center_x, 2) +
            np.power(y - muonringparam.ring_center_y, 2))
        ring_dist = np.abs(dist - muonringparam.ring_radius)

        muonringparam = muonring.fit(
            x, y, img * (ring_dist < muonringparam.ring_radius * 0.4))

        muonringparam.tel_id = telid
        muonringparam.obs_id = event.dl0.obs_id
        muonringparam.event_id = event.dl0.event_id
        dist_mask = np.abs(
            dist - muonringparam.ring_radius) < muonringparam.ring_radius * 0.4
        pix_im = image * dist_mask
        nom_dist = np.sqrt(
            np.power(muonringparam.ring_center_x, 2) +
            np.power(muonringparam.ring_center_y, 2))

        minpix = muon_cuts['min_pix'][dict_index]  # 0.06*numpix #or 8%

        mir_rad = np.sqrt(teldes.optics.mirror_area.to("m2") / np.pi)

        # Camera containment radius -  better than nothing - guess pixel
        # diameter of 0.11, all cameras are perfectly circular   cam_rad =
        # np.sqrt(numpix*0.11/(2.*np.pi))

        if (npix_above_threshold(pix_im, tailcuts[0]) > 0.1 * minpix
                and npix_composing_ring(pix_im) > minpix
                and nom_dist < muon_cuts['CamRad'][dict_index]
                and muonringparam.ring_radius < 1.5 * u.deg
                and muonringparam.ring_radius > 1. * u.deg):
            muonringparam.ring_containment = ring_containment(
                muonringparam.ring_radius, muon_cuts['CamRad'][dict_index],
                muonringparam.ring_center_x, muonringparam.ring_center_y)

            # Guess HESS is 0.16
            # sec_rad = 0.*u.m
            # sct = False
            # if numpix == 2048 and mir_rad > 2.*u.m and mir_rad < 2.1*u.m:
            #     sec_rad = 1.*u.m
            #     sct = True
            #
            # Store muon ring parameters (passing cuts stage 1)
            # muonringlist[idx] = muonringparam

            tellist.append(telid)
            muonringlist.append(muonringparam)
            muonintensitylist.append(None)

            ctel = MuonLineIntegrate(
                mir_rad,
                hole_radius=muon_cuts['HoleRad'][dict_index],
                pixel_width=muon_cuts['AngPixW'][dict_index],
                sct_flag=muon_cuts['SCT'][dict_index],
                secondary_radius=muon_cuts['SecRad'][dict_index])

            if image.shape[0] == muon_cuts['total_pix'][dict_index]:
                muonintensityoutput = ctel.fit_muon(
                    muonringparam.ring_center_x, muonringparam.ring_center_y,
                    muonringparam.ring_radius, x[dist_mask], y[dist_mask],
                    image[dist_mask])

                muonintensityoutput.tel_id = telid
                muonintensityoutput.obs_id = event.dl0.obs_id
                muonintensityoutput.event_id = event.dl0.event_id
                muonintensityoutput.mask = dist_mask

                idx_ring = np.nonzero(pix_im)
                muonintensityoutput.ring_completeness = ring_completeness(
                    x[idx_ring],
                    y[idx_ring],
                    pix_im[idx_ring],
                    muonringparam.ring_radius,
                    muonringparam.ring_center_x,
                    muonringparam.ring_center_y,
                    threshold=30,
                    bins=30)
                muonintensityoutput.ring_size = np.sum(pix_im)

                dist_ringwidth_mask = np.abs(
                    dist - muonringparam.ring_radius) < (
                        muonintensityoutput.ring_width)
                pix_ringwidth_im = image * dist_ringwidth_mask
                idx_ringwidth = np.nonzero(pix_ringwidth_im)

                muonintensityoutput.ring_pix_completeness = npix_above_threshold(
                    pix_ringwidth_im[idx_ringwidth], tailcuts[0]) / len(
                        pix_im[idx_ringwidth])

                logger.debug(
                    "Tel %d Impact parameter = %s mir_rad=%s "
                    "ring_width=%s", telid,
                    muonintensityoutput.impact_parameter, mir_rad,
                    muonintensityoutput.ring_width)
                conditions = [
                    muonintensityoutput.impact_parameter * u.m <
                    muon_cuts['Impact'][dict_index][1] * mir_rad,
                    muonintensityoutput.impact_parameter >
                    muon_cuts['Impact'][dict_index][0],
                    muonintensityoutput.ring_width <
                    muon_cuts['RingWidth'][dict_index][1],
                    muonintensityoutput.ring_width >
                    muon_cuts['RingWidth'][dict_index][0]
                ]

                if all(conditions):
                    muonintensityparam = muonintensityoutput
                    idx = tellist.index(telid)
                    muonintensitylist[idx] = muonintensityparam
                    logger.debug("Muon found in tel %d,  tels in event=%d",
                                 telid, len(event.dl0.tels_with_data))
                else:
                    continue

    return muon_event_param
Esempio n. 2
0
def analyze_muon_event(event):
    """
    Generic muon event analyzer.

    Parameters
    ----------
    event : ctapipe dl1 event container


    Returns
    -------
    muonringparam, muonintensityparam : MuonRingParameter
    and MuonIntensityParameter container event

    """

    names = ['LST:LSTCam', 'MST:NectarCam', 'MST:FlashCam', 'MST-SCT:SCTCam',
             'SST-1M:DigiCam', 'SST-GCT:CHEC', 'SST-ASTRI:ASTRICam', 'SST-ASTRI:CHEC']
    tail_cuts = [(5, 7), (5, 7), (10, 12), (5, 7),
                (5, 7), (5, 7), (5, 7), (5, 7)]  # 10, 12?
    impact = [(0.2, 0.9), (0.1, 0.95), (0.2, 0.9), (0.2, 0.9),
              (0.1, 0.95), (0.1, 0.95), (0.1, 0.95), (0.1, 0.95)] * u.m
    ringwidth = [(0.04, 0.08), (0.02, 0.1), (0.01, 0.1), (0.02, 0.1),
                 (0.01, 0.5), (0.02, 0.2), (0.02, 0.2), (0.02, 0.2)] * u.deg
    total_pix = [1855., 1855., 1764., 11328., 1296., 2048., 2368., 2048]
    # 8% (or 6%) as limit
    min_pix = [148., 148., 141., 680., 104., 164., 142., 164]
    # Need to either convert from the pixel area in m^2 or check the camera specs
    ang_pixel_width = [0.1, 0.2, 0.18, 0.067, 0.24, 0.2, 0.17, 0.2, 0.163] * u.deg
    # Found from TDRs (or the pixel area)
    hole_rad = [0.308 * u.m, 0.244 * u.m, 0.244 * u.m,
                4.3866 * u.m, 0.160 * u.m, 0.130 * u.m,
                0.171 * u.m, 0.171 * u.m]  # Assuming approximately spherical hole
    cam_rad = [2.26, 3.96, 3.87, 4., 4.45, 2.86, 5.25, 2.86] * u.deg
    # Above found from the field of view calculation
    sec_rad = [0. * u.m, 0. * u.m, 0. * u.m, 2.7 * u.m,
               0. * u.m, 1. * u.m, 1.8 * u.m, 1.8 * u.m]
    sct = [False, False, False, True, False, True, True, True]
    # Added cleaning here. All these options should go to an input card
    cleaning = True

    muon_cuts = {'Name': names, 'tail_cuts': tail_cuts, 'Impact': impact,
                 'RingWidth': ringwidth, 'total_pix': total_pix,
                 'min_pix': min_pix, 'CamRad': cam_rad, 'SecRad': sec_rad,
                 'SCT': sct, 'AngPixW': ang_pixel_width, 'HoleRad': hole_rad}
    logger.debug(muon_cuts)

    muonringlist = []  # [None] * len(event.dl0.tels_with_data)
    muonintensitylist = []  # [None] * len(event.dl0.tels_with_data)
    tellist = []
    muon_event_param = {'TelIds': tellist,
                        'MuonRingParams': muonringlist,
                        'MuonIntensityParams': muonintensitylist}

    for telid in event.dl0.tels_with_data:

        logger.debug("Analysing muon event for tel %d", telid)
        image = event.dl1.tel[telid].image[0]

        # Get geometry
        teldes = event.inst.subarray.tel[telid]
        geom = teldes.camera
        x, y = geom.pix_x, geom.pix_y

        dict_index = muon_cuts['Name'].index(str(teldes))
        logger.debug('found an index of %d for camera %d',
                     dict_index, geom.cam_id)

        tailcuts = muon_cuts['tail_cuts'][dict_index]
        logger.debug("Tailcuts are %s", tailcuts)

        clean_mask = tailcuts_clean(geom, image, picture_thresh=tailcuts[0],
                                    boundary_thresh=tailcuts[1])

        # TODO: correct this hack for values over 90
        altval = event.mcheader.run_array_direction[1]
        if altval > Angle(90, unit=u.deg):
            warnings.warn('Altitude over 90 degrees')
            altval = Angle(90, unit=u.deg)

        telescope_pointing = SkyCoord(
            alt=altval,
            az=event.mcheader.run_array_direction[0],
            frame=HorizonFrame()
        )
        camera_coord = SkyCoord(
            x=x, y=y,
            frame=CameraFrame(
                focal_length=teldes.optics.equivalent_focal_length,
                rotation=geom.pix_rotation,
                telescope_pointing=telescope_pointing,
            )
        )

        nom_coord = camera_coord.transform_to(
            NominalFrame(origin=telescope_pointing)
        )
        x = nom_coord.delta_az.to(u.deg)
        y = nom_coord.delta_alt.to(u.deg)

        if(cleaning):
            img = image * clean_mask
        else:
            img = image

        muonring = ChaudhuriKunduRingFitter(None)

        logger.debug("img: %s mask: %s, x=%s y= %s", np.sum(image),
                     np.sum(clean_mask), x, y)

        if not sum(img):  # Nothing left after tail cuts
            continue

        muonringparam = muonring.fit(x, y, image * clean_mask)

        dist = np.sqrt(np.power(x - muonringparam. ring_center_x, 2)
                       + np.power(y - muonringparam.ring_center_y, 2))
        ring_dist = np.abs(dist - muonringparam.ring_radius)

        muonringparam = muonring.fit(
            x, y, img * (ring_dist < muonringparam.ring_radius * 0.4)
        )

        dist = np.sqrt(np.power(x - muonringparam.ring_center_x, 2) +
                       np.power(y - muonringparam.ring_center_y, 2))
        ring_dist = np.abs(dist - muonringparam.ring_radius)

        muonringparam = muonring.fit(
            x, y, img * (ring_dist < muonringparam.ring_radius * 0.4)
        )

        muonringparam.tel_id = telid
        muonringparam.obs_id = event.dl0.obs_id
        muonringparam.event_id = event.dl0.event_id
        dist_mask = np.abs(dist - muonringparam.
                           ring_radius) < muonringparam.ring_radius * 0.4
        pix_im = image * dist_mask
        nom_dist = np.sqrt(np.power(muonringparam.ring_center_x,
                                    2) + np.power(muonringparam.ring_center_y, 2))

        minpix = muon_cuts['min_pix'][dict_index]  # 0.06*numpix #or 8%

        mir_rad = np.sqrt(teldes.optics.mirror_area.to("m2") / np.pi)

        # Camera containment radius -  better than nothing - guess pixel
        # diameter of 0.11, all cameras are perfectly circular   cam_rad =
        # np.sqrt(numpix*0.11/(2.*np.pi))

        if(npix_above_threshold(pix_im, tailcuts[0]) > 0.1 * minpix
           and npix_composing_ring(pix_im) > minpix
           and nom_dist < muon_cuts['CamRad'][dict_index]
           and muonringparam.ring_radius < 1.5 * u.deg
           and muonringparam.ring_radius > 1. * u.deg):
            muonringparam.ring_containment = ring_containment(
                muonringparam.ring_radius,
                muon_cuts['CamRad'][dict_index],
                muonringparam.ring_center_x,
                muonringparam.ring_center_y)

            # Guess HESS is 0.16
            # sec_rad = 0.*u.m
            # sct = False
            # if numpix == 2048 and mir_rad > 2.*u.m and mir_rad < 2.1*u.m:
            #     sec_rad = 1.*u.m
            #     sct = True
            #
            # Store muon ring parameters (passing cuts stage 1)
            # muonringlist[idx] = muonringparam

            tellist.append(telid)
            muonringlist.append(muonringparam)
            muonintensitylist.append(None)

            ctel = MuonLineIntegrate(
                mir_rad, hole_radius=muon_cuts['HoleRad'][dict_index],
                pixel_width=muon_cuts['AngPixW'][dict_index],
                sct_flag=muon_cuts['SCT'][dict_index],
                secondary_radius=muon_cuts['SecRad'][dict_index]
            )

            if image.shape[0] == muon_cuts['total_pix'][dict_index]:
                muonintensityoutput = ctel.fit_muon(muonringparam.ring_center_x,
                                                    muonringparam.ring_center_y,
                                                    muonringparam.ring_radius,
                                                    x[dist_mask], y[dist_mask],
                                                    image[dist_mask])

                muonintensityoutput.tel_id = telid
                muonintensityoutput.obs_id = event.dl0.obs_id
                muonintensityoutput.event_id = event.dl0.event_id
                muonintensityoutput.mask = dist_mask

                idx_ring = np.nonzero(pix_im)
                muonintensityoutput.ring_completeness = ring_completeness(
                    x[idx_ring], y[idx_ring], pix_im[idx_ring],
                    muonringparam.ring_radius,
                    muonringparam.ring_center_x,
                    muonringparam.ring_center_y,
                    threshold=30,
                    bins=30)
                muonintensityoutput.ring_size = np.sum(pix_im)

                dist_ringwidth_mask = np.abs(dist - muonringparam.ring_radius
                                             ) < (muonintensityoutput.ring_width)
                pix_ringwidth_im = image * dist_ringwidth_mask
                idx_ringwidth = np.nonzero(pix_ringwidth_im)

                muonintensityoutput.ring_pix_completeness = npix_above_threshold(
                    pix_ringwidth_im[idx_ringwidth], tailcuts[0]) / len(
                    pix_im[idx_ringwidth])

                logger.debug("Tel %d Impact parameter = %s mir_rad=%s "
                             "ring_width=%s", telid,
                             muonintensityoutput.impact_parameter, mir_rad,
                             muonintensityoutput.ring_width)
                conditions = [
                    muonintensityoutput.impact_parameter * u.m <
                    muon_cuts['Impact'][dict_index][1] * mir_rad,

                    muonintensityoutput.impact_parameter
                    > muon_cuts['Impact'][dict_index][0],

                    muonintensityoutput.ring_width
                    < muon_cuts['RingWidth'][dict_index][1],

                    muonintensityoutput.ring_width
                    > muon_cuts['RingWidth'][dict_index][0]
                ]

                if all(conditions):
                    muonintensityparam = muonintensityoutput
                    idx = tellist.index(telid)
                    muonintensitylist[idx] = muonintensityparam
                    logger.debug("Muon found in tel %d,  tels in event=%d",
                                 telid, len(event.dl0.tels_with_data))
                else:
                    continue

    return muon_event_param
Esempio n. 3
0
def analyze_muon_event(event, params=None, geom_dict=None):
    """
    Generic muon event analyzer. 

    Parameters
    ----------
    event : ctapipe dl1 event container


    Returns
    -------
    muonringparam, muonintensityparam : MuonRingParameter 
    and MuonIntensityParameter container event

    """
    # Declare a dict to define the muon cuts (ASTRI and SCT missing)
    muon_cuts = {}

    names = [
        'LST:LSTCam', 'MST:NectarCam', 'MST:FlashCam', 'MST-SCT:SCTCam',
        'SST-1M:DigiCam', 'SST-GCT:CHEC', 'SST-ASTRI:ASTRICam',
        'SST-ASTRI:CHEC'
    ]
    TailCuts = [(5, 7), (5, 7), (10, 12), (5, 7), (5, 7), (5, 7), (5, 7),
                (5, 7)]  # 10, 12?
    impact = [(0.2, 0.9), (0.1, 0.95), (0.2, 0.9), (0.2, 0.9), (0.1, 0.95),
              (0.1, 0.95), (0.1, 0.95), (0.1, 0.95)] * u.m
    ringwidth = [(0.04, 0.08), (0.02, 0.1), (0.01, 0.1), (0.02, 0.1),
                 (0.01, 0.5), (0.02, 0.2), (0.02, 0.2), (0.02, 0.2)] * u.deg
    TotalPix = [1855., 1855., 1764., 11328., 1296., 2048., 2368., 2048]
    # 8% (or 6%) as limit
    MinPix = [148., 148., 141., 680., 104., 164., 142., 164]
    # Need to either convert from the pixel area in m^2 or check the camera specs
    AngPixelWidth = [0.1, 0.2, 0.18, 0.067, 0.24, 0.2, 0.17, 0.2, 0.163
                     ] * u.deg
    # Found from TDRs (or the pixel area)
    hole_rad = [
        0.308 * u.m, 0.244 * u.m, 0.244 * u.m, 4.3866 * u.m, 0.160 * u.m,
        0.130 * u.m, 0.171 * u.m, 0.171 * u.m
    ]  # Assuming approximately spherical hole
    cam_rad = [2.26, 3.96, 3.87, 4., 4.45, 2.86, 5.25, 2.86] * u.deg
    # Above found from the field of view calculation
    sec_rad = [
        0. * u.m, 0. * u.m, 0. * u.m, 2.7 * u.m, 0. * u.m, 1. * u.m, 1.8 * u.m,
        1.8 * u.m
    ]
    sct = [False, False, False, True, False, True, True, True]

    muon_cuts = {
        'Name': names,
        'TailCuts': TailCuts,
        'Impact': impact,
        'RingWidth': ringwidth,
        'TotalPix': TotalPix,
        'MinPix': MinPix,
        'CamRad': cam_rad,
        'SecRad': sec_rad,
        'SCT': sct,
        'AngPixW': AngPixelWidth,
        'HoleRad': hole_rad
    }
    logger.debug(muon_cuts)

    muonringlist = []  # [None] * len(event.dl0.tels_with_data)
    muonintensitylist = []  # [None] * len(event.dl0.tels_with_data)
    tellist = []
    muon_event_param = {
        'TelIds': tellist,
        'MuonRingParams': muonringlist,
        'MuonIntensityParams': muonintensitylist
    }

    for telid in event.dl0.tels_with_data:

        logger.debug("Analysing muon event for tel %d", telid)
        muonringparam = None
        muonintensityparam = None
        image = event.dl1.tel[telid].image[0]

        # Get geometry
        teldes = event.inst.subarray.tel[telid]
        geom = teldes.camera
        x, y = geom.pix_x, geom.pix_y

        dict_index = muon_cuts['Name'].index(str(teldes))
        logger.debug('found an index of %d for camera %d', dict_index,
                     geom.cam_id)

        tailcuts = muon_cuts['TailCuts'][dict_index]
        logger.debug("Tailcuts are %s", tailcuts)

        clean_mask = tailcuts_clean(geom,
                                    image,
                                    picture_thresh=tailcuts[0],
                                    boundary_thresh=tailcuts[1])
        camera_coord = CameraFrame(
            x=x,
            y=y,
            focal_length=teldes.optics.equivalent_focal_length,
            rotation=geom.pix_rotation)

        # TODO: correct this hack for values over 90
        altval = event.mcheader.run_array_direction[1]
        if (altval > np.pi / 2.):
            altval = np.pi / 2.

        altaz = HorizonFrame(alt=altval * u.rad,
                             az=event.mcheader.run_array_direction[0] * u.rad)
        nom_coord = camera_coord.transform_to(
            NominalFrame(array_direction=altaz, pointing_direction=altaz))
        x = nom_coord.x.to(u.deg)
        y = nom_coord.y.to(u.deg)

        img = image * clean_mask
        muonring = ChaudhuriKunduRingFitter(None)

        logger.debug("img: %s mask: %s, x=%s y= %s", np.sum(image),
                     np.sum(clean_mask), x, y)

        if not sum(img):  # Nothing left after tail cuts
            continue

        muonringparam = muonring.fit(x, y, image * clean_mask)

        dist = np.sqrt(
            np.power(x - muonringparam.ring_center_x, 2) +
            np.power(y - muonringparam.ring_center_y, 2))
        ring_dist = np.abs(dist - muonringparam.ring_radius)

        muonringparam = muonring.fit(
            x, y, img * (ring_dist < muonringparam.ring_radius * 0.4))

        dist = np.sqrt(
            np.power(x - muonringparam.ring_center_x, 2) +
            np.power(y - muonringparam.ring_center_y, 2))
        ring_dist = np.abs(dist - muonringparam.ring_radius)

        muonringparam = muonring.fit(
            x, y, img * (ring_dist < muonringparam.ring_radius * 0.4))

        muonringparam.tel_id = telid
        muonringparam.obs_id = event.dl0.obs_id
        muonringparam.event_id = event.dl0.event_id
        dist_mask = np.abs(
            dist - muonringparam.ring_radius) < muonringparam.ring_radius * 0.4
        pix_im = image * dist_mask
        nom_dist = np.sqrt(
            np.power(muonringparam.ring_center_x, 2) +
            np.power(muonringparam.ring_center_y, 2))

        minpix = muon_cuts['MinPix'][dict_index]  # 0.06*numpix #or 8%

        mir_rad = np.sqrt(teldes.optics.mirror_area.to("m2") / (np.pi))

        # Camera containment radius -  better than nothing - guess pixel
        # diameter of 0.11, all cameras are perfectly circular   cam_rad =
        # np.sqrt(numpix*0.11/(2.*np.pi))

        if (np.sum(pix_im > tailcuts[0]) > 0.1 * minpix
                and np.sum(pix_im) > minpix
                and nom_dist < muon_cuts['CamRad'][dict_index]
                and muonringparam.ring_radius < 1.5 * u.deg
                and muonringparam.ring_radius > 1. * u.deg):

            # Guess HESS is 0.16
            # sec_rad = 0.*u.m
            # sct = False
            # if numpix == 2048 and mir_rad > 2.*u.m and mir_rad < 2.1*u.m:
            #     sec_rad = 1.*u.m
            #     sct = True
            #
            # Store muon ring parameters (passing cuts stage 1)
            # muonringlist[idx] = muonringparam

            tellist.append(telid)
            muonringlist.append(muonringparam)
            muonintensitylist.append(None)

            ctel = MuonLineIntegrate(
                mir_rad,
                hole_radius=muon_cuts['HoleRad'][dict_index],
                pixel_width=muon_cuts['AngPixW'][dict_index],
                sct_flag=muon_cuts['SCT'][dict_index],
                secondary_radius=muon_cuts['SecRad'][dict_index])

            if (image.shape[0] == muon_cuts['TotalPix'][dict_index]):
                muonintensityoutput = ctel.fit_muon(
                    muonringparam.ring_center_x, muonringparam.ring_center_y,
                    muonringparam.ring_radius, x[dist_mask], y[dist_mask],
                    image[dist_mask])

                muonintensityoutput.tel_id = telid
                muonintensityoutput.obs_id = event.dl0.obs_id
                muonintensityoutput.event_id = event.dl0.event_id
                muonintensityoutput.mask = dist_mask

                logger.debug(
                    "Tel %d Impact parameter = %s mir_rad=%s "
                    "ring_width=%s", telid,
                    muonintensityoutput.impact_parameter, mir_rad,
                    muonintensityoutput.ring_width)
                conditions = [
                    muonintensityoutput.impact_parameter * u.m <
                    muon_cuts['Impact'][dict_index][1] * mir_rad,
                    muonintensityoutput.impact_parameter >
                    muon_cuts['Impact'][dict_index][0],
                    muonintensityoutput.ring_width <
                    muon_cuts['RingWidth'][dict_index][1],
                    muonintensityoutput.ring_width >
                    muon_cuts['RingWidth'][dict_index][0]
                ]

                if all(conditions):
                    muonintensityparam = muonintensityoutput
                    idx = tellist.index(telid)
                    muonintensitylist[idx] = muonintensityparam
                    logger.debug("Muon found in tel %d,  tels in event=%d",
                                 telid, len(event.dl0.tels_with_data))
                else:
                    continue

    return muon_event_param
Esempio n. 4
0
def analyze_muon_event(event_id, image, geom, equivalent_focal_length,
                       mirror_area, plot_rings, plots_path):
    """
    Analyze an event to fit a muon ring

    Paramenters
    ---------
    event_id:   `int` id of the analyzed event
    image:      `np.ndarray` number of photoelectrons in each pixel
    geom:       CameraGeometry
    equivalent_focal_length: `float` focal length of the telescope
    mirror_area: `float` mirror area of the telescope
    plot_rings: `bool` plot the muon ring
    plots_path: `string` path to store the figures

    Returns
    ---------
    muonintensityoutput ``
    muonringparam       ``
    good_ring           `bool` it determines whether the ring can be used for analysis or not

    TODO: several hard-coded quantities that can go into a configuration file
    """

    tailcuts = [10, 5]

    cam_rad = 2.26 * u.deg

    # some cuts for good ring selection:
    min_pix = 148  # (8%) minimum number of pixels in the ring with >0 signal
    min_pix_fraction_after_cleaning = 0.1  # minimum fraction of the ring pixels that must be above tailcuts[0]
    min_ring_radius = 0.8 * u.deg  # minimum ring radius
    max_ring_radius = 1.5 * u.deg  # maximum ring radius
    max_radial_stdev = 0.1 * u.deg  # maximum standard deviation of the light distribution along ring radius
    max_radial_excess_kurtosis = 1.  # maximum excess kurtosis
    min_impact_parameter = 0.2  # in fraction of mirror radius
    max_impact_parameter = 0.9  # in fraction of mirror radius
    ring_integration_width = 0.25  # +/- integration range along ring radius, in fraction of ring radius (was 0.4 until 20200326)
    outer_ring_width = 0.2  # in fraction of ring radius, width of ring just outside the integrated muon ring, used to check pedestal bias

    x, y = pixel_coords_to_telescope(geom, equivalent_focal_length)
    muonringparam, clean_mask, dist, image_clean = fit_muon(
        x, y, image, geom, tailcuts)

    mirror_radius = np.sqrt(mirror_area / np.pi)
    dist_mask = np.abs(dist - muonringparam.ring_radius
                       ) < muonringparam.ring_radius * ring_integration_width
    pix_ring = image * dist_mask
    pix_outside_ring = image * ~dist_mask

    # mask to select pixels just outside the ring that will be integrated to obtain the ring's intensity:
    dist_mask_2 = np.logical_and(
        ~dist_mask,
        np.abs(dist - muonringparam.ring_radius) < muonringparam.ring_radius *
        (ring_integration_width + outer_ring_width))
    pix_ring_2 = image[dist_mask_2]

    #    nom_dist = np.sqrt(np.power(muonringparam.ring_center_x,2)
    #                    + np.power(muonringparam.ring_center_y, 2))

    muonringparam.ring_containment = ring_containment(
        muonringparam.ring_radius, cam_rad, muonringparam.ring_center_x,
        muonringparam.ring_center_y)

    radial_distribution = radial_light_distribution(
        muonringparam.ring_center_x, muonringparam.ring_center_y,
        x[clean_mask], y[clean_mask], image[clean_mask])

    # Do complicated calculations (minuit-based max likelihood ring fit) only for selected rings:
    candidate_clean_ring = all([
        radial_distribution['standard_dev'] < max_radial_stdev,
        radial_distribution['excess_kurtosis'] < max_radial_excess_kurtosis,
        npix_above_threshold(
            pix_ring, tailcuts[0]) > min_pix_fraction_after_cleaning * min_pix,
        npix_composing_ring(pix_ring) > min_pix,
        muonringparam.ring_radius < max_ring_radius,
        muonringparam.ring_radius > min_ring_radius
    ])

    if candidate_clean_ring:
        ctel = MuonLineIntegrate(mirror_radius,
                                 hole_radius=0.308 * u.m,
                                 pixel_width=0.1 * u.deg,
                                 sct_flag=False,
                                 secondary_radius=0. * u.m)

        muonintensityoutput = ctel.fit_muon(muonringparam.ring_center_x,
                                            muonringparam.ring_center_y,
                                            muonringparam.ring_radius,
                                            x[dist_mask], y[dist_mask],
                                            image[dist_mask])

        dist_ringwidth_mask = np.abs(dist - muonringparam.ring_radius) < (
            muonintensityoutput.ring_width)
        # We do the calculation of the ring completeness (i.e. fraction of whole circle) using the pixels
        # within the "width" fitted using MuonLineIntegrate.
        muonintensityoutput.ring_completeness = ring_completeness(
            x[dist_ringwidth_mask],
            y[dist_ringwidth_mask],
            image[dist_ringwidth_mask],
            muonringparam.ring_radius,
            muonringparam.ring_center_x,
            muonringparam.ring_center_y,
            threshold=30,
            bins=30)

        pix_ringwidth_im = image[dist_ringwidth_mask]
        muonintensityoutput.ring_pix_completeness = npix_above_threshold(
            pix_ringwidth_im, tailcuts[0]) / len(pix_ringwidth_im)

    else:
        muonintensityoutput = MuonIntensityParameter()
        # Set default values for cases in which the muon intensity fit (with MuonLineIntegrate) is not done:
        muonintensityoutput.ring_width = np.nan * u.deg
        muonintensityoutput.impact_parameter_pos_x = np.nan * u.m
        muonintensityoutput.impact_parameter_pos_y = np.nan * u.m
        muonintensityoutput.impact_parameter = np.nan * u.m
        muonintensityoutput.ring_pix_completeness = np.nan
        muonintensityoutput.ring_completeness = np.nan

    muonintensityoutput.mask = dist_mask
    muonintensityoutput.ring_size = np.sum(pix_ring)
    size_outside_ring = np.sum(pix_outside_ring * clean_mask)

    # This is just mean charge per pixel in pixels just around the ring (on the outer side):
    mean_pixel_charge_around_ring = np.sum(pix_ring_2) / len(pix_ring_2)

    if candidate_clean_ring:
        print(
            "Impact parameter={:.3f}, ring_width={:.3f}, ring radius={:.3f}, ring completeness={:.3f}"
            .format(
                muonintensityoutput.impact_parameter,
                muonintensityoutput.ring_width,
                muonringparam.ring_radius,
                muonintensityoutput.ring_completeness,
            ))
    # Now add the conditions based on the detailed muon ring fit made by MuonLineIntegrate:
    conditions = [
        candidate_clean_ring,
        muonintensityoutput.impact_parameter <
        max_impact_parameter * mirror_radius,
        muonintensityoutput.impact_parameter >
        min_impact_parameter * mirror_radius,

        # TODO: To be applied when we have decent optics.
        # muonintensityoutput.ring_width
        # < 0.08,
        # NOTE: inside "candidate_clean_ring" cuts there is already a cut in the st dev of light distribution along ring radius,
        # which is also a measure of the ring width

        # muonintensityoutput.ring_width
        # > 0.04
    ]

    muonintensityparam = muonintensityoutput
    if all(conditions):
        good_ring = True
    else:
        good_ring = False

    if (plot_rings and plots_path and good_ring):
        focal_length = equivalent_focal_length
        ring_telescope = SkyCoord(delta_az=muonringparam.ring_center_x,
                                  delta_alt=muonringparam.ring_center_y,
                                  frame=TelescopeFrame())

        ring_camcoord = ring_telescope.transform_to(
            CameraFrame(
                focal_length=focal_length,
                rotation=geom.cam_rotation,
            ))
        centroid = (ring_camcoord.x.value, ring_camcoord.y.value)
        radius = muonringparam.ring_radius
        width = muonintensityoutput.ring_width
        ringrad_camcoord = 2 * radius.to(u.rad) * focal_length
        ringwidthfrac = width / radius
        ringrad_inner = ringrad_camcoord * (1. - ringwidthfrac)
        ringrad_outer = ringrad_camcoord * (1. + ringwidthfrac)

        fig, ax = plt.subplots(figsize=(10, 10))
        plot_muon_event(ax, geom, image * clean_mask, centroid,
                        ringrad_camcoord, ringrad_inner, ringrad_outer,
                        event_id)

        plt.figtext(
            0.15, 0.20, 'radial std dev: {0:.3f}'.format(
                radial_distribution['standard_dev']))
        plt.figtext(
            0.15, 0.18, 'radial excess kurtosis: {0:.3f}'.format(
                radial_distribution['excess_kurtosis']))
        plt.figtext(0.15, 0.16, 'fitted ring width: {0:.3f}'.format(width))
        plt.figtext(
            0.15, 0.14, 'ring completeness: {0:.3f}'.format(
                muonintensityoutput.ring_completeness))

        fig.savefig('{}/Event_{}_fitted.png'.format(plots_path, event_id))

    if (plot_rings and not plots_path):
        print("You are trying to plot without giving a path!")

    return muonintensityparam, size_outside_ring, muonringparam, good_ring, radial_distribution, mean_pixel_charge_around_ring
Esempio n. 5
0
def analyze_muon_event(event_id, image, geom, equivalent_focal_length,
                       mirror_area, plot_rings, plots_path):
    """
    Analyze an event to fit a muon ring

    Paramenters
    ---------
    event_id:   `int` id of the analyzed event
    image:      `np.ndarray` number of photoelectrons in each pixel
    geom:       CameraGeometry
    equivalent_focal_length: `float` focal length of the telescope
    mirror_area: `float` mirror area of the telescope
    plot_rings: `bool` plot the muon ring
    plots_path: `string` path to store the figures

    Returns
    ---------
    muonintensityoutput ``
    muonringparam       ``
    good_ring           `bool` it determines whether the ring can be used for analysis or not

    TODO: several hard-coded quantities that can go into a configuration file
    """

    tailcuts = [5, 10]

    cam_rad = 2.26 * u.deg
    min_pix = 148  # 8%

    x, y = get_muon_center(geom, equivalent_focal_length)
    muonringparam, clean_mask, dist, image_clean = fit_muon(
        x, y, image, geom, tailcuts)

    mirror_radius = np.sqrt(mirror_area / np.pi)
    dist_mask = np.abs(
        dist - muonringparam.ring_radius) < muonringparam.ring_radius * 0.4
    pix_ring = image * dist_mask
    pix_out_ring = image * ~dist_mask

    nom_dist = np.sqrt(
        np.power(muonringparam.ring_center_x, 2) +
        np.power(muonringparam.ring_center_y, 2))

    muonringparam.ring_containment = ring_containment(
        muonringparam.ring_radius, cam_rad, muonringparam.ring_center_x,
        muonringparam.ring_center_y)

    ctel = MuonLineIntegrate(mirror_radius,
                             hole_radius=0.308 * u.m,
                             pixel_width=0.1 * u.deg,
                             sct_flag=False,
                             secondary_radius=0. * u.m)

    muonintensityoutput = ctel.fit_muon(muonringparam.ring_center_x,
                                        muonringparam.ring_center_y,
                                        muonringparam.ring_radius,
                                        x[dist_mask], y[dist_mask],
                                        image[dist_mask])
    muonintensityoutput.mask = dist_mask
    idx_ring = np.nonzero(pix_ring)
    muonintensityoutput.ring_completeness = ring_completeness(
        x[idx_ring],
        y[idx_ring],
        pix_ring[idx_ring],
        muonringparam.ring_radius,
        muonringparam.ring_center_x,
        muonringparam.ring_center_y,
        threshold=30,
        bins=30)
    muonintensityoutput.ring_size = np.sum(pix_ring)
    size_outside_ring = np.sum(pix_out_ring * clean_mask)
    dist_ringwidth_mask = np.abs(dist - muonringparam.ring_radius) < (
        muonintensityoutput.ring_width)
    pix_ringwidth_im = image * dist_ringwidth_mask
    idx_ringwidth = np.nonzero(pix_ringwidth_im)

    muonintensityoutput.ring_pix_completeness = npix_above_threshold(
        pix_ringwidth_im[idx_ringwidth], tailcuts[0]) / len(
            pix_ring[idx_ringwidth])

    print(
        "Impact parameter = %s"
        "ring_width=%s, ring radius=%s, ring completeness=%s" %
        (muonintensityoutput.impact_parameter, muonintensityoutput.ring_width,
         muonringparam.ring_radius, muonintensityoutput.ring_completeness))

    conditions = [
        muonintensityoutput.impact_parameter <
        0.9 * mirror_radius,  # 90% inside the mirror
        muonintensityoutput.impact_parameter >
        0.2 * mirror_radius,  # 20% inside the mirror
        npix_above_threshold(pix_ring, tailcuts[0]) > 0.1 * min_pix,
        npix_composing_ring(pix_ring) > min_pix,
        muonringparam.ring_radius < 1.5 * u.deg,
        muonringparam.ring_radius > 1. * u.deg
        # TODO: To be applied when we have decent optics
        # muonintensityoutput.ring_width
        # < 0.08,

        # muonintensityoutput.ring_width
        # > 0.04
    ]

    muonintensityparam = muonintensityoutput
    if all(conditions):
        good_ring = True
    else:
        good_ring = False

    if (plot_rings and plots_path and good_ring):
        altaz = AltAz(alt=70 * u.deg, az=0 * u.deg)
        focal_length = equivalent_focal_length
        ring_nominal = SkyCoord(delta_az=muonringparam.ring_center_x,
                                delta_alt=muonringparam.ring_center_y,
                                frame=NominalFrame(origin=altaz))

        ring_camcoord = ring_nominal.transform_to(
            CameraFrame(focal_length=focal_length,
                        rotation=geom.pix_rotation,
                        telescope_pointing=altaz))
        centroid = (ring_camcoord.x.value, ring_camcoord.y.value)
        radius = muonringparam.ring_radius
        width = muonintensityoutput.ring_width
        ringrad_camcoord = 2 * radius.to(u.rad) * focal_length
        ringwidthfrac = width / radius
        ringrad_inner = ringrad_camcoord * (1. - ringwidthfrac)
        ringrad_outer = ringrad_camcoord * (1. + ringwidthfrac)

        fig, ax = plt.subplots(figsize=(10, 10))
        plot_muon_event(ax, geom, image * clean_mask, centroid,
                        ringrad_camcoord, ringrad_inner, ringrad_outer,
                        event_id)

        fig.savefig('{}/Event_{}_fitted.png'.format(plots_path, event_id))

    if (plot_rings and not plots_path):
        print("You are trying to plot without giving a path!")

    return muonintensityparam, size_outside_ring, muonringparam, good_ring