Example #1
0
def test_full_likelihood():
    """
    Simple test of likelihood, test against known values for high and low signal cases. Check
    That full calculation and the gaussian approx become equal at high signal
    """
    spe = 0.5 # Single photo-electron width
    pedestal = 1 # width of the pedestal distribution

    image_small = [0,1,2]
    expectation_small = [1,1,1]

    full_like_small = poisson_likelihood_full(image_small, expectation_small, spe, pedestal)
    # Check against known values
    assert np.sum(np.abs(full_like_small - np.asarray([2.75630505, 2.62168656, 3.39248449]))) < 1e-6

    image_large = [40,50,60]
    expectation_large = [50,50,50]
    full_like_large = poisson_likelihood_full(image_large, expectation_large, spe, pedestal)
    # Check against known values
    assert np.sum(np.abs(full_like_large - np.asarray([7.45489137, 5.99305388, 7.66226007]))) < 1e-6

    gaus_like_large = poisson_likelihood_gaussian(image_large, expectation_large, spe, pedestal)

    # Check thats in large signal case the full expectation is equal to the gaussian approximation (to 5%)
    assert np.all(np.abs((full_like_large-gaus_like_large)/full_like_large) < 0.05)
Example #2
0
def test_full_likelihood():
    """
    Simple test of likelihood, test against known values for high and low 
    signal cases. Check that full calculation and the gaussian approx become 
    equal at high signal.
    """
    spe = 0.5  # Single photo-electron width
    pedestal = 1  # width of the pedestal distribution

    image_small = [0, 1, 2]
    expectation_small = [1, 1, 1]

    full_like_small = poisson_likelihood_full(image_small, expectation_small,
                                              spe, pedestal)
    # Check against known values
    assert np.sum(
        np.abs(full_like_small -
               np.asarray([2.75630505, 2.62168656, 3.39248449]))) < 1e-6

    image_large = [40, 50, 60]
    expectation_large = [50, 50, 50]
    full_like_large = poisson_likelihood_full(image_large, expectation_large,
                                              spe, pedestal)
    # Check against known values
    assert np.sum(
        np.abs(full_like_large -
               np.asarray([7.45489137, 5.99305388, 7.66226007]))) < 1e-6

    gaus_like_large = poisson_likelihood_gaussian(image_large,
                                                  expectation_large, spe,
                                                  pedestal)

    # Check thats in large signal case the full expectation is equal to the
    # gaussian approximation (to 5%)
    assert np.all(
        np.abs((full_like_large - gaus_like_large) / full_like_large) < 0.05)
