# -*- coding: utf-8 -*- """ ReducePyKLPlot fills KL histograms for digits and slab hits, draws them, refreshes the canvases and prints to eps at the end of run. """ # This file is part of MAUS: http://micewww.pp.rl.ac.uk:8080/projects/maus # # MAUS is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # MAUS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with MAUS. If not, see . # # Turn off false positives related to ROOT #pylint: disable = E1101 # Disable messages about too many branches and too many lines. #pylint: disable = R0912 #pylint: disable = R0915 import ROOT from ReducePyROOTHistogram import ReducePyROOTHistogram class ReducePyKLPlot(ReducePyROOTHistogram): # pylint: disable=R0902 """ ReducePyKLPlots plots several KL histograms. Currently the following histograms are filled: - KL charge in ADC units for every digit. - KL charge product for every cell. - Beam profile in Y direction. - 2-dim map of kl digits. Histograms are drawn on different canvases. The canvases are refreshed every N spills where N = refresh_rate set via refresh_rate data card value (see below). The default is to run ROOT in batch mode To run in interactive mode, set root_batch_mode = 0 in the data card (see below). At the end of the run, the canvases are printed to eps files A sequence of 1 histogram is output as JSON documents of form: @verbatim {"image": {"keywords": [...list of image keywords...], "description":"...a description of the image...", "tag": TAG, "image_type": "eps", "data": "...base 64 encoded image..."}} @endverbatim If "histogram_auto_number" (see below) is "true" then the TAG will have a number N appended where N means that the histogram was produced as a consequence of the (N + 1)th spill processed by the worker. The number will be zero-padded to form a six digit string e.g. "00000N". If "histogram_auto_number" is false then no such number is appended In cases where a spill is input that contains errors (e.g. is badly formatted or is missing the data needed to update a histogram) then a spill is output which is just the input spill with an "errors" field containing the error e.g. @verbatim {"errors": {..., "bad_json_document": "unable to do json.loads on input"}} {"errors": {..., "no_space_points": "no space points"}} @endverbatim The caller can configure the worker and specify: -Image type ("histogram_image_type"). Must be one of those supported by ROOT (currently "ps", "eps", "gif", "jpg", "jpeg", "pdf", "svg", "png"). Default "eps". -Auto-number ("histogram_auto_number"). Default: false. Flag that determines if the image tag (see above) has the spill count appended to it or not. -ROOT batch mode ("root_batch_mode"). Default: 0 (false). Flag indicating whether ROOT should be run in batch or interactive mode. -Refresh rate ("refresh_rate"). Default: 5. Number of spills input before the next set of histograms are output. """ def __init__(self): """ Set initial attribute values. @param self Object reference. """ ReducePyROOTHistogram.__init__(self) # Do initialisation specific to this class. # Refresh_rate determines how often (in spill counts) the # canvases are updated. self.refresh_rate = 5 # Histogram initializations. they are defined explicitly in # init_histos. self.hadc = None self.hadc_product = None self.hprofile = None self.digitkl = None self.canvas_kl = None # Has an end_of_run been processed? self.run_ended = False self.cnv = None self.style = None def _configure_at_birth(self, config_doc): """ Configure worker from data cards. Overrides super-class method. @param self Object reference. @param config_doc JSON document. @returns True if configuration succeeded. """ # Read in configuration flags and parameters - these will # overwrite whatever defaults were set in __init__. if 'refresh_rate' in config_doc: self.refresh_rate = int(config_doc["refresh_rate"]) # Initialize histograms, setup root canvases, and set root # styles. self.__init_histos() self.run_ended = False return True def _update_histograms(self, spill): """Update the Histograms """ daq_evtype = spill.GetSpill().GetDaqEventType() if daq_evtype == "end_of_run": if (not self.run_ended): self.update_histos() self.run_ended = True return self.get_histogram_images() else: return [] # do not try to get data from start/end spill markers data_spill = True if daq_evtype == "start_of_run" \ or daq_evtype == "start_of_burst" \ or daq_evtype == "end_of_burst": data_spill = False # Get KL digits & fill the relevant histograms. if data_spill and not self.get_digits(spill): raise ValueError("kl digits not in spill") # Get KL cell hits & fill the relevant histograms. if data_spill and not self.get_cell_hits(spill): raise ValueError("cell hits not in spill") # Refresh canvases at requested frequency. #print self.refresh_rate if self.spill_count % self.refresh_rate == 0: self.update_histos() return self.get_histogram_images() else: return [] def get_cell_hits(self, data): """ Get the KL cell hits and update the histograms. @param self Object reference. @param spill Current spill. @return True if no errors or False if no "cell_hits" in the spill. """ if data.GetSpill().GetReconEventSize() == 0: raise ValueError("recon_events not in spill") reconevents = data.GetSpill().GetReconEvents() for evn in range(data.GetSpill().GetReconEventSize()): kl_event = reconevents[evn].GetKLEvent() if kl_event is None: raise ValueError("kl_event not in recon_events") cellhit = kl_event.GetKLEventCellHit() if cellhit is None: return False kl_cellhits = cellhit.GetKLCellHitArray() for i in range(kl_cellhits.size()): if kl_cellhits[i] is None: continue prod = kl_cellhits[i].GetChargeProduct() self.hadc_product.Fill(prod) return True def get_digits(self, data): """ Get the KL digits and update the histograms. @param self Object reference. @param spill Current spill. @return True if no errors or False if no "kl_digits" in the spill. """ if data.GetSpill().GetReconEventSize() == 0: raise ValueError("recon_events not in spill") reconevents = data.GetSpill().GetReconEvents() for evn in range(data.GetSpill().GetReconEventSize()): kl_event = reconevents[evn].GetKLEvent() if kl_event is None: raise ValueError("kl_event not in recon_events") kl_digit = kl_event.GetKLEventDigit() if kl_digit is None: return False kl_digits = kl_digit.GetKLDigitArray() for i in range(kl_digits.size()): if kl_digits[i] is None: continue charge = kl_digits[i].GetChargeMm() cell = kl_digits[i].GetCell() side = kl_digits[i].GetPmt() self.hadc.Fill(charge) self.hprofile.Fill(cell) self.digitkl.Fill(cell, side) return True def __init_histos(self): #pylint: disable=R0201, R0914 """ Initialise ROOT to display histograms. @param self Object reference. """ # white canvas self.cnv = ROOT.gROOT.SetStyle("Plain") #turn off stat box self.style = ROOT.gStyle.SetOptStat(0) #sensible color palette self.style = ROOT.gStyle.SetPalette(1) # xy grid on canvas self.style = ROOT.gStyle.SetPadGridX(1) self.style = ROOT.gStyle.SetPadGridY(1) # define histograms self.hadc = ROOT.TH1F("h1", "ADC", 100, 0, 5000) self.hadc.GetXaxis().SetTitle("ADC") self.hadc_product = ROOT.TH1F("h2", "ADC Product", 100, 0, 5000) self.hadc_product.GetXaxis().SetTitle("ADC product") self.hprofile = ROOT.TH1F("h3", "Beam Y-profile", 21, -0.5, 20.5) self.hprofile.GetXaxis().SetTitle("Cell") self.digitkl = ROOT.TH2F("h4", "Digits", 21, -0.5, 20.5, 2, -0.5, 1.5) self.digitkl.GetXaxis().SetTitle("Cell") self.digitkl.GetYaxis().SetTitle("PMT") self.digitkl.GetYaxis().SetBinLabel(1, "0") self.digitkl.GetYaxis().SetBinLabel(2, "1") # Create canvases # # Draw() of histos has to be done only once # for updating the histograms, just Modified() and Update() on canvases # the update/refresh is done in update_histos() self.canvas_kl = ROOT.TCanvas("kl", "kl") self.canvas_kl.Divide(2, 2) self.canvas_kl.cd(1) self.hadc.Draw() self.canvas_kl.cd(2) self.hadc_product.Draw() self.canvas_kl.cd(3) self.hprofile.Draw() self.canvas_kl.cd(4) self.digitkl.Draw("text&&colz") return True def update_histos(self): """ Updates histogram canvases. This is called only when the number of spills is divisible by the refresh rate. @param self Object reference. """ #self.canvas_kl.cd(1) #self.hadc.Draw() #self.canvas_kl.cd(2) #self.hadc_product.Draw() #self.canvas_kl.cd(3) #self.hprofile.Draw() #self.canvas_kl.cd(4) #self.digitkl.Draw("text&&colz") self.canvas_kl.Update() def get_histogram_images(self): """ Get histograms as JSON documents. @returns list of 1 JSON document containing the images. """ image_list = [] tag = "KL" keywords = ["KL"] description = "KL" doc = ReducePyROOTHistogram.get_image_doc( \ self, keywords, description, tag, self.canvas_kl) image_list.append(doc) return image_list def _cleanup_at_death(self): """ Reinitialise histograms at death and print out new (empty) images """ self.__init_histos() self.get_histogram_images()