25 @class TMultiChannelAnalyzer
26 @brief analysis package for analysing data acquired by MCA
31 @brief initialze object
32 @param Qmeas calculated measured charge
33 @param Qmeas_err calculated error of measured charge
34 @param dEdX stopping power in certain point
35 @param dL effective pad length
36 @param W elecrton-ion piar creation energy
37 @param Qe elementary charge
38 @param G calculated gain
39 @param G_err calculated error of gain
40 @param E calculated theoretical energy deposit in pad
41 @param a 1-st order term of calibration parameter
42 @param b 0-th order term of calibration parameter
43 @param calib_path calibration data path
44 @param datas gain measurement datas
45 @param input_values gain measurement datas path
46 @param calib_fit_xs data list for the data point x near each peak
47 @param calib_fit_ys data list for the gausian value near each peak
48 @param calib_means mean values list from fitting result by iminuit
49 @param calib_sigmas sigma values list from fitting result by iminuit
50 @param data_files list of data path
51 @param data_files_name list of data path name
52 @param datas_x lits of mca data points (x)
53 @param datas_y lits of mca data points (y)
54 @param datas_h lits of mca data points (frequency)
55 @param datas_peak_count list of found peak count
56 @param datas_peak_indices list of found peak index
57 @param datas_fit_xs list of data point x picked up mca data around peak
58 @param datas_fit_ys list of gausian values picked up mca data around peak
59 @param datas_fit_oys list of data point y picked up mca data around peak
60 @param datas_means list of fitted mean value
61 @param datas_sigmas list of fitted sigma value
62 @param voltages input voltage values
76 self.calib_path =
None
78 self.input_values = []
79 self.calib_fit_xs = []
82 self.calib_sigmas = []
84 self.data_files_name = []
88 self.datas_peak_count = []
89 self.datas_peak_indices = []
90 self.datas_fit_xs = []
91 self.datas_fit_ys = []
92 self.datas_fit_oys = []
94 self.datas_sigmas = []
99 @brief get calculated measured charge and error
100 @return Qmeas and error of Qmeas ([float, float, ... , float], [float, float, ... , float])
102 return self.Qmeas, self.Qmeas_err
106 @brief get calculated gain and error
107 @return Gain and error of Gain ([float, float, ... , float], [float, float, ... , float])
109 return self.G, self.G_err
111 def set_voltages(self, val):
113 @brief set list of voltage value
114 @param val reference values [float, float, ... , float]
119 def set_dEdX(self, val):
121 @brief set energy loss in small interval at certain point
122 @param val reference values
127 def set_dL(self, val):
129 @brief set effective pad length
130 @param val reference values
135 def set_W(self, val):
137 @brief set electron-ion pair cratrion energy
138 @param val reference values
143 def calculate_Qmeas(self):
145 @brief calculate measured charge
148 calculation result is stored to `self.Qmeas` and `self.Qmeas_err`
152 if len(self.datas_means)*len(self.datas_sigmas)>0:
153 self.Qmeas = self.linear( np.array(self.datas_means).flatten(), self.b, self.a)
154 self.Qmeas_err = self.a * np.array(self.datas_sigmas).flatten()
156 print(
'measured values are empty.')
158 def calculate_Gain(self):
160 @brief calculate gain
163 calculation result is stored to self.G and self.G_err
167 if len(self.Qmeas)*len(self.Qmeas_err)>0:
168 self.G = self.Qmeas / ( self.dEdX * self.dL * self.Qe / self.W )
169 self.G_err = self.Qmeas_err / ( self.dEdX * self.dL * self.Qe / self.W )
171 print(
'measured values are empty.')
173 def gaussian(self, x, amplitude, mean, sigma):
175 @brief calculate gaussian value
176 @param amplitude constant
177 @param mean mean value
178 @param sigma sigma value
179 @return gaus(amplitude, mean, sigma)
181 return amplitude * np.exp(-0.5 * ((x - mean) / sigma) ** 2)
183 def linear(self, x, p0, p1):
185 @brief calculate liner transform value
186 @param x reference values
187 @param p0 coefficient of 0-th order term
188 @param p1 coefficient of 1-st order term
189 @return gaus(amplitude, mean, sigma)
193 def set_calibration_file_path(self, file_path):
195 @brief set path of calibration data
196 @param file_path file path for mca data
199 self.calib_path = file_path
201 def set_input_values(self, values):
203 @brief set path of measured data
204 @param file_path file path for mca data
207 self.input_values = values
209 def find_peak(self, smooth_parameter=3, input_label='calib' ,debug=False):
211 @brief search peaks for calibraion data
214 should be set before executinf this function.
215 result id stored to, self.calib_data_peak_count and self.calib_data_peak_indices for calibration, self.datas_peak_count, self.datas_peak_indices for data
217 @param smooth_parameter smear parameter for original histogram
218 @param input_label label for data (calibration:'calib', data list:'data')
219 @param debug debug flag. if true, dump peak finding result (default:False)
222 if input_label ==
'calib':
223 self.calib_data_x, self.calib_data_y = util.dataforming.read_spe_file(self.calib_path)
224 self.calib_data_h = util.dataforming.create_histogram_data_from_points(self.calib_data_x, self.calib_data_y)
225 smoothed_data = gaussian_filter(self.calib_data_y, smooth_parameter)
226 self.calib_data_peak_count, self.calib_data_peak_indices = util.dataforming.find_peaks(smoothed_data)
229 print(
"(calibration) number of peak:", self.calib_data_peak_count,
", index of peak:", self.calib_data_peak_indices)
232 for i
in range(len(self.data_files)):
234 file_name = os.path.basename(self.data_files[i])
235 data_x, data_y = util.dataforming.read_spe_file(self.data_files[i])
236 data_h = util.dataforming.create_histogram_data_from_points(data_x, data_y)
237 smoothed_data = gaussian_filter(data_y, smooth_parameter)
238 data_peak_count, data_peak_indices = util.dataforming.find_peaks(smoothed_data)
240 self.datas_x.append(data_x)
241 self.datas_y.append(data_y)
242 self.datas_h.append(data_h)
243 self.datas_peak_count.append(data_peak_count)
244 self.datas_peak_indices.append(data_peak_indices)
245 self.data_files_name.append(os.path.splitext(file_name)[0])
248 print(
"(data) :", i,
" number of peak:", data_peak_count,
", index of peak:", data_peak_indices)
250 def fit_calibration_data(self, width=30, initial_sigma=10, plot_flag=False):
252 @brief peak fitting for calibration data
255 fitting result is stored to `self.calib_fit_xs`, `self.calib_fit_ys`, `self.calib_means`, `self.calib_sigmas`
257 @param width fitting range from center position (`self.calib_data_x[self.calib_data_peak_indices[i]]`)
258 @param initial_sigma initial value for using iminuit fitting
259 @param plot_flag plot flag. if true, draw data by plotly (default:False)
262 for i
in range(len(self.calib_data_peak_indices)):
263 xarr = np.array(self.calib_data_x)
264 yarr = np.array(self.calib_data_y)
266 fit_min, fit_max = self.calib_data_x[self.calib_data_peak_indices[i]] - width, self.calib_data_x[self.calib_data_peak_indices[i]] + width
267 mask = (xarr >= fit_min) & (xarr <= fit_max)
268 xarr_mask = xarr[mask]
269 yarr_mask = yarr[mask]
272 least_squares = LeastSquares(xarr_mask, yarr_mask, np.ones_like(yarr_mask), self.gaussian)
273 m = Minuit(least_squares, amplitude=self.calib_data_y[self.calib_data_peak_indices[i]], mean=self.calib_data_x[self.calib_data_peak_indices[i]], sigma=initial_sigma)
277 self.calib_fit_xs.append(xarr_mask)
278 self.calib_fit_ys.append(self.gaussian(xarr_mask, m.values[
'amplitude'], m.values[
'mean'], m.values[
'sigma']))
280 self.calib_means.append(m.values[
'mean'])
281 self.calib_sigmas.append(m.values[
'sigma'])
284 categories = [
"mca"] * len(self.calib_data_h)
285 df = pd.DataFrame({
'Value': self.calib_data_h,
'Category': categories})
286 fig = px.histogram(df, x=
'Value', color=
'Category', nbins=1023, labels={
'Value':
'Channel'}, title=
'MCA Histogram')
288 for i
in range(len(self.calib_means)):
291 x=self.calib_fit_xs[i], y=self.calib_fit_ys[i], mode=
'lines',
292 name=f
"FID:{i}, ({self.calib_means[i]:3.2f}, {self.calib_sigmas[i]:3.2f})",
293 line=dict(color=
'red', width=1.5), opacity=0.5))
297 def calculate_calibration_parameters(self, plot_flag=False):
299 @brief calculate calibration paramters
302 calibraion paramter is determined by iminuit fitting.
304 his function sould be executed after executing `self.fit_calibration_data()`.
305 fitting result is stored to `self.a`(1st), `self.b`(0th)
307 @param plot_flag plot flag. if true, draw data by plotly (default:False)
310 if len(self.calib_sigmas) == len(self.input_values):
311 self.calib_means_err = []
312 for i
in range(len(self.calib_means)):
313 self.calib_means_err.append(self.calib_sigmas[i]/self.calib_means[i])
315 least_squares = LeastSquares(self.input_values, self.calib_means, self.calib_means_err, self.linear)
316 m = Minuit(least_squares, p0=0, p1=100)
321 self.b = -params[
'p0']/params[
'p1']
322 self.a = 1/params[
'p1']
327 y=self.input_values, x=self.calib_means,
328 error_x=dict(type=
'data', array=self.calib_means_err, thickness=2, width=5, visible=
True),
329 mode=
'markers', name=
'fitted data plot'))
330 fig.update_layout(title=
'Input Values vs Fitted Values', yaxis_title=
'Input values', xaxis_title=
'Fitted value', title_x=0.05, title_font=dict(size=20))
333 x=np.array(self.calib_means), y=self.linear(np.array(self.calib_means), self.b , self.a), mode=
'lines',
334 name=f
"{self.b:.3f}+{self.a:.3f}x",
335 line=dict(color=
'red', width=2), opacity=0.5))
338 print(
'peak number does not match input list.')
339 print(
'calibration parameter calculation process wad skipped.')
341 def remove_fitted_peak(self, list_label='calib', index_list=[]):
343 @brief remove specific peak
344 @param list_label label for data (calibration:'calib', data list:'data')
345 @param index_list list of index to be deleted
348 if list_label ==
'calib':
349 for index
in sorted(index_list, reverse=
True):
350 if 0 <= index < len(self.calib_sigmas):
351 del self.calib_sigmas[index]
353 for index
in sorted(index_list, reverse=
True):
354 if 0 <= index < len(self.calib_means):
355 del self.calib_means[index]
357 for i
in range(len(self.datas_means)):
358 for index
in sorted(index_list[i], reverse=
True):
359 if 0 <= index < len(self.datas_fit_xs[i]):
360 del self.datas_fit_xs[i][index]
362 for index
in sorted(index_list[i], reverse=
True):
363 if 0 <= index < len(self.datas_fit_ys[i]):
364 del self.datas_fit_ys[i][index]
366 for index
in sorted(index_list[i], reverse=
True):
367 if 0 <= index < len(self.datas_fit_oys[i]):
368 del self.datas_fit_oys[i][index]
370 for index
in sorted(index_list[i], reverse=
True):
371 if 0 <= index < len(self.datas_means[i]):
372 del self.datas_means[i][index]
374 for index
in sorted(index_list[i], reverse=
True):
375 if 0 <= index < len(self.datas_sigmas[i]):
376 del self.datas_sigmas[i][index]
378 def get_data_file_path_list(self, filepath='./*spe'):
380 @brief sort and set data file list
383 can use wildcard with input path
385 @param filepath file path
388 self.data_files = sorted(glob.glob(filepath))
390 def add_data_file_path_list(self, filepath='./*spe'):
392 @brief sort and add data file list
395 can use wildcard with input path
397 @param filepath file path
400 add_files = sorted(glob.glob(filepath))
401 for i
in range(len(add_files)):
402 self.data_files.append(add_files[i])
404 def check_data_file(self):
406 @brief dump load file list
409 for i
in range(len(self.data_files)):
410 print(self.data_files[i])
413 def fit_data(self, width=30, initial_sigma=10):
415 @brief peak fitting for list of mca data
418 fitting result is stored to `self.datas_fit_xs`, `self.datas_fit_ys`, `self.datas_fit_oys`, `self.datas_means`, `self.datas_sigmas`
420 @param width fitting range from center position (`self.calib_data_x[self.calib_data_peak_indices[i]]`)
421 @param initial_sigma initial value for using iminuit fitting
424 for j
in range(len(self.datas_x)):
432 for i
in range(len(self.datas_peak_indices[j])):
433 xarr = np.array(self.datas_x[j])
434 yarr = np.array(self.datas_y[j])
436 fit_min, fit_max = self.datas_x[j][self.datas_peak_indices[j][i]] - width, self.datas_x[j][self.datas_peak_indices[j][i]] + width
437 mask = (xarr >= fit_min) & (xarr <= fit_max)
438 xarr_mask = xarr[mask]
439 yarr_mask = yarr[mask]
441 least_squares = LeastSquares(xarr_mask, yarr_mask, np.ones_like(yarr_mask), self.gaussian)
442 m = Minuit(least_squares, amplitude=self.datas_y[j][self.datas_peak_indices[j][i]], mean=self.datas_x[j][self.datas_peak_indices[j][i]], sigma=initial_sigma)
446 data_fit_xs.append(xarr_mask)
447 data_fit_ys.append(self.gaussian(xarr_mask, m.values[
'amplitude'], m.values[
'mean'], m.values[
'sigma']))
448 data_fit_oys.append(yarr_mask)
449 data_means.append(m.values[
'mean'])
450 data_sigmas.append(m.values[
'sigma'])
452 self.datas_fit_xs.append(data_fit_xs)
453 self.datas_fit_ys.append(data_fit_ys)
454 self.datas_fit_oys.append(data_fit_oys)
455 self.datas_means.append(data_means)
456 self.datas_sigmas.append(data_sigmas)
458 def draw_fitted_datas(self):
460 @brief draw fitting result for list of mca data
465 for i
in range(len(self.datas_fit_xs)):
466 for j
in range(len(self.datas_fit_xs[i])):
468 data_h = util.dataforming.create_histogram_data_from_points(self.datas_fit_xs[i][j], self.datas_fit_oys[i][j])
471 categories = [f
"{self.data_files_name[i]}, FID:{i}, HID:{j}"] * len(data_h)
475 categories = categories + [f
"{self.data_files_name[i]}, FID:{i}, HID:{j}"] * len(data_h)
476 histograms = histograms + data_h
480 df = pd.DataFrame({
'Value': histograms,
'Category': categories})
481 fig = px.histogram(df, x=
"Value", color=
"Category", nbins=1023, opacity=0.75)
483 for i
in range(len(self.datas_fit_xs)):
484 for j
in range(len(self.datas_fit_xs[i])):
487 x=self.datas_fit_xs[i][j], y=self.datas_fit_ys[i][j], mode=
'lines',
488 name=f
"FID:{i}, HID:{j}, ({self.calib_means[i]:3.2f}, {self.calib_sigmas[i]:3.2f})",
489 line=dict(color=
'black', width=1.5), opacity=0.5))
493 def draw_gain_curve(self):
495 @brief draw fitting result for list of mca data
498 this function should be executed after executing `self.draw_fitted_datas()`, `self.calculate_Qmeas()`, `self.calculate_Gain()`
504 x=self.voltages, y=self.G,
505 error_y=dict(type=
'data', array=self.G_err, thickness=2, width=5, visible=
True),
506 name=
'fitted data plot'))
507 fig.update_layout(title=
'Gain Curve', yaxis_title=
'Gain', xaxis_title=
'Voltage [V]', title_x=0.05, title_font=dict(size=20))