Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:18:46

0001 /*      hltDiff: compare TriggerResults event by event
0002  *
0003  *      Compare two TriggerResults collections event by event.
0004  *      These can come from two collections in the same file(s), or from two different sets of files.
0005  */
0006 
0007 #include <vector>
0008 #include <set>
0009 #include <string>
0010 #include <iostream>
0011 #include <sstream>
0012 #include <fstream>
0013 #include <cstdio>
0014 #include <iomanip>
0015 #include <memory>
0016 #include <algorithm>
0017 
0018 #include <cstring>
0019 #include <unistd.h>
0020 #include <getopt.h>
0021 #include <cstdio>
0022 #include <cmath>
0023 
0024 #include <boost/algorithm/string.hpp>
0025 
0026 #include <TFile.h>
0027 #include <TCanvas.h>
0028 #include <TH1F.h>
0029 #include <TH2F.h>
0030 #include <TGraphAsymmErrors.h>
0031 
0032 #include "FWCore/Common/interface/TriggerNames.h"
0033 #include "FWCore/Utilities/interface/InputTag.h"
0034 #include "FWCore/ParameterSet/interface/Registry.h"
0035 #include "DataFormats/Common/interface/TriggerResults.h"
0036 #include "DataFormats/HLTReco/interface/TriggerObject.h"
0037 #include "DataFormats/HLTReco/interface/TriggerEvent.h"
0038 #include "DataFormats/FWLite/interface/Handle.h"
0039 #include "DataFormats/FWLite/interface/Event.h"
0040 #include "DataFormats/FWLite/interface/ChainEvent.h"
0041 #include "HLTrigger/HLTcore/interface/HLTConfigData.h"
0042 
0043 void error(std::ostream& out) { out << "Try 'hltDiff --help' for more information" << std::endl; }
0044 
0045 void error(std::ostream& out, const char* message) {
0046   out << message << std::endl;
0047   error(out);
0048 }
0049 
0050 void error(std::ostream& out, const std::string& message) {
0051   out << message << std::endl;
0052   error(out);
0053 }
0054 
0055 class HLTConfigInterface {
0056 public:
0057   virtual ~HLTConfigInterface() = default;
0058   virtual std::string const& processName() const = 0;
0059   virtual unsigned int size() const = 0;
0060   virtual unsigned int size(unsigned int trigger) const = 0;
0061   virtual std::string const& triggerName(unsigned int trigger) const = 0;
0062   virtual unsigned int triggerIndex(unsigned int trigger) const = 0;
0063   virtual std::string const& moduleLabel(unsigned int trigger, unsigned int module) const = 0;
0064   virtual std::string const& moduleType(unsigned int trigger, unsigned int module) const = 0;
0065   virtual bool prescaler(unsigned int trigger, unsigned int module) const = 0;
0066 };
0067 
0068 class HLTConfigDataEx : public HLTConfigInterface {
0069 public:
0070   explicit HLTConfigDataEx(HLTConfigData data) : data_(std::move(data)), moduleTypes_(size()), prescalers_(size()) {
0071     for (unsigned int t = 0; t < data_.size(); ++t) {
0072       prescalers_[t].resize(size(t));
0073       moduleTypes_[t].resize(size(t));
0074       for (unsigned int m = 0; m < data_.size(t); ++m) {
0075         std::string type = data_.moduleType(moduleLabel(t, m));
0076         prescalers_[t][m] = (type == "HLTPrescaler");
0077         moduleTypes_[t][m] = &*moduleTypeSet_.insert(std::move(type)).first;
0078       }
0079     }
0080   }
0081   ~HLTConfigDataEx() override = default;
0082   std::string const& processName() const override { return data_.processName(); }
0083 
0084   unsigned int size() const override { return data_.size(); }
0085 
0086   unsigned int size(unsigned int trigger) const override { return data_.size(trigger); }
0087 
0088   virtual std::vector<std::string> const& triggerNames() const { return data_.triggerNames(); }
0089 
0090   std::string const& triggerName(unsigned int trigger) const override { return data_.triggerName(trigger); }
0091 
0092   unsigned int triggerIndex(unsigned int trigger) const override { return trigger; }
0093 
0094   std::string const& moduleLabel(unsigned int trigger, unsigned int module) const override {
0095     return data_.moduleLabel(trigger, module);
0096   }
0097 
0098   std::string const& moduleType(unsigned int trigger, unsigned int module) const override {
0099     return *moduleTypes_.at(trigger).at(module);
0100   }
0101 
0102   bool prescaler(unsigned int trigger, unsigned int module) const override {
0103     return prescalers_.at(trigger).at(module);
0104   }
0105 
0106 private:
0107   HLTConfigData data_;
0108   std::set<std::string> moduleTypeSet_;
0109   std::vector<std::vector<std::string const*>> moduleTypes_;
0110   std::vector<std::vector<bool>> prescalers_;
0111 };
0112 
0113 const char* event_state(bool state) { return state ? "accepted" : "rejected"; }
0114 
0115 class HLTCommonConfig {
0116 public:
0117   enum class Index { First = 0, Second = 1 };
0118 
0119   class View : public HLTConfigInterface {
0120   public:
0121     View(HLTCommonConfig const& config, HLTCommonConfig::Index index) : config_(config), index_(index) {}
0122     ~View() override = default;
0123     std::string const& processName() const override;
0124     unsigned int size() const override;
0125     unsigned int size(unsigned int trigger) const override;
0126     std::string const& triggerName(unsigned int trigger) const override;
0127     unsigned int triggerIndex(unsigned int trigger) const override;
0128     std::string const& moduleLabel(unsigned int trigger, unsigned int module) const override;
0129     std::string const& moduleType(unsigned int trigger, unsigned int module) const override;
0130     bool prescaler(unsigned int trigger, unsigned int module) const override;
0131 
0132   private:
0133     HLTCommonConfig const& config_;
0134     Index index_;
0135   };
0136 
0137   HLTCommonConfig(HLTConfigDataEx const& first, HLTConfigDataEx const& second)
0138       : first_(first), second_(second), firstView_(*this, Index::First), secondView_(*this, Index::Second) {
0139     for (unsigned int f = 0; f < first.size(); ++f)
0140       for (unsigned int s = 0; s < second.size(); ++s)
0141         if (first.triggerName(f) == second.triggerName(s)) {
0142           triggerIndices_.push_back(std::make_pair(f, s));
0143           break;
0144         }
0145   }
0146 
0147   View const& getView(Index index) const {
0148     if (index == Index::First)
0149       return firstView_;
0150     else
0151       return secondView_;
0152   }
0153 
0154   std::string const& processName(Index index) const {
0155     if (index == Index::First)
0156       return first_.processName();
0157     else
0158       return second_.processName();
0159   }
0160 
0161   unsigned int size(Index index) const { return triggerIndices_.size(); }
0162 
0163   unsigned int size(Index index, unsigned int trigger) const {
0164     if (index == Index::First)
0165       return first_.size(trigger);
0166     else
0167       return second_.size(trigger);
0168   }
0169 
0170   std::string const& triggerName(Index index, unsigned int trigger) const {
0171     if (index == Index::First)
0172       return first_.triggerName(triggerIndices_.at(trigger).first);
0173     else
0174       return second_.triggerName(triggerIndices_.at(trigger).second);
0175   }
0176 
0177   unsigned int triggerIndex(Index index, unsigned int trigger) const {
0178     if (index == Index::First)
0179       return triggerIndices_.at(trigger).first;
0180     else
0181       return triggerIndices_.at(trigger).second;
0182   }
0183 
0184   std::string const& moduleLabel(Index index, unsigned int trigger, unsigned int module) const {
0185     if (index == Index::First)
0186       return first_.moduleLabel(triggerIndices_.at(trigger).first, module);
0187     else
0188       return second_.moduleLabel(triggerIndices_.at(trigger).second, module);
0189   }
0190 
0191   std::string const& moduleType(Index index, unsigned int trigger, unsigned int module) const {
0192     if (index == Index::First)
0193       return first_.moduleType(triggerIndices_.at(trigger).first, module);
0194     else
0195       return second_.moduleType(triggerIndices_.at(trigger).second, module);
0196   }
0197 
0198   bool prescaler(Index index, unsigned int trigger, unsigned int module) const {
0199     if (index == Index::First)
0200       return first_.prescaler(triggerIndices_.at(trigger).first, module);
0201     else
0202       return second_.prescaler(triggerIndices_.at(trigger).second, module);
0203   }
0204 
0205 private:
0206   HLTConfigDataEx const& first_;
0207   HLTConfigDataEx const& second_;
0208 
0209   View firstView_;
0210   View secondView_;
0211 
0212   std::vector<std::pair<unsigned int, unsigned int>> triggerIndices_;
0213 };
0214 
0215 std::string const& HLTCommonConfig::View::processName() const { return config_.processName(index_); }
0216 
0217 unsigned int HLTCommonConfig::View::size() const { return config_.size(index_); }
0218 
0219 unsigned int HLTCommonConfig::View::size(unsigned int trigger) const { return config_.size(index_, trigger); }
0220 
0221 std::string const& HLTCommonConfig::View::triggerName(unsigned int trigger) const {
0222   return config_.triggerName(index_, trigger);
0223 }
0224 
0225 unsigned int HLTCommonConfig::View::triggerIndex(unsigned int trigger) const {
0226   return config_.triggerIndex(index_, trigger);
0227 }
0228 
0229 std::string const& HLTCommonConfig::View::moduleLabel(unsigned int trigger, unsigned int module) const {
0230   return config_.moduleLabel(index_, trigger, module);
0231 }
0232 
0233 std::string const& HLTCommonConfig::View::moduleType(unsigned int trigger, unsigned int module) const {
0234   return config_.moduleType(index_, trigger, module);
0235 }
0236 
0237 bool HLTCommonConfig::View::prescaler(unsigned int trigger, unsigned int module) const {
0238   return config_.prescaler(index_, trigger, module);
0239 }
0240 
0241 enum State {
0242   Ready = edm::hlt::Ready,
0243   Pass = edm::hlt::Pass,
0244   Fail = edm::hlt::Fail,
0245   Exception = edm::hlt::Exception,
0246   Prescaled,
0247   Invalid
0248 };
0249 
0250 const char* path_state(State state) {
0251   static const char* message[] = {"not run", "accepted", "rejected", "exception", "prescaled", "invalid"};
0252 
0253   if (state > 0 and state < Invalid)
0254     return message[state];
0255   else
0256     return message[Invalid];
0257 }
0258 
0259 inline State prescaled_state(int state, int path, int module, HLTConfigInterface const& config) {
0260   if (state == Fail and config.prescaler(path, module))
0261     return Prescaled;
0262   return (State)state;
0263 }
0264 
0265 // return a copy of a string denoting an InputTag without the process name
0266 // i.e.
0267 //    "module"                  --> "module"
0268 //    "module:instance"         --> "module:instance"
0269 //    "module::process"         --> "module"
0270 //    "module:instance:process" --> "module:instance"
0271 //
0272 std::string strip_process_name(std::string const& s) {
0273   if (std::count(s.begin(), s.end(), ':') == 2) {
0274     // remove the process name and the second ':' separator
0275     size_t end = s.find_last_of(':');
0276     if (end > 0 and s.at(end - 1) == ':')
0277       // no instance name, remove also the first ':' separator
0278       --end;
0279     return s.substr(0, end);
0280   } else {
0281     // no process name, return the string unchanged
0282     return s;
0283   }
0284 }
0285 
0286 void print_detailed_path_state(std::ostream& out, State state, int path, int module, HLTConfigInterface const& config) {
0287   auto const& label = config.moduleLabel(path, module);
0288   auto const& type = config.moduleType(path, module);
0289 
0290   out << "'" << path_state(state) << "'";
0291   if (state == Fail)
0292     out << " by module " << module << " '" << label << "' [" << type << "]";
0293   else if (state == Exception)
0294     out << " at module " << module << " '" << label << "' [" << type << "]";
0295 }
0296 
0297 void print_trigger_candidates(std::ostream& out, trigger::TriggerEvent const& summary, edm::InputTag const& filter) {
0298   // find the index of the collection of trigger candidates corresponding to the filter
0299   unsigned int index = summary.filterIndex(filter);
0300 
0301   if (index >= summary.sizeFilters()) {
0302     // the collection of trigger candidates corresponding to the filter could not be found
0303     out << "            not found\n";
0304     return;
0305   }
0306 
0307   if (summary.filterKeys(index).empty()) {
0308     // the collection of trigger candidates corresponding to the filter is empty
0309     out << "            none\n";
0310     return;
0311   }
0312 
0313   for (unsigned int i = 0; i < summary.filterKeys(index).size(); ++i) {
0314     auto key = summary.filterKeys(index)[i];
0315     auto id = summary.filterIds(index)[i];
0316     trigger::TriggerObject const& candidate = summary.getObjects().at(key);
0317     out << "            "
0318         << "filter id: " << id << ", "
0319         << "object id: " << candidate.id() << ", "
0320         << "pT: " << candidate.pt() << ", "
0321         << "eta: " << candidate.eta() << ", "
0322         << "phi: " << candidate.phi() << ", "
0323         << "mass: " << candidate.mass() << "\n";
0324   }
0325 }
0326 
0327 void print_trigger_collection(std::ostream& out, trigger::TriggerEvent const& summary, std::string const& tag) {
0328   auto iterator = std::find(summary.collectionTags().begin(), summary.collectionTags().end(), tag);
0329   if (iterator == summary.collectionTags().end()) {
0330     // the collection of trigger candidates could not be found
0331     out << "            not found\n";
0332     return;
0333   }
0334 
0335   unsigned int index = iterator - summary.collectionTags().begin();
0336   unsigned int begin = (index == 0) ? 0 : summary.collectionKey(index - 1);
0337   unsigned int end = summary.collectionKey(index);
0338 
0339   if (end == begin) {
0340     // the collection of trigger candidates is empty
0341     out << "            none\n";
0342     return;
0343   }
0344 
0345   for (unsigned int key = begin; key < end; ++key) {
0346     trigger::TriggerObject const& candidate = summary.getObjects().at(key);
0347     out << "            "
0348         << "object id: " << candidate.id() << ", "
0349         << "pT: " << candidate.pt() << ", "
0350         << "eta: " << candidate.eta() << ", "
0351         << "phi: " << candidate.phi() << ", "
0352         << "mass: " << candidate.mass() << "\n";
0353   }
0354 }
0355 
0356 std::string getProcessNameFromBranch(std::string const& branch) {
0357   std::vector<boost::iterator_range<std::string::const_iterator>> tokens;
0358   boost::split(tokens, branch, boost::is_any_of("_."), boost::token_compress_off);
0359   return boost::copy_range<std::string>(tokens[3]);
0360 }
0361 
0362 std::unique_ptr<HLTConfigDataEx> getHLTConfigData(fwlite::EventBase const& event, std::string process) {
0363   auto const& history = event.processHistory();
0364   if (process.empty()) {
0365     // determine the process name from the most recent "TriggerResults" object
0366     auto const& branch =
0367         event.getBranchNameFor(edm::Wrapper<edm::TriggerResults>::typeInfo(), "TriggerResults", "", process.c_str());
0368     process = getProcessNameFromBranch(branch);
0369   }
0370 
0371   edm::ProcessConfiguration config;
0372   if (not history.getConfigurationForProcess(process, config)) {
0373     std::cerr << "error: the process " << process << " is not in the Process History" << std::endl;
0374     exit(1);
0375   }
0376   const edm::ParameterSet* pset = edm::pset::Registry::instance()->getMapped(config.parameterSetID());
0377   if (pset == nullptr) {
0378     std::cerr << "error: the configuration for the process " << process << " is not available in the Provenance"
0379               << std::endl;
0380     exit(1);
0381   }
0382   return std::make_unique<HLTConfigDataEx>(HLTConfigData(pset));
0383 }
0384 
0385 struct TriggerDiff {
0386   TriggerDiff() : count(0), gained(0), lost(0), internal(0) {}
0387 
0388   unsigned int count;
0389   unsigned int gained;
0390   unsigned int lost;
0391   unsigned int internal;
0392 
0393   static std::string format(unsigned int value, char sign = '+') {
0394     if (value == 0)
0395       return std::string("-");
0396 
0397     char buffer[12];  // sign, 10 digits, null
0398     memset(buffer, 0, 12);
0399 
0400     unsigned int digit = 10;
0401     while (value > 0) {
0402       buffer[digit] = value % 10 + 48;
0403       value /= 10;
0404       --digit;
0405     }
0406     buffer[digit] = sign;
0407 
0408     return std::string(buffer + digit);
0409   }
0410 
0411   unsigned int total() const { return this->gained + this->lost + this->internal; }
0412 };
0413 
0414 std::ostream& operator<<(std::ostream& out, TriggerDiff diff) {
0415   out << std::setw(12) << diff.count << std::setw(12) << TriggerDiff::format(diff.gained, '+') << std::setw(12)
0416       << TriggerDiff::format(diff.lost, '-') << std::setw(12) << TriggerDiff::format(diff.internal, '~');
0417   return out;
0418 }
0419 
0420 class JsonOutputProducer {
0421 private:
0422   static size_t tab_spaces;
0423 
0424   // static variables and methods for printing specific JSON elements
0425   static std::string indent(size_t _nTabs) {
0426     std::string str = "\n";
0427     while (_nTabs) {
0428       int nSpaces = tab_spaces;
0429       while (nSpaces) {
0430         str.push_back(' ');
0431         nSpaces--;
0432       }
0433       _nTabs--;
0434     }
0435 
0436     return str;
0437   }
0438 
0439   static std::string key(const std::string& _key, const std::string& _delim = "") {
0440     std::string str = "\"\":";
0441     str.insert(1, _key);
0442     str.append(_delim);
0443 
0444     return str;
0445   }
0446 
0447   static std::string key_string(const std::string& _key, const std::string& _string, const std::string& _delim = "") {
0448     std::string str = key(_key, _delim);
0449     str.push_back('"');
0450     str.append(_string);
0451     str.push_back('"');
0452     return str;
0453   }
0454 
0455   static std::string key_int(const std::string& _key, int _int, const std::string& _delim = "") {
0456     std::string str = key(_key, _delim);
0457     str.append(std::to_string(_int));
0458 
0459     return str;
0460   }
0461 
0462   static std::string string(const std::string& _string, const std::string& _delim = "") {
0463     std::string str = "\"\"";
0464     str.insert(1, _string);
0465     str.append(_delim);
0466 
0467     return str;
0468   }
0469 
0470   static std::string list_string(const std::vector<std::string>& _values, const std::string& _delim = "") {
0471     std::string str = "[";
0472     for (auto it = _values.begin(); it != _values.end(); ++it) {
0473       str.append(_delim);
0474       str.push_back('"');
0475       str.append(*it);
0476       str.push_back('"');
0477       if (it != --_values.end())
0478         str.push_back(',');
0479     }
0480     str.append(_delim);
0481     str.push_back(']');
0482 
0483     return str;
0484   }
0485 
0486 public:
0487   bool writeJson;
0488   std::string out_filename_base;
0489   bool useSingleOutFile;
0490   // structs holding particular JSON objects
0491   struct JsonConfigurationBlock {
0492     std::string file_base;  // common part at the beginning of all files
0493     std::vector<std::string> files;
0494     std::string process;
0495     std::vector<std::string> skipped_triggers;
0496 
0497     std::string serialise(size_t _indent = 0) const {
0498       std::ostringstream json;
0499       json << indent(_indent);  // line
0500       json << key_string("file_base", file_base) << ',';
0501       json << indent(_indent);  // line
0502       json << key("files") << list_string(files) << ',';
0503       json << indent(_indent);  // line
0504       json << key_string("process", process) << ',';
0505       json << indent(_indent);  // line
0506       json << key("skipped_triggers") << list_string(skipped_triggers);
0507 
0508       return json.str();
0509     }
0510 
0511     void extractFileBase() {
0512       std::string file0 = files.at(0);
0513       // determining the last position at which all filenames have the same character
0514       for (size_t i = 0; i < file0.length(); ++i) {
0515         bool identicalInAll = true;
0516         char character = file0.at(i);
0517         for (std::string file : files) {
0518           if (file.at(i) == character)
0519             continue;
0520           identicalInAll = false;
0521           break;
0522         }
0523         if (!identicalInAll)
0524           break;
0525         file_base.push_back(character);
0526       }
0527       const unsigned int file_base_len = file_base.length();
0528       if (file_base_len < 1)
0529         return;
0530       // removing the file_base from each filename
0531       for (std::string& file : files) {
0532         file.erase(0, file_base_len);
0533       }
0534     }
0535 
0536     JsonConfigurationBlock() : file_base(""), files(0), process(""), skipped_triggers(0) {}
0537   };
0538 
0539   struct JsonConfiguration {
0540     JsonConfigurationBlock o;  // old
0541     JsonConfigurationBlock n;  // new
0542     bool prescales;
0543     int events;
0544 
0545     std::string serialise(size_t _indent = 0) const {
0546       std::ostringstream json;
0547       json << indent(_indent) << key("configuration") << '{';  // line open
0548       json << indent(_indent + 1) << key("o") << '{';          // line open
0549       json << o.serialise(_indent + 2);                        // block
0550       json << indent(_indent + 1) << "},";                     // line close
0551       json << indent(_indent + 1) << key("n") << '{';          // line open
0552       json << n.serialise(_indent + 2);                        // line block
0553       json << indent(_indent + 1) << "},";                     // line close
0554       std::string prescales_str = prescales ? "true" : "false";
0555       json << indent(_indent + 1) << key("prescales") << prescales_str << ',';  // line
0556       json << indent(_indent + 1) << key("events") << events;                   // line
0557       json << indent(_indent) << "}";                                           // line close
0558 
0559       return json.str();
0560     }
0561 
0562     JsonConfiguration() : o(), n() {}
0563   };
0564 
0565   struct JsonVars {
0566     std::vector<std::string> state;
0567     std::vector<std::string> trigger;
0568     std::vector<std::pair<int, int>> trigger_passed_count;  // <old, new>
0569     std::vector<std::string> label;
0570     std::vector<std::string> type;
0571 
0572     std::string serialise(size_t _indent = 0) const {
0573       std::ostringstream json;
0574       json << indent(_indent) << key("vars") << '{';                                 // line open
0575       json << indent(_indent + 1) << key("state") << list_string(state) << ',';      // line
0576       json << indent(_indent + 1) << key("trigger") << list_string(trigger) << ',';  // line
0577       json << indent(_indent + 1) << key("trigger_passed_count") << '[';             // line
0578       for (auto it = trigger_passed_count.begin(); it != trigger_passed_count.end(); ++it) {
0579         json << '{' << key("o") << (*it).first << ',' << key("n") << (*it).second << '}';
0580         if (it != trigger_passed_count.end() - 1)
0581           json << ',';
0582       }
0583       json << "],";
0584       json << indent(_indent + 1) << key("label") << list_string(label) << ',';  // line
0585       json << indent(_indent + 1) << key("type") << list_string(type);           // line
0586       json << indent(_indent) << '}';                                            // line close
0587 
0588       return json.str();
0589     }
0590 
0591     JsonVars() : state(0), trigger(0), trigger_passed_count(0), label(0), type(0) {}
0592   };
0593 
0594   // class members
0595   JsonConfiguration configuration;
0596   JsonVars vars;
0597 
0598 private:
0599   unsigned int labelId(std::string labelName) {
0600     unsigned int id = std::find(vars.label.begin(), vars.label.end(), labelName) - vars.label.begin();
0601     if (id < vars.label.size())
0602       return id;
0603     vars.label.push_back(labelName);
0604     return vars.label.size() - 1;
0605   }
0606 
0607   unsigned int typeId(std::string typeName) {
0608     unsigned int id = std::find(vars.type.begin(), vars.type.end(), typeName) - vars.type.begin();
0609     if (id < vars.type.size())
0610       return id;
0611     vars.type.push_back(typeName);
0612     return vars.type.size() - 1;
0613   }
0614 
0615 public:
0616   struct JsonEventState {
0617     State s;  // state
0618     int m;    // module id
0619     int l;    // label id
0620     int t;    // type id
0621 
0622     std::string serialise(size_t _indent = 0) const {
0623       std::ostringstream json;
0624       json << key_int("s", int(s));  // line
0625       // No more information needed if the state is 'accepted'
0626       if (s == State::Pass)
0627         return json.str();
0628       json << ',';
0629       json << key_int("m", m) << ',';
0630       json << key_int("l", l) << ',';
0631       json << key_int("t", t);
0632 
0633       return json.str();
0634     }
0635 
0636     JsonEventState() : s(State::Ready), m(-1), l(-1), t(-1) {}
0637     JsonEventState(State _s, int _m, int _l, int _t) : s(_s), m(_m), l(_l), t(_t) {}
0638   };
0639 
0640   struct JsonTriggerEventState {
0641     int tr;            // trigger id
0642     JsonEventState o;  // old
0643     JsonEventState n;  // new
0644 
0645     std::string serialise(size_t _indent = 0) const {
0646       std::ostringstream json;
0647       json << indent(_indent) << key_int("t", tr) << ',';                   // line
0648       json << indent(_indent) << key("o") << '{' << o.serialise() << "},";  // line
0649       json << indent(_indent) << key("n") << '{' << n.serialise() << "}";   // line
0650 
0651       return json.str();
0652     }
0653 
0654     JsonTriggerEventState() : tr(-1), o(), n() {}
0655     JsonTriggerEventState(int _tr) : tr(_tr), o(), n() {}
0656   };
0657 
0658   struct JsonEvent {
0659     int run;
0660     int lumi;
0661     int event;
0662     std::vector<JsonTriggerEventState> triggerStates;
0663 
0664     std::string serialise(size_t _indent = 0) const {
0665       std::ostringstream json;
0666       json << indent(_indent) << '{' << "\"r\"" << ':' << run << ",\"l\":" << lumi << ",\"e\":" << event
0667            << ",\"t\":[";  // line open
0668       for (auto it = triggerStates.begin(); it != triggerStates.end(); ++it) {
0669         json << '{';                           // line open
0670         json << (*it).serialise(_indent + 2);  // block
0671         json << indent(_indent + 1) << '}';    // line close
0672         if (it != --triggerStates.end())
0673           json << ',';
0674       }
0675       json << indent(_indent) << ']' << '}';  // line close
0676 
0677       return json.str();
0678     }
0679 
0680     JsonEvent(int _run, int _lumi, int _event) : run(_run), lumi(_lumi), event(_event), triggerStates(0) {}
0681 
0682     JsonTriggerEventState& pushTrigger(int _tr) {
0683       // check whether the last trigger is the one
0684       if (!triggerStates.empty()) {
0685         JsonTriggerEventState& lastTrigger = triggerStates.back();
0686         if (lastTrigger.tr == _tr)
0687           return lastTrigger;
0688       }
0689       triggerStates.push_back(JsonTriggerEventState(_tr));
0690       return triggerStates.back();
0691     }
0692   };
0693 
0694   // class members
0695   std::map<int, std::vector<JsonEvent>> m_run_events;
0696 
0697   // methods
0698   JsonOutputProducer(bool _writeJson, std::string _file_name)
0699       : writeJson(_writeJson), out_filename_base(std::move(_file_name)) {
0700     useSingleOutFile = out_filename_base.length() > 0;
0701   }
0702 
0703   JsonEvent& pushEvent(int _run, int _lumi, int _event) {
0704     // ensuring that this RUN is present in the producer
0705     if ((m_run_events.count(_run) == 0 && !useSingleOutFile) || m_run_events.empty())
0706       m_run_events.emplace(_run, std::vector<JsonEvent>());
0707     std::vector<JsonEvent>& v_events = useSingleOutFile ? m_run_events.begin()->second : m_run_events.at(_run);
0708     // check whether the last  event is the one
0709     if (!v_events.empty()) {
0710       JsonEvent& lastEvent = v_events.back();
0711       if (lastEvent.run == _run && lastEvent.lumi == _lumi && lastEvent.event == _event)
0712         return lastEvent;
0713     }
0714     v_events.push_back(JsonEvent(_run, _lumi, _event));
0715     return v_events.back();
0716   }
0717 
0718   JsonEventState eventState(State _s, int _m, const std::string& _l, const std::string& _t) {
0719     return JsonEventState(_s, _m, this->labelId(_l), this->typeId(_t));
0720   }
0721 
0722   std::string output_filename_base(int _run) const {
0723     if (useSingleOutFile)
0724       return out_filename_base;
0725 
0726     char name[1000];
0727     sprintf(name,
0728             "DQM_V0001_R%.9d__OLD_%s__NEW_%s_DQM",
0729             _run,
0730             configuration.o.process.c_str(),
0731             configuration.n.process.c_str());
0732 
0733     return std::string(name);
0734   }
0735 
0736   void write() {
0737     if (!writeJson)
0738       return;
0739     std::set<std::string> filesCreated, filesNotCreated;
0740     std::ofstream out_file;
0741     if (!m_run_events.empty()) {
0742       // Creating a separate file for each run
0743       for (const auto& runEvents : m_run_events) {
0744         const int run = runEvents.first;
0745         const std::vector<JsonEvent>& v_events = runEvents.second;
0746         // Writing the output to a JSON file
0747         std::string output_name = output_filename_base(run) += ".json";
0748         out_file.open(output_name, std::ofstream::out);
0749         if (out_file.good()) {
0750           out_file << '{';  // line open
0751           out_file << configuration.serialise(1) << ',';
0752           out_file << vars.serialise(1) << ',';
0753           // writing block for each event
0754           out_file << indent(1) << key("events") << '[';  // line open
0755           for (auto it = v_events.begin(); it != v_events.end(); ++it) {
0756             out_file << (*it).serialise(2);
0757             if (it != --v_events.end())
0758               out_file << ',';
0759           }
0760           out_file << indent(1) << ']';  // line close
0761           out_file << indent(0) << "}";  // line close
0762           out_file.close();
0763           // Adding file name to the list of created files
0764           filesCreated.insert(output_name);
0765         } else
0766           filesNotCreated.insert(output_name);
0767       }
0768     } else {
0769       // Creating a single file containing with only configuration part
0770       std::string output_name = output_filename_base(0) += ".json";
0771       out_file.open(output_name, std::ofstream::out);
0772       if (out_file.good()) {
0773         out_file << '{';  // line open
0774         out_file << configuration.serialise(1) << ',';
0775         out_file << vars.serialise(1) << ',';
0776         // writing block for each event
0777         out_file << indent(1) << key("events") << '[';  // line open
0778         // for (std::vector<JsonEvent>::const_iterator it = v_events.begin(); it != v_events.end(); ++it) {
0779         //   out_file << (*it).serialise(2);
0780         //   if (it != --v_events.end()) out_file << ',';
0781         // }
0782         out_file << indent(1) << ']';  // line close
0783         out_file << indent(0) << "}";  // line close
0784         out_file.close();
0785         // Adding file name to the list of created files
0786         filesCreated.insert(output_name);
0787       } else
0788         filesNotCreated.insert(output_name);
0789     }
0790 
0791     if (!filesCreated.empty()) {
0792       std::cout << "Created the following JSON files:" << std::endl;
0793       for (const std::string& filename : filesCreated)
0794         std::cout << " " << filename << std::endl;
0795     }
0796 
0797     if (!filesNotCreated.empty()) {
0798       std::cout << "Failed to create the following JSON files (check output directory and its permissions):"
0799                 << std::endl;
0800       for (const std::string& filename : filesNotCreated)
0801         std::cout << " " << filename << std::endl;
0802     }
0803   }
0804 };
0805 
0806 size_t JsonOutputProducer::tab_spaces = 0;
0807 
0808 class SummaryOutputProducer {
0809 private:
0810   const JsonOutputProducer& json;
0811   int run;
0812 
0813   struct Pair {
0814     double v;
0815     double e;
0816 
0817     Pair(double _v, double _e) : v(_v), e(_e){};
0818     Pair(int _v, int _e) : v(_v), e(_e){};
0819   };
0820 
0821   struct Event {
0822     int run;
0823     int lumi;
0824     int event;
0825 
0826     Event(int _run, int _lumi, int _event) : run(_run), lumi(_lumi), event(_event){};
0827     bool operator<(const Event& b) const { return std::tie(run, lumi, event) < std::tie(b.run, b.lumi, b.event); }
0828   };
0829 
0830   struct GenericSummary {
0831     const JsonOutputProducer& json;
0832     int id;
0833     std::string name;
0834     std::set<Event> v_gained;
0835     std::set<Event> v_lost;
0836     std::set<Event> v_changed;
0837 
0838     GenericSummary(int _id, const JsonOutputProducer& _json, const std::vector<std::string>& _names)
0839         : json(_json), id(_id) {
0840       name = _names.at(id);
0841     }
0842 
0843     int addEntry(const JsonOutputProducer::JsonEvent& _event, const int _triggerIndex) {
0844       const JsonOutputProducer::JsonTriggerEventState& state = _event.triggerStates.at(_triggerIndex);
0845       const Event event = Event(_event.run, _event.lumi, _event.event);
0846       int moduleId = state.o.l;
0847       if (state.o.s == State::Pass && state.n.s == State::Fail) {
0848         moduleId = state.n.l;
0849         v_lost.insert(event);
0850       } else if (state.o.s == State::Fail && state.n.s == State::Pass) {
0851         v_gained.insert(event);
0852       } else if (state.o.s == State::Fail && state.n.s == State::Fail) {
0853         v_changed.insert(event);
0854       }
0855 
0856       return moduleId;
0857     }
0858 
0859     Pair gained() const { return Pair(double(v_gained.size()), sqrt(double(v_gained.size()))); }
0860 
0861     Pair lost() const { return Pair(double(v_lost.size()), sqrt(double(v_lost.size()))); }
0862 
0863     Pair changed() const { return Pair(double(v_changed.size()), sqrt(double(v_changed.size()))); }
0864 
0865     bool keepForC() const { return !v_changed.empty(); }
0866 
0867     bool keepForGL() const { return !v_gained.empty() || !v_lost.empty(); }
0868   };
0869 
0870   struct TriggerSummary : GenericSummary {
0871     int accepted_o;
0872     int accepted_n;
0873     std::map<int, GenericSummary> m_modules;
0874 
0875     TriggerSummary(int _id, const JsonOutputProducer& _json)
0876         : GenericSummary(_id, _json, _json.vars.trigger),
0877           accepted_o(_json.vars.trigger_passed_count.at(id).first),
0878           accepted_n(_json.vars.trigger_passed_count.at(id).second) {}
0879 
0880     void addEntry(const JsonOutputProducer::JsonEvent& _event,
0881                   const int _triggerIndex,
0882                   const std::vector<std::string>& _moduleNames) {
0883       int moduleLabelId = GenericSummary::addEntry(_event, _triggerIndex);
0884       // Updating number of events affected by the particular module
0885       if (m_modules.count(moduleLabelId) == 0)
0886         m_modules.emplace(moduleLabelId, GenericSummary(moduleLabelId, json, _moduleNames));
0887       m_modules.at(moduleLabelId).addEntry(_event, _triggerIndex);
0888     }
0889 
0890     Pair gained(int type = 0) const {
0891       Pair gained(GenericSummary::gained());
0892       if (type == 0)
0893         return gained;  // Absolute number of affected events
0894       double all(accepted_n);
0895       Pair fraction = Pair(gained.v / (all + 1e-10), sqrt(all) / (all + 1e-10));
0896       if (type == 1)
0897         return fraction;  // Relative number of affected events with respect to all accepted
0898       if (type == 2)
0899         return Pair(std::max(0.0, fraction.v - fraction.e), 0.0);  // Smallest value given the uncertainty
0900       return Pair(fraction.v / (fraction.e + 1e-10), 0.0);         // Significance of the effect as N std. deviations
0901     }
0902 
0903     Pair lost(int type = 0) const {
0904       Pair lost(GenericSummary::lost());
0905       if (type == 0)
0906         return lost;
0907       double all(accepted_o);
0908       Pair fraction = Pair(lost.v / (all + 1e-10), sqrt(all) / (all + 1e-10));
0909       if (type == 1)
0910         return fraction;
0911       if (type == 2)
0912         return Pair(std::max(0.0, fraction.v - fraction.e), 0.0);  // Smallest value given the uncertainty
0913       return Pair(fraction.v / (fraction.e + 1e-10), 0.0);
0914     }
0915 
0916     Pair changed(int type = 0) const {
0917       Pair changed(GenericSummary::changed());
0918       if (type == 0)
0919         return changed;
0920       double all(json.configuration.events - accepted_o);
0921       Pair fraction = Pair(changed.v / (all + 1e-10), sqrt(all) / (all + 1e-10));
0922       if (type == 1)
0923         return fraction;
0924       if (type == 2)
0925         return Pair(std::max(0.0, fraction.v - fraction.e), 0.0);  // Smallest value given the uncertainty
0926       return Pair(fraction.v / (fraction.e + 1e-10), 0.0);
0927     }
0928   };
0929 
0930 private:
0931   std::map<int, TriggerSummary> m_triggerSummary;
0932   std::map<int, GenericSummary> m_moduleSummary;
0933 
0934   void prepareSummaries(const int _run, const std::vector<JsonOutputProducer::JsonEvent>& _events) {
0935     this->run = _run;
0936     // Initialising the summary objects for trigger/module
0937     m_triggerSummary.clear();
0938     m_moduleSummary.clear();
0939     const size_t nTriggers(json.vars.trigger.size());
0940     const size_t nModules(json.vars.label.size());
0941     for (size_t i = 0; i < nTriggers; ++i)
0942       m_triggerSummary.emplace(i, TriggerSummary(i, json));
0943     for (size_t i = 0; i < nModules; ++i)
0944       m_moduleSummary.emplace(i, GenericSummary(i, json, json.vars.label));
0945 
0946     // Add each affected trigger in each event to the trigger/module summary objects
0947     for (const JsonOutputProducer::JsonEvent& event : _events) {
0948       for (size_t iTrigger = 0; iTrigger < event.triggerStates.size(); ++iTrigger) {
0949         const JsonOutputProducer::JsonTriggerEventState& state = event.triggerStates.at(iTrigger);
0950         m_triggerSummary.at(state.tr).addEntry(event, iTrigger, json.vars.label);
0951         const int moduleId = state.o.s == State::Fail ? state.o.l : state.n.l;
0952         m_moduleSummary.at(moduleId).addEntry(event, iTrigger);
0953       }
0954     }
0955   }
0956 
0957   bool writeHistograms(std::string& file_name) const {
0958     // Storing histograms to a ROOT file
0959     file_name = json.output_filename_base(this->run) += ".root";
0960     auto out_file = new TFile(file_name.c_str(), "RECREATE");
0961     if (not out_file or out_file->IsZombie()) {
0962       out_file->Close();
0963       return false;
0964     }
0965 
0966     std::map<std::string, TH1*> m_histo;
0967     // Counting the numbers of bins for different types of histograms
0968     // *_c - changed; *_gl - gained or lost
0969     int nTriggers(0), nTriggers_c(0), nTriggers_gl(0), nModules_c(0), nModules_gl(0);
0970 
0971     for (const auto& idSummary : m_triggerSummary) {
0972       if (idSummary.second.accepted_o > 0)
0973         ++nTriggers;
0974       if (idSummary.second.keepForGL())
0975         ++nTriggers_gl;
0976       if (idSummary.second.keepForC())
0977         ++nTriggers_c;
0978     }
0979     for (const auto& idSummary : m_moduleSummary) {
0980       if (idSummary.second.keepForGL())
0981         ++nModules_gl;
0982       if (idSummary.second.keepForC())
0983         ++nModules_c;
0984     }
0985     // Manually increasing N bins to have histograms with meaningful axis ranges
0986     nTriggers = std::max(1, nTriggers);
0987     nTriggers_gl = std::max(1, nTriggers_gl);
0988     nTriggers_c = std::max(1, nTriggers_c);
0989     nModules_c = std::max(1, nModules_c);
0990     nModules_gl = std::max(1, nModules_gl);
0991 
0992     // Initialising overview histograms
0993     std::string name = "trigger_accepted";
0994     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events accepted^{OLD}", nTriggers, 0, nTriggers));
0995     name = "trigger_gained";
0996     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events gained", nTriggers_gl, 0, nTriggers_gl));
0997     name = "trigger_lost";
0998     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events lost", nTriggers_gl, 0, nTriggers_gl));
0999     name = "trigger_changed";
1000     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events changed", nTriggers_c, 0, nTriggers_c));
1001     name = "trigger_gained_frac";
1002     m_histo.emplace(name, new TH1F(name.c_str(), ";;#frac{gained}{accepted}", nTriggers_gl, 0, nTriggers_gl));
1003     name = "trigger_lost_frac";
1004     m_histo.emplace(name, new TH1F(name.c_str(), ";;#frac{lost}{accepted}", nTriggers_gl, 0, nTriggers_gl));
1005     name = "trigger_changed_frac";
1006     m_histo.emplace(name, new TH1F(name.c_str(), ";;#frac{changed}{all - accepted}", nTriggers_c, 0, nTriggers_c));
1007     name = "module_changed";
1008     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events changed", nModules_c, 0, nModules_c));
1009     name = "module_gained";
1010     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events gained", nModules_gl, 0, nModules_gl));
1011     name = "module_lost";
1012     m_histo.emplace(name, new TH1F(name.c_str(), ";;Events lost", nModules_gl, 0, nModules_gl));
1013 
1014     // Filling the per-trigger bins in the summary histograms
1015     size_t bin(0), bin_c(0), bin_gl(0);
1016     for (const auto& idSummary : m_triggerSummary) {
1017       const TriggerSummary& summary = idSummary.second;
1018       if (summary.accepted_o > 0) {
1019         ++bin;
1020         // Setting bin contents
1021         m_histo.at("trigger_accepted")->SetBinContent(bin, summary.accepted_o);
1022         // Setting bin labels
1023         m_histo.at("trigger_accepted")->GetXaxis()->SetBinLabel(bin, summary.name.c_str());
1024       }
1025       if (summary.keepForGL()) {
1026         ++bin_gl;
1027         // Setting bin contents
1028         m_histo.at("trigger_gained")->SetBinContent(bin_gl, summary.gained().v);
1029         m_histo.at("trigger_lost")->SetBinContent(bin_gl, -summary.lost().v);
1030         m_histo.at("trigger_gained_frac")->SetBinContent(bin_gl, summary.gained(1).v);
1031         m_histo.at("trigger_lost_frac")->SetBinContent(bin_gl, -summary.lost(1).v);
1032         // Setting bin errors
1033         m_histo.at("trigger_gained_frac")->SetBinError(bin_gl, summary.gained(1).e);
1034         m_histo.at("trigger_lost_frac")->SetBinError(bin_gl, -summary.lost(1).e);
1035         // Setting bin labels
1036         m_histo.at("trigger_gained")->GetXaxis()->SetBinLabel(bin_gl, summary.name.c_str());
1037         m_histo.at("trigger_lost")->GetXaxis()->SetBinLabel(bin_gl, summary.name.c_str());
1038         m_histo.at("trigger_gained_frac")->GetXaxis()->SetBinLabel(bin_gl, summary.name.c_str());
1039         m_histo.at("trigger_lost_frac")->GetXaxis()->SetBinLabel(bin_gl, summary.name.c_str());
1040       }
1041       if (summary.keepForC()) {
1042         ++bin_c;
1043         // Setting bin contents
1044         m_histo.at("trigger_changed")->SetBinContent(bin_c, summary.changed().v);
1045         m_histo.at("trigger_changed_frac")->SetBinContent(bin_c, summary.changed(1).v);
1046         // Setting bin errors
1047         m_histo.at("trigger_changed_frac")->SetBinError(bin_c, summary.changed(1).e);
1048         // Setting bin labels
1049         m_histo.at("trigger_changed")->GetXaxis()->SetBinLabel(bin_c, summary.name.c_str());
1050         m_histo.at("trigger_changed_frac")->GetXaxis()->SetBinLabel(bin_c, summary.name.c_str());
1051       }
1052     }
1053 
1054     // Filling the per-module bins in the summary histograms
1055     bin = 0;
1056     bin_c = 0;
1057     bin_gl = 0;
1058     for (const auto& idSummary : m_moduleSummary) {
1059       ++bin;
1060       const GenericSummary& summary = idSummary.second;
1061       if (summary.keepForGL()) {
1062         ++bin_gl;
1063         // Setting bin contents
1064         m_histo.at("module_gained")->SetBinContent(bin_gl, summary.gained().v);
1065         m_histo.at("module_lost")->SetBinContent(bin_gl, -summary.lost().v);
1066         // Setting bin labels
1067         m_histo.at("module_gained")->GetXaxis()->SetBinLabel(bin_gl, summary.name.c_str());
1068         m_histo.at("module_lost")->GetXaxis()->SetBinLabel(bin_gl, summary.name.c_str());
1069       }
1070       if (summary.keepForC()) {
1071         ++bin_c;
1072         // Setting bin contents
1073         m_histo.at("module_changed")->SetBinContent(bin_c, summary.changed().v);
1074         // Setting bin labels
1075         m_histo.at("module_changed")->GetXaxis()->SetBinLabel(bin_c, summary.name.c_str());
1076       }
1077     }
1078 
1079     // Styling the histograms
1080     for (const auto& nameHisto : m_histo) {
1081       const std::string name = nameHisto.first;
1082       TH1* histo = nameHisto.second;
1083       if (name.find("gained") != std::string::npos || name.find("changed") != std::string::npos) {
1084         if (name.find("frac") != std::string::npos)
1085           histo->GetYaxis()->SetRangeUser(0.0, 1.0);
1086       }
1087       if (name.find("lost") != std::string::npos) {
1088         if (name.find("frac") != std::string::npos)
1089           histo->GetYaxis()->SetRangeUser(-1.0, 0.0);
1090       }
1091     }
1092 
1093     // Storing the histograms in a proper folder according to the DQM convention
1094     char savePath[1000];
1095     sprintf(savePath, "DQMData/Run %d/HLT/Run summary/EventByEvent/", this->run);
1096     out_file->mkdir(savePath);
1097     gDirectory->cd(savePath);
1098     gDirectory->Write();
1099     for (const auto& nameHisto : m_histo)
1100       nameHisto.second->Write(nameHisto.first.c_str());
1101     out_file->Close();
1102 
1103     return true;
1104   }
1105 
1106   bool writeCSV_trigger(std::string& file_name) const {
1107     bool ret = false;
1108 
1109     file_name = json.output_filename_base(this->run) += "_trigger.csv";
1110     FILE* out_file = fopen(file_name.c_str(), "w");
1111 
1112     if (out_file) {
1113       fprintf(out_file,
1114               "Total,Accepted OLD,Accepted NEW,Gained,Lost,|G|/A_N + "
1115               "|L|/AO,sigma(AN)+sigma(AO),Changed,C/(T-AO),sigma(T-AO),trigger\n");
1116       for (const auto& idSummary : m_triggerSummary) {
1117         const SummaryOutputProducer::TriggerSummary& S = idSummary.second;
1118         fprintf(out_file,
1119                 "%d,%d,%d,%+.f,%+.f,%.2f%%,%.2f%%,~%.f,~%.2f%%,%.2f%%,%s\n",
1120                 this->json.configuration.events,
1121                 S.accepted_o,
1122                 S.accepted_n,
1123                 S.gained().v,
1124                 -1.0 * S.lost().v,
1125                 (S.gained(1).v + S.lost(1).v) * 100.0,
1126                 (S.gained(1).e + S.lost(1).e) * 100.0,
1127                 S.changed().v,
1128                 S.changed(1).v * 100.0,
1129                 S.changed(1).e * 100.0,
1130                 S.name.c_str());
1131       }
1132       fclose(out_file);
1133       ret = true;
1134     }
1135 
1136     return ret;
1137   }
1138 
1139   bool writeCSV_module(std::string& file_name) const {
1140     bool ret = false;
1141 
1142     file_name = json.output_filename_base(this->run) += "_module.csv";
1143     FILE* out_file = fopen(file_name.c_str(), "w");
1144 
1145     if (out_file) {
1146       fprintf(out_file, "Total,Gained,Lost,Changed,module\n");
1147       for (const auto& idSummary : m_moduleSummary) {
1148         const SummaryOutputProducer::GenericSummary& S = idSummary.second;
1149         fprintf(out_file,
1150                 "%d,+%.f,-%.f,~%.f,%s\n",
1151                 this->json.configuration.events,
1152                 S.gained().v,
1153                 S.lost().v,
1154                 S.changed().v,
1155                 S.name.c_str());
1156       }
1157       fclose(out_file);
1158       ret = true;
1159     }
1160 
1161     return ret;
1162   }
1163 
1164 public:
1165   bool storeROOT;
1166   bool storeCSV;
1167 
1168   SummaryOutputProducer(const JsonOutputProducer& _json, bool _storeROOT, bool _storeCSV)
1169       : json(_json), run(0), storeROOT(_storeROOT), storeCSV(_storeCSV) {}
1170 
1171   void write() {
1172     std::vector<std::string> filesCreated, filesNotCreated;
1173     // Processing every run from the JSON producer
1174     if (!json.m_run_events.empty()) {
1175       for (const auto& runEvents : json.m_run_events) {
1176         prepareSummaries(runEvents.first, runEvents.second);
1177         if (storeROOT) {
1178           std::string fName;
1179           auto& fNameVec = writeHistograms(fName) ? filesCreated : filesNotCreated;
1180           fNameVec.push_back(fName);
1181         }
1182         if (storeCSV) {
1183           std::string fNameTrigger;
1184           auto& fNameTriggerVec = writeCSV_trigger(fNameTrigger) ? filesCreated : filesNotCreated;
1185           fNameTriggerVec.push_back(fNameTrigger);
1186 
1187           std::string fNameModule;
1188           auto& fNameModuleVec = writeCSV_module(fNameModule) ? filesCreated : filesNotCreated;
1189           fNameModuleVec.push_back(fNameModule);
1190         }
1191       }
1192     } else {
1193       if (storeROOT) {
1194         std::string fName;
1195         auto& fNameVec = writeHistograms(fName) ? filesCreated : filesNotCreated;
1196         fNameVec.push_back(fName);
1197       }
1198       if (storeCSV) {
1199         std::string fNameTrigger;
1200         auto& fNameTriggerVec = writeCSV_trigger(fNameTrigger) ? filesCreated : filesNotCreated;
1201         fNameTriggerVec.push_back(fNameTrigger);
1202 
1203         std::string fNameModule;
1204         auto& fNameModuleVec = writeCSV_module(fNameModule) ? filesCreated : filesNotCreated;
1205         fNameModuleVec.push_back(fNameModule);
1206       }
1207     }
1208 
1209     if (!filesCreated.empty()) {
1210       std::cout << "Created the following summary files:" << std::endl;
1211       for (const std::string& filename : filesCreated)
1212         std::cout << " " << filename << std::endl;
1213     }
1214 
1215     if (!filesNotCreated.empty()) {
1216       std::cout << "Failed to create the following summary files (check output directory and its permissions):"
1217                 << std::endl;
1218       for (const std::string& filename : filesNotCreated)
1219         std::cout << " " << filename << std::endl;
1220     }
1221   }
1222 };
1223 
1224 bool check_file(std::string const& file) {
1225   std::unique_ptr<TFile> f(TFile::Open(file.c_str()));
1226   return (f and not f->IsZombie());
1227 }
1228 
1229 bool check_files(std::vector<std::string> const& files) {
1230   bool flag = true;
1231   for (auto const& file : files)
1232     if (not check_file(file)) {
1233       flag = false;
1234       std::cerr << "hltDiff: error: file " << file << " does not exist, or is not a regular file." << std::endl;
1235     }
1236   return flag;
1237 }
1238 
1239 class HltDiff {
1240 public:
1241   std::vector<std::string> old_files;
1242   std::string old_process;
1243   std::vector<std::string> new_files;
1244   std::string new_process;
1245   unsigned int max_events;
1246   bool ignore_prescales;
1247   bool csv_out;
1248   bool json_out;
1249   bool root_out;
1250   std::string output_file;
1251   bool file_check;
1252   bool debug;
1253   bool quiet;
1254   unsigned int verbose;
1255 
1256   HltDiff()
1257       : old_files(0),
1258         old_process(""),
1259         new_files(0),
1260         new_process(""),
1261         max_events(1e9),
1262         ignore_prescales(true),
1263         csv_out(false),
1264         json_out(false),
1265         root_out(false),
1266         output_file(""),
1267         file_check(false),
1268         debug(false),
1269         quiet(false),
1270         verbose(0) {}
1271 
1272   void compare() const {
1273     std::shared_ptr<fwlite::ChainEvent> old_events;
1274     std::shared_ptr<fwlite::ChainEvent> new_events;
1275 
1276     if (not file_check or check_files(old_files))
1277       old_events = std::make_shared<fwlite::ChainEvent>(old_files);
1278     else
1279       return;
1280 
1281     if (new_files.size() == 1 and new_files[0] == "-")
1282       new_events = old_events;
1283     else if (not file_check or check_files(new_files))
1284       new_events = std::make_shared<fwlite::ChainEvent>(new_files);
1285     else
1286       return;
1287 
1288     // creating the structure holding data for JSON and ROOT output
1289     JsonOutputProducer json(json_out, output_file);
1290 
1291     json.configuration.prescales = ignore_prescales;
1292     // setting the old configuration
1293     json.configuration.o.process = old_process;
1294     json.configuration.o.files = old_files;
1295     json.configuration.o.extractFileBase();
1296     // setting the new configuration
1297     json.configuration.n.process = new_process;
1298     json.configuration.n.files = new_files;
1299     json.configuration.n.extractFileBase();
1300 
1301     // initialising configurations to be compared
1302     std::unique_ptr<HLTConfigDataEx> old_config_data;
1303     std::unique_ptr<HLTConfigDataEx> new_config_data;
1304     std::unique_ptr<HLTCommonConfig> common_config;
1305     HLTConfigInterface const* old_config = nullptr;
1306     HLTConfigInterface const* new_config = nullptr;
1307 
1308     unsigned int counter = 0;
1309     unsigned int skipped = 0;
1310     unsigned int affected = 0;
1311     bool new_run = true;
1312     std::vector<TriggerDiff> differences;
1313 
1314     // loop over the reference events
1315     const unsigned int nEvents = std::min((int)old_events->size(), (int)max_events);
1316     const unsigned int counter_denominator = std::max(1, int(nEvents / 10));
1317     for (old_events->toBegin(); not old_events->atEnd(); ++(*old_events)) {
1318       // printing progress on every 10%
1319       if (counter % (counter_denominator) == 0) {
1320         std::cout << "Processed events: " << counter << " out of " << nEvents << " ("
1321                   << 10 * counter / (counter_denominator) << "%)" << std::endl;
1322       }
1323 
1324       // seek the same event in the "new" files
1325       edm::EventID const& id = old_events->id();
1326       if (new_events != old_events and not new_events->to(id)) {
1327         if (debug)
1328           std::cerr << "run " << id.run() << ", lumi " << id.luminosityBlock() << ", event " << id.event()
1329                     << ": not found in the 'new' files, skipping." << std::endl;
1330         ++skipped;
1331         continue;
1332       }
1333 
1334       // read the TriggerResults and TriggerEvent
1335       fwlite::Handle<edm::TriggerResults> old_results_h;
1336       edm::TriggerResults const* old_results = nullptr;
1337       old_results_h.getByLabel<fwlite::Event>(*old_events->event(), "TriggerResults", "", old_process.c_str());
1338       if (old_results_h.isValid())
1339         old_results = old_results_h.product();
1340       else {
1341         if (debug)
1342           std::cerr << "run " << id.run() << ", lumi " << id.luminosityBlock() << ", event " << id.event()
1343                     << ": 'old' TriggerResults not found, skipping." << std::endl;
1344         continue;
1345       }
1346 
1347       fwlite::Handle<trigger::TriggerEvent> old_summary_h;
1348       trigger::TriggerEvent const* old_summary = nullptr;
1349       old_summary_h.getByLabel<fwlite::Event>(*old_events->event(), "hltTriggerSummaryAOD", "", old_process.c_str());
1350       if (old_summary_h.isValid())
1351         old_summary = old_summary_h.product();
1352 
1353       fwlite::Handle<edm::TriggerResults> new_results_h;
1354       edm::TriggerResults const* new_results = nullptr;
1355       new_results_h.getByLabel<fwlite::Event>(*new_events->event(), "TriggerResults", "", new_process.c_str());
1356       if (new_results_h.isValid())
1357         new_results = new_results_h.product();
1358       else {
1359         if (debug)
1360           std::cerr << "run " << id.run() << ", lumi " << id.luminosityBlock() << ", event " << id.event()
1361                     << ": 'new' TriggerResults not found, skipping." << std::endl;
1362         continue;
1363       }
1364 
1365       fwlite::Handle<trigger::TriggerEvent> new_summary_h;
1366       trigger::TriggerEvent const* new_summary = nullptr;
1367       new_summary_h.getByLabel<fwlite::Event>(*new_events->event(), "hltTriggerSummaryAOD", "", new_process.c_str());
1368       if (new_summary_h.isValid())
1369         new_summary = new_summary_h.product();
1370 
1371       // initialise the trigger configuration
1372       if (new_run) {
1373         new_run = false;
1374         old_events->fillParameterSetRegistry();
1375         new_events->fillParameterSetRegistry();
1376 
1377         old_config_data = getHLTConfigData(*old_events->event(), old_process);
1378         new_config_data = getHLTConfigData(*new_events->event(), new_process);
1379         if (new_config_data->triggerNames() == old_config_data->triggerNames()) {
1380           old_config = old_config_data.get();
1381           new_config = new_config_data.get();
1382         } else {
1383           common_config = std::make_unique<HLTCommonConfig>(*old_config_data, *new_config_data);
1384           old_config = &common_config->getView(HLTCommonConfig::Index::First);
1385           new_config = &common_config->getView(HLTCommonConfig::Index::Second);
1386           std::cout << "Warning: old and new TriggerResults come from different HLT menus. Only the common "
1387                     << old_config->size() << " triggers are compared.\n"
1388                     << std::endl;
1389         }
1390 
1391         differences.clear();
1392         differences.resize(old_config->size());
1393 
1394         // adding the list of selected triggers to JSON output
1395         std::vector<std::string> states_str;
1396         for (int i = State::Ready; i != State::Invalid; i++)
1397           states_str.push_back(std::string(path_state(static_cast<State>(i))));
1398         json.vars.state = states_str;
1399         for (size_t triggerId = 0; triggerId < old_config->size(); ++triggerId) {
1400           json.vars.trigger.push_back(old_config->triggerName(triggerId));
1401           json.vars.trigger_passed_count.push_back(std::pair<int, int>(0, 0));
1402         }
1403         // getting names of triggers existing only in the old configuration
1404         for (auto const& it : old_config_data->triggerNames()) {
1405           if (std::find(json.vars.trigger.begin(), json.vars.trigger.end(), it) != json.vars.trigger.end())
1406             continue;
1407           json.configuration.o.skipped_triggers.push_back(it);
1408         }
1409         // getting names of triggers existing only in the new configuration
1410         for (auto const& it : new_config_data->triggerNames()) {
1411           if (std::find(json.vars.trigger.begin(), json.vars.trigger.end(), it) != json.vars.trigger.end())
1412             continue;
1413           json.configuration.n.skipped_triggers.push_back(it);
1414         }
1415       }
1416 
1417       // compare the TriggerResults
1418       bool needs_header = true;
1419       bool event_affected = false;
1420       for (unsigned int p = 0; p < old_config->size(); ++p) {
1421         // FIXME explicitly converting the indices is a hack, it should be properly encapsulated instead
1422         unsigned int old_index = old_config->triggerIndex(p);
1423         unsigned int new_index = new_config->triggerIndex(p);
1424         State old_state = prescaled_state(old_results->state(old_index), p, old_results->index(old_index), *old_config);
1425         State new_state = prescaled_state(new_results->state(new_index), p, new_results->index(new_index), *new_config);
1426 
1427         if (old_state == Pass) {
1428           ++differences.at(p).count;
1429         }
1430         if (old_state == Pass)
1431           ++json.vars.trigger_passed_count.at(p).first;
1432         if (new_state == Pass)
1433           ++json.vars.trigger_passed_count.at(p).second;
1434 
1435         bool trigger_affected = false;
1436         if (not ignore_prescales or (old_state != Prescaled and new_state != Prescaled)) {
1437           if (old_state == Pass and new_state != Pass) {
1438             ++differences.at(p).lost;
1439             trigger_affected = true;
1440           } else if (old_state != Pass and new_state == Pass) {
1441             ++differences.at(p).gained;
1442             trigger_affected = true;
1443           } else if (old_results->index(old_index) != new_results->index(new_index)) {
1444             ++differences.at(p).internal;
1445             trigger_affected = true;
1446           }
1447         }
1448 
1449         if (not trigger_affected)
1450           continue;
1451 
1452         event_affected = true;
1453         const unsigned int old_moduleIndex = old_results->index(old_index);
1454         const unsigned int new_moduleIndex = new_results->index(new_index);
1455         // storing the event to JSON, without any trigger results for the moment
1456         JsonOutputProducer::JsonEvent& event = json.pushEvent(id.run(), id.luminosityBlock(), id.event());
1457         JsonOutputProducer::JsonTriggerEventState& state = event.pushTrigger(p);
1458         state.o = json.eventState(old_state,
1459                                   old_moduleIndex,
1460                                   old_config->moduleLabel(p, old_moduleIndex),
1461                                   old_config->moduleType(p, old_moduleIndex));
1462         state.n = json.eventState(new_state,
1463                                   new_moduleIndex,
1464                                   new_config->moduleLabel(p, new_moduleIndex),
1465                                   new_config->moduleType(p, new_moduleIndex));
1466 
1467         if (verbose > 0) {
1468           if (needs_header) {
1469             needs_header = false;
1470             std::cout << "run " << id.run() << ", lumi " << id.luminosityBlock() << ", event " << id.event() << ": "
1471                       << "old result is '" << event_state(old_results->accept()) << "', "
1472                       << "new result is '" << event_state(new_results->accept()) << "'" << std::endl;
1473           }
1474           // print the Trigger path and filter responsible for the discrepancy
1475           std::cout << "    Path " << old_config->triggerName(p) << ":\n"
1476                     << "        old state is ";
1477           print_detailed_path_state(std::cout, old_state, p, old_moduleIndex, *old_config);
1478           std::cout << ",\n"
1479                     << "        new state is ";
1480           print_detailed_path_state(std::cout, new_state, p, new_moduleIndex, *new_config);
1481           std::cout << std::endl;
1482         }
1483         if (verbose > 1 and old_summary and new_summary) {
1484           // print TriggerObjects for the filter responsible for the discrepancy
1485           unsigned int module = std::min(old_moduleIndex, new_moduleIndex);
1486           std::cout << "    Filter " << old_config->moduleLabel(p, module) << ":\n";
1487           std::cout << "        old trigger candidates:\n";
1488           print_trigger_candidates(std::cout,
1489                                    *old_summary,
1490                                    edm::InputTag(old_config->moduleLabel(p, module), "", old_config->processName()));
1491           std::cout << "        new trigger candidates:\n";
1492           print_trigger_candidates(std::cout,
1493                                    *new_summary,
1494                                    edm::InputTag(new_config->moduleLabel(p, module), "", new_config->processName()));
1495         }
1496         if (verbose > 0)
1497           std::cout << std::endl;
1498       }
1499       if (event_affected)
1500         ++affected;
1501 
1502       // compare the TriggerEvent
1503       if (event_affected and verbose > 2 and old_summary and new_summary) {
1504         std::map<std::string, std::pair<std::string, std::string>> collections;
1505         for (auto const& old_collection : old_summary->collectionTags())
1506           collections[strip_process_name(old_collection)].first = old_collection;
1507         for (auto const& new_collection : new_summary->collectionTags())
1508           collections[strip_process_name(new_collection)].second = new_collection;
1509 
1510         for (auto const& collection : collections) {
1511           std::cout << "    Collection " << collection.first << ":\n";
1512           std::cout << "        old trigger candidates:\n";
1513           print_trigger_collection(std::cout, *old_summary, collection.second.first);
1514           std::cout << "        new trigger candidates:\n";
1515           print_trigger_collection(std::cout, *new_summary, collection.second.second);
1516           std::cout << std::endl;
1517         }
1518       }
1519 
1520       ++counter;
1521       if (nEvents and counter >= nEvents)
1522         break;
1523     }
1524 
1525     json.configuration.events = counter;
1526 
1527     if (not counter) {
1528       std::cout << "There are no common events between the old and new files";
1529       if (skipped)
1530         std::cout << ", " << skipped << " events were skipped";
1531       std::cout << "." << std::endl;
1532     } else {
1533       std::cout << "Found " << counter << " matching events, out of which " << affected
1534                 << " have different HLT results";
1535       if (skipped)
1536         std::cout << ", " << skipped << " events were skipped";
1537       std::cout << "\n" << std::endl;
1538     }
1539     // Printing the summary of affected triggers with affected-event counts
1540     if (!quiet and old_config) {
1541       bool summaryHeaderPrinted = false;
1542       for (size_t p = 0; p < old_config->size(); ++p) {
1543         if (differences.at(p).total() < 1)
1544           continue;
1545         if (!summaryHeaderPrinted)
1546           std::cout << std::setw(12) << "Events" << std::setw(12) << "Accepted" << std::setw(12) << "Gained"
1547                     << std::setw(12) << "Lost" << std::setw(12) << "Other"
1548                     << "  "
1549                     << "Trigger" << std::endl;
1550         std::cout << std::setw(12) << counter << differences.at(p) << "  " << old_config->triggerName(p) << std::endl;
1551         summaryHeaderPrinted = true;
1552       }
1553     }
1554 
1555     // writing all the required output
1556     json.write();  // to JSON file for interactive visualisation
1557     SummaryOutputProducer summary(json, this->root_out, this->csv_out);
1558     summary.write();  // to ROOT file for fast validation with static plots
1559   }
1560 
1561   void usage(std::ostream& out) const {
1562     out << "\
1563 usage: hltDiff -o|--old-files FILE1.ROOT [FILE2.ROOT ...] [-O|--old-process LABEL[:INSTANCE[:PROCESS]]]\n\
1564                -n|--new-files FILE1.ROOT [FILE2.ROOT ...] [-N|--new-process LABEL[:INSTANCE[:PROCESS]]]\n\
1565                [-m|--max-events MAXEVENTS] [-p|--prescales] [-c|--csv-output] [-j|--json-output]\n\
1566                [-r|--root-output] [-f|--file-check] [-d|--debug] [-q|--quiet] [-v|--verbose]\n\
1567                [-h|--help] [-F|--output-file] FILE_NAME\n\
1568 \n\
1569   -o|--old-files FILE1.ROOT [FILE2.ROOT ...]\n\
1570       input file(s) with the old (reference) trigger results\n\
1571 \n\
1572   -O|--old-process PROCESS\n\
1573       process name of the collection with the old (reference) trigger results\n\
1574       default: take the 'TriggerResults' from the last process\n\
1575 \n\
1576   -n|--new-files FILE1.ROOT [FILE2.ROOT ...]\n\
1577       input file(s) with the new trigger results to be compared with the reference\n\
1578       to read these from a different collection in the same files as\n\
1579       the reference, use '-n -' and specify the collection with -N (see below)\n\
1580 \n\
1581   -N|--new-process PROCESS\n\
1582       process name of the collection with the new (reference) trigger results\n\
1583       default: take the 'TriggerResults' from the last process\n\
1584 \n\
1585   -m|--max-events MAXEVENTS\n\
1586       compare only the first MAXEVENTS events\n\
1587       default: compare all the events in the original (reference) files\n\
1588 \n\
1589   -p|--prescales\n\
1590       do not ignore differences caused by HLTPrescaler modules\n\
1591 \n\
1592   -c|--csv-output\n\
1593       produce comparison results in a CSV format\n\
1594 \n\
1595   -j|--json-output\n\
1596       produce comparison results in a JSON format\n\
1597 \n\
1598   -r|--root-output\n\
1599       produce comparison results as histograms in a ROOT file\n\
1600 \n\
1601   -F|--output-file FILE_NAME\n\
1602       combine all RUNs to files with the specified custom name: FILE_NAME.json, FILE_NAME.root\n\
1603       default: a separate output file will be produced for each RUN with names suitable for the DQM GUI\n\
1604 \n\
1605   -f|--file-check\n\
1606       check existence of every old and new file before running the comparison\n\
1607       safer if files are run for the first time, but can cause a substantial delay\n\
1608 \n\
1609   -d|--debug\n\
1610       display messages about missing events and collections\n\
1611 \n\
1612   -q|--quiet\n\
1613       don't display summary printout with the list of affected trigger paths\n\
1614 \n\
1615   -v|--verbose LEVEL\n\
1616       set verbosity level:\n\
1617       1: event-by-event comparison results\n\
1618       2: + print the trigger candidates of the affected filters\n\
1619       3: + print all the trigger candidates for the affected events\n\
1620       default: 1\n\
1621 \n\
1622   -h|--help\n\
1623       print this help message, and exit"
1624         << std::endl;
1625   }
1626 };
1627 
1628 int main(int argc, char** argv) {
1629   // options
1630   const char optstring[] = "dfo:O:n:N:m:pcjrF:v::hq";
1631   const option longopts[] = {
1632       option{"debug", no_argument, nullptr, 'd'},
1633       option{"file-check", no_argument, nullptr, 'f'},
1634       option{"old-files", required_argument, nullptr, 'o'},
1635       option{"old-process", required_argument, nullptr, 'O'},
1636       option{"new-files", required_argument, nullptr, 'n'},
1637       option{"new-process", required_argument, nullptr, 'N'},
1638       option{"max-events", required_argument, nullptr, 'm'},
1639       option{"prescales", no_argument, nullptr, 'p'},
1640       option{"csv-output", optional_argument, nullptr, 'c'},
1641       option{"json-output", optional_argument, nullptr, 'j'},
1642       option{"root-output", optional_argument, nullptr, 'r'},
1643       option{"output-file", optional_argument, nullptr, 'F'},
1644       option{"verbose", optional_argument, nullptr, 'v'},
1645       option{"help", no_argument, nullptr, 'h'},
1646       option{"quiet", no_argument, nullptr, 'q'},
1647   };
1648 
1649   // Creating an HltDiff object with the default configuration
1650   auto hlt = new HltDiff();
1651 
1652   // parse the command line options
1653   int c = -1;
1654   while ((c = getopt_long(argc, argv, optstring, longopts, nullptr)) != -1) {
1655     switch (c) {
1656       case 'd':
1657         hlt->debug = true;
1658         break;
1659 
1660       case 'f':
1661         hlt->file_check = true;
1662         break;
1663 
1664       case 'o':
1665         hlt->old_files.emplace_back(optarg);
1666         while (optind < argc) {
1667           if (argv[optind][0] == '-')
1668             break;
1669           hlt->old_files.emplace_back(argv[optind]);
1670           ++optind;
1671         }
1672         break;
1673 
1674       case 'O':
1675         hlt->old_process = optarg;
1676         break;
1677 
1678       case 'n':
1679         hlt->new_files.emplace_back(optarg);
1680         while (optind < argc) {
1681           if (argv[optind][0] == '-')
1682             break;
1683           hlt->new_files.emplace_back(argv[optind]);
1684           ++optind;
1685         }
1686         break;
1687 
1688       case 'N':
1689         hlt->new_process = optarg;
1690         break;
1691 
1692       case 'm':
1693         hlt->max_events = atoi(optarg);
1694         break;
1695 
1696       case 'p':
1697         hlt->ignore_prescales = false;
1698         break;
1699 
1700       case 'c':
1701         hlt->csv_out = true;
1702         break;
1703 
1704       case 'j':
1705         hlt->json_out = true;
1706         break;
1707 
1708       case 'r':
1709         hlt->root_out = true;
1710         break;
1711 
1712       case 'F':
1713         hlt->output_file = optarg;
1714         break;
1715 
1716       case 'v':
1717         hlt->verbose = 1;
1718         if (optarg) {
1719           hlt->verbose = std::max(1, atoi(optarg));
1720         } else if (!optarg && nullptr != argv[optind] && '-' != argv[optind][0]) {
1721           // workaround for a bug in getopt which doesn't allow space before optional arguments
1722           const char* tmp_optarg = argv[optind++];
1723           hlt->verbose = std::max(1, atoi(tmp_optarg));
1724         }
1725         break;
1726 
1727       case 'h':
1728         hlt->usage(std::cerr);
1729         exit(0);
1730         break;
1731 
1732       case 'q':
1733         hlt->quiet = true;
1734         break;
1735 
1736       default:
1737         error(std::cerr);
1738         exit(1);
1739         break;
1740     }
1741   }
1742 
1743   if (hlt->old_files.empty()) {
1744     error(std::cerr, "hltDiff: please specify the 'old' file(s)");
1745     exit(1);
1746   }
1747   if (hlt->new_files.empty()) {
1748     error(std::cerr, "hltDiff: please specify the 'new' file(s)");
1749     exit(1);
1750   }
1751 
1752   hlt->compare();
1753 
1754   return 0;
1755 }