All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dedx.py
Go to the documentation of this file.
1 import numpy as np
2 from scipy.interpolate import interp2d
3 import landau
4 from matplotlib import gridspec
5 from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
6 
7 # Calculate the MPV dEdx v. R.R. for muons
8 
9 # From: https://pdg.lbl.gov/2012/AtomicNuclearProperties/MUON_ELOSS_TABLES/muonloss_289.pdf
10 LAr_density_gmL = 1.3973
11 
12 # Reference data
13 CSDA_RR_REF = np.array([
14  9.833e-1,
15  1.786e0,
16  3.321e0,
17  6.598e0,
18  1.058e1,
19  3.084e1,
20  4.250e1,
21  6.732e1,
22  1.063e2,
23  1.725e2,
24  2.385e2,
25  4.934e2,
26  6.163e2
27 ]) / LAr_density_gmL
28 
29 KE_REF = np.array([
30  10.,
31  14.,
32  20.,
33  30.,
34  40.,
35  80.,
36  100.,
37  140.,
38  200.,
39  300.,
40  400.,
41  800.,
42  1000.
43 ])
44 
45 # Constants
46 mass_electron = 0.5109989461 # MeV https://pdg.lbl.gov/2020/listings/rpp2020-list-K-plus-minus.pdf
47 mass = 105.6583745 # MeV https://pdg.lbl.gov/2020/listings/rpp2020-list-muon.pdf
48 Ival = 188.0e-6
49 Zval = 18.0
50 Aval = 39.948
51 Kfactor = 0.307075
52 
53 def Calc_MPV_DEDX(pitch, T):
54  gamma = (mass+T)/mass
55  beta = np.power(1.0-np.power(gamma,-2.0),0.5)
56  Wmax = (2.0*mass_electron*np.power(beta,2.0)*np.power(gamma,2.0))/(1.0+2.0*gamma*(mass_electron/mass)+np.power(mass_electron/mass,2.0))
57 
58  # Medium energy
59  dens_factor = 2.0*np.log(10)*np.log10(beta*gamma)-5.2146+0.19559*np.power(3.0-np.log10(beta*gamma),3.0)
60  # low energy
61  dens_factor[np.log10(beta*gamma) < 0.2] = 0.
62  # high energy
63  dens_factor[np.log10(beta*gamma) > 3.0] = (2.0*np.log(10)*np.log10(beta*gamma)-5.2146)[np.log10(beta*gamma) > 3.0]
64  xi = (Kfactor/2.0)*(Zval/Aval)*np.power(beta,-2.0)*LAr_density_gmL*pitch
65  kappa = xi/Wmax
66  dEdx_MPV = xi*(np.log((2.0*mass_electron*np.power(beta*gamma,2.0))/Ival)+np.log(xi/Ival)+0.200-np.power(beta,2.0)-dens_factor)/pitch
67 
68  return dEdx_MPV
69 
71  gamma = (mass+T)/mass
72  beta = np.power(1.0-np.power(gamma,-2.0),0.5)
73  Wmax = (2.0*mass_electron*np.power(beta,2.0)*np.power(gamma,2.0))/(1.0+2.0*gamma*(mass_electron/mass)+np.power(mass_electron/mass,2.0))
74 
75  # Medium energy
76  dens_factor = 2.0*np.log(10)*np.log10(beta*gamma)-5.2146+0.19559*np.power(3.0-np.log10(beta*gamma),3.0)
77  # low energy
78  dens_factor[np.log10(beta*gamma) < 0.2] = 0.
79  dens_factor[beta < 1e-6] = 0.
80  # high energy
81  dens_factor[np.log10(beta*gamma) > 3.0] = (2.0*np.log(10)*np.log10(beta*gamma)-5.2146)[np.log10(beta*gamma) > 3.0]
82  dEdx_mean = LAr_density_gmL*Kfactor*(Zval/Aval)*np.power(beta,-2.0)*(0.5*np.log(2.0*mass_electron*np.power(beta,2.0)*np.power(gamma,2.0)*Wmax*np.power(Ival,-2.0))-np.power(beta,2.0)-dens_factor/2.0)
83 
84  return dEdx_mean
85 
86 # Map R.R. to KE
88  KE_points_max = 1000.
89  dRR = 0.01
90  thisKE = KE_points_max
91 
92  KE_points = [thisKE]
93  RR_points = [0.]
94 
95  while thisKE > 0.0:
96  deltaKE = Calc_MEAN_DEDX(np.array([thisKE])) * dRR
97  RR_points.append(RR_points[-1] + dRR)
98  thisKE -= deltaKE[0]
99  KE_points.append(thisKE)
100 
101  KE_points = np.array(list(reversed(KE_points[:-1])))
102  RR_points = np.array(RR_points[:-1])
103 
104 
105  # Map KE to MPV dE/dx
106 
107  # pitches
108  PITCH_points = np.linspace(0.2, 3, 28*100+1)
109 
110  KE_points_2d = np.tile(KE_points, (PITCH_points.size, 1))
111  RR_points_2d = np.tile(RR_points, (PITCH_points.size, 1))
112  PITCH_points_2d = np.tile(PITCH_points, (KE_points.size, 1)).T
113 
114  MPV_dEdx_points_2d = Calc_MPV_DEDX(PITCH_points_2d, KE_points_2d)
115 
116  RRpitch2dEdx = interp2d(RR_points, PITCH_points, MPV_dEdx_points_2d, kind="cubic")
117 
118  return RRpitch2dEdx
119 
120 RRpitch2dEdx = make_mpv_map()
121 
122 # ArgoNeuT params
123 MODA = 0.930
124 MODB = 0.212
125 Wion = 1e3 / 4.237e7
126 Efield = 0.5
127 
128 def recombination(dEdx, A=MODA, B=MODB, E=Efield):
129  alpha = A
130  beta = B / (LAr_density_gmL * E)
131 
132  dQdx = np.log(alpha + dEdx*beta) / (Wion * beta)
133  return dQdx
134 
135 # ICARUS params
136 k = 0.0486
137 A = 0.8
138 
140  R = A / (1 + k*dEdx / (Efield*LAr_density_gmL))
141  return R * dEdx / Wion
142 
143 def landau_gaus(X, *p):
144  mpv, eta, sigma, A = p
145  sigma = np.minimum(sigma, 100*eta)
146  return landau.landau.gauss_landau(X, mpv, eta, sigma, A)
147 
148 def langau_chi2(x, y, yerr, popt):
149  return np.sum(((landau_gaus(x, *popt) - y) / yerr)**2)
150 
151 def gain_predicted_MPV(RRs, CAL, pitch, A=MODA, B=MODB, E=Efield):
152  dEdxs = RRpitch2dEdx(RRs, pitch)
153  dQdxs = recombination(dEdxs, A, B, E)
154  return dQdxs / CAL
155 
156 def gain_predicted_MPV_Birks(RRs, CAL, pitch):
157  dEdxs = RRpitch2dEdx(RRs, pitch)
158  dQdxs = Birks_recombination(dEdxs)
159  return dQdxs / CAL
160 
161 def gain_chi2(RRs, CAL, MPV, err, pitch, when, A=MODA, B=MODB):
162  dEdxs = RRpitch2dEdx(RRs, pitch)
163  dQdxs = recombination(dEdxs, A, B)
164  dQdxs_ADC = np.outer(1. / CAL, dQdxs[when])
165  chi2s = (MPV[when] - dQdxs_ADC)**2 / err[when]**2
166  return np.sum(chi2s, axis=-1)
167 
168 def gain_chi2_Birks(RRs, CAL, MPV, err, pitch, when):
169  dEdxs = RRpitch2dEdx(RRs, pitch)
170  dQdxs = Birks_recombination(dEdxs)
171  dQdxs_ADC = np.outer(1. / CAL, dQdxs[when])
172  chi2s = (MPV[when] - dQdxs_ADC)**2 / err[when]**2
173  return np.sum(chi2s, axis=-1)
174 
175 def valid_mpv(RRs, MPV, err, minRR=2.):
176  return np.isfinite(err) & (RRs > minRR) & (err/MPV < 0.1)
177 
178 def calibrate_plot(fig, Xlist, preds, datas, errs, text=None, title=None, labels=None):
179  if not isinstance(preds, list):
180  preds = [preds]
181 
182  if not isinstance(datas, list):
183  datas = [datas]
184 
185  if not isinstance(Xlist, list):
186  Xlist = [Xlist]
187 
188  assert(len(preds) == len(datas) == len(Xlist))
189 
190  gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])
191  ax1 = fig.subplot(gs[0])
192  ax2 = fig.subplot(gs[1], sharex = ax1)
193 
194  colors = fig.rcParams['axes.prop_cycle'].by_key()['color']
195 
196  ps = []
197  ds = []
198 
199  for i, (Xs,pred) in enumerate(zip(Xlist,preds)):
200  color = None if len(preds) == 1 else colors[i]
201  label = "Cal. Fit"
202  if labels is not None:
203  label = label + " " + labels[i]
204  p = ax1.plot(Xs, pred, label=label, color=color)
205  ps.append(p[0])
206 
207  for i, (Xs, data, err) in enumerate(zip(Xlist, datas, errs)):
208  color = None if len(preds) == 1 else colors[i]
209  label = "Data M.P.V."
210  if labels is not None:
211  label = label + " " + labels[i]
212  d = ax1.errorbar(Xs, data, yerr=err, ls="none",
213  color=color, label=label, marker=".", markersize=5.)
214  ds.append(d)
215 
216  leg_labels = ["Fit/Data" for _ in ps]
217  if labels is not None:
218  leg_labels = [ll+l for ll,l in zip(leg_labels, labels)]
219 
220  ax1.legend(list(zip(ps, ds)), leg_labels, numpoints=1,
221  handler_map={tuple: HandlerTuple(ndivide=None)})
222 
223  ax2.set_label("Residual Range [cm]")
224  ax1.set_ylabel("dQ/dx [ADDC/cm]")
225 
226  if text:
227  ax1.text(0.5, 2.25, text, transform=fig.gca().transAxes, verticalalignment="top")
228  if title:
229  ax1.set_title(title)
230 
231  ax1.get_shared_x_axes().join(ax1, ax2)
232  fig.subplots_adjust(hspace=.0)
233 
234  for i, (Xs, data, pred, err) in enumerate(zip(Xlist, datas, preds, errs)):
235  color = None if len(preds) == 1 else colors[i]
236  ax2.plot(Xs, (data - pred) / err, color=color) #, linestyle="None", marker=".", markersize=5)
237 
238  ax2.set_ylim([-4, 4])
239  ax2.axhline(0, color="gray")
240  ax2.axhline(1, color="gray", linestyle="--")
241  ax2.axhline(-1, color="gray", linestyle="--")
242  ax2.axhline(2, color="gray", linestyle=":")
243  ax2.axhline(-2, color="gray", linestyle=":")
244  ax2.set_ylabel("Data - Pred. [$\\sigma$]")
245  ax2.set_xlabel("Residual Range [cm]")
246 
247  yticks = ax2.yaxis.get_major_ticks()
248  yticks[-1].label1.set_visible(False)
249  return ax1, ax2
def langau_chi2
Definition: dedx.py:148
def gain_chi2_Birks
Definition: dedx.py:168
tuple RRpitch2dEdx
Definition: dedx.py:120
def calibrate_plot
Definition: dedx.py:178
def Birks_recombination
Definition: dedx.py:139
def gain_predicted_MPV
Definition: dedx.py:151
def Calc_MPV_DEDX
Definition: dedx.py:53
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
def make_mpv_map
Definition: dedx.py:87
S join(S const &sep, Coll const &s)
Returns a concatenation of strings in s separated by sep.
def Calc_MEAN_DEDX
Definition: dedx.py:70
def valid_mpv
Definition: dedx.py:175
def landau_gaus
Definition: dedx.py:143
def recombination
Definition: dedx.py:128
def gain_chi2
Definition: dedx.py:161
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:295
def gain_predicted_MPV_Birks
Definition: dedx.py:156
list
Definition: file_to_url.sh:28