Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-10-25 09:55:50

0001 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
0002 #include "DataFormats/Common/interface/Handle.h"
0003 #include "FWCore/Utilities/interface/EDGetToken.h"
0004 #include "FWCore/Framework/interface/GetterOfProducts.h"
0005 #include "FWCore/Framework/interface/TypeMatch.h"
0006 
0007 #include "FWCore/Framework/interface/MakerMacros.h"
0008 #include "DataFormats/Common/interface/View.h"
0009 
0010 #include "FWCore/Framework/interface/Frameworkfwd.h"
0011 #include "FWCore/Framework/interface/stream/EDProducer.h"
0012 
0013 #include "FWCore/ServiceRegistry/interface/Service.h"
0014 #include "FWCore/Framework/interface/TriggerNamesService.h"
0015 #include "FWCore/ParameterSet/interface/Registry.h"
0016 
0017 #include "DataFormats/Common/interface/PathStatus.h"
0018 #include "FWCore/Framework/interface/ConsumesCollector.h"
0019 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0020 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0021 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0022 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
0023 #include "FWCore/Utilities/interface/EDGetToken.h"
0024 #include "FWCore/Utilities/interface/Exception.h"
0025 #include "FWCore/Utilities/interface/InputTag.h"
0026 #include "FWCore/Utilities/interface/propagate_const.h"
0027 
0028 #include <boost/spirit/include/qi.hpp>
0029 
0030 #include "DataFormats/L1Trigger/interface/P2GTCandidate.h"
0031 #include "DataFormats/L1Trigger/interface/P2GTAlgoBlock.h"
0032 
0033 #include <string>
0034 #include <memory>
0035 #include <utility>
0036 #include <map>
0037 #include <vector>
0038 #include <set>
0039 #include <tuple>
0040 
0041 /** pathStatusExpression is borrowed from \class edm::PathStatusFilter by W. David Dagenhart */
0042 
0043 namespace qi = boost::spirit::qi;
0044 namespace ascii = boost::spirit::ascii;
0045 
0046 namespace pathStatusExpression {
0047   class Evaluator {
0048   public:
0049     virtual ~Evaluator() {}
0050 
0051     enum EvaluatorType { Name, Not, And, Or, BeginParen };
0052     virtual EvaluatorType type() const = 0;
0053 
0054     virtual const char* pathName() const { return ""; }
0055 
0056     virtual void setLeft(std::unique_ptr<Evaluator>&&) {}
0057     virtual void setRight(std::unique_ptr<Evaluator>&&) {}
0058 
0059     virtual void print(std::ostream& out, unsigned int indentation) const {}
0060     virtual void init(edm::ConsumesCollector&) {}
0061     virtual bool evaluate(edm::Event const& event) const { return true; };
0062   };
0063 
0064   class Operand : public Evaluator {
0065   public:
0066     Operand(std::vector<char> const& pathName) : pathName_(pathName.begin(), pathName.end()) {}
0067 
0068     EvaluatorType type() const override { return Name; }
0069 
0070     void print(std::ostream& out, unsigned int indentation) const override {
0071       out << std::string(indentation, ' ') << pathName_ << "\n";
0072     }
0073 
0074     void init(edm::ConsumesCollector& iC) override { token_ = iC.consumes<edm::PathStatus>(edm::InputTag(pathName_)); }
0075 
0076     bool evaluate(edm::Event const& event) const override { return event.get(token_).accept(); }
0077 
0078   private:
0079     std::string pathName_;
0080     edm::EDGetTokenT<edm::PathStatus> token_;
0081   };
0082 
0083   class NotOperator : public Evaluator {
0084   public:
0085     EvaluatorType type() const override { return Not; }
0086 
0087     void setLeft(std::unique_ptr<Evaluator>&& v) override { operand_ = std::move(v); }
0088 
0089     void print(std::ostream& out, unsigned int indentation) const override {
0090       out << std::string(indentation, ' ') << "not\n";
0091       operand_->print(out, indentation + 4);
0092     }
0093 
0094     void init(edm::ConsumesCollector& iC) override { operand_->init(iC); }
0095 
0096     bool evaluate(edm::Event const& event) const override { return !operand_->evaluate(event); }
0097 
0098   private:
0099     edm::propagate_const<std::unique_ptr<Evaluator>> operand_;
0100   };
0101 
0102   template <typename T>
0103   class BinaryOperator : public Evaluator {
0104   public:
0105     EvaluatorType type() const override;
0106 
0107     void setLeft(std::unique_ptr<Evaluator>&& v) override { left_ = std::move(v); }
0108     void setRight(std::unique_ptr<Evaluator>&& v) override { right_ = std::move(v); }
0109 
0110     void print(std::ostream& out, unsigned int indentation) const override;
0111 
0112     void init(edm::ConsumesCollector& iC) override {
0113       left_->init(iC);
0114       right_->init(iC);
0115     }
0116 
0117     bool evaluate(edm::Event const& event) const override {
0118       T op;
0119       return op(left_->evaluate(event), right_->evaluate(event));
0120     }
0121 
0122   private:
0123     edm::propagate_const<std::unique_ptr<Evaluator>> left_;
0124     edm::propagate_const<std::unique_ptr<Evaluator>> right_;
0125   };
0126 
0127   template <>
0128   inline Evaluator::EvaluatorType BinaryOperator<std::logical_and<bool>>::type() const {
0129     return And;
0130   }
0131 
0132   template <>
0133   inline Evaluator::EvaluatorType BinaryOperator<std::logical_or<bool>>::type() const {
0134     return Or;
0135   }
0136 
0137   template <>
0138   void BinaryOperator<std::logical_and<bool>>::print(std::ostream& out, unsigned int indentation) const {
0139     out << std::string(indentation, ' ') << "and\n";
0140     left_->print(out, indentation + 4);
0141     right_->print(out, indentation + 4);
0142   }
0143   template <>
0144   void BinaryOperator<std::logical_or<bool>>::print(std::ostream& out, unsigned int indentation) const {
0145     out << std::string(indentation, ' ') << "or\n";
0146     left_->print(out, indentation + 4);
0147     right_->print(out, indentation + 4);
0148   }
0149 
0150   using AndOperator = BinaryOperator<std::logical_and<bool>>;
0151   using OrOperator = BinaryOperator<std::logical_or<bool>>;
0152 
0153   class BeginParenthesis : public Evaluator {
0154   public:
0155     EvaluatorType type() const override { return BeginParen; }
0156   };
0157 
0158   // This class exists to properly handle the precedence of the
0159   // operators and also handle the order of operations specified
0160   // by parentheses. (search for shunting yard algorithm on the
0161   // internet for a description of this algorithm)
0162   class ShuntingYardAlgorithm {
0163   public:
0164     void addPathName(std::vector<char> const& s) {
0165       operandStack.push_back(std::make_unique<Operand>(s));
0166       pathNames_.emplace_back(s.begin(), s.end());
0167     }
0168 
0169     void addOperatorNot() {
0170       if (operatorStack.empty() || operatorStack.back()->type() != Evaluator::Not) {
0171         operatorStack.push_back(std::make_unique<NotOperator>());
0172       } else {
0173         // Two Not operations in a row cancel and are the same as no operation at all.
0174         operatorStack.pop_back();
0175       }
0176     }
0177 
0178     void moveBinaryOperator() {
0179       std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
0180       backEvaluator->setRight(std::move(operandStack.back()));
0181       operandStack.pop_back();
0182       backEvaluator->setLeft(std::move(operandStack.back()));
0183       operandStack.pop_back();
0184       operandStack.push_back(std::move(backEvaluator));
0185       operatorStack.pop_back();
0186     }
0187 
0188     void moveNotOperator() {
0189       std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
0190       backEvaluator->setLeft(std::move(operandStack.back()));
0191       operandStack.pop_back();
0192       operandStack.push_back(std::move(backEvaluator));
0193       operatorStack.pop_back();
0194     }
0195 
0196     void addOperatorAnd() {
0197       while (!operatorStack.empty()) {
0198         std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
0199         if (backEvaluator->type() == Evaluator::And) {
0200           moveBinaryOperator();
0201         } else if (backEvaluator->type() == Evaluator::Not) {
0202           moveNotOperator();
0203         } else {
0204           break;
0205         }
0206       }
0207       operatorStack.push_back(std::make_unique<AndOperator>());
0208     }
0209 
0210     void addOperatorOr() {
0211       while (!operatorStack.empty()) {
0212         std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
0213         if (backEvaluator->type() == Evaluator::And || backEvaluator->type() == Evaluator::Or) {
0214           moveBinaryOperator();
0215         } else if (backEvaluator->type() == Evaluator::Not) {
0216           moveNotOperator();
0217         } else {
0218           break;
0219         }
0220       }
0221       operatorStack.push_back(std::make_unique<OrOperator>());
0222     }
0223 
0224     void addBeginParenthesis() { operatorStack.push_back(std::make_unique<BeginParenthesis>()); }
0225 
0226     void addEndParenthesis() {
0227       while (!operatorStack.empty()) {
0228         std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
0229         if (backEvaluator->type() == Evaluator::BeginParen) {
0230           operatorStack.pop_back();
0231           break;
0232         }
0233         if (backEvaluator->type() == Evaluator::And || backEvaluator->type() == Evaluator::Or) {
0234           moveBinaryOperator();
0235         } else if (backEvaluator->type() == Evaluator::Not) {
0236           moveNotOperator();
0237         }
0238       }
0239     }
0240 
0241     std::unique_ptr<Evaluator> finish() {
0242       while (!operatorStack.empty()) {
0243         std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
0244         // Just a sanity check. The grammar defined for the boost Spirit parser
0245         // should catch any errors of this type before we get here.
0246         if (backEvaluator->type() == Evaluator::BeginParen) {
0247           throw cms::Exception("LogicError") << "Should be impossible to get this error. Contact a Framework developer";
0248         }
0249         if (backEvaluator->type() == Evaluator::And || backEvaluator->type() == Evaluator::Or) {
0250           moveBinaryOperator();
0251         } else if (backEvaluator->type() == Evaluator::Not) {
0252           moveNotOperator();
0253         }
0254       }
0255       // Just a sanity check. The grammar defined for the boost Spirit parser
0256       // should catch any errors of this type before we get here.
0257       if (!operatorStack.empty() || operandStack.size() != 1U) {
0258         throw cms::Exception("LogicError") << "Should be impossible to get this error. Contact a Framework developer";
0259       }
0260       std::unique_ptr<Evaluator> temp = std::move(operandStack.back());
0261       operandStack.pop_back();
0262       return temp;
0263     }
0264 
0265     const std::vector<std::string>& pathNames() { return pathNames_; }
0266 
0267   private:
0268     std::vector<std::string> pathNames_;
0269     std::vector<std::unique_ptr<Evaluator>> operandStack;
0270     std::vector<std::unique_ptr<Evaluator>> operatorStack;
0271   };
0272 
0273   // Use boost Spirit to parse the logical expression character string
0274   template <typename Iterator>
0275   class Grammar : public qi::grammar<Iterator, ascii::space_type> {
0276   public:
0277     Grammar(ShuntingYardAlgorithm* algorithm) : Grammar::base_type(expression), algorithm_(algorithm) {
0278       // setup functors that call into shunting algorithm while parsing the logical expression
0279       auto addPathName = std::bind(&ShuntingYardAlgorithm::addPathName, algorithm_, std::placeholders::_1);
0280       auto addOperatorNot = std::bind(&ShuntingYardAlgorithm::addOperatorNot, algorithm_);
0281       auto addOperatorAnd = std::bind(&ShuntingYardAlgorithm::addOperatorAnd, algorithm_);
0282       auto addOperatorOr = std::bind(&ShuntingYardAlgorithm::addOperatorOr, algorithm_);
0283       auto addBeginParenthesis = std::bind(&ShuntingYardAlgorithm::addBeginParenthesis, algorithm_);
0284       auto addEndParenthesis = std::bind(&ShuntingYardAlgorithm::addEndParenthesis, algorithm_);
0285 
0286       // Define the syntax allowed in the logical expressions
0287       pathName = !unaryOperator >> !binaryOperatorTest >> (+qi::char_("a-zA-Z0-9_"))[addPathName];
0288       binaryOperand = (qi::lit('(')[addBeginParenthesis] >> expression >> qi::lit(')')[addEndParenthesis]) |
0289                       (unaryOperator[addOperatorNot] >> binaryOperand) | pathName;
0290       afterOperator = ascii::space | &qi::lit('(') | &qi::eoi;
0291       unaryOperator = qi::lit("not") >> afterOperator;
0292       // The only difference in the next two is that one calls a functor and the other does not
0293       binaryOperatorTest = (qi::lit("and") >> afterOperator) | (qi::lit("or") >> afterOperator);
0294       binaryOperator =
0295           (qi::lit("and") >> afterOperator)[addOperatorAnd] | (qi::lit("or") >> afterOperator)[addOperatorOr];
0296       expression = binaryOperand % binaryOperator;
0297     }
0298 
0299   private:
0300     qi::rule<Iterator> pathName;
0301     qi::rule<Iterator, ascii::space_type> binaryOperand;
0302     qi::rule<Iterator> afterOperator;
0303     qi::rule<Iterator> unaryOperator;
0304     qi::rule<Iterator> binaryOperatorTest;
0305     qi::rule<Iterator> binaryOperator;
0306     qi::rule<Iterator, ascii::space_type> expression;
0307 
0308     ShuntingYardAlgorithm* algorithm_;
0309   };
0310 }  // namespace pathStatusExpression
0311 
0312 using namespace l1t;
0313 
0314 class L1GTAlgoBlockProducer : public edm::stream::EDProducer<> {
0315 public:
0316   explicit L1GTAlgoBlockProducer(const edm::ParameterSet&);
0317   ~L1GTAlgoBlockProducer() override = default;
0318 
0319   static void fillDescriptions(edm::ConfigurationDescriptions&);
0320 
0321   void beginRun(const edm::Run& iRun, const edm::EventSetup& iSetup) override;
0322 
0323 private:
0324   struct AlgoDefinition {
0325     edm::propagate_const<std::unique_ptr<pathStatusExpression::Evaluator>> evaluator_;
0326     std::vector<std::string> pathNames_;
0327     std::set<std::tuple<std::string, std::string>> filtModules_;
0328   };
0329 
0330   void produce(edm::Event&, const edm::EventSetup&) override;
0331 
0332   edm::GetterOfProducts<P2GTCandidateVectorRef> getterOfPassedReferences_;
0333   std::map<std::string, AlgoDefinition> algoDefinitions_;
0334 };
0335 
0336 void L1GTAlgoBlockProducer::fillDescriptions(edm::ConfigurationDescriptions& description) {
0337   edm::ParameterSetDescription algoDesc;
0338   algoDesc.add<std::string>("name", "");
0339   algoDesc.add<std::string>("expression");
0340 
0341   edm::ParameterSetDescription desc;
0342   desc.addVPSet("algorithms", algoDesc);
0343 
0344   description.addWithDefaultLabel(desc);
0345 }
0346 
0347 L1GTAlgoBlockProducer::L1GTAlgoBlockProducer(const edm::ParameterSet& config)
0348     : getterOfPassedReferences_(edm::TypeMatch(), this) {
0349   edm::ConsumesCollector iC(consumesCollector());
0350 
0351   for (const auto& algoConfig : config.getParameterSetVector("algorithms")) {
0352     const std::string logicalExpression = algoConfig.getParameter<std::string>("expression");
0353     std::string name = algoConfig.getParameter<std::string>("name");
0354     if (name.empty()) {
0355       name = logicalExpression;
0356     }
0357 
0358     pathStatusExpression::ShuntingYardAlgorithm shuntingYardAlgorithm;
0359     pathStatusExpression::Grammar<std::string::const_iterator> grammar(&shuntingYardAlgorithm);
0360 
0361     auto it = logicalExpression.cbegin();
0362     if (!qi::phrase_parse(it, logicalExpression.cend(), grammar, ascii::space) || (it != logicalExpression.cend())) {
0363       throw cms::Exception("Configuration") << "Syntax error in logical expression. Here is an example of how\n"
0364                                             << "the syntax should look:\n"
0365                                             << "    \"path1 and not (path2 or not path3)\"\n"
0366                                             << "The expression must contain alternating appearances of operands\n"
0367                                             << "which are path names and binary operators which can be \'and\'\n"
0368                                             << "or \'or\', with a path name at the beginning and end. There\n"
0369                                             << "must be at least one path name. In addition to the alternating\n"
0370                                             << "path names and binary operators, the unary operator \'not\' can\n"
0371                                             << "be inserted before a path name or a begin parenthesis.\n"
0372                                             << "Parentheses are allowed. Parentheses must come in matching pairs.\n"
0373                                             << "Matching begin and end parentheses must contain a complete and\n"
0374                                             << "syntactically correct logical expression. There must be at least\n"
0375                                             << "one space or parenthesis between operators and path names. Extra\n"
0376                                             << "space is ignored and OK. Path names can only contain upper and\n"
0377                                             << "lower case letters, numbers, and underscores. A path name cannot\n"
0378                                             << "be the same as an operator name.\n";
0379     }
0380 
0381     AlgoDefinition definition;
0382 
0383     for (const std::string& pathName : shuntingYardAlgorithm.pathNames()) {
0384       definition.pathNames_.push_back(pathName);
0385     }
0386 
0387     definition.evaluator_ = shuntingYardAlgorithm.finish();
0388 
0389     definition.evaluator_->init(iC);
0390     algoDefinitions_.emplace(std::move(name), std::move(definition));
0391   }
0392 
0393   callWhenNewProductsRegistered(getterOfPassedReferences_);
0394   produces<P2GTAlgoBlockCollection>();
0395 }
0396 
0397 void L1GTAlgoBlockProducer::beginRun(const edm::Run& iRun, const edm::EventSetup& iSetup) {
0398   const std::string& pName = edm::Service<edm::service::TriggerNamesService>()->getProcessName();
0399 
0400   edm::ProcessConfiguration cfg;
0401 
0402   iRun.processHistory().getConfigurationForProcess(pName, cfg);
0403 
0404   const edm::ParameterSet* pset = edm::pset::Registry::instance()->getMapped(cfg.parameterSetID());
0405 
0406   for (auto& [name, algoDef] : algoDefinitions_) {
0407     for (const std::string& pathName : algoDef.pathNames_) {
0408       if (pset->existsAs<std::vector<std::string>>(pathName)) {
0409         const auto& modules = pset->getParameter<std::vector<std::string>>(pathName);
0410         for (const auto& mod : modules) {
0411           if (mod.front() != std::string("-") && pset->exists(mod)) {
0412             const auto& modPSet = pset->getParameterSet(mod);
0413             if (modPSet.getParameter<std::string>("@module_edm_type") == "EDFilter") {
0414               if (modPSet.getParameter<std::string>("@module_type") == "L1GTSingleObjectCond") {
0415                 algoDef.filtModules_.insert({mod, modPSet.getParameter<edm::InputTag>("tag").instance()});
0416               } else if (modPSet.getParameter<std::string>("@module_type") == "L1GTDoubleObjectCond") {
0417                 algoDef.filtModules_.insert(
0418                     {mod, modPSet.getParameterSet("collection1").getParameter<edm::InputTag>("tag").instance()});
0419                 algoDef.filtModules_.insert(
0420                     {mod, modPSet.getParameterSet("collection2").getParameter<edm::InputTag>("tag").instance()});
0421               } else if (modPSet.getParameter<std::string>("@module_type") == "L1GTTripleObjectCond") {
0422                 algoDef.filtModules_.insert(
0423                     {mod, modPSet.getParameterSet("collection1").getParameter<edm::InputTag>("tag").instance()});
0424                 algoDef.filtModules_.insert(
0425                     {mod, modPSet.getParameterSet("collection2").getParameter<edm::InputTag>("tag").instance()});
0426                 algoDef.filtModules_.insert(
0427                     {mod, modPSet.getParameterSet("collection3").getParameter<edm::InputTag>("tag").instance()});
0428               } else if (modPSet.getParameter<std::string>("@module_type") == "L1GTQuadObjectCond") {
0429                 algoDef.filtModules_.insert(
0430                     {mod, modPSet.getParameterSet("collection1").getParameter<edm::InputTag>("tag").instance()});
0431                 algoDef.filtModules_.insert(
0432                     {mod, modPSet.getParameterSet("collection2").getParameter<edm::InputTag>("tag").instance()});
0433                 algoDef.filtModules_.insert(
0434                     {mod, modPSet.getParameterSet("collection3").getParameter<edm::InputTag>("tag").instance()});
0435                 algoDef.filtModules_.insert(
0436                     {mod, modPSet.getParameterSet("collection4").getParameter<edm::InputTag>("tag").instance()});
0437               }
0438             }
0439           }
0440         }
0441       }
0442     }
0443   }
0444 }
0445 
0446 void L1GTAlgoBlockProducer::produce(edm::Event& event, const edm::EventSetup& eventSetup) {
0447   std::vector<edm::Handle<P2GTCandidateVectorRef>> handles;
0448   getterOfPassedReferences_.fillHandles(event, handles);
0449 
0450   std::unique_ptr<P2GTAlgoBlockCollection> algoCollection = std::make_unique<P2GTAlgoBlockCollection>();
0451   algoCollection->reserve(algoDefinitions_.size());
0452 
0453   for (const auto& [name, algoDef] : algoDefinitions_) {
0454     bool initial = algoDef.evaluator_->evaluate(event);
0455     // TODO apply prescale and bunch mask
0456 
0457     P2GTCandidateVectorRef trigObjects;
0458 
0459     if (initial) {
0460       for (const auto& handle : handles) {
0461         const std::string& module = handle.provenance()->moduleLabel();
0462         const std::string& instance = handle.provenance()->productInstanceName();
0463 
0464         if (algoDef.filtModules_.count({module, instance}) > 0) {
0465           trigObjects.insert(trigObjects.end(), handle->begin(), handle->end());
0466         }
0467       }
0468     }
0469 
0470     algoCollection->emplace_back(name, initial, initial, initial, std::move(trigObjects));
0471   }
0472 
0473   event.put(std::move(algoCollection));
0474 }
0475 
0476 DEFINE_FWK_MODULE(L1GTAlgoBlockProducer);