Example #3
0
    def get_likelihood(self, source_x, source_y, core_x, core_y,
                       energy, x_max_scale, goodness_of_fit=False):
        """Get the likelihood that the image predicted at the given test
        position matches the camera image.

        Parameters
        ----------
        source_x: float
            Source position of shower in the nominal system (in deg)
        source_y: float
            Source position of shower in the nominal system (in deg)
        core_x: float
            Core position of shower in tilted telescope system (in m)
        core_y: float
            Core position of shower in tilted telescope system (in m)
        energy: float
            Shower energy (in TeV)
        x_max_scale: float
            Scaling factor applied to geometrically calculated Xmax
        goodness_of_fit: boolean
            Determines whether expected likelihood should be subtracted from result
        Returns
        -------
        float: Likelihood the model represents the camera image at this position

        """
        # First we add units back onto everything.  Currently not
        # handled very well, maybe in future we could just put
        # everything in the correct units when loading in the class
        # and ignore them from then on

        zenith = (np.pi / 2) - self.array_direction.alt.to(u.rad).value
        azimuth = self.array_direction.az

        # Geometrically calculate the depth of maximum given this test position
        x_max = self.get_shower_max(source_x, source_y,
                                    core_x, core_y,
                                    zenith)
        x_max *= x_max_scale

        # Calculate expected Xmax given this energy
        x_max_exp = guess_shower_depth(energy)  # / np.cos(20*u.deg)

        # Convert to binning of Xmax
        x_max_bin = x_max - x_max_exp

        # Check for range
        if x_max_bin > 200:
            x_max_bin = 200
        if x_max_bin < -100:
            x_max_bin = -100

        # Calculate impact distance for all telescopes
        impact = np.sqrt(np.power(self.tel_pos_x - core_x, 2)
                         + np.power(self.tel_pos_y - core_y, 2))
        # And the expected rotation angle
        phi = np.arctan2((self.tel_pos_x - core_x),
                         (self.tel_pos_y - core_y)) * u.rad

        # Rotate and translate all pixels such that they match the
        # template orientation
        pix_y_rot, pix_x_rot = self.rotate_translate(
            self.pixel_x,
            self.pixel_y,
            source_x, source_y, phi
        )

        # In the interpolator class we can gain speed advantages by using masked arrays
        # so we need to make sure here everything is masked
        prediction = ma.zeros(self.image.shape)
        prediction.mask = ma.getmask(self.image)

        time_gradients = np.zeros((self.image.shape[0],2))

        # Loop over all telescope types and get prediction
        for tel_type in np.unique(self.tel_types).tolist():
            type_mask = self.tel_types == tel_type
            prediction[type_mask] = \
                self.image_prediction(tel_type, energy *
                                      np.ones_like(impact[type_mask]),
                                      impact[type_mask], x_max_bin *
                                      np.ones_like(impact[type_mask]),
                                      pix_x_rot[type_mask] * (180 / math.pi) * -1,
                                      pix_y_rot[type_mask] * (180 / math.pi))

            if self.use_time_gradient:
                time_gradients[type_mask] = \
                    self.predict_time(tel_type,
                                      energy * np.ones_like(impact[type_mask]),
                                      impact[type_mask],
                                      x_max_bin * np.ones_like(impact[type_mask]))

        if self.use_time_gradient:
            time_mask = np.logical_and(np.invert(ma.getmask(self.image)),
                                       self.time > 0)
            weight = np.sqrt(self.image) * time_mask
            rv = norm()

            sx = pix_x_rot * weight
            sxx = pix_x_rot * pix_x_rot * weight

            sy = self.time * weight
            sxy = self.time * pix_x_rot * weight
            d = weight.sum(axis=1) * sxx.sum(axis=1) - sx.sum(axis=1) * sx.sum(axis=1)
            time_fit = (weight.sum(axis=1) * sxy.sum(axis=1) - sx.sum(axis=1) * sy.sum(
                axis=1)) / d
            time_fit /= -1 * (180 / math.pi)
            chi2 = -2 * np.log(rv.pdf((time_fit - time_gradients.T[0])/
                                        time_gradients.T[1]))

        # Likelihood function will break if we find a NaN or a 0
        prediction[np.isnan(prediction)] = 1e-8
        prediction[prediction < 1e-8] = 1e-8
        prediction *= self.template_scale

        # Get likelihood that the prediction matched the camera image
        like = poisson_likelihood_gaussian(self.image, prediction, self.spe, self.ped)
        like[np.isnan(like)] = 1e9
        like *= np.invert(ma.getmask(self.image))
        like = ma.MaskedArray(like, mask=ma.getmask(self.image))

        array_like = like
        if goodness_of_fit:
            return np.sum(like - mean_poisson_likelihood_gaussian(prediction, self.spe,
                                                                  self.ped))

        prior_pen = 0
        # Add prior penalities if we have them
        array_like += 1e-8
        if "energy" in self.priors:
            prior_pen += energy_prior(energy, index=-1)
        if "xmax" in self.priors:
            prior_pen += xmax_prior(energy, x_max)

        array_like += prior_pen / float(len(array_like))

        if self.array_return:
            array_like = array_like.ravel()
            return array_like[np.invert(ma.getmask(array_like))]

        final_sum = array_like.sum()
        if self.use_time_gradient:
            final_sum += chi2.sum() #* np.sum(ma.getmask(self.image))

        return final_sum
