aboutsummaryrefslogtreecommitdiff
path: root/utils/plot-muons
diff options
context:
space:
mode:
Diffstat (limited to 'utils/plot-muons')
-rwxr-xr-xutils/plot-muons268
1 files changed, 268 insertions, 0 deletions
diff --git a/utils/plot-muons b/utils/plot-muons
new file mode 100755
index 0000000..2a6e0a2
--- /dev/null
+++ b/utils/plot-muons
@@ -0,0 +1,268 @@
+#!/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/>.
+"""
+Script to plot the fit results for stopping muons and Michels. To run it just
+run:
+
+ $ ./plot-muon [list of fit results]
+
+Currently it will plot energy distributions for external muons, stopping muons,
+and michel electrons.
+"""
+from __future__ import print_function, division
+import numpy as np
+from scipy.stats import iqr, poisson
+from matplotlib.lines import Line2D
+from scipy.stats import iqr, norm, beta
+from scipy.special import spence
+from itertools import izip_longest
+
+particle_id = {20: 'e', 22: r'\mu'}
+
+def plot_hist2(df, muons=False, norm=1.0, label=None, color=None):
+ for id, df_id in sorted(df.groupby('id')):
+ if id == 20:
+ plt.subplot(2,3,1)
+ elif id == 22:
+ plt.subplot(2,3,2)
+ elif id == 2020:
+ plt.subplot(2,3,4)
+ elif id == 2022:
+ plt.subplot(2,3,5)
+ elif id == 2222:
+ plt.subplot(2,3,6)
+
+ if muons:
+ plt.hist(np.log10(df_id.ke.values/1000), bins=np.linspace(0,4.5,100), histtype='step', weights=np.tile(norm,len(df_id.ke.values)), label=label, color=color)
+ plt.xlabel("log10(Energy (GeV))")
+ else:
+ bins = np.logspace(np.log10(20),np.log10(10e3),21)
+ plt.hist(df_id.ke.values, bins=bins, histtype='step', weights=np.tile(norm,len(df_id.ke.values)), label=label, color=color)
+ plt.gca().set_xscale("log")
+ plt.xlabel("Energy (MeV)")
+ plt.title('$' + ''.join([particle_id[int(''.join(x))] for x in grouper(str(id),2)]) + '$')
+
+ if len(df):
+ plt.tight_layout()
+
+if __name__ == '__main__':
+ import argparse
+ import numpy as np
+ import pandas as pd
+ import sys
+ import h5py
+ from sddm.plot_energy import *
+ from sddm.plot import *
+ from sddm import setup_matplotlib
+
+ parser = argparse.ArgumentParser("plot fit results")
+ parser.add_argument("filenames", nargs='+', help="input files")
+ parser.add_argument("--save", action='store_true', default=False, help="save corner plots for backgrounds")
+ parser.add_argument("--mc", nargs='+', required=True, help="atmospheric MC files")
+ parser.add_argument("--nhit-thresh", type=int, default=None, help="nhit threshold to apply to events before processing (should only be used for testing to speed things up)")
+ args = parser.parse_args()
+
+ setup_matplotlib(args.save)
+
+ import matplotlib.pyplot as plt
+
+ # Loop over runs to prevent using too much memory
+ evs = []
+ rhdr = pd.concat([read_hdf(filename, "rhdr").assign(filename=filename) for filename in args.filenames],ignore_index=True)
+ for run, df in rhdr.groupby('run'):
+ evs.append(get_events(df.filename.values, merge_fits=True, nhit_thresh=args.nhit_thresh))
+ ev = pd.concat(evs)
+ ev_mc = get_events(args.mc, merge_fits=True)
+
+ # Drop events without fits
+ ev = ev[~np.isnan(ev.fmin)]
+ ev_mc = ev_mc[~np.isnan(ev_mc.fmin)]
+
+ ev = ev.reset_index()
+ ev_mc = ev_mc.reset_index()
+
+ # Set all prompt events in the MC to be muons
+ #ev_mc.loc[ev_mc.prompt,'muon'] = True
+
+ # FIXME: TESTING
+ ev_mc.loc[ev_mc.prompt & (ev_mc.id == 22) & (ev_mc.r_psup > 0.9),'muon'] = True
+
+ # First, do basic data cleaning which is done for all events.
+ ev = ev[ev.signal | ev.muon]
+ ev_mc = ev_mc[ev_mc.signal | ev_mc.muon]
+
+ # 00-orphan cut
+ ev = ev[(ev.gtid & 0xff) != 0]
+ ev_mc = ev_mc[(ev_mc.gtid & 0xff) != 0]
+
+ # Now, we select events tagged by the muon tag which should tag only
+ # external muons. We keep the sample of muons since it's needed later to
+ # identify Michel electrons and to apply the muon follower cut
+ muons = ev[ev.muon]
+ muons_mc = ev_mc[ev_mc.muon]
+
+ # Try to identify Michel electrons. Currently, the event selection is based
+ # on Richie's thesis. Here, we do the following:
+ #
+ # 1. Apply more data cleaning cuts to potential Michel electrons
+ # 2. Nhit >= 100
+ # 3. It must be > 800 ns and less than 20 microseconds from a prompt event
+ # or a muon
+ michel = ev[ev.michel]
+ michel_mc = ev_mc[ev_mc.michel]
+
+ # remove events 200 microseconds after a muon
+ ev = ev.groupby('run',group_keys=False).apply(muon_follower_cut)
+
+ muons = muons[muons.psi < 6]
+ muons_mc = muons_mc[muons_mc.psi < 6]
+
+ handles = [Line2D([0], [0], color='C0'),
+ Line2D([0], [0], color='C1')]
+ labels = ('Data','Monte Carlo')
+
+ fig = plt.figure()
+ plot_hist2(michel,color='C0')
+ plot_hist2(michel_mc,norm=len(michel)/len(michel_mc),color='C1')
+ despine(fig,trim=True)
+ fig.legend(handles,labels,loc='upper right')
+ if args.save:
+ plt.savefig("michel_electrons.pdf")
+ plt.savefig("michel_electrons.eps")
+ else:
+ plt.suptitle("Michel Electrons")
+
+ fig = plt.figure()
+ plot_hist2(muons,muons=True,color='C0')
+ plot_hist2(muons_mc,norm=len(muons)/len(muons_mc),muons=True,color='C1')
+ despine(fig,trim=True)
+ if len(muons):
+ plt.tight_layout()
+ fig.legend(handles,labels,loc='upper right')
+ if args.save:
+ plt.savefig("external_muons.pdf")
+ plt.savefig("external_muons.eps")
+ else:
+ plt.suptitle("External Muons")
+
+ # Plot the energy and angular distribution for external muons
+ fig = plt.figure()
+ plt.subplot(2,1,1)
+ plt.hist(muons.ke.values, bins=np.logspace(3,7,100), histtype='step', color='C0', label="Data")
+ plt.hist(muons_mc.ke.values, bins=np.logspace(3,7,100), histtype='step', color='C1', label="Monte Carlo")
+ plt.legend()
+ plt.xlabel("Energy (MeV)")
+ plt.gca().set_xscale("log")
+ plt.subplot(2,1,2)
+ plt.hist(np.cos(muons.theta.values), bins=np.linspace(-1,1,100), histtype='step', color='C0', label="Data")
+ plt.hist(np.cos(muons_mc.theta.values), bins=np.linspace(-1,1,100), histtype='step', color='C1', label="Monte Carlo")
+ plt.legend()
+ despine(fig,trim=True)
+ plt.xlabel(r"$\cos(\theta)$")
+ plt.tight_layout()
+ if args.save:
+ plt.savefig("muon_energy_cos_theta.pdf")
+ plt.savefig("muon_energy_cos_theta.eps")
+ else:
+ plt.suptitle("Muons")
+
+ stopping_muons = pd.merge(ev[ev.muon & ev.stopping_muon],michel,left_on=['run','gtid'],right_on=['run','muon_gtid'],suffixes=('','_michel'))
+ stopping_muons_mc = pd.merge(ev_mc[ev_mc.muon & ev_mc.stopping_muon],michel_mc,left_on=['run','gtid'],right_on=['run','muon_gtid'],suffixes=('','_michel'))
+
+ if len(stopping_muons):
+ # project muon to PSUP
+ stopping_muons['dx'] = stopping_muons.apply(get_dx,axis=1)
+ stopping_muons_mc['dx'] = stopping_muons_mc.apply(get_dx,axis=1)
+ # energy based on distance travelled
+ stopping_muons['T_dx'] = dx_to_energy(stopping_muons.dx)
+ stopping_muons_mc['T_dx'] = dx_to_energy(stopping_muons_mc.dx)
+ stopping_muons['dT'] = stopping_muons['energy1'] - stopping_muons['T_dx']
+ stopping_muons_mc['dT'] = stopping_muons_mc['energy1'] - stopping_muons_mc['T_dx']
+
+ print(stopping_muons[['run','gtid','energy1','T_dx','dT','gtid_michel','r_michel','ftp_r_michel','id','r']])
+
+ fig = plt.figure()
+ plt.hist((stopping_muons['energy1']-stopping_muons['T_dx'])*100/stopping_muons['T_dx'], bins=np.linspace(-100,100,200), histtype='step', color='C0', label="Data")
+ plt.hist((stopping_muons_mc['energy1']-stopping_muons_mc['T_dx'])*100/stopping_muons_mc['T_dx'], bins=np.linspace(-100,100,200), histtype='step', color='C1', label="Monte Carlo")
+ plt.legend()
+ despine(fig,trim=True)
+ plt.xlabel("Fractional energy difference (\%)")
+ plt.title("Fractional energy difference for Stopping Muons")
+ plt.tight_layout()
+ if args.save:
+ plt.savefig("stopping_muon_fractional_energy_difference.pdf")
+ plt.savefig("stopping_muon_fractional_energy_difference.eps")
+ else:
+ plt.title("Stopping Muon Fractional Energy Difference")
+
+ # 100 bins between 50 MeV and 10 GeV
+ bins = np.linspace(50,2000,10)
+
+ pd_bins = pd.cut(stopping_muons['T_dx'],bins)
+ pd_bins_mc = pd.cut(stopping_muons_mc['T_dx'],bins)
+
+ T = (bins[1:] + bins[:-1])/2
+
+ dT = stopping_muons.groupby(pd_bins)['dT'].agg(['mean','sem','std',std_err,median,median_err,iqr_std,iqr_std_err])
+ dT_mc = stopping_muons_mc.groupby(pd_bins_mc)['dT'].agg(['mean','sem','std',std_err,median,median_err,iqr_std,iqr_std_err])
+
+ fig = plt.figure()
+ plt.errorbar(T, dT['median']*100/T, yerr=dT['median_err']*100/T, color='C0', label="Data")
+ plt.errorbar(T, dT_mc['median']*100/T, yerr=dT_mc['median_err']*100/T, color='C1', label="Monte Carlo")
+ plt.gca().set_ylim(-500,500)
+ plt.legend()
+ despine(fig,trim=True)
+ plt.xlabel("Kinetic Energy (MeV)")
+ plt.ylabel(r"Energy bias (\%)")
+ plt.tight_layout()
+ if args.save:
+ plt.savefig("stopping_muon_energy_bias.pdf")
+ plt.savefig("stopping_muon_energy_bias.eps")
+ else:
+ plt.title("Stopping Muon Energy Bias")
+
+ fig = plt.figure()
+ plt.errorbar(T, dT['iqr_std']*100/T, yerr=dT['iqr_std_err']*100/T, color='C0', label="Data")
+ plt.errorbar(T, dT_mc['iqr_std']*100/T, yerr=dT_mc['iqr_std_err']*100/T, color='C1', label="Monte Carlo")
+ plt.gca().set_ylim(-500,500)
+ despine(fig,trim=True)
+ plt.xlabel("Kinetic Energy (MeV)")
+ plt.ylabel(r"Energy resolution (\%)")
+ plt.tight_layout()
+ if args.save:
+ plt.savefig("stopping_muon_energy_resolution.pdf")
+ plt.savefig("stopping_muon_energy_resolution.eps")
+ else:
+ plt.title("Stopping Muon Energy Resolution")
+
+ # For the Michel energy plot, we only look at the single particle electron
+ # fit
+ michel = michel[michel.id == 20]
+
+ fig = plt.figure()
+ bins = np.linspace(0,100,41)
+ plt.hist(michel.ke.values, bins=bins, histtype='step', color='C0', label="Data")
+ plt.hist(michel_mc.ke.values, weights=np.tile(len(michel)/len(michel_mc),len(michel_mc.ke.values)), bins=bins, histtype='step', color='C1', label="Monte Carlo")
+ despine(fig,trim=True)
+ plt.xlabel("Energy (MeV)")
+ plt.tight_layout()
+ plt.legend()
+ if args.save:
+ plt.savefig("michel_electrons_ke.pdf")
+ plt.savefig("michel_electrons_ke.eps")
+ else:
+ plt.title("Michel Electrons")
+ plt.show()