diff --git a/benchmarks/dis/analysis/truth_reconstruction.py b/benchmarks/dis/analysis/truth_reconstruction.py
new file mode 100644
index 0000000000000000000000000000000000000000..3089488c5a10fa24d385f8663488497eb7b83c5c
--- /dev/null
+++ b/benchmarks/dis/analysis/truth_reconstruction.py
@@ -0,0 +1,315 @@
+import os
+import numpy as np
+import uproot as ur
+import awkward as ak
+import matplotlib.pyplot as plt
+import matplotlib as mpl
+import mplhep 
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--rec_file', type=str, help='Reconstructed track file.')
+parser.add_argument('--config', type=str, help='Momentum configuration.')
+parser.add_argument('--nevents', type=float, help='Number of events to process.')
+parser.add_argument('--results_path', type=str, help='Output directory.')
+args = parser.parse_args()
+kwargs = vars(args)
+
+rec_file = args.rec_file
+config = args.config
+Nevents = int(args.nevents)
+r_path = args.results_path + '/truth_reconstruction/'
+
+for array in ur.iterate(rec_file + ':events',['MCParticles/MCParticles.generatorStatus',
+                                          'MCParticles/MCParticles.PDG',
+                                          'MCParticles/MCParticles.momentum.x',
+                                          'MCParticles/MCParticles.momentum.y',
+                                          'MCParticles/MCParticles.momentum.z',
+                                          'ReconstructedParticles/ReconstructedParticles.PDG',
+                                          'ReconstructedParticles/ReconstructedParticles.momentum.x',
+                                          'ReconstructedParticles/ReconstructedParticles.momentum.y',
+                                          'ReconstructedParticles/ReconstructedParticles.momentum.z',
+                                          'ReconstructedParticlesAssoc/ReconstructedParticlesAssoc.simID',
+                                          'ReconstructedParticlesAssoc/ReconstructedParticlesAssoc.recID',],step_size=Nevents):
+    PDG_mc = array['MCParticles/MCParticles.PDG']
+    px_mc = array['MCParticles/MCParticles.momentum.x']
+    py_mc = array['MCParticles/MCParticles.momentum.y']
+    pz_mc = array['MCParticles/MCParticles.momentum.z']
+    PDG_rc = array['ReconstructedParticles/ReconstructedParticles.PDG']
+    px_rc = array['ReconstructedParticles/ReconstructedParticles.momentum.x']
+    py_rc = array['ReconstructedParticles/ReconstructedParticles.momentum.y']
+    pz_rc = array['ReconstructedParticles/ReconstructedParticles.momentum.z']
+    simID = array['ReconstructedParticlesAssoc/ReconstructedParticlesAssoc.simID']
+    recID = array['ReconstructedParticlesAssoc/ReconstructedParticlesAssoc.recID']
+
+momentum_mc = np.sqrt(((px_mc**2)+(py_mc**2)+(pz_mc**2)))
+theta_mc = np.arctan2(np.sqrt(px_mc**2+py_mc**2), pz_mc)
+phi_mc = np.arctan2(py_mc, px_mc)
+
+momentum_rc = np.sqrt(((px_rc**2)+(py_rc**2)+(pz_rc**2)))
+theta_rc = np.arctan2(np.sqrt(px_rc**2+py_rc**2), pz_rc)
+phi_rc = np.arctan2(py_rc, px_rc)
+
+booll = (PDG_mc[simID])==(PDG_rc[recID])
+boolean_pion = np.logical_or(ak.Array(PDG_mc[simID][booll])==-211, ak.Array(PDG_mc[simID][booll])==+211)
+boolean_proton = np.logical_or(ak.Array(PDG_mc[simID][booll])==-2212, ak.Array(PDG_mc[simID][booll])==+2212)
+boolean_electron = ak.Array(PDG_mc[simID][booll])==11
+boolean_neutron = ak.Array(PDG_mc[simID][booll])==2112
+boolean_photon = ak.Array(PDG_mc[simID][booll])==22
+
+MC_list = [ak.Array(momentum_mc[simID][booll]),
+           ak.Array(theta_mc[simID][booll]),
+           ak.Array(phi_mc[simID][booll]),
+           -np.log(np.tan((ak.Array(theta_mc[simID][booll]))/2))]
+RC_list = [ak.Array(momentum_rc[recID][booll]),
+           ak.Array(theta_rc[recID][booll]),
+           ak.Array(phi_rc[recID][booll]),
+           -np.log(np.tan((ak.Array(theta_rc[recID][booll]))/2))]
+M_list = [ak.Array(momentum_mc[simID][booll]),
+          ak.Array(momentum_mc[simID][booll][boolean_pion]),
+          ak.Array(momentum_mc[simID][booll][boolean_proton]),
+          ak.Array(momentum_mc[simID][booll][boolean_electron]),
+          ak.Array(momentum_mc[simID][booll][boolean_neutron]),
+          ak.Array(momentum_mc[simID][booll][boolean_photon])]
+title_list = ['Momentum','Theta','Phi','Eta']
+
+if Nevents == 100:
+    ssize = 1
+else:
+    ssize = 0.01
+
+particle = config.split('-')[0].strip()
+particle_dict = {'e':[boolean_electron,'Electrons'],'pi':[boolean_pion,'Pions']}
+###################
+
+for i in range(len(MC_list)):
+    X1 = MC_list[i]
+    Y1 = RC_list[i]
+    X_list = [ak.Array(X1),
+          ak.Array(X1[boolean_pion]),
+          ak.Array(X1[boolean_proton]),
+          ak.Array(X1[boolean_electron]),
+          ak.Array(X1[boolean_neutron]),
+          ak.Array(X1[boolean_photon])]
+    Y_list = [ak.Array(Y1),
+          ak.Array(Y1[boolean_pion]),
+          ak.Array(Y1[boolean_proton]),
+          ak.Array(Y1[boolean_electron]),
+          ak.Array(Y1[boolean_neutron]),
+          ak.Array(Y1[boolean_photon])]
+
+    X_plot = list(np.zeros(len(X_list)))
+    Y_plot = list(np.zeros(len(X_list)))
+
+    for j in range(len(X_list)):
+        X = X_list[j]
+        Y = Y_list[j]
+        X_len = ak.count(X,axis=None)
+        Y_len = ak.count(Y,axis=None)
+        if X_len > Y_len: 
+            F_boolean = np.ones_like(Y) == 1
+        else: 
+            F_boolean = np.ones_like(X) == 1
+        X_s = np.array(ak.flatten(X[F_boolean])) 
+        Y_s = np.array(ak.flatten(Y[F_boolean]))
+        if i == 0:   #Momentum
+            ratio = np.array((ak.Array(Y_s)/ak.Array(X_s)))
+        else:
+            ratio = np.array((ak.Array(Y_s)-(ak.Array(X_s))))
+        X_plot[j] = X_s
+        Y_plot[j] = ratio
+
+    fig = plt.figure()
+    gs = fig.add_gridspec(3, 2, wspace=0)
+    (ax1, ax2), (ax3, ax4),(ax5, ax6) = gs.subplots(sharex=True, sharey=True)
+    # fig.suptitle('')
+    if i == 1:   #theta
+        X_plot[0],X_plot[1],X_plot[2],X_plot[3],X_plot[4],X_plot[5] = -X_plot[0],-X_plot[1],-X_plot[2],-X_plot[3],-X_plot[4],-X_plot[5]
+    ax1.scatter(X_plot[0], Y_plot[0], s = ssize)
+    ax2.scatter(X_plot[1], Y_plot[1], s = ssize)
+    ax3.scatter(X_plot[2], Y_plot[2], s = ssize)
+    ax4.scatter(X_plot[3], Y_plot[3], s = ssize)
+    ax5.scatter(X_plot[4], Y_plot[4], s = ssize)
+    ax6.scatter(X_plot[5], Y_plot[5], s = ssize)
+
+    ax_list = [ax1,ax2,ax3,ax4,ax5]
+    if i == 0:
+        ax1.set_ylabel('rc/mc')
+        ax3.set_ylabel('rc/mc')
+        ax5.set_ylabel('rc/mc') 
+        title ='ratio'
+        for ax in ax_list:
+            ax.set_yscale('log')
+            ax.set_xscale('log')
+    else:
+        ax1.set_ylabel('rc-mc')
+        ax3.set_ylabel('rc-mc')
+        ax5.set_ylabel('rc-mc') 
+        title ='difference'
+    ax2.set_title('Pions')
+    ax3.set_title('Protons')
+    ax4.set_title('Electrons')
+    ax5.set_title('Neutrons')
+    ax6.set_title('Photons')
+
+    if i == 3: #Eta
+        ax1.set_xlim(-5.5,5.5)
+    if i == 1: #Theta
+        ax1.set_xlim(-np.pi,0)
+        ax5.set_xlabel('- %s mc'%(title_list[i]))
+        ax6.set_xlabel('- %s mc'%(title_list[i]))
+        x_range = [0,np.pi]
+    else:
+        ax5.set_xlabel('%s mc'%(title_list[i]))
+        ax6.set_xlabel('%s mc'%(title_list[i]))
+        x_range = list(ax1.get_xlim())
+    fig.set_figwidth(20)
+    fig.set_figheight(10)
+    ax1.set_title('%s %s  %s  %s events'%(title_list[i],title,config,Nevents))
+    plt.savefig(os.path.join(r_path, '%s_%s_%s.png' %  (title_list[i],title,config)))
+    plt.close()
+    ############
+    
+    #Correlation
+    X_len = ak.count(X1,axis=None)
+    Y_len = ak.count(Y1,axis=None)
+    if X_len > Y_len: 
+        F_boolean = np.ones_like(Y1) == 1
+    else: 
+        F_boolean = np.ones_like(X1) == 1
+    X_s = np.array(ak.flatten(X1[F_boolean])) 
+    Y_s = np.array(ak.flatten(Y1[F_boolean])) 
+
+    if i == 0 and particle in particle_dict.keys(): #Momentum in Single events
+        h, xedges, yedges, image = plt.hist2d(x=X_s,y= Y_s,  bins = 11) 
+    else:    
+        h, xedges, yedges, image = plt.hist2d(x=X_s,y= Y_s,  bins = 11, range = [x_range,x_range]) 
+    plt.close()
+
+    col_sum = ak.sum(h,axis=-1) #number of events in each (verticle) column 
+    norm_h = [] #norm_h is the normalized matrix
+    norm_h_text = [] #display labels matrix
+    for j in range(len(col_sum)):
+        if col_sum[j] != 0:
+            norm_c = h[j]/col_sum[j] #normalized column = column values divide by sum of the column
+        else:
+            norm_c = h[j]
+        norm_h.append(norm_c)
+        norm_c_text = [ '%.3f' % elem for elem in norm_c ] #display value to 3 dp
+        norm_h_text.append(norm_c_text)
+    
+    fig, axs = plt.subplots(1, 2, figsize=(20, 10))
+    if i == 0 and particle in particle_dict.keys(): #Momentum in Single events
+        axs[0].hist2d(x=X_s,y=Y_s, bins = 11)
+    else: 
+        axs[0].hist2d(x=X_s,y=Y_s, bins = 11,range = [x_range,x_range]) 
+    mplhep.hist2dplot(H=norm_h,norm=mpl.colors.LogNorm(vmin= 1e-4, vmax= 1),labels=norm_h_text, xbins = xedges, ybins = yedges, ax=axs[1])
+    
+    axs[0].set_title('%s Histogram'%(title_list[i]))
+    axs[0].set_xlabel('%s_mc'%(title_list[i]))
+    axs[0].set_ylabel('%s_rc'%(title_list[i]))
+    axs[1].set_xlabel('%s_mc'%(title_list[i]))
+    axs[1].set_ylabel('%s_rc'%(title_list[i]))
+    axs[1].set_title('%s Correlation'%(title_list[i]))
+    fig.suptitle('%s  %s events'%(config,Nevents))
+    plt.savefig(os.path.join(r_path, '%s_correlation_%s.png' %  (title_list[i],config)))
+
+###############
+
+    if i > 0:
+        for j in range(len(X_list)):
+            X = X_list[j]
+            Y = Y_list[j]
+            M_mc = M_list[j]
+            boolean_M = np.ones_like(M_mc) == 1
+            X_s = np.array(ak.flatten(X[boolean_M])) 
+            Y_s = np.array(ak.flatten(Y[boolean_M])) 
+            M_s = np.array(ak.flatten(M_mc))
+            ratio = np.array((ak.Array(Y_s)-(ak.Array(X_s))))
+            X_plot[j] = M_s
+            Y_plot[j] = ratio
+
+        fig = plt.figure()
+        gs = fig.add_gridspec(3, 2, wspace=0)
+        (ax1, ax2), (ax3, ax4),(ax5, ax6) = gs.subplots(sharex=True, sharey=True)
+        # fig.suptitle('')
+        ax1.scatter(X_plot[0], Y_plot[0], s = ssize)
+        ax2.scatter(X_plot[1], Y_plot[1], s = ssize)
+        ax3.scatter(X_plot[2], Y_plot[2], s = ssize)
+        ax4.scatter(X_plot[3], Y_plot[3], s = ssize)
+        ax5.scatter(X_plot[4], Y_plot[4], s = ssize)
+        ax6.scatter(X_plot[5], Y_plot[5], s = ssize)
+        ax1.set_xscale('log')
+        ax1.set_ylabel('rc-mc')
+        ax3.set_ylabel('rc-mc')
+        ax5.set_ylabel('rc-mc')
+        ax5.set_xlabel('Momentum mc')
+        ax6.set_xlabel('Momentum mc')
+        ax2.set_title('Pions')
+        ax3.set_title('Protons')
+        ax4.set_title('Electrons')
+        ax5.set_title('Neutrons')
+        ax6.set_title('Photons')
+        fig.set_figwidth(20)
+        fig.set_figheight(10)
+        ax1.set_title('%s Difference Vs Momentum  %s  %s events'%(title_list[i],config,Nevents))
+        plt.savefig(os.path.join(r_path, '%s_difference_vs_momentum_%s.png' %  (title_list[i],config)))
+
+################
+
+def particle_plots(boolean_particle):
+    theta_mc_fil = ak.Array(theta_mc[simID][booll])[boolean_particle]
+    theta_rc_fil = ak.Array(theta_rc[recID][booll])[boolean_particle]
+    phi_mc_fil = ak.Array(phi_mc[simID][booll])[boolean_particle]
+    phi_rc_fil = ak.Array(phi_rc[recID][booll])[boolean_particle]
+
+    theta_mc_fil_len = ak.count(theta_mc_fil,axis=None)
+    theta_rc_fil_len = ak.count(theta_rc_fil,axis=None)
+    if theta_mc_fil_len > theta_rc_fil_len:
+        F_boolean = np.ones_like(theta_rc_fil) == 1
+    else: 
+        F_boolean = np.ones_like(theta_mc_fil) == 1
+    theta_mc_F = np.array(ak.flatten(theta_mc_fil[F_boolean]))
+    theta_rc_F = np.array(ak.flatten(theta_rc_fil[F_boolean]))
+    phi_mc_F = np.array(ak.flatten(phi_mc_fil[F_boolean]))
+    phi_rc_F = np.array(ak.flatten(phi_rc_fil[F_boolean]))
+    ratio = np.array((ak.Array(theta_rc_F)-(ak.Array(theta_mc_F))))
+
+    fig = plt.figure()
+    gs = fig.add_gridspec(2, 2, wspace=0.01)
+    (ax1, ax2), (ax3, ax4) = gs.subplots(sharex=True, sharey=True)
+    ax1.scatter(-theta_mc_F, ratio, s = ssize)
+    ax2.scatter(-theta_rc_F, ratio, s = ssize)
+    ax3.scatter(-theta_mc_F, phi_mc_F, s = ssize)
+    ax4.scatter(-theta_rc_F, phi_rc_F, s = ssize)
+    ax1.set_ylabel('rc-mc')
+    ax2.set_ylabel('rc-mc')
+    ax3.set_ylabel('Phi mc')
+    ax4.set_ylabel('Phi rc')
+    ax1.set_xlabel('- Theta mc')
+    ax2.set_xlabel('- Theta rc')
+    ax3.set_xlabel('- Theta mc')
+    ax4.set_xlabel('- Theta rc')
+    fig.set_figwidth(20)
+    fig.set_figheight(10)
+
+if particle in particle_dict.keys():
+    boolean_particle = particle_dict[particle][0]
+    particle_name = particle_dict[particle][1]
+    particle_plots(boolean_particle)
+
+    plt.suptitle('%s in %s  %s events'%(particle_name,config,Nevents))
+    plt.savefig(os.path.join(r_path, '%s_%s.png' %  (particle_name,config)))
+else:
+    for i in [[boolean_photon,'Photons'],[boolean_electron,'Electrons'],[boolean_pion,'Pions']]:
+        boolean_particle = i[0]
+        particle_name = i[1]
+        particle_plots(boolean_particle)
+
+        plt.suptitle('%s in %s  %s events'%(particle_name,config,Nevents))
+        plt.savefig(os.path.join(r_path, '%s_%s.png' %  (particle_name,config)))
+
+
+
+
+
diff --git a/benchmarks/dis/dis.sh b/benchmarks/dis/dis.sh
index 33fb9a3125c123b7e347a6da81f4b00d5dd24beb..2c25719a64a05c61a3c3e3b7cead3597dfc56d10 100755
--- a/benchmarks/dis/dis.sh
+++ b/benchmarks/dis/dis.sh
@@ -128,6 +128,12 @@ if [[ "$?" -ne "0" ]] ; then
   exit 1
 fi
 