Example #4
0
    def get_likelihood(self, source_x, source_y, core_x, core_y, energy,
                       x_max_scale):
        """Get the likelihood that the image predicted at the given test
        position matches the camera image.

        Parameters
        ----------
        source_x: float
            Source position of shower in the nominal system (in deg)
        source_y: float
            Source position of shower in the nominal system (in deg)
        core_x: float
            Core position of shower in tilted telescope system (in m)
        core_y: float
            Core position of shower in tilted telescope system (in m)
        energy: float
            Shower energy (in TeV)
        x_max_scale: float
            Scaling factor applied to geometrically calculated Xmax

        Returns
        -------
        float: Likelihood the model represents the camera image at this position

        """

        # First we add units back onto everything.  Currently not
        # handled very well, maybe in future we could just put
        # everything in the correct units when loading in the class
        # and ignore them from then on

        zenith = 90 * u.deg - self.array_direction.alt
        azimuth = self.array_direction.az

        # Geometrically calculate the depth of maximum given this test position
        x_max = self.get_shower_max(source_x, source_y, core_x, core_y,
                                    zenith.to(u.rad).value) * x_max_scale
        # Calculate expected Xmax given this energy
        x_max_exp = guess_shower_depth(energy * u.TeV)

        # Convert to binning of Xmax, addition of 100 can probably be removed
        x_max_bin = x_max - x_max_exp

        # Check for range
        if x_max_bin > 250 * (u.g * u.cm**-2):
            x_max_bin = 250 * (u.g * u.cm**-2)
        if x_max_bin < -250 * (u.g * u.cm**-2):
            x_max_bin = -250 * (u.g * u.cm**-2)

        x_max_bin = x_max_bin.value

        array_like = None

        for tel_count in self.image:  # Loop over all telescopes
            # Calculate impact distance for all telescopes
            impact = np.sqrt(
                pow(self.tel_pos_x[tel_count] - core_x, 2) +
                pow(self.tel_pos_y[tel_count] - core_y, 2))
            # And the expected rotation angle
            phi = np.arctan2(
                (self.tel_pos_y[tel_count] - core_y),
                (self.tel_pos_x[tel_count] - core_x))  # - (math.pi/2.)

            # Rotate and translate all pixels such that they match the
            # template orientation
            pix_x_rot, pix_y_rot = self.rotate_translate(
                self.pixel_x[tel_count] * -1, self.pixel_y[tel_count],
                source_x, source_y, phi)

            # Then get the predicted image, convert pixel positions to deg
            prediction = self.image_prediction(self.type[tel_count], zenith,
                                               azimuth, energy, impact,
                                               x_max_bin,
                                               pix_x_rot * (180 / math.pi),
                                               pix_y_rot * (180 / math.pi))
            prediction[np.isnan(prediction)] = 0
            prediction[prediction < 1e-6] = 1e-6

            # Scale templates to match simulations
            prediction *= self.scale[self.type[tel_count]]
            # prediction *= self.pixel_area[tel_count]

            # Get likelihood that the prediction matched the camera image
            like = poisson_likelihood_gaussian(self.image[tel_count],
                                               prediction, self.spe,
                                               self.ped[tel_count])
            if np.any(prediction == np.inf):
                print("inf found at ", self.type[tel_count], zenith, azimuth,
                      energy, impact, x_max_bin)
            like[np.isnan(like)] = 1e9
            if array_like is None:
                array_like = like
            else:
                array_like = np.append(array_like, like)

        prior_pen = 0
        # Add prior penalities if we have them
        array_like += 1e-8
        if "energy" in self.priors:
            prior_pen += energy_prior(energy, index=-2)
        if "xmax" in self.priors:
            prior_pen += xmax_prior(energy, x_max)

        array_like += prior_pen / float(len(array_like))
        if self.array_return:
            return array_like
        return np.sum(array_like)
