Module pmt_analysis.plotting.afterpulses
Expand source code
import os
import warnings
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
from typing import Optional
class PlottingAfterpulses:
"""
Class for plots related to the afterpulse processing.
Attributes:
df: Pandas dataframe with after pulse candidates and their properties.
ap_rate_dict: Dictionary with resulting afterpulse rate value and uncertainty.
adc_f: ADC sampling frequency in samples per second as provided by same attribute of
`pmt_analysis.utils.input.ADCRawData`.
adc_area_to_e: Conversion factor pulse area in ADC units to charge in units of elementary charge.
Attribute of `pmt_analysis.utils.input.ADCRawData` objects.
gain: PMT gain (in units of read out electrons per induced photoelectron).
Obtainable with `pmt_analysis.analysis.model_independent.GainModelIndependent` class.
save_plots: Bool defining if plots are saved (as png and pdf).
show_plots: Bool defining if plots are displayed.
save_dir: Target directory for saving the plots.
save_name_suffix: Measurement specific figure name suffix.
"""
def __init__(self, df: pd.DataFrame, adc_f: float, ap_rate_dict: dict = None,
save_plots: bool = False, show_plots: bool = True,
save_dir: Optional[str] = None, save_name_suffix: Optional[str] = None,
adc_area_to_e: Optional[float] = None, gain: Optional[float] = None):
"""Init of the PlottingAfterpulses class.
Generation of standard plots with respect to the afterpulse processing.
Args:
df: Pandas dataframe with after pulse candidates and their properties.
adc_f: ADC sampling frequency in samples per second as provided by same attribute of
`pmt_analysis.utils.input.ADCRawData`.
ap_rate_dict: Dictionary with resulting afterpulse rate value and uncertainty.
save_plots: Bool defining if plots are saved (as png and pdf).
show_plots: Bool defining if plots are displayed.
save_dir: Target directory for saving the plots.
save_name_suffix: Measurement specific figure name suffix.
adc_area_to_e: Conversion factor pulse area in ADC units to charge in units of elementary charge.
Attribute of `pmt_analysis.utils.input.ADCRawData` objects.
gain: PMT gain (in units of read out electrons per induced photoelectron).
Obtainable with `pmt_analysis.analysis.model_independent.GainModelIndependent` class.
"""
self.df = df
self.ap_rate_dict = ap_rate_dict
self.adc_f = adc_f
self.adc_area_to_e = adc_area_to_e
self.gain = gain
self.save_plots = save_plots
self.show_plots = show_plots
self.save_dir = save_dir
self.save_name_suffix = save_name_suffix
if save_plots & (save_dir is None):
raise NameError('save_dir must be defined if save_plots is True.')
if save_plots & (save_name_suffix is None):
raise NameError('save_name_suffix must be defined if save_plots is True.')
def plot_wf(self, i: int = 0):
"""Plot i-th afterpulse candidate waveform.
Args:
i: Integer-location based index of waveform in `df` to be plotted.
"""
if (i >= self.df.shape[0]) or (i < 0):
raise IndexError('Integer-location based index i must be between 0 and {}'.format(self.df.shape[0]-1))
separability = 'Separable' if self.df.iloc[i]['separable'] else 'Non-Separable'
x_dummy = np.arange(len(self.df.iloc[i]['input_data_converted'])) / self.adc_f * 1e9
plt.step(x_dummy, self.df.iloc[i]['input_data_converted'], where='mid')
plt.axvline(x=self.df.iloc[i]['p0_position'] / self.adc_f * 1e9, c='gray', linestyle='dashed', zorder=-1,
label='Peak Positions\n'+r'$\Delta$t = {} ns'.format(self.df.iloc[i]['t_diff_ns']))
plt.axvline(x=self.df.iloc[i]['p1_position'] / self.adc_f * 1e9, c='gray', linestyle='dashed', zorder=-1)
plt.axvspan(self.df.iloc[i]['p0_lower_bound'] / self.adc_f * 1e9 - 0.5,
self.df.iloc[i]['p0_upper_bound'] / self.adc_f * 1e9,
color='C1', lw=0, alpha=0.4, zorder=-2,
label='Main Pulse')
plt.axvspan(self.df.iloc[i]['p1_lower_bound'] / self.adc_f * 1e9,
self.df.iloc[i]['p1_upper_bound'] / self.adc_f * 1e9 + 0.5,
color='C3', lw=0, alpha=0.4, zorder=-2,
label='Afterpulse\n({})'.format(separability))
plt.xlim(x_dummy[0] - 0.5, x_dummy[-1] + 0.5)
plt.xlabel('Time [ns]')
plt.ylabel('Amplitude [ADC Bins]')
legend = plt.legend(loc=1, bbox_to_anchor=(0.99, 0.99))
legend.get_frame().set_linewidth(matplotlib.rcParams['axes.linewidth'])
filename = 'ap_candidate_wf_example_{}_{}'.format(i, self.save_name_suffix)
if self.save_plots:
plt.savefig(os.path.join(self.save_dir, filename + '.png'))
plt.savefig(os.path.join(self.save_dir, filename + '.pdf'))
if self.show_plots:
plt.show()
else:
plt.close()
def plot_first_n_wfs(self, n: int = 3):
"""Plot first n afterpulse candidate waveforms.
Args:
n: Number of afterpulse candidate waveforms to be plotted.
"""
for i in range(n):
self.plot_wf(i)
def plot_hist_tdiff(self):
"""Plot time differences after pulse - main pulse for both all and separable afterpulse candidates."""
x_dummy = np.arange(0, int(self.df['t_diff_ns'].max()), step=int(1e9 / self.adc_f))
n_all, bins_edges, _ = plt.hist(self.df['t_diff_ns'], bins=x_dummy - 0.5,
histtype='step', color='C0',
label='All Afterpulse Candidates')
bins_centers = (bins_edges[1:] + bins_edges[:-1]) / 2
plt.fill_between(bins_centers, n_all - np.sqrt(n_all), n_all + np.sqrt(n_all),
color='C0', alpha=0.5, zorder=-1)
n_sep, _, _ = plt.hist(self.df[self.df['separable']]['t_diff_ns'], bins=x_dummy - 0.5,
histtype='step', color='C1',
label='Separable Afterpulse Candidates')
plt.fill_between(bins_centers, n_sep - np.sqrt(n_sep), n_sep + np.sqrt(n_sep),
color='C1', alpha=0.5, zorder=-1)
plt.xlabel('Time Difference [ns]')
plt.ylabel('Entries')
plt.yscale('log')
plt.xlim(right=x_dummy[-1])
legend = plt.legend(loc=1, bbox_to_anchor=(0.99, 0.99))
legend.get_frame().set_linewidth(matplotlib.rcParams['axes.linewidth'])
filename = 'ap_tdiff_{}'.format(self.save_name_suffix)
if self.save_plots:
plt.savefig(os.path.join(self.save_dir, filename + '.png'))
plt.savefig(os.path.join(self.save_dir, filename + '.pdf'))
if self.show_plots:
plt.show()
else:
plt.close()
def plot_ap_area_vs_tdiff(self, separable_only=True, ymax=15, show_thr=True):
"""Plot afterpulse area vs time difference histogram.
Args:
separable_only: Constrain to afterpulse candidates separable from main pulse.
ymax: Upper vertical axis plot limit.
show_thr: Display amplitude and time thresholds for `ap_rate_separable_above_thr`.
"""
well_defined = True
if self.adc_area_to_e is None:
warnings.warn('Attribute adc_area_to_e set to None, will default to value 3047.6 for ADC V1730D.')
adc_area_to_e = 3047.6
well_defined = False
else:
adc_area_to_e = self.adc_area_to_e
if self.gain is None:
warnings.warn('Attribute gain set to None, will default to value 3e6.')
gain = 3e6
well_defined = False
else:
gain = self.gain
self.df['p1_area_conv'] = self.df['p1_area'] * adc_area_to_e / gain
self.df['t_diff_us'] = self.df['t_diff_ns'] / 1e3
binsx = np.arange(0, round(self.df['t_diff_us'].max(), 2), 0.01)
binsy = np.arange(0, ymax, 0.1)
if separable_only:
plt.hist2d(self.df[self.df['separable']]['t_diff_us'], self.df[self.df['separable']]['p1_area_conv'],
bins=(binsx, binsy), norm=matplotlib.colors.LogNorm())
else:
plt.hist2d(self.df['t_diff_us'], self.df['p1_area_conv'],
bins=(binsx, binsy), norm=matplotlib.colors.LogNorm())
if show_thr:
if self.ap_rate_dict is None:
warnings.warn('Cannot display thresholds as parameter ap_rate_dict is None.')
else:
if self.ap_rate_dict['area_thr_ap'] is not None:
if self.ap_rate_dict['t_thr_ap'] is not None:
plt.plot([self.ap_rate_dict['t_thr_ap']/1e3, binsx[-1]],
[self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain]*2,
color='gray')
else:
plt.axhline(y=self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain, color='gray')
if self.ap_rate_dict['t_thr_ap'] is not None:
if self.ap_rate_dict['area_thr_ap'] is not None:
plt.plot([self.ap_rate_dict['t_thr_ap']/1e3]*2,
[self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain, binsy[-1]],
color='gray')
else:
plt.axvline(x=self.ap_rate_dict['t_thr_ap']/1e3, color='gray')
plt.xlabel(r'Time Difference [$\mu$s]')
plt.xlim(binsx[0], binsx[-1])
plt.ylim(binsy[0], binsy[-1])
if well_defined:
plt.ylabel('Afterpulse Area [PE]')
else:
plt.ylabel('Afterpulse Area [A.U.]')
plt.colorbar(label="Entries")
if separable_only:
filename = 'ap_area_vs_tdiff_separable_{}'.format(self.save_name_suffix)
else:
filename = 'ap_area_vs_tdiff_{}'.format(self.save_name_suffix)
if self.save_plots:
plt.savefig(os.path.join(self.save_dir, filename + '.png'))
plt.savefig(os.path.join(self.save_dir, filename + '.pdf'))
if self.show_plots:
plt.show()
else:
plt.close()
self.df.drop(columns=['p1_area_conv', 't_diff_us'], inplace=True)
def plot_essentials(self):
"""Plot essential plots for afterpulse studies."""
self.plot_first_n_wfs(n=5)
self.plot_hist_tdiff()
self.plot_ap_area_vs_tdiff(separable_only=False)
self.plot_ap_area_vs_tdiff(separable_only=True)
Classes
class PlottingAfterpulses (df: pandas.core.frame.DataFrame, adc_f: float, ap_rate_dict: dict = None, save_plots: bool = False, show_plots: bool = True, save_dir: Optional[str] = None, save_name_suffix: Optional[str] = None, adc_area_to_e: Optional[float] = None, gain: Optional[float] = None)
-
Class for plots related to the afterpulse processing.
Attributes
df
- Pandas dataframe with after pulse candidates and their properties.
ap_rate_dict
- Dictionary with resulting afterpulse rate value and uncertainty.
adc_f
- ADC sampling frequency in samples per second as provided by same attribute of
ADCRawData
. adc_area_to_e
- Conversion factor pulse area in ADC units to charge in units of elementary charge.
Attribute of
ADCRawData
objects. gain
- PMT gain (in units of read out electrons per induced photoelectron).
Obtainable with
GainModelIndependent
class. save_plots
- Bool defining if plots are saved (as png and pdf).
show_plots
- Bool defining if plots are displayed.
save_dir
- Target directory for saving the plots.
save_name_suffix
- Measurement specific figure name suffix.
Init of the PlottingAfterpulses class.
Generation of standard plots with respect to the afterpulse processing.
Args
df
- Pandas dataframe with after pulse candidates and their properties.
adc_f
- ADC sampling frequency in samples per second as provided by same attribute of
ADCRawData
. ap_rate_dict
- Dictionary with resulting afterpulse rate value and uncertainty.
save_plots
- Bool defining if plots are saved (as png and pdf).
show_plots
- Bool defining if plots are displayed.
save_dir
- Target directory for saving the plots.
save_name_suffix
- Measurement specific figure name suffix.
adc_area_to_e
- Conversion factor pulse area in ADC units to charge in units of elementary charge.
Attribute of
ADCRawData
objects. gain
- PMT gain (in units of read out electrons per induced photoelectron).
Obtainable with
GainModelIndependent
class.
Expand source code
class PlottingAfterpulses: """ Class for plots related to the afterpulse processing. Attributes: df: Pandas dataframe with after pulse candidates and their properties. ap_rate_dict: Dictionary with resulting afterpulse rate value and uncertainty. adc_f: ADC sampling frequency in samples per second as provided by same attribute of `pmt_analysis.utils.input.ADCRawData`. adc_area_to_e: Conversion factor pulse area in ADC units to charge in units of elementary charge. Attribute of `pmt_analysis.utils.input.ADCRawData` objects. gain: PMT gain (in units of read out electrons per induced photoelectron). Obtainable with `pmt_analysis.analysis.model_independent.GainModelIndependent` class. save_plots: Bool defining if plots are saved (as png and pdf). show_plots: Bool defining if plots are displayed. save_dir: Target directory for saving the plots. save_name_suffix: Measurement specific figure name suffix. """ def __init__(self, df: pd.DataFrame, adc_f: float, ap_rate_dict: dict = None, save_plots: bool = False, show_plots: bool = True, save_dir: Optional[str] = None, save_name_suffix: Optional[str] = None, adc_area_to_e: Optional[float] = None, gain: Optional[float] = None): """Init of the PlottingAfterpulses class. Generation of standard plots with respect to the afterpulse processing. Args: df: Pandas dataframe with after pulse candidates and their properties. adc_f: ADC sampling frequency in samples per second as provided by same attribute of `pmt_analysis.utils.input.ADCRawData`. ap_rate_dict: Dictionary with resulting afterpulse rate value and uncertainty. save_plots: Bool defining if plots are saved (as png and pdf). show_plots: Bool defining if plots are displayed. save_dir: Target directory for saving the plots. save_name_suffix: Measurement specific figure name suffix. adc_area_to_e: Conversion factor pulse area in ADC units to charge in units of elementary charge. Attribute of `pmt_analysis.utils.input.ADCRawData` objects. gain: PMT gain (in units of read out electrons per induced photoelectron). Obtainable with `pmt_analysis.analysis.model_independent.GainModelIndependent` class. """ self.df = df self.ap_rate_dict = ap_rate_dict self.adc_f = adc_f self.adc_area_to_e = adc_area_to_e self.gain = gain self.save_plots = save_plots self.show_plots = show_plots self.save_dir = save_dir self.save_name_suffix = save_name_suffix if save_plots & (save_dir is None): raise NameError('save_dir must be defined if save_plots is True.') if save_plots & (save_name_suffix is None): raise NameError('save_name_suffix must be defined if save_plots is True.') def plot_wf(self, i: int = 0): """Plot i-th afterpulse candidate waveform. Args: i: Integer-location based index of waveform in `df` to be plotted. """ if (i >= self.df.shape[0]) or (i < 0): raise IndexError('Integer-location based index i must be between 0 and {}'.format(self.df.shape[0]-1)) separability = 'Separable' if self.df.iloc[i]['separable'] else 'Non-Separable' x_dummy = np.arange(len(self.df.iloc[i]['input_data_converted'])) / self.adc_f * 1e9 plt.step(x_dummy, self.df.iloc[i]['input_data_converted'], where='mid') plt.axvline(x=self.df.iloc[i]['p0_position'] / self.adc_f * 1e9, c='gray', linestyle='dashed', zorder=-1, label='Peak Positions\n'+r'$\Delta$t = {} ns'.format(self.df.iloc[i]['t_diff_ns'])) plt.axvline(x=self.df.iloc[i]['p1_position'] / self.adc_f * 1e9, c='gray', linestyle='dashed', zorder=-1) plt.axvspan(self.df.iloc[i]['p0_lower_bound'] / self.adc_f * 1e9 - 0.5, self.df.iloc[i]['p0_upper_bound'] / self.adc_f * 1e9, color='C1', lw=0, alpha=0.4, zorder=-2, label='Main Pulse') plt.axvspan(self.df.iloc[i]['p1_lower_bound'] / self.adc_f * 1e9, self.df.iloc[i]['p1_upper_bound'] / self.adc_f * 1e9 + 0.5, color='C3', lw=0, alpha=0.4, zorder=-2, label='Afterpulse\n({})'.format(separability)) plt.xlim(x_dummy[0] - 0.5, x_dummy[-1] + 0.5) plt.xlabel('Time [ns]') plt.ylabel('Amplitude [ADC Bins]') legend = plt.legend(loc=1, bbox_to_anchor=(0.99, 0.99)) legend.get_frame().set_linewidth(matplotlib.rcParams['axes.linewidth']) filename = 'ap_candidate_wf_example_{}_{}'.format(i, self.save_name_suffix) if self.save_plots: plt.savefig(os.path.join(self.save_dir, filename + '.png')) plt.savefig(os.path.join(self.save_dir, filename + '.pdf')) if self.show_plots: plt.show() else: plt.close() def plot_first_n_wfs(self, n: int = 3): """Plot first n afterpulse candidate waveforms. Args: n: Number of afterpulse candidate waveforms to be plotted. """ for i in range(n): self.plot_wf(i) def plot_hist_tdiff(self): """Plot time differences after pulse - main pulse for both all and separable afterpulse candidates.""" x_dummy = np.arange(0, int(self.df['t_diff_ns'].max()), step=int(1e9 / self.adc_f)) n_all, bins_edges, _ = plt.hist(self.df['t_diff_ns'], bins=x_dummy - 0.5, histtype='step', color='C0', label='All Afterpulse Candidates') bins_centers = (bins_edges[1:] + bins_edges[:-1]) / 2 plt.fill_between(bins_centers, n_all - np.sqrt(n_all), n_all + np.sqrt(n_all), color='C0', alpha=0.5, zorder=-1) n_sep, _, _ = plt.hist(self.df[self.df['separable']]['t_diff_ns'], bins=x_dummy - 0.5, histtype='step', color='C1', label='Separable Afterpulse Candidates') plt.fill_between(bins_centers, n_sep - np.sqrt(n_sep), n_sep + np.sqrt(n_sep), color='C1', alpha=0.5, zorder=-1) plt.xlabel('Time Difference [ns]') plt.ylabel('Entries') plt.yscale('log') plt.xlim(right=x_dummy[-1]) legend = plt.legend(loc=1, bbox_to_anchor=(0.99, 0.99)) legend.get_frame().set_linewidth(matplotlib.rcParams['axes.linewidth']) filename = 'ap_tdiff_{}'.format(self.save_name_suffix) if self.save_plots: plt.savefig(os.path.join(self.save_dir, filename + '.png')) plt.savefig(os.path.join(self.save_dir, filename + '.pdf')) if self.show_plots: plt.show() else: plt.close() def plot_ap_area_vs_tdiff(self, separable_only=True, ymax=15, show_thr=True): """Plot afterpulse area vs time difference histogram. Args: separable_only: Constrain to afterpulse candidates separable from main pulse. ymax: Upper vertical axis plot limit. show_thr: Display amplitude and time thresholds for `ap_rate_separable_above_thr`. """ well_defined = True if self.adc_area_to_e is None: warnings.warn('Attribute adc_area_to_e set to None, will default to value 3047.6 for ADC V1730D.') adc_area_to_e = 3047.6 well_defined = False else: adc_area_to_e = self.adc_area_to_e if self.gain is None: warnings.warn('Attribute gain set to None, will default to value 3e6.') gain = 3e6 well_defined = False else: gain = self.gain self.df['p1_area_conv'] = self.df['p1_area'] * adc_area_to_e / gain self.df['t_diff_us'] = self.df['t_diff_ns'] / 1e3 binsx = np.arange(0, round(self.df['t_diff_us'].max(), 2), 0.01) binsy = np.arange(0, ymax, 0.1) if separable_only: plt.hist2d(self.df[self.df['separable']]['t_diff_us'], self.df[self.df['separable']]['p1_area_conv'], bins=(binsx, binsy), norm=matplotlib.colors.LogNorm()) else: plt.hist2d(self.df['t_diff_us'], self.df['p1_area_conv'], bins=(binsx, binsy), norm=matplotlib.colors.LogNorm()) if show_thr: if self.ap_rate_dict is None: warnings.warn('Cannot display thresholds as parameter ap_rate_dict is None.') else: if self.ap_rate_dict['area_thr_ap'] is not None: if self.ap_rate_dict['t_thr_ap'] is not None: plt.plot([self.ap_rate_dict['t_thr_ap']/1e3, binsx[-1]], [self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain]*2, color='gray') else: plt.axhline(y=self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain, color='gray') if self.ap_rate_dict['t_thr_ap'] is not None: if self.ap_rate_dict['area_thr_ap'] is not None: plt.plot([self.ap_rate_dict['t_thr_ap']/1e3]*2, [self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain, binsy[-1]], color='gray') else: plt.axvline(x=self.ap_rate_dict['t_thr_ap']/1e3, color='gray') plt.xlabel(r'Time Difference [$\mu$s]') plt.xlim(binsx[0], binsx[-1]) plt.ylim(binsy[0], binsy[-1]) if well_defined: plt.ylabel('Afterpulse Area [PE]') else: plt.ylabel('Afterpulse Area [A.U.]') plt.colorbar(label="Entries") if separable_only: filename = 'ap_area_vs_tdiff_separable_{}'.format(self.save_name_suffix) else: filename = 'ap_area_vs_tdiff_{}'.format(self.save_name_suffix) if self.save_plots: plt.savefig(os.path.join(self.save_dir, filename + '.png')) plt.savefig(os.path.join(self.save_dir, filename + '.pdf')) if self.show_plots: plt.show() else: plt.close() self.df.drop(columns=['p1_area_conv', 't_diff_us'], inplace=True) def plot_essentials(self): """Plot essential plots for afterpulse studies.""" self.plot_first_n_wfs(n=5) self.plot_hist_tdiff() self.plot_ap_area_vs_tdiff(separable_only=False) self.plot_ap_area_vs_tdiff(separable_only=True)
Methods
def plot_ap_area_vs_tdiff(self, separable_only=True, ymax=15, show_thr=True)
-
Plot afterpulse area vs time difference histogram.
Args
separable_only
- Constrain to afterpulse candidates separable from main pulse.
ymax
- Upper vertical axis plot limit.
show_thr
- Display amplitude and time thresholds for
ap_rate_separable_above_thr
.
Expand source code
def plot_ap_area_vs_tdiff(self, separable_only=True, ymax=15, show_thr=True): """Plot afterpulse area vs time difference histogram. Args: separable_only: Constrain to afterpulse candidates separable from main pulse. ymax: Upper vertical axis plot limit. show_thr: Display amplitude and time thresholds for `ap_rate_separable_above_thr`. """ well_defined = True if self.adc_area_to_e is None: warnings.warn('Attribute adc_area_to_e set to None, will default to value 3047.6 for ADC V1730D.') adc_area_to_e = 3047.6 well_defined = False else: adc_area_to_e = self.adc_area_to_e if self.gain is None: warnings.warn('Attribute gain set to None, will default to value 3e6.') gain = 3e6 well_defined = False else: gain = self.gain self.df['p1_area_conv'] = self.df['p1_area'] * adc_area_to_e / gain self.df['t_diff_us'] = self.df['t_diff_ns'] / 1e3 binsx = np.arange(0, round(self.df['t_diff_us'].max(), 2), 0.01) binsy = np.arange(0, ymax, 0.1) if separable_only: plt.hist2d(self.df[self.df['separable']]['t_diff_us'], self.df[self.df['separable']]['p1_area_conv'], bins=(binsx, binsy), norm=matplotlib.colors.LogNorm()) else: plt.hist2d(self.df['t_diff_us'], self.df['p1_area_conv'], bins=(binsx, binsy), norm=matplotlib.colors.LogNorm()) if show_thr: if self.ap_rate_dict is None: warnings.warn('Cannot display thresholds as parameter ap_rate_dict is None.') else: if self.ap_rate_dict['area_thr_ap'] is not None: if self.ap_rate_dict['t_thr_ap'] is not None: plt.plot([self.ap_rate_dict['t_thr_ap']/1e3, binsx[-1]], [self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain]*2, color='gray') else: plt.axhline(y=self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain, color='gray') if self.ap_rate_dict['t_thr_ap'] is not None: if self.ap_rate_dict['area_thr_ap'] is not None: plt.plot([self.ap_rate_dict['t_thr_ap']/1e3]*2, [self.ap_rate_dict['area_thr_ap']*adc_area_to_e/gain, binsy[-1]], color='gray') else: plt.axvline(x=self.ap_rate_dict['t_thr_ap']/1e3, color='gray') plt.xlabel(r'Time Difference [$\mu$s]') plt.xlim(binsx[0], binsx[-1]) plt.ylim(binsy[0], binsy[-1]) if well_defined: plt.ylabel('Afterpulse Area [PE]') else: plt.ylabel('Afterpulse Area [A.U.]') plt.colorbar(label="Entries") if separable_only: filename = 'ap_area_vs_tdiff_separable_{}'.format(self.save_name_suffix) else: filename = 'ap_area_vs_tdiff_{}'.format(self.save_name_suffix) if self.save_plots: plt.savefig(os.path.join(self.save_dir, filename + '.png')) plt.savefig(os.path.join(self.save_dir, filename + '.pdf')) if self.show_plots: plt.show() else: plt.close() self.df.drop(columns=['p1_area_conv', 't_diff_us'], inplace=True)
def plot_essentials(self)
-
Plot essential plots for afterpulse studies.
Expand source code
def plot_essentials(self): """Plot essential plots for afterpulse studies.""" self.plot_first_n_wfs(n=5) self.plot_hist_tdiff() self.plot_ap_area_vs_tdiff(separable_only=False) self.plot_ap_area_vs_tdiff(separable_only=True)
def plot_first_n_wfs(self, n: int = 3)
-
Plot first n afterpulse candidate waveforms.
Args
n
- Number of afterpulse candidate waveforms to be plotted.
Expand source code
def plot_first_n_wfs(self, n: int = 3): """Plot first n afterpulse candidate waveforms. Args: n: Number of afterpulse candidate waveforms to be plotted. """ for i in range(n): self.plot_wf(i)
def plot_hist_tdiff(self)
-
Plot time differences after pulse - main pulse for both all and separable afterpulse candidates.
Expand source code
def plot_hist_tdiff(self): """Plot time differences after pulse - main pulse for both all and separable afterpulse candidates.""" x_dummy = np.arange(0, int(self.df['t_diff_ns'].max()), step=int(1e9 / self.adc_f)) n_all, bins_edges, _ = plt.hist(self.df['t_diff_ns'], bins=x_dummy - 0.5, histtype='step', color='C0', label='All Afterpulse Candidates') bins_centers = (bins_edges[1:] + bins_edges[:-1]) / 2 plt.fill_between(bins_centers, n_all - np.sqrt(n_all), n_all + np.sqrt(n_all), color='C0', alpha=0.5, zorder=-1) n_sep, _, _ = plt.hist(self.df[self.df['separable']]['t_diff_ns'], bins=x_dummy - 0.5, histtype='step', color='C1', label='Separable Afterpulse Candidates') plt.fill_between(bins_centers, n_sep - np.sqrt(n_sep), n_sep + np.sqrt(n_sep), color='C1', alpha=0.5, zorder=-1) plt.xlabel('Time Difference [ns]') plt.ylabel('Entries') plt.yscale('log') plt.xlim(right=x_dummy[-1]) legend = plt.legend(loc=1, bbox_to_anchor=(0.99, 0.99)) legend.get_frame().set_linewidth(matplotlib.rcParams['axes.linewidth']) filename = 'ap_tdiff_{}'.format(self.save_name_suffix) if self.save_plots: plt.savefig(os.path.join(self.save_dir, filename + '.png')) plt.savefig(os.path.join(self.save_dir, filename + '.pdf')) if self.show_plots: plt.show() else: plt.close()
def plot_wf(self, i: int = 0)
-
Plot i-th afterpulse candidate waveform.
Args
i
- Integer-location based index of waveform in
df
to be plotted.
Expand source code
def plot_wf(self, i: int = 0): """Plot i-th afterpulse candidate waveform. Args: i: Integer-location based index of waveform in `df` to be plotted. """ if (i >= self.df.shape[0]) or (i < 0): raise IndexError('Integer-location based index i must be between 0 and {}'.format(self.df.shape[0]-1)) separability = 'Separable' if self.df.iloc[i]['separable'] else 'Non-Separable' x_dummy = np.arange(len(self.df.iloc[i]['input_data_converted'])) / self.adc_f * 1e9 plt.step(x_dummy, self.df.iloc[i]['input_data_converted'], where='mid') plt.axvline(x=self.df.iloc[i]['p0_position'] / self.adc_f * 1e9, c='gray', linestyle='dashed', zorder=-1, label='Peak Positions\n'+r'$\Delta$t = {} ns'.format(self.df.iloc[i]['t_diff_ns'])) plt.axvline(x=self.df.iloc[i]['p1_position'] / self.adc_f * 1e9, c='gray', linestyle='dashed', zorder=-1) plt.axvspan(self.df.iloc[i]['p0_lower_bound'] / self.adc_f * 1e9 - 0.5, self.df.iloc[i]['p0_upper_bound'] / self.adc_f * 1e9, color='C1', lw=0, alpha=0.4, zorder=-2, label='Main Pulse') plt.axvspan(self.df.iloc[i]['p1_lower_bound'] / self.adc_f * 1e9, self.df.iloc[i]['p1_upper_bound'] / self.adc_f * 1e9 + 0.5, color='C3', lw=0, alpha=0.4, zorder=-2, label='Afterpulse\n({})'.format(separability)) plt.xlim(x_dummy[0] - 0.5, x_dummy[-1] + 0.5) plt.xlabel('Time [ns]') plt.ylabel('Amplitude [ADC Bins]') legend = plt.legend(loc=1, bbox_to_anchor=(0.99, 0.99)) legend.get_frame().set_linewidth(matplotlib.rcParams['axes.linewidth']) filename = 'ap_candidate_wf_example_{}_{}'.format(i, self.save_name_suffix) if self.save_plots: plt.savefig(os.path.join(self.save_dir, filename + '.png')) plt.savefig(os.path.join(self.save_dir, filename + '.pdf')) if self.show_plots: plt.show() else: plt.close()