+python benchmarks/dis/analysis/truth_reconstruction.py --rec_file ${REC_FILE} --config ${PLOT_TAG} --results_path ${RESULTS_PATH} --nevents ${JUGGLER_N_EVENTS}
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR running truth_reconstruction script"
+  exit 1
+fi
+
 CONFIG="${TMP_PATH}/${PLOT_TAG}.raw.json"
 cat << EOF > ${CONFIG}
 {
diff --git a/benchmarks/dis/env.sh b/benchmarks/dis/env.sh
index e6357f413576e2bc6e8cce30ed7df44551c1ea67..cbd42351dcd9a5ed19a9ac3e8077f12c5e516582 100644
--- a/benchmarks/dis/env.sh
+++ b/benchmarks/dis/env.sh
@@ -47,6 +47,7 @@ echo "TMP_PATH:               ${TMP_PATH}"
 ## if not too big).
 RESULTS_PATH="results/${BENCHMARK_TAG}/${BEAM_TAG}/minQ2=${MINQ2}"
 mkdir -p ${RESULTS_PATH}
+mkdir -p "${RESULTS_PATH}/truth_reconstruction"
 export RESULTS_PATH=`realpath ${RESULTS_PATH}`
 echo "RESULTS_PATH:           ${RESULTS_PATH}"
 
diff --git a/benchmarks/single/analyze.sh b/benchmarks/single/analyze.sh
index 547ec8bcbd67c526063f20d8fa4464c33a140eda..6271888dfc00173af9d9bdf661041970d6415e7e 100644
--- a/benchmarks/single/analyze.sh
+++ b/benchmarks/single/analyze.sh
@@ -9,3 +9,9 @@ if [[ "$?" -ne "0" ]] ; then
   echo "ERROR analysis failed"
   exit 1
 fi
+
+python benchmarks/dis/analysis/truth_reconstruction.py --rec_file ${JUGGLER_REC_FILE} --config ${JUGGLER_FILE_NAME_TAG} --results_path ${RESULTS_PATH} --nevents ${JUGGLER_N_EVENTS}
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR running truth_reconstruction script"
+  exit 1
+fi
diff --git a/benchmarks/single/common.sh b/benchmarks/single/common.sh
index e7ef78aef96ecfffd4e115cb289f38aa9808e29f..2f8d58a27fd5a5d89f80c3440fe10a94127d64f4 100644
--- a/benchmarks/single/common.sh
+++ b/benchmarks/single/common.sh
@@ -6,3 +6,12 @@ export JUGGLER_FILE_NAME_TAG="${1:-e-_1GeV_45to135deg}"
 export JUGGLER_GEN_FILE="benchmarks/single/${JUGGLER_FILE_NAME_TAG}.steer"
 export JUGGLER_SIM_FILE="sim_output/sim_${JUGGLER_FILE_NAME_TAG}.edm4hep.root"
 export JUGGLER_REC_FILE="sim_output/rec_${JUGGLER_FILE_NAME_TAG}.root"
+
+export BENCHMARK_TAG="single"
+echo "Setting up the local environment for the ${BENCHMARK_TAG^^} benchmarks"
+
+RESULTS_PATH="results/${BENCHMARK_TAG}"
+mkdir -p ${RESULTS_PATH}
+mkdir -p "${RESULTS_PATH}/truth_reconstruction"
+export RESULTS_PATH=`realpath ${RESULTS_PATH}`
+echo "RESULTS_PATH:           ${RESULTS_PATH}"