Example #5
0
    def get_likelihood(self, source_x, source_y, core_x, core_y, energy,
                       x_max_scale):
        """Get the likelihood that the image predicted at the given test
        position matches the camera image.

        Parameters
        ----------
        source_x: float
            Source position of shower in the nominal system (in deg)
        source_y: float
            Source position of shower in the nominal system (in deg)
        core_x: float
            Core position of shower in tilted telescope system (in m)
        core_y: float
            Core position of shower in tilted telescope system (in m)
        energy: float
            Shower energy (in TeV)
        x_max_scale: float
            Scaling factor applied to geometrically calculated Xmax

        Returns
        -------
        float: Likelihood the model represents the camera image at this position

        """

        # First we add units back onto everything.  Currently not
        # handled very well, maybe in future we could just put
        # everything in the correct units when loading in the class
        # and ignore them from then on

        zenith = 90 * u.deg - self.array_direction[0]
        azimuth = self.array_direction[1]
        # Geometrically calculate the depth of maximum given this test position

        if self.fit_xmax:
            x_max = self.get_shower_max(source_x, source_y, core_x, core_y,
                                        zenith.to(u.rad).value) * x_max_scale

            # Calculate expected Xmax given this energy
            x_max_exp = 300 + 93 * np.log10(energy)

            # Convert to binning of Xmax, addition of 100 can probably be
            # removed
            x_max_bin = x_max.value - x_max_exp

            # Check for range
            if x_max_bin > 100:
                x_max_bin = 100
            if x_max_bin < -100:
                x_max_bin = -100
        else:
            x_max_bin = x_max_scale * 37.8
            if x_max_bin < 13:
                x_max_bin = 13

        sum_like = 0
        for tel_count in self.image:  # Loop over all telescopes
            # Calculate impact distance for all telescopes
            impact = np.sqrt(
                pow(self.tel_pos_x[tel_count] - core_x, 2) +
                pow(self.tel_pos_y[tel_count] - core_y, 2))
            # And the expected rotation angle
            phi = np.arctan2(
                (self.tel_pos_y[tel_count] - core_y),
                (self.tel_pos_x[tel_count] - core_x))  # - (math.pi/2.)

            # Rotate and translate all pixels such that they match the
            # template orientation
            pix_x_rot, pix_y_rot = self.rotate_translate(
                self.pixel_x[tel_count] * -1, self.pixel_y[tel_count],
                source_x, source_y, phi)

            # Then get the predicted image, convert pixel positions to deg
            prediction = self.image_prediction(self.type[tel_count], zenith,
                                               azimuth, energy, impact,
                                               x_max_bin,
                                               pix_x_rot * (180 / math.pi),
                                               pix_y_rot * (180 / math.pi))
            prediction[np.isnan(prediction)] = 0
            prediction[prediction < 1e-6] = 1e-6

            # Scale templates to match simulations
            prediction *= self.scale[self.type[tel_count]]
            # Get likelihood that the prediction matched the camera image
            like = poisson_likelihood_gaussian(self.image[tel_count],
                                               prediction, self.spe,
                                               self.ped[tel_count])
            if np.any(prediction == np.inf):
                print("inf found at ", self.type[tel_count], zenith, azimuth,
                      energy, impact, x_max_bin)
            like[np.isnan(like)] = 1e9
            sum_like += np.sum(like)
            if np.sum(prediction) is 0:
                sum_like += 1e9

        return sum_like
