def get_tc_infos(self, tc): rs = RunSelector(tc) return [ SelectionInfo( rs.select_runs_from_runplan(rp, dut + 1, unselect=True)) for rp in sorted(self.RunPlans[tc]) for dut in range(rs.get_n_duts(run_plan=rp)) ]
def __init__(self, multi, first_run=None, end_run=None, test_campaign=None, type_=None, verbose=False): self.Multi = multi self.Type = type_ self.Selection = RunSelector(testcampaign=test_campaign, verbose=verbose) self.Run = self.Selection.Run self.StartAtRun = choose(first_run, self.get_all_runs()[0] if self.Multi else self.find_last_converted()) self.StopAtRun = 1e9 if not multi or end_run is None else int(end_run) self.Runs = self.load_runs() self.Selection.select_runs(*[run.Number for run in self.Runs])
def __init__(self, selection_name=None, verbose=False): self.Name = selection_name Analysis.__init__( self, verbose=verbose, results_dir='selections', sub_dir=selection_name if selection_name is not None else '') self.print_start(run=selection_name, tc=False, prnt=verbose) # Main self.Selections = self.load_selections() self.Selection = self.load_selection(selection_name) # Config self.DUTParser = load_parser( join(self.Dir, 'config', 'DiamondAliases.ini')) # Info self.RS = RunSelector() # dummy for information self.RunPlans = self.load_runplans() self.TestCampaigns = self.load_test_campaigns() self.Info = self.load_selection_info() self.DUTName = self.load_dut_name() self.NPlans = len(self.Info) if self.Info else None self.Ana = self.load_dummy() if self.Info: self.show_selection() self.print_finished(prnt=verbose)
def load_selection_info(self): selections = [] if self.Selection is None: return for tc, rps in self.Selection.items(): for rp, dut_nrs in rps.items(): for dut_nr in array([dut_nrs]).flatten(): selections.append( SelectionInfo( RunSelector(tc).select_runs_from_runplan( rp, dut_nr, unselect=True))) return selections
def __init__(self, analysis=None, test_campaign=None, dut=None, begin=None, end=None, averaging=None, verbose=None): Analysis.__init__(self, test_campaign if analysis is None else analysis.TCString, verbose=verbose, sub_dir='currents') # Settings self.Averaging = averaging self.TimeZone = timezone('Europe/Zurich') self.DataDir = join(self.TCDir, 'hv') # Config self.Ana = analysis self.IsCollection = hasattr(analysis, 'Runs') self.Type = self.Ana.Type if analysis is not None and self.IsCollection else 'None' self.RunSelection = RunSelector(testcampaign=self.TCString) self.RunLogs = self.RunSelection.RunInfos self.Run = self.RunSelection.Run if analysis is None else self.Ana.FirstAnalysis.Run if self.IsCollection else self.Ana.Run if self.IsCollection: self.Runs = self.Ana.Runs # required for plotting self.RunPlan = self.load_run_plan() # required for plotting self.HVConfig = self.load_parser() self.Bias = self.Ana.Bias if hasattr(self.Ana, 'Bias') else None self.Draw.ServerDir = analysis.Draw.ServerDir if analysis is not None else None # Times self.Begin, self.End = self.load_times(begin, end, dut) # DUT self.DUT = self.init_dut(dut) # HV Device Info self.Number = self.load_device_number() self.Channel = self.load_device_channel() self.Name = self.HVConfig.get('HV{}'.format(self.Number), 'name') self.Brand = remove_digits(self.Name.split('-')[0]) self.Model = self.HVConfig.get('HV{}'.format(self.Number), 'model') self.Precision = .005 if '237' in self.Name else .05 # data self.IgnoreJumps = True self.Data = self.load_data()
def __init__(self, sel: RunSelector): self.RunInfo = sel.RunInfos self.Run = sel.Run self.TCString = sel.TCString self.RunPlan = sel.SelectedRunplan self.DUT = sel.SelectedDUT self.DUTName = self.DUT.Name self.DUTNr = self.DUT.Number self.IsPixel = 'pix' in (self.DUT.Type if type(self.DUT.Type) is str else self.DUT.Type[self.TCString]) self.Verbose = sel.Run.Verbose self.Bias = self.DUT.Bias self.Irradiation = self.DUT.get_irradiation(self.TCString) self.Type = sel.SelectedType.lower() self.Runs = sel.get_selected_runs() self.PulserType = sel.PulserType self.DUT = PixelDUT(self.DUT.Number, self.RunInfo[self.Runs[0]])
def get_rp_diamonds(tc, rp): sel = RunSelector(tc).select_runs_from_runplan(rp, unselect=True) return sel.get_dut_names()
class Currents(Analysis): """reads in information from the keithley log file""" VCol = 602 # 807 CCol = 899 # 418 MS = .3 def __init__(self, analysis=None, test_campaign=None, dut=None, begin=None, end=None, averaging=None, verbose=None): Analysis.__init__(self, test_campaign if analysis is None else analysis.TCString, verbose=verbose, sub_dir='currents') # Settings self.Averaging = averaging self.TimeZone = timezone('Europe/Zurich') self.DataDir = join(self.TCDir, 'hv') # Config self.Ana = analysis self.IsCollection = hasattr(analysis, 'Runs') self.Type = self.Ana.Type if analysis is not None and self.IsCollection else 'None' self.RunSelection = RunSelector(testcampaign=self.TCString) self.RunLogs = self.RunSelection.RunInfos self.Run = self.RunSelection.Run if analysis is None else self.Ana.FirstAnalysis.Run if self.IsCollection else self.Ana.Run if self.IsCollection: self.Runs = self.Ana.Runs # required for plotting self.RunPlan = self.load_run_plan() # required for plotting self.HVConfig = self.load_parser() self.Bias = self.Ana.Bias if hasattr(self.Ana, 'Bias') else None self.Draw.ServerDir = analysis.Draw.ServerDir if analysis is not None else None # Times self.Begin, self.End = self.load_times(begin, end, dut) # DUT self.DUT = self.init_dut(dut) # HV Device Info self.Number = self.load_device_number() self.Channel = self.load_device_channel() self.Name = self.HVConfig.get('HV{}'.format(self.Number), 'name') self.Brand = remove_digits(self.Name.split('-')[0]) self.Model = self.HVConfig.get('HV{}'.format(self.Number), 'model') self.Precision = .005 if '237' in self.Name else .05 # data self.IgnoreJumps = True self.Data = self.load_data() # ---------------------------------------- # region INIT def load_data(self): data_file = join(self.DataDir, 'data.hdf5') if not file_exists(data_file): self.convert_data() data = h5py.File(data_file, 'r')[f'{self.Name}_CH{self.Channel}'] data = data[(data['timestamps'] >= time_stamp(self.Begin)) & (data['timestamps'] <= time_stamp(self.End))] if not data.size: return if self.IgnoreJumps: # filter out jumps data = data[where(abs(data['currents']) < 1e20)] # take out very large unphysical currents data = data[where(abs(data['currents'][:-1]) * 100 > abs(data['currents'][1:]))[0] + 1] # take out the events that are 100 larger than the previous if not data.size: return data['currents'] *= 1e9 * sign(mean(data['currents'])) # convert to nA and flip sign if current is negative if self.Ana is not None: data['timestamps'] -= uint32(data['timestamps'][0] - time_stamp(self.load_ana_start_time())) # synchronise time vectors return data def reload_data(self, ignore_jumps): if ignore_jumps != self.IgnoreJumps: self.IgnoreJumps = ignore_jumps self.Data = self.load_data() def load_run_plan(self): return self.RunSelection.SelectedRunplan if self.Ana is None else self.Ana.RunPlan if self.IsCollection else None def load_parser(self): file_path = join(self.DataDir, 'config.ini') if not file_exists(file_path): critical('HV info file "{f}" does not exist'.format(f=file_path)) return Config(file_path) def load_times(self, begin, end, dut=1): if self.Ana is None: if str(begin).isdigit(): # run number or run plan is provided self.RunSelection.select_runs_in_range(begin, end if end is not None else begin, dut) if end or end is None else self.RunSelection.select_runs_from_runplan(begin) return self.RunSelection.get_start_time(), self.RunSelection.get_end_time() else: # actual time strings are provided return (self.TimeZone.localize(datetime.strptime('{}-{}'.format(self.TestCampaign.year, t), '%Y-%m/%d-%H:%M:%S')) for t in [begin, end]) return self.load_ana_start_time(), self.load_ana_end_time() def load_time(self, t, t_log): t = self.TimeZone.localize(datetime.fromtimestamp(t)) if t is not None else t_log return t_log if t.year < 2000 or t.day != t_log.day else t def load_ana_start_time(self): ana = self.Ana if not self.IsCollection else self.Ana.FirstAnalysis return self.load_time(ana.Run.StartTime if hasattr(ana.Run, 'StartTime') else None, ana.Run.LogStart) def load_ana_end_time(self): ana = self.Ana if not self.IsCollection else self.Ana.LastAnalysis return self.load_time(ana.Run.EndTime if hasattr(ana.Run, 'EndTime') else None, ana.Run.LogEnd) def init_dut(self, number): if self.Ana is not None: return self.Ana.DUT elif self.RunSelection.has_selected_runs: return self.RunSelection.SelectedDUT from dut import DUT return DUT(number, next(log for log in self.RunLogs.values() if conv_log_time(log['starttime0']) > self.Begin)) def load_device_str(self): if self.Ana is not None: run_info = self.Ana.FirstAnalysis.Run.Info if self.IsCollection else self.Ana.Run.Info elif self.RunSelection.has_selected_runs: run_info = self.RunLogs[self.RunSelection.get_selected_runs()[0]] else: run_info = next(log for log in self.RunLogs.values() if conv_log_time(log['starttime0']) > self.Begin) return str(run_info['dia{}supply'.format(self.DUT.Number)]) def load_device_number(self): return self.load_device_str().split('-')[0] def load_device_channel(self): words = self.load_device_str().split('-') return words[1] if len(words) > 1 else '0' # endregion INIT # ---------------------------------------- # ---------------------------------------- # region GET def get_runs(self): return self.Ana.get_runs() def get_fluxes(self, pbar=False): return self.Ana.get_fluxes(pbar=pbar) def get_analyses(self): return self.Ana.get_analyses() # endregion GET # ---------------------------------------- # ---------------------------------------- # region DATA ACQUISITION def get_log_date(self, name): log_date = ''.join(basename(name).split('_')[-6:]) return self.TimeZone.localize(datetime.strptime(log_date, '%Y%m%d%H%M%S.log')) def convert_data(self): info('converting hv text files to hdf5 ...') self.PBar.start(len(glob(join(self.DataDir, '*', '*.log')))) f = h5py.File(join(self.DataDir, 'data.hdf5'), 'w') for d in glob(join(self.DataDir, '*_*')): arrays = [] for file_name in sorted(glob(join(d, '*.log'))): if getsize(file_name) == 0: remove_file(file_name) self.PBar.update() continue log_date = self.get_log_date(file_name) data = genfromtxt(file_name, usecols=arange(3), dtype=[('timestamps', 'U10'), ('voltages', 'f2'), ('currents', 'f4')], invalid_raise=False) data = data[invert(isnan(data['voltages']))] # remove text entries data['timestamps'] = array(log_date.strftime('%Y-%m-%d ') + char.array(data['timestamps'])).astype(datetime64) - datetime64('1970-01-01T00:00:00') - log_date.utcoffset().seconds data = data.astype([('timestamps', 'u4'), ('voltages', 'f2'), ('currents', 'f4')]) arrays.append(data) self.PBar.update() if len(arrays): f.create_dataset(basename(d), data=concatenate(arrays)) # endregion DATA ACQUISITION # ---------------------------------------- # ---------------------------------------- # region GET def get(self): if self.Ana is not None and not self.Ana.DUT.Bias: warning('Bias of run {} is 0!'.format(self.Run.Number)) return ufloat(0, 0) elif self.Data is None: warning('no current data!') return ufloat(0, 0) else: h = self.draw_distribution(show=False) if h.GetEntries() < 3: return None m, s = mean_sigma(*get_hist_vecs(h, err=False), err=False) fit = h.Fit('gaus', 'sq0', '', m - s, m + s) fm, fs = fit.Parameter(1), fit.Parameter(2) if .8 * m < fit.Parameter(1) < 1.2 * m and s > 0 and fs < fm and fit.ParError(1) < m: # only use gauss fit if its not deviating too much from the the mean current = ufloat(fm, fit.ParError(1) + self.Precision / 2) else: current = ufloat(h.GetMean(), h.GetMeanError() + self.Precision / 2) return current def get_title(self): bias_str = 'at {b} V'.format(b=self.Bias) if self.Bias else '' run_str = '{n}'.format(n=self.Run.Number) if not self.IsCollection else 'Plan {rp}'.format(rp=self.Ana.RunPlan) return 'Currents of {dia} {b} - Run {r} - {n}'.format(dia=self.DUT.Name, b=bias_str, r=run_str, n=self.Name) def get_first_log(self): names = sorted(glob(join(self.DataDir, '{}_CH{}'.format(self.Name, self.Channel), '*.log'))) for i, name in enumerate(names): if self.get_log_date(name) > self.Begin: return names[i - 1] def get_run_number(self): return None if self.Ana is None else 'RP{}'.format(self.Ana.RunPlan) if self.IsCollection else self.Ana.Run.Number def get_t0(self): return self.Data['timestamps'][0] # endregion GET # ---------------------------------------- # ---------------------------------------- # region PLOTTING def draw(self, rel_time=False, ignore_jumps=True, v_range=None, c_range=None, averaging=1, draw_opt='al', with_flux=False, f_range=None, cunit='nA', **dkw): self.reload_data(ignore_jumps) if self.Data is None: return warning('no current data!') t, c, v = (average_list(self.Data[n], averaging) for n in ['timestamps', 'currents', 'voltages']) gv = self.Draw.graph(t, v, title=self.get_title(), y_tit='Voltage [nA]', yax_col=self.VCol, color=self.VCol, y_range=choose(v_range, [-1100, 1100]), l_off_x=10, x_ticks=0, show=False, lw=2) if with_flux: gv = self.Ana.draw_flux(rel_time=rel_time, show=False) format_histo(gv, title=self.get_title(), fill_color=4000, fill_style=4000, lw=3, y_range=choose(f_range, [5, 20000]), y_tit='Flux [kHz/cm^{2}]') c = array(c) / {'nA': 1, '#muA': 1000}[cunit] gc = Draw.make_tgrapherrors(t, c) format_histo(gc, x_tit='Time [hh:mm]', y_tit=f'Current [{cunit}]', yax_col=self.CCol, color=self.CCol, y_range=choose(c_range, [round_down_to(min(c)), round_up_to(max(c))])) x_range = [gv.GetBinLowEdge(1), gv.GetBinLowEdge(gv.GetNbinsX() + 1)] if with_flux else [t[0], t[-1]] for g in [gc, gv]: format_histo(g, lab_size=.05, x_off=1.05, tit_size=.06, t_ax_off=t[0] if rel_time else 0, y_off=.8, center_y=True, x_range=x_range, markersize=self.MS, stats=0) m = [.09, .09, .2, .02] self.Draw(gv, **prep_kw(dkw, **Draw.mode(2), m=m, draw_opt='{}y+'.format('hist' if with_flux else 'al'), logy=with_flux)) Draw.tpad('pc', transparent=True, margins=m) gc.Draw(draw_opt) self.Draw.save_plots(**prep_kw(dkw, savename=f'{self.get_run_number()}_{self.DUT}{"Flux" if with_flux else ""}', ftype='png')) return gc def draw_profile(self, bw=5, show=True): x, y = self.Data['timestamps'], self.Data['currents'] return self.Draw.profile(x, y, make_bins(x[0], x[-1], bw), 'Leakage Current', x_tit='Time [hh:mm]', y_tit='Current [nA]', t_ax_off=0, markersize=.7, w=1.5, h=.75, lm=.08, y_off=.8, show=show, stats=set_entries()) def draw_distribution(self, show=True): m, s = mean_sigma(self.Data['currents'], err=False) xmin, xmax = m - 4 * max(s, .1), m + 4 * max(s, .1) return self.Draw.distribution(self.Data['currents'], make_bins(xmin, xmax, self.Precision * 2), 'Current Distribution', show=show, x_tit='Current [nA]') def draw_flux_correlation(self, bin_fac=1, show=True): p1 = self.draw_profile(show=False) p2 = self.Ana.Tel.draw_flux(show=False) ybins = log_bins(int(sqrt(p1.GetNbinsX()) * bin_fac) * 3, .1, p1.GetMaximum() * 2) xbins = log_bins(int(sqrt(p1.GetNbinsX()) * bin_fac), .1, p2.GetMaximum() * 2) h = TH2F('gfcc', 'Correlation of Flux and Current', *(xbins + ybins)) for i in range(p1.GetNbinsX()): if p1.GetBinContent(i) and p2.GetBinContent(i): h.Fill(p2.GetBinContent(i), p1.GetBinContent(i)) format_histo(h, y_tit='Current [nA]', x_tit='Flux [kHz/cm^{2}', stats=0, y_off=1.2) self.Draw(h, 'FluxCurrent', draw_opt='colz', rm=.18, show=show, lm=.12, logy=True, logx=True) def draw_iv(self, show=True): x, y = self.Data['voltages'], self.Data['currents'] return self.Draw.graph(x, y, 0, self.Precision, title='I-V Curve for {}'.format(self.DUT.Name), x_tit='Voltage [V]', y_tit='Current [nA]', show=show) @staticmethod def curr_args(y): return {'y_tit': 'Current [nA]', 'yax_col': Currents.CCol, 'color': Currents.CCol, 'y_range': ax_range(y, rnd=True), 'l_off_x': 1} def draw_ph(self, ph_range=None, **kwargs): x, y = self.Data['timestamps'], self.Data['currents'] xargs = {'x_range': ax_range(x, fl=.05, fh=.05), 't_ax_off': 0, 'center_x': True} self.Draw.graph(Draw.make_graph_from_profile(self.Ana.draw_pulse_height(rel_t=False, show=False)[0]), **Draw.mode(2, rm=.08), **xargs, y_tit='Pulse Height [au]', y_range=ph_range) g = self.Draw.graph(x, y, 'g', Draw.tpad('pc', transparent=True), **self.curr_args(y), **kwargs, **Draw.mode(2, rm=.08), draw_opt='aly+') format_histo(g, **xargs, yax_col=Currents.CCol)
class AutoConvert: def __init__(self, multi, first_run=None, end_run=None, test_campaign=None, type_=None, verbose=False): self.Multi = multi self.Type = type_ self.Selection = RunSelector(testcampaign=test_campaign, verbose=verbose) self.Run = self.Selection.Run self.StartAtRun = choose(first_run, self.get_all_runs()[0] if self.Multi else self.find_last_converted()) self.StopAtRun = 1e9 if not multi or end_run is None else int(end_run) self.Runs = self.load_runs() self.Selection.select_runs(*[run.Number for run in self.Runs]) def find_last_converted(self): rdir = 'pads' if self.Type == 'pad' else 'pixel' if self.Type == 'pixel' else '*' converted = [int(remove_letters(basename(name))) for name in glob(join(self.Selection.Run.TCDir, 'root', rdir, 'TrackedRun*.root'))] return max(converted) if converted else self.get_all_runs()[0] def get_all_runs(self): all_runs = self.Selection.get_runplan_runs() return [r for r in all_runs if self.Selection.get_type(r) == self.Type] if self.Type is not None else all_runs def load_run(self, nr): return Run(nr, self.Selection.TCString, load_tree=False, verbose=self.Selection.Run.Verbose) def load_runs(self): runs = array([r for r in self.get_all_runs() if not file_exists(self.Selection.get_final_file_path(r))], 'i2') return [self.load_run(run) for run in runs[(runs >= self.StartAtRun) & (runs <= self.StopAtRun)]] def load_logged_runs(self): runs = self.Selection.load_runs() return runs[runs >= self.StartAtRun] def get_next_run(self): last = self.find_last_converted() runs = self.load_logged_runs() return None if not runs.size or last == runs[-1] else runs[0] if last is None else next(run for run in runs if run > last) def print_progress(self): pbar = PBar(len(self.load_runs()), counter=True, t='h') while not pbar.is_finished(): sleep(5) pbar.update(pbar.N - len(self.load_runs()) - 1) def auto_convert(self): """Sequential conversion with check if the file is currently written. For usage during beam tests.""" self.Runs = self.load_logged_runs() if self.Runs.size > 1: info(f'Converting runs {self.Runs[0]} - {self.Runs[-1]}') # self.multi() while max(self.load_logged_runs()) <= self.StopAtRun: run = self.get_next_run() t0 = time() while run is None: info(f'waiting for new run {self.find_last_converted() + 1} since {get_running_time(t0)}', endl=False) sleep(5) run = self.get_next_run() raw_file = self.Run.Converter.get_raw_file_path(run) if not file_exists(raw_file, warn=True): continue t0 = time() while file_is_beeing_written(raw_file): info(f'waiting until run {run} is finished since {get_running_time(t0)}', endl=False) sleep(5) r = Run(run, self.Run.TCString) info(f'{run} --> {timedelta(seconds=round(time() - r.InitTime))}') def multi(self): """parallel conversion""" info(f'Creating pool with {cpu_count()} processes') if not all([file_exists(run.Converter.RawFilePath) for run in self.Runs]): self.Selection.copy_raw_files(sel=True) with Pool() as pool: result = pool.starmap_async(make_run, [(run.Number, self.Selection.TCString) for run in self.Runs]) print() runs = result.get() print_small_banner('Summary:') for run in runs: delta = timedelta(seconds=round(run.TInit)) speed = 'NOT CONVERTED!' if run.RootFile is None else f'{run.NEvents}, {run.NEvents / delta.total_seconds():1.0f} Events/s' print(f'{run} --> {delta} ({speed})') def run(self): if not len(self.Runs): return info('There are no runs to convert :-)') self.multi() if self.Multi else self.auto_convert()
def get_high_rate_run(self, high=True): from src.run_selection import RunSelector return int(RunSelector(testcampaign=self.TCString).get_high_rate_run(self.Number, high))