aboutsummaryrefslogtreecommitdiff
path: root/utils
ModeNameSize
-rw-r--r--.gitignore19logplain
-rw-r--r--Makefile656logplain
-rwxr-xr-xanalyze-genie-mc14585logplain
-rwxr-xr-xapply-atmospheric-oscillations14514logplain
-rwxr-xr-xcalculate-atmospheric-oscillations4350logplain
-rwxr-xr-xcalculate_limits.py22733logplain
-rwxr-xr-xcat-grid-jobs6720logplain
-rwxr-xr-xconvert-genie-to-gst1509logplain
-rwxr-xr-xconvert-ratdb1855logplain
-rwxr-xr-xdc27579logplain
-rwxr-xr-xdc-closure-test24443logplain
-rwxr-xr-xdelete-zdabs1687logplain
-rwxr-xr-xfit-wrapper115logplain
-rwxr-xr-xgen-dark-matter13280logplain
-rw-r--r--index.py3298logplain
-rwxr-xr-xmv-grid-files2949logplain
-rwxr-xr-xplot7101logplain
-rwxr-xr-xplot-atmospheric-fluxes3880logplain
-rwxr-xr-xplot-atmospheric-oscillations2418logplain
-rwxr-xr-xplot-energy17521logplain
-rwxr-xr-xplot-fit-results10037logplain
-rwxr-xr-xplot-likelihood1645logplain
-rwxr-xr-xplot-root-results7949logplain
d---------sddm224logplain
-rwxr-xr-xsetup.py277logplain
-rwxr-xr-xsubmit-grid-jobs25580logplain
-rwxr-xr-xzdab-reprocess10942logplain
*/ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#!/usr/bin/env python
# Copyright (c) 2019, Anthony Latorre <tlatorre at uchicago>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <https://www.gnu.org/licenses/>.

from __future__ import print_function, division
import yaml
try:
    from yaml import CLoader as Loader
except ImportError:
    from yaml.loader import SafeLoader as Loader
import numpy as np
from scipy.stats import iqr
from matplotlib.lines import Line2D

# on retina screens, the default plots are way too small
# by using Qt5 and setting QT_AUTO_SCREEN_SCALE_FACTOR=1
# Qt5 will scale everything using the dpi in ~/.Xresources
import matplotlib
matplotlib.use("Qt5Agg")

matplotlib.rc('font', size=22)

IDP_E_MINUS  =    20
IDP_MU_MINUS =    22

SNOMAN_MASS = {
    20: 0.511,
    21: 0.511,
    22: 105.658,
    23: 105.658
}

def plot_hist(x, label=None):
    # determine the bin width using the Freedman Diaconis rule
    # see https://en.wikipedia.org/wiki/Freedman%E2%80%93Diaconis_rule
    h = 2*iqr(x)/len(x)**(1/3)
    n = max(int((np.max(x)-np.min(x))/h),10)
    bins = np.linspace(np.min(x),np.max(x),n)
    plt.hist(x, bins=bins, histtype='step', label=label)

def plot_legend(n):
    plt.figure(n)
    ax = plt.gca()
    handles, labels = ax.get_legend_handles_labels()
    new_handles = [Line2D([],[],c=h.get_edgecolor()) for h in handles]
    plt.legend(handles=new_handles,labels=labels)

def get_stats(x):
    """
    Returns a tuple (mean, error mean, std, error std) for the values in x.

    The formula for the standard error on the standard deviation comes from
    https://stats.stackexchange.com/questions/156518.
    """
    mean = np.mean(x)
    std = np.std(x)
    n = len(x)
    u4 = np.mean((x-mean)**4)
    error = np.sqrt((u4-(n-3)*std**4/(n-1))/n)/(2*std)
    return mean, std/np.sqrt(n), std, error