Example #6
0
    def get_likelihood(self, source_x, source_y, core_x, core_y,
                       energy, x_max_scale):
        """Get the likelihood that the image predicted at the given test
        position matches the camera image.

        Parameters
        ----------
        source_x: float
            Source position of shower in the nominal system (in deg)
        source_y: float
            Source position of shower in the nominal system (in deg)
        core_x: float
            Core position of shower in tilted telescope system (in m)
        core_y: float
            Core position of shower in tilted telescope system (in m)
        energy: float
            Shower energy (in TeV)
        x_max_scale: float
            Scaling factor applied to geometrically calculated Xmax

        Returns
        -------
        float: Likelihood the model represents the camera image at this position

        """

        # First we add units back onto everything.  Currently not
        # handled very well, maybe in future we could just put
        # everything in the correct units when loading in the class
        # and ignore them from then on

        zenith = 90*u.deg - self.array_direction.alt
        azimuth = self.array_direction.az

        # Geometrically calculate the depth of maximum given this test position
        x_max = self.get_shower_max(source_x, source_y,
                                    core_x, core_y,
                                    zenith.to(u.rad).value) * x_max_scale
        # Calculate expected Xmax given this energy
        x_max_exp = guess_shower_depth(energy*u.TeV)

        # Convert to binning of Xmax, addition of 100 can probably be removed
        x_max_bin = x_max - x_max_exp

        # Check for range
        if x_max_bin > 250 * (u.g*u.cm**-2):
            x_max_bin = 250 * (u.g*u.cm**-2)
        if x_max_bin < -250 * (u.g*u.cm**-2):
            x_max_bin = -250 * (u.g*u.cm**-2)

        x_max_bin = x_max_bin.value

        array_like = None

        for tel_count in self.image:  # Loop over all telescopes
            # Calculate impact distance for all telescopes
            impact = np.sqrt(pow(self.tel_pos_x[tel_count] - core_x, 2)
                             + pow(self.tel_pos_y[tel_count] - core_y, 2))
            # And the expected rotation angle
            phi = np.arctan2((self.tel_pos_y[tel_count] - core_y),
                             (self.tel_pos_x[tel_count] - core_x))  # - (math.pi/2.)

            # Rotate and translate all pixels such that they match the
            # template orientation
            pix_x_rot, pix_y_rot = self.rotate_translate(
                self.pixel_x[tel_count] * -1,
                self.pixel_y[tel_count],
                source_x, source_y, phi
            )

            # Then get the predicted image, convert pixel positions to deg
            prediction = self.image_prediction(
                self.type[tel_count],
                zenith, azimuth, energy, impact, x_max_bin,
                pix_x_rot * (180 / math.pi),
                pix_y_rot * (180 / math.pi)
            )
            prediction[np.isnan(prediction)] = 0
            prediction[prediction < 1e-6] = 1e-6

            # Scale templates to match simulations
            prediction *= self.scale[self.type[tel_count]]
            #prediction *= self.pixel_area[tel_count]

            # Get likelihood that the prediction matched the camera image
            like = poisson_likelihood_gaussian(self.image[tel_count],
                                               prediction,
                                               self.spe,
                                               self.ped[tel_count])
            if np.any(prediction == np.inf):
                print("inf found at ", self.type[tel_count], zenith,
                      azimuth, energy, impact, x_max_bin)
            like[np.isnan(like)] = 1e9
            if array_like is None:
                array_like = like
            else:
                array_like = np.append(array_like,like)

        prior_pen = 0
        # Add prior penalities if we have them
        array_like += 1e-8
        if "energy" in self.priors:
            prior_pen += energy_prior(energy, index=-2)
        if "xmax" in self.priors:
            prior_pen += xmax_prior(energy, x_max)

        array_like += prior_pen/float(len(array_like))
        if self.array_return:
            return array_like
        return np.sum(array_like)
Example #7
0
    def get_likelihood(self, source_x, source_y, core_x, core_y,
                       energy, x_max_scale, goodness_of_fit=False):
        """Get the likelihood that the image predicted at the given test
        position matches the camera image.

        Parameters
        ----------
        source_x: float
            Source position of shower in the nominal system (in deg)
        source_y: float
            Source position of shower in the nominal system (in deg)
        core_x: float
            Core position of shower in tilted telescope system (in m)
        core_y: float
            Core position of shower in tilted telescope system (in m)
        energy: float
            Shower energy (in TeV)
        x_max_scale: float
            Scaling factor applied to geometrically calculated Xmax
        goodness_of_fit: boolean
            Determines whether expected likelihood should be subtracted from result
        Returns
        -------
        float: Likelihood the model represents the camera image at this position

        """
        # First we add units back onto everything.  Currently not
        # handled very well, maybe in future we could just put
        # everything in the correct units when loading in the class
        # and ignore them from then on

        zenith = (np.pi / 2) - self.array_direction.alt.to(u.rad).value
        azimuth = self.array_direction.az

        # Geometrically calculate the depth of maximum given this test position
        x_max = self.get_shower_max(source_x, source_y,
                                    core_x, core_y,
                                    zenith)
        x_max *= x_max_scale

        # Calculate expected Xmax given this energy
        x_max_exp = guess_shower_depth(energy)  # / np.cos(20*u.deg)

        # Convert to binning of Xmax
        x_max_bin = x_max - x_max_exp

        # Check for range
        if x_max_bin > 200:
            x_max_bin = 200
        if x_max_bin < -100:
            x_max_bin = -100

        # Calculate impact distance for all telescopes
        impact = np.sqrt(np.power(self.tel_pos_x - core_x, 2)
                         + np.power(self.tel_pos_y - core_y, 2))
        # And the expected rotation angle
        phi = np.arctan2((self.tel_pos_x - core_x),
                         (self.tel_pos_y - core_y)) * u.rad

        # Rotate and translate all pixels such that they match the
        # template orientation
        pix_y_rot, pix_x_rot = self.rotate_translate(
            self.pixel_x,
            self.pixel_y,
            source_x, source_y, phi
        )

        # In the interpolator class we can gain speed advantages by using masked arrays
        # so we need to make sure here everything is masked
        prediction = ma.zeros(self.image.shape)
        prediction.mask = ma.getmask(self.image)

        time_gradients = np.zeros((self.image.shape[0],2))

        # Loop over all telescope types and get prediction
        for tel_type in np.unique(self.tel_types).tolist():
            type_mask = self.tel_types == tel_type
            prediction[type_mask] = \
                self.image_prediction(tel_type, energy *
                                      np.ones_like(impact[type_mask]),
                                      impact[type_mask], x_max_bin *
                                      np.ones_like(impact[type_mask]),
                                      pix_x_rot[type_mask] * (180 / math.pi) * -1,
                                      pix_y_rot[type_mask] * (180 / math.pi))

            if self.use_time_gradient:
                time_gradients[type_mask] = \
                    self.predict_time(tel_type,
                                      energy * np.ones_like(impact[type_mask]),
                                      impact[type_mask],
                                      x_max_bin * np.ones_like(impact[type_mask]))

        if self.use_time_gradient:
            time_mask = np.logical_and(np.invert(ma.getmask(self.image)),
                                       self.time > 0)
            weight = np.sqrt(self.image) * time_mask
            rv = norm()

            sx = pix_x_rot * weight
            sxx = pix_x_rot * pix_x_rot * weight

            sy = self.time * weight
            sxy = self.time * pix_x_rot * weight
            d = weight.sum(axis=1) * sxx.sum(axis=1) - sx.sum(axis=1) * sx.sum(axis=1)
            time_fit = (weight.sum(axis=1) * sxy.sum(axis=1) - sx.sum(axis=1) * sy.sum(
                axis=1)) / d
            time_fit /= -1 * (180 / math.pi)
            chi2 = -2 * np.log(rv.pdf((time_fit - time_gradients.T[0])/
                                        time_gradients.T[1]))

        # Likelihood function will break if we find a NaN or a 0
        prediction[np.isnan(prediction)] = 1e-8
        prediction[prediction < 1e-8] = 1e-8
        prediction *= self.template_scale

        # Get likelihood that the prediction matched the camera image
        like = poisson_likelihood_gaussian(self.image, prediction, self.spe, self.ped)
        like[np.isnan(like)] = 1e9
        like *= np.invert(ma.getmask(self.image))
        like = ma.MaskedArray(like, mask=ma.getmask(self.image))

        array_like = like
        if goodness_of_fit:
            return np.sum(like - mean_poisson_likelihood_gaussian(prediction, self.spe,
                                                                  self.ped))

        prior_pen = 0
        # Add prior penalities if we have them
        array_like += 1e-8
        if "energy" in self.priors:
            prior_pen += energy_prior(energy, index=-1)
        if "xmax" in self.priors:
            prior_pen += xmax_prior(energy, x_max)

        array_like += prior_pen / float(len(array_like))

        if self.array_return:
            array_like = array_like.ravel()
            return array_like[np.invert(ma.getmask(array_like))]

        final_sum = array_like.sum()
        if self.use_time_gradient:
            final_sum += chi2.sum() #* np.sum(ma.getmask(self.image))

        return final_sum
Example #8
0
    def get_likelihood(self, source_x, source_y, core_x, core_y,
                       energy, x_max_scale):
        """Get the likelihood that the image predicted at the given test
        position matches the camera image.

        Parameters
        ----------
        source_x: float
            Source position of shower in the nominal system (in deg)
        source_y: float
            Source position of shower in the nominal system (in deg)
        core_x: float
            Core position of shower in tilted telescope system (in m)
        core_y: float
            Core position of shower in tilted telescope system (in m)
        energy: float
            Shower energy (in TeV)
        x_max_scale: float
            Scaling factor applied to geometrically calculated Xmax

        Returns
        -------
        float: Likelihood the model represents the camera image at this position

        """

        # First we add units back onto everything.  Currently not
        # handled very well, maybe in future we could just put
        # everything in the correct units when loading in the class
        # and ignore them from then on

        zenith = 90 * u.deg - self.array_direction[0]
        azimuth = self.array_direction[1]
        # Geometrically calculate the depth of maximum given this test position

        if self.fit_xmax:
            x_max = self.get_shower_max(source_x, source_y,
                                        core_x, core_y,
                                        zenith.to(u.rad).value) * x_max_scale

            # Calculate expected Xmax given this energy
            x_max_exp = 300 + 93 * np.log10(energy)

            # Convert to binning of Xmax, addition of 100 can probably be
            # removed
            x_max_bin = x_max.value - x_max_exp

            # Check for range
            if x_max_bin > 100:
                x_max_bin = 100
            if x_max_bin < -100:
                x_max_bin = -100
        else:
            x_max_bin = x_max_scale * 37.8
            if x_max_bin < 13:
                x_max_bin = 13

        sum_like = 0
        for tel_count in self.image:  # Loop over all telescopes
            # Calculate impact distance for all telescopes
            impact = np.sqrt(pow(self.tel_pos_x[tel_count] - core_x, 2)
                             + pow(self.tel_pos_y[tel_count] - core_y, 2))
            # And the expected rotation angle
            phi = np.arctan2((self.tel_pos_y[tel_count] - core_y),
                             (self.tel_pos_x[tel_count] - core_x))  # - (math.pi/2.)

            # Rotate and translate all pixels such that they match the
            # template orientation
            pix_x_rot, pix_y_rot = self.rotate_translate(
                self.pixel_x[tel_count] * -1,
                self.pixel_y[tel_count],
                source_x, source_y, phi
            )

            # Then get the predicted image, convert pixel positions to deg
            prediction = self.image_prediction(
                self.type[tel_count],
                zenith, azimuth, energy, impact, x_max_bin,
                pix_x_rot * (180 / math.pi),
                pix_y_rot * (180 / math.pi)
            )
            prediction[np.isnan(prediction)] = 0
            prediction[prediction < 1e-6] = 1e-6

            # Scale templates to match simulations
            prediction *= self.scale[self.type[tel_count]]
            # Get likelihood that the prediction matched the camera image
            like = poisson_likelihood_gaussian(self.image[tel_count],
                                               prediction,
                                               self.spe,
                                               self.ped[tel_count])
            if np.any(prediction == np.inf):
                print("inf found at ", self.type[tel_count], zenith,
                      azimuth, energy, impact, x_max_bin)
            like[np.isnan(like)] = 1e9
            sum_like += np.sum(like)
            if np.sum(prediction) is 0:
                sum_like += 1e9

        return sum_like