if __name__ == '__main__':
    import argparse
    import matplotlib.pyplot as plt
    import numpy as np

    parser = argparse.ArgumentParser("plot fit results")
    parser.add_argument("filenames", nargs='+', help="input files")
    args = parser.parse_args()

    events = []

    for filename in args.filenames:
        print(filename)
        with open(filename) as f:
            data = yaml.load(f.read(),Loader=Loader)

        a = np.ma.empty(len(data['data']),
                        dtype=[('id',np.int),       # particle id
                               ('T', np.double),    # true energy
                               ('dx',np.double),    # dx
                               ('dy',np.double),    # dy
                               ('dz',np.double),    # dz
                               ('dT',np.double),    # dT
                               ('theta',np.double), # theta
                               ('ratio',np.double), # likelihood ratio
                               ('te',np.double),    # time electron
                               ('tm',np.double),    # time muon
                               ('Te',np.double)]    # electron energy
                    )

        for i, event in enumerate(data['data']):
            # get the particle ID
            id = event['mcgn'][0]['id']

            a[i]['id'] = id

            if 'fit' not in event['ev'][0]:
                # if nhit < 100 we don't fit the event
                continue

            if id not in event['ev'][0]['fit']:
                a[i] = np.ma.masked
                continue

            mass = SNOMAN_MASS[id]
            # for some reason it's the *second* track which seems to contain the
            # initial energy
            true_energy = event['mcgn'][0]['energy']
            # The MCTK bank has the particle's total energy (except for neutrons)
            # so we need to convert it into kinetic energy
            ke = true_energy - mass

            fit = event['ev'][0]['fit']

            a[i]['T'] = ke
            energy = fit[id]['energy']
            a[i]['dT'] = energy-ke

            # store the fit position residuals
            true_posx = event['mcgn'][0]['posx']
            posx = fit[id]['posx']
            a[i]['dx'] = posx-true_posx
            true_posy = event['mcgn'][0]['posy']
            posy = fit[id]['posy']
            a[i]['dy'] = posy-true_posy
            true_posz = event['mcgn'][0]['posz']
            posz = fit[id]['posz']
            a[i]['dz'] = posz-true_posz

            # compute the angle between the fit direction and the true
            # direction
            dirx = event['mcgn'][0]['dirx']
            diry = event['mcgn'][0]['diry']
            dirz = event['mcgn'][0]['dirz']
            true_dir = [dirx,diry,dirz]
            true_dir = np.array(true_dir)/np.linalg.norm(true_dir)
            theta = fit[id]['theta']
            phi = fit[id]['phi']
            dir = [np.sin(theta)*np.cos(phi),np.sin(theta)*np.sin(phi),np.cos(theta)]
            dir = np.array(dir)/np.linalg.norm(dir)
            a[i]['theta'] = np.degrees(np.arccos(np.dot(true_dir,dir)))

            # compute the log likelihood ratio
            if IDP_E_MINUS in fit and IDP_MU_MINUS in fit:
                fmin_electron = fit[IDP_E_MINUS]['fmin']
                fmin_muon = fit[IDP_MU_MINUS]['fmin']
                a[i]['ratio'] = fmin_muon-fmin_electron
            else:
                a[i]['ratio'] = np.ma.masked

            # store the time taken for electron and muon fits
            if IDP_E_MINUS in fit:
                a[i]['te'] = fit[IDP_E_MINUS]['time']
                a[i]['Te'] = fit[IDP_E_MINUS]['energy']
            else:
                a[i]['te'] = np.ma.masked
                a[i]['Te'] = np.ma.masked
            if IDP_MU_MINUS in fit:
                a[i]['tm'] = fit[IDP_MU_MINUS]['time']
            else:
                a[i]['tm'] = np.ma.masked

        events.append(a)

    a = np.ma.concatenate(events)

    bins = np.arange(50,1000,100)

    stats_array = np.ma.empty(len(bins)-1,
                     dtype=[('T',             np.double),
                            ('dT',            np.double),
                            ('dT_err',        np.double),
                            ('dT_std',        np.double),
                            ('dT_std_err',    np.double),
                            ('dx',            np.double),
                            ('dx_err',        np.double),
                            ('dx_std',        np.double),
                            ('dx_std_err',    np.double),
                            ('dy',            np.double),
                            ('dy_err',        np.double),
                            ('dy_std',        np.double),
                            ('dy_std_err',    np.double),
                            ('dz',            np.double),
                            ('dz_err',        np.double),
                            ('dz_std',        np.double),
                            ('dz_std_err',    np.double),
                            ('theta',         np.double),
                            ('theta_err',     np.double),
                            ('theta_std',     np.double),
                            ('theta_std_err', np.double)])

    stats = {IDP_E_MINUS: stats_array, IDP_MU_MINUS: stats_array.copy()}

    for id in stats:
        electron_events = a[a['id'] == id]

        for i, (ablah, b) in enumerate(zip(bins[:-1], bins[1:])):
            events = electron_events[(electron_events['T'] >= ablah) & (electron_events['T'] < b)]

            if len(events) < 2:
                stats[id][i] = np.ma.masked
                continue

            stats[id][i]['T'] = (ablah+b)/2
            mean, mean_error, std, std_error = get_stats(events['dT'].compressed())
            stats[id][i]['dT'] = mean
            stats[id][i]['dT_err'] = mean_error
            stats[id][i]['dT_std'] = std
            stats[id][i]['dT_std_err'] = std_error
            mean, mean_error, std, std_error = get_stats(events['dx'].compressed())
            stats[id][i]['dx'] = mean
            stats[id][i]['dx_err'] = mean_error
            stats[id][i]['dx_std'] = std
            stats[id][i]['dx_std_err'] = std_error
            mean, mean_error, std, std_error = get_stats(events['dy'].compressed())
            stats[id][i]['dy'] = mean
            stats[id][i]['dy_err'] = mean_error
            stats[id][i]['dy_std'] = std
            stats[id][i]['dy_std_err'] = std_error
            mean, mean_error, std, std_error = get_stats(events['dz'].compressed())
            stats[id][i]['dz'] = mean
            stats[id][i]['dz_err'] = mean_error
            stats[id][i]['dz_std'] = std
            stats[id][i]['dz_std_err'] = std_error
            mean, mean_error, std, std_error = get_stats(events['theta'].compressed())
            stats[id][i]['theta'] = mean
            stats[id][i]['theta_err'] = mean_error
            stats[id][i]['theta_std'] = std
            stats[id][i]['theta_std_err'] = std_error

    for id in stats:
        label = 'Muon' if id == IDP_MU_MINUS else 'Electron'

        T = stats[id]['T']
        dT = stats[id]['dT']
        dT_err = stats[id]['dT_err']
        std_dT = stats[id]['dT_std']
        std_dT_err = stats[id]['dT_std_err']
        dx = stats[id]['dx']
        dx_err = stats[id]['dx_err']
        std_dx = stats[id]['dx_std']
        std_dx_err = stats[id]['dx_std_err']
        dy = stats[id]['dy']
        dy_err = stats[id]['dy_err']
        std_dy = stats[id]['dy_std']
        std_dy_err = stats[id]['dy_std_err']
        dz = stats[id]['dz']
        dz_err = stats[id]['dz_err']
        std_dz = stats[id]['dz_std']
        std_dz_err = stats[id]['dz_std_err']
        theta = stats[id]['theta']
        theta_err = stats[id]['theta_err']
        std_theta = stats[id]['theta_std']
        std_theta_err = stats[id]['theta_std_err']

        plt.figure(1)
        plt.errorbar(T,dT*100/T,yerr=dT_err*100/T,fmt='o',label=label)
        plt.xlabel("Kinetic Energy (MeV)")
        plt.ylabel("Energy bias (%)")
        plt.title("Energy Bias")
        plt.legend()

        plt.figure(2)
        plt.errorbar(T,std_dT*100/T,yerr=std_dT_err*100/T,fmt='o',label=label)
        plt.xlabel("Kinetic Energy (MeV)")
        plt.ylabel("Energy resolution (%)")
        plt.title("Energy Resolution")
        plt.legend()

        plt.figure(3)
        plt.errorbar(T,dx,yerr=dx_err,fmt='o',label='%s (x)' % label)
        plt.errorbar(T,dy,yerr=dy_err,fmt='o',label='%s (y)' % label)
        plt.errorbar(T,dz,yerr=dz_err,fmt='o',label='%s (z)' % label)
        plt.xlabel("Kinetic Energy (MeV)")
        plt.ylabel("Position bias (cm)")
        plt.title("Position Bias")
        plt.legend()

        plt.figure(4)
        plt.errorbar(T,std_dx,yerr=std_dx_err,fmt='o',label='%s (x)' % label)
        plt.errorbar(T,std_dy,yerr=std_dy_err,fmt='o',label='%s (y)' % label)
        plt.errorbar(T,std_dz,yerr=std_dz_err,fmt='o',label='%s (z)' % label)
        plt.xlabel("Kinetic Energy (MeV)")
        plt.ylabel("Position resolution (cm)")
        plt.title("Position Resolution")
        plt.ylim((0,plt.gca().get_ylim()[1]))
        plt.legend()

        plt.figure(5)
        plt.errorbar(T,std_theta,yerr=std_theta_err,fmt='o',label=label)
        plt.xlabel("Kinetic Energy (MeV)")
        plt.ylabel("Angular resolution (deg)")
        plt.title("Angular Resolution")
        plt.ylim((0,plt.gca().get_ylim()[1]))
        plt.legend()

        plt.figure(6)
        plt.scatter(a[a['id'] == id]['Te'],a[a['id'] == id]['ratio'],label=label)
        plt.xlabel("Reconstructed Electron Energy (MeV)")
        plt.ylabel(r"Log Likelihood Ratio (e/$\mu$)")
        plt.title("Log Likelihood Ratio vs Reconstructed Electron Energy")
        plt.legend()
    plt.show()