Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-29 08:51:04

0001 // -*- C++ -*-
0002 #include "Rivet/Analysis.hh"
0003 #include "Rivet/Projections/FinalState.hh"
0004 #include "Rivet/Projections/FastJets.hh"
0005 #include "Rivet/Projections/LeptonFinder.hh"
0006 #include "Rivet/Projections/TauFinder.hh"
0007 #include "Rivet/Projections/MissingMomentum.hh"
0008 #include "Rivet/Projections/DirectFinalState.hh"
0009 #include "Rivet/Projections/Smearing.hh"
0010 #include "Rivet/Tools/Cutflow.hh"
0011 
0012 namespace Rivet {
0013 
0014 
0015   /// @brief Simplified analysis API with predefined physics objects
0016   class SimpleAnalysis : public Analysis {
0017   public:
0018 
0019     /// Physics-object ID classes
0020     enum class IDClass { LOOSE, MEDIUM, TIGHT };
0021 
0022     /// Constructor, passing arg to base class and supporting subclassing
0023     SimpleAnalysis(const std::string& name)
0024       : Analysis(name)
0025     {    }
0026 
0027     /// Destructor to support subclassing
0028     virtual ~SimpleAnalysis()
0029     {    }
0030 
0031 
0032     /// @name Analysis methods
0033     /// @{
0034 
0035     /// Initialise built-in projections before the run, after user init()
0036     ///
0037     /// @todo Only register default proj if the name hasn't already
0038     /// been used, to allow user overrides
0039     void postInit() {
0040 
0041       // Electron projections
0042       FinalState es(_acut && _ecut && Cuts::abspid == PID::ELECTRON);
0043       LeptonFinder es_prompt(_acut && _ecut && Cuts::abspid == PID::ELECTRON, 0.1);
0044       for (const auto& classkv : _idclasses()) {
0045         const IDClass& idc = classkv.first;
0046         const string p = "Electrons";
0047         const string aname = _pckey(true, idc, p);
0048         if (!hasProjection(aname))
0049           declare(SmearedParticles(es, _effs_electron[idc], _smear_electron), aname);
0050         const string pname = _pckey(false, idc, p);
0051         if (!hasProjection(pname))
0052           declare(SmearedParticles(es_prompt, _effs_electron[idc], _smear_electron), pname);
0053       }
0054 
0055       // Muon projections
0056       FinalState ms(_acut && _mcut && Cuts::abspid == PID::MUON);
0057       LeptonFinder ms_prompt(_acut && _mcut && Cuts::abspid == PID::MUON, 0.1);
0058       for (const auto& classkv : _idclasses()) {
0059         const IDClass& idc = classkv.first;
0060         const string p = "Muons";
0061         const string aname = _pckey(true, idc, p);
0062         if (!hasProjection(aname))
0063           declare(SmearedParticles(ms, _effs_muon[idc], _smear_muon), aname);
0064         const string pname = _pckey(false, idc, p);
0065         if (!hasProjection(pname))
0066           declare(SmearedParticles(ms_prompt, _effs_muon[idc], _smear_muon), pname);
0067       }
0068 
0069       // Photon projections
0070       FinalState ys(_acut && _phocut && Cuts::abspid == PID::PHOTON);
0071       DirectFinalState ys_prompt(_acut && _phocut && Cuts::abspid == PID::PHOTON);
0072       for (const auto& classkv : _idclasses()) {
0073         const IDClass& idc = classkv.first;
0074         const string p = "Photons";
0075         const string aname = _pckey(true, idc, p);
0076         if (!hasProjection(aname))
0077           declare(SmearedParticles(ys, _effs_photon[idc], _smear_photon), aname);
0078         const string pname = _pckey(false, idc, p);
0079         if (!hasProjection(pname))
0080           declare(SmearedParticles(ys_prompt, _effs_photon[idc], _smear_photon), pname);
0081       }
0082 
0083       // Tau projections
0084       TauFinder ts(TauDecay::HADRONIC, LeptonOrigin::ANY, _acut && _tcut);
0085       TauFinder ts_prompt(TauDecay::HADRONIC, LeptonOrigin::NODECAY, _acut && _tcut);
0086       for (const auto& classkv : _idclasses()) {
0087         const IDClass& idc = classkv.first;
0088         const string p = "Taus";
0089         const string aname = _pckey(true, idc, p);
0090         if (!hasProjection(aname))
0091           declare(SmearedParticles(ts, _effs_tau[idc], _smear_tau), aname);
0092         const string pname = _pckey(false, idc, p);
0093         if (!hasProjection(pname))
0094           declare(SmearedParticles(ts_prompt, _effs_tau[idc], _smear_tau), pname);
0095       }
0096 
0097       // Track projection
0098       const FinalState trkfs(_acut && _trkcut && Cuts::abscharge > 0);
0099       if (!hasProjection("Tracks"))
0100         declare(SmearedParticles(trkfs, _eff_trk, _smear_trk), "Tracks");
0101 
0102       // Jet projections
0103       const FinalState allfs(_acut);
0104       declare(allfs, "AllParticles");
0105       for (const auto& jditem : _jdefs) {
0106         const JetScheme& jd = jditem.second;
0107         /// @todo Allow multiple jet-smearing / tagging fns... how to default?
0108         FastJets jetfs(allfs, jd.alg, jd.R, jd.muons, jd.invis);
0109         /// @todo The override is more complicated here since users already control the names... throw an exception instead?
0110         if (!hasProjection("Jets"+jditem.first))
0111           declare(SmearedJets(jetfs, _smears_jet[jditem.first], _effs_btag[jditem.first]), "Jets"+jditem.first);
0112       }
0113 
0114       // Missing momentum projection
0115       if (!hasProjection("MET"))
0116         declare(SmearedMET(MissingMomentum(allfs), _smearps_met, _smear_met), "MET");
0117     }
0118 
0119 
0120     /// Reset per-event members, before the user analyze()
0121     void preAnalyze(const Event& event) {
0122       _electrons.clear();
0123       _muons.clear();
0124       _taus.clear();
0125       _photons.clear();
0126       _jets.clear();
0127       _bjets.clear();
0128     }
0129 
0130     /// @}
0131 
0132 
0133 
0134     /// @name Detector and reco config
0135     /// @{
0136 
0137     /// @name Detector smearing setup (call in init)
0138     /// @{
0139 
0140     /// Set the electron reco functions for a given ID class
0141     void setElectronReco(const ParticleEffFn& eff, const ParticleSmearFn& smear=PARTICLE_SMEAR_IDENTITY, IDClass idc=IDClass::MEDIUM) {
0142       _effs_electron[idc] = eff;
0143       _smear_electron = smear;
0144     }
0145     /// Set the muon reco functions for a given ID class
0146     void setMuonReco(const ParticleEffFn& eff, const ParticleSmearFn& smear=PARTICLE_SMEAR_IDENTITY, IDClass idc=IDClass::MEDIUM) {
0147       _effs_muon[idc] = eff;
0148       _smear_muon = smear;
0149     }
0150     /// Set the photon reco functions for a given ID class
0151     void setPhotonReco(const ParticleEffFn& eff, const ParticleSmearFn& smear=PARTICLE_SMEAR_IDENTITY, IDClass idc=IDClass::MEDIUM) {
0152       _effs_photon[idc] = eff;
0153       _smear_photon = smear;
0154     }
0155     /// Set the tau reco functions for a given ID class
0156     void setTauReco(const ParticleEffFn& eff, const ParticleSmearFn& smear=PARTICLE_SMEAR_IDENTITY, IDClass idc=IDClass::MEDIUM) {
0157       _effs_tau[idc] = eff;
0158       _smear_tau = smear;
0159     }
0160     /// Set the track reco functions for a given jet name
0161     void setTrkReco(const ParticleEffFn& eff, const ParticleSmearFn& smear=PARTICLE_SMEAR_IDENTITY) {
0162       _eff_trk = eff;
0163       _smear_trk = smear;
0164     }
0165     /// Set the jet reco functions for a given jet name
0166     void setJetReco(const JetSmearFn& smear, const JetEffFn& btageff, const std::string& jetname="DEFAULT") {
0167       _smears_jet[jetname] = smear;
0168       _effs_btag[jetname] = btageff;
0169     }
0170     /// Set the MET reco-params function
0171     void setMETReco(const METSmearParamsFn& smearps) {
0172       _smearps_met = smearps;
0173     }
0174     /// Set the MET reco functions
0175     void setMETReco(const METSmearParamsFn& smearps, const METSmearFn& smear) {
0176       _smearps_met = smearps;
0177       _smear_met = smear;
0178     }
0179 
0180     /// @}
0181 
0182 
0183     /// @name Baseline object config (call in init)
0184     ///
0185     /// @todo Allow specialising cuts to ID class
0186     /// @{
0187     void setAccCut(const Cut& cut) { _acut = cut; }
0188     void setElectronCut(const Cut& cut) { _ecut = cut; }
0189     void setMuonCut(const Cut& cut) { _mcut = cut; }
0190     void setPhotonCut(const Cut& cut) { _phocut = cut; }
0191     void setTauCut(const Cut& cut) { _tcut = cut; }
0192     void setTrkCut(const Cut& cut) { _trkcut = cut; }
0193     void setJetDef(const JetScheme& jdef, const std::string& jetname="DEFAULT") { _jdefs[jetname] = jdef; }
0194     void setJetCut(const Cut& cut, const std::string& jetname="DEFAULT") { _jcuts[jetname] = cut; }
0195     /// @}
0196 
0197     /// @}
0198 
0199 
0200 
0201     /// @name Obtaining physics objects
0202     /// @{
0203 
0204     /// Get the pT-ordered set of reconstructed prompt electrons
0205     const Particles& electrons(IDClass eclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0206       const std::string kpc = _pckey(include_nonprompt, eclass);
0207       if (_electrons.find(kpc) == _electrons.end()) { //< if not cached
0208         _electrons[kpc] = apply<ParticleFinder>(currentEvent(), "Electrons"+kpc).particlesByPt();
0209       }
0210       return _electrons.at(kpc);
0211     }
0212     /// Get the pT-ordered set of reconstructed prompt electrons, with extra cuts
0213     Particles electrons(const Cut& cut, IDClass eclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0214       return select(electrons(eclass, include_nonprompt), cut);
0215     }
0216 
0217 
0218     /// Get the pT-ordered set of reconstructed muons
0219     const Particles& muons(IDClass muclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0220       const std::string kpc = _pckey(include_nonprompt, muclass);
0221       if (_muons.find(kpc) == _muons.end()) { //< if not cached
0222         _muons[kpc] = apply<ParticleFinder>(currentEvent(), "Muons"+kpc).particlesByPt();
0223       }
0224       return _muons.at(kpc);
0225     }
0226     /// Get the pT-ordered set of reconstructed muons, with extra cuts
0227     Particles muons(const Cut& cut, IDClass muclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0228       return select(muons(muclass, include_nonprompt), cut);
0229     }
0230 
0231 
0232     /// Get the pT-ordered set of reconstructed prompt and isolated photons
0233     const Particles& photons(IDClass phoclass=IDClass::MEDIUM, bool include_nonprompt=false, double dRiso=0.4, double isofrac=0.1) const {
0234       const std::string kpc = _pckey(include_nonprompt, phoclass);
0235       if (_photons.find(kpc) == _photons.end()) { //< if not cached
0236         Particles ystmp = apply<ParticleFinder>(currentEvent(), "Photons"+kpc).particlesByPt();
0237         const Particles& clusters = apply<ParticleFinder>(currentEvent(), "AllParticles").particles();
0238         _photons[kpc].reserve(ystmp.size());
0239         for (const Particle& y : ystmp) {
0240           double sumpt = 0;
0241           for (const Particle& cl : select(clusters, deltaRLess(y, dRiso))) {
0242             if (deltaR(cl, y) == 0.01) continue; //< irresolvably close
0243             sumpt += cl.pT();
0244           }
0245           if (sumpt/y.pT() < isofrac) _photons[kpc].push_back(y);
0246         }
0247       }
0248       return _photons.at(kpc);
0249     }
0250     /// Get the pT-ordered set of reconstructed prompt and isolated photons, with extra cuts
0251     Particles photons(const Cut& cut, IDClass phoclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0252       return select(photons(phoclass, include_nonprompt), cut);
0253     }
0254 
0255 
0256     /// Get the pT-ordered set of reconstructed prompt (hadronic) taus
0257     const Particles& taus(IDClass tauclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0258       const std::string kpc = _pckey(include_nonprompt, tauclass);
0259       if (_taus.find(kpc) == _taus.end()) { //< if not cached
0260         _taus[kpc] = apply<ParticleFinder>(currentEvent(), "Taus"+kpc).particlesByPt();
0261       }
0262       return _taus.at(kpc);
0263     }
0264     /// Get the pT-ordered set of reconstructed prompt (hadronic) taus, with extra cuts
0265     Particles taus(const Cut& cut, IDClass tauclass=IDClass::MEDIUM, bool include_nonprompt=false) const {
0266       return select(taus(tauclass, include_nonprompt), cut);
0267     }
0268 
0269 
0270     /// Get the pT-ordered set of tracks (charged particles)
0271     ///
0272     /// @note This less-used object type is not cached, as they are too low-level
0273     /// for e.g. overlap removal post-projection filtering.
0274     const Particles tracks() const {
0275       return apply<ParticleFinder>(currentEvent(), "Tracks").particlesByPt();
0276     }
0277     /// Get the pT-ordered set of tracks (charged particles), with extra cuts
0278     ///
0279     /// @note This less-used object type is not cached, as they are too low-level
0280     /// for e.g. overlap removal post-projection filtering.
0281     Particles tracks(const Cut& cut) const {
0282       return apply<ParticleFinder>(currentEvent(), "Tracks").particlesByPt(cut);
0283     }
0284 
0285 
0286     /// Get the set of jet-collection names (also usable for b-jets)
0287     const std::vector<std::string> jetnames() const {
0288       std::vector<std::string> rtn;
0289       rtn.reserve(_jets.size());
0290       for (const auto& kv : _jets) rtn.push_back(kv.first);
0291       return rtn;
0292     }
0293 
0294     /// Get a pT-ordered named set of reconstructed jets
0295     const Jets& jets(const std::string& jetname="DEFAULT") const {
0296       if (_jets.find(jetname) == _jets.end()) { //< not cached
0297         /// @todo Error handling if no projection or cut registered for that jet name
0298         _jets[jetname] = apply<JetFinder>(currentEvent(), "Jets"+jetname).jetsByPt(_jcuts.at(jetname));
0299       }
0300       return _jets.at(jetname);
0301     }
0302     /// Get a pT-ordered named set of reconstructed jets, with extra cuts
0303     Jets jets(const Cut& cut, const std::string& jetname="DEFAULT") const {
0304       return select(jets(jetname), cut);
0305     }
0306 
0307 
0308     /// Get a pT-ordered named set of reconstructed b-tagged jets
0309     const Jets& bjets(const std::string& jetname="DEFAULT") const {
0310       if (_bjets.find(jetname) == _bjets.end()) { //< not cached
0311         _bjets[jetname] = select(jets(jetname), hasBTag(_bcut, _bdeltaR));
0312       }
0313       return _bjets.at(jetname);
0314     }
0315     /// Get a pT-ordered named set of reconstructed b-tagged jets, with extra cuts
0316     Jets bjets(const Cut& cut, const std::string& jetname="DEFAULT") const {
0317       return select(bjets(jetname), cut);
0318     }
0319 
0320 
0321     /// Get the MET vector
0322     Vector3 vmet() const {
0323       return apply<METFinder>(currentEvent(), "MET").vectorMissingPt();
0324     }
0325 
0326     /// Get the MET scalar
0327     double met() const {
0328       return vmet().mod();
0329     }
0330 
0331     /// Get the SET scalar
0332     double set() const {
0333       return apply<METFinder>(currentEvent(), "MET").scalarEt();
0334     }
0335 
0336     /// Get the MET significance
0337     double metSignf() const {
0338       return apply<SmearedMET>(currentEvent(), "MET").missingEtSignf();
0339     }
0340 
0341     /// @}
0342 
0343 
0344     /// @name Event-manipulation functions
0345     /// @{
0346 
0347     /// @brief Perform a simple, in-place overlap removal between all physics objects
0348     ///
0349     /// @note This is not a specific experiment, physics group, or analysis' overlap
0350     /// removal scheme: in general, every analysis does something different and if you
0351     /// want an exact replication of the experimental procedure, you need to implement
0352     /// exactly (as far as possible at truth/smeared-level) the procedure used in the
0353     /// original analysis. This isn't Rivet-specific: it applies to any analysis preservation!
0354     ///
0355     /// This implementation will do a reasonable basic job of removing double-counting,
0356     /// though. It first removes any jets (including b-jets) within 0.2 of an electron or
0357     /// muon. Then removes photons too close to electrons. And finally removes any electron
0358     /// or muon within the radius of the remaining jet. Suggestions for improvement or
0359     /// flexible/user-friendly generalisation are welcome!
0360     ///
0361     /// @warning Note that this currently does no overlap-removal with respect to taus.
0362     ///
0363     /// @todo Anything specific to taus? (E.g. OR wrt jets based on #tracks?) To b-jets?
0364     void doSimpleOverlapRemoval(IDClass eclass=IDClass::MEDIUM, IDClass muclass=IDClass::MEDIUM,
0365                                 IDClass phoclass=IDClass::MEDIUM, //IDClass tauclass=IDClass::MEDIUM,
0366                                 bool include_nonprompt=false, const std::string& jetname="DEFAULT") {
0367       // Get non-const handles for filtering (and ensure objects exist)
0368       Jets& myjets = const_cast<Jets&>(jets(jetname));
0369       Jets& mybjets = const_cast<Jets&>(bjets(jetname));
0370       Particles& myes = const_cast<Particles&>(electrons(eclass, include_nonprompt));
0371       Particles& myms = const_cast<Particles&>(muons(muclass, include_nonprompt));
0372       //Particles& myts = const_cast<Particles&>(taus(tauclass, include_nonprompt));
0373       Particles& myys = const_cast<Particles&>(photons(phoclass, include_nonprompt));
0374       // Remove all jets within dR < 0.2 of a dressed lepton
0375       idiscardIfAnyDeltaRLess(myjets, myes, 0.2);
0376       idiscardIfAnyDeltaRLess(myjets, myms, 0.2);
0377       idiscardIfAnyDeltaRLess(mybjets, myes, 0.2);
0378       idiscardIfAnyDeltaRLess(mybjets, myms, 0.2);
0379       // Remove all isolated photons within dR < 0.2 of a dressed electron
0380       /// @todo Also remove some electrons?
0381       idiscardIfAnyDeltaRLess(myys, myes, 0.2);
0382       // Remove dressed leptons within the radius of a remaining jet
0383       idiscardIfAnyDeltaRLess(myes, myjets, _jdefs.at(jetname).R);
0384       idiscardIfAnyDeltaRLess(myms, myjets, _jdefs.at(jetname).R);
0385       /// @todo Add anything for hadronic taus? Tau-jet overlap is probably "correct"
0386     }
0387 
0388 
0389     /// Re-cluster small-radius jets as large-radius ones, writing to a new jet collection
0390     // void doJetRecluster(/* jet collection names (in and out), recluster-alg enum and dR */) {
0391     //   /// @todo ...
0392     // }
0393 
0394     /// @}
0395 
0396 
0397     /// @name Stats helpers
0398     /// @{
0399 
0400     /// Helper function to elide the xsec/sumW scalefactor
0401     template<typename T>
0402     void scaleToIntLumi(T& ao, double intlumi) {
0403       scale(ao, intlumi*crossSection()/sumOfWeights());
0404     }
0405 
0406     /// @}
0407 
0408 
0409     /// @name Projection access
0410     /// @{
0411 
0412     /// Direct access to the underlying projection
0413     const ParticleFinder& electronsProj(IDClass eclass=IDClass::MEDIUM, bool include_nonprompt=false) {
0414       return getProjection<ParticleFinder>(_pckey(include_nonprompt, eclass, "Electrons"));
0415     }
0416     /// Direct access to the underlying projection
0417     const ParticleFinder& muonsProj(IDClass muclass=IDClass::MEDIUM, bool include_nonprompt=false) {
0418       return getProjection<ParticleFinder>(_pckey(include_nonprompt, muclass, "Muons"));
0419     }
0420     /// Direct access to the underlying projection
0421     const ParticleFinder& tausProj(IDClass tauclass=IDClass::MEDIUM, bool include_nonprompt=false) {
0422       return getProjection<ParticleFinder>(_pckey(include_nonprompt, tauclass, "Taus"));
0423     }
0424     /// Direct access to the underlying projection
0425     const ParticleFinder& photonsProj(IDClass phoclass=IDClass::MEDIUM, bool include_nonprompt=false) {
0426       return getProjection<ParticleFinder>(_pckey(include_nonprompt, phoclass, "Photons"));
0427     }
0428     /// Direct access to the underlying projection
0429     const ParticleFinder& tracksProj() {
0430       return getProjection<ParticleFinder>("Tracks");
0431     }
0432     /// Direct access to the underlying projection
0433     const JetFinder& jetsProj(const std::string& jetname="DEFAULT") {
0434       return getProjection<JetFinder>("Jets_"+jetname);
0435     }
0436     /// Direct access to the underlying projection
0437     const METFinder& metProj() { return getProjection<METFinder>("MET"); }
0438 
0439     /// @}
0440 
0441 
0442   private:
0443 
0444     /// @name Efficiency and smearing functions
0445     /// @{
0446     std::map<IDClass, ParticleEffFn> _effs_electron{ { IDClass::LOOSE, PARTICLE_EFF_ONE }, { IDClass::MEDIUM, PARTICLE_EFF_ONE }, { IDClass::TIGHT, PARTICLE_EFF_ONE } };
0447     std::map<IDClass, ParticleEffFn> _effs_muon{ { IDClass::LOOSE, PARTICLE_EFF_ONE }, { IDClass::MEDIUM, PARTICLE_EFF_ONE }, { IDClass::TIGHT, PARTICLE_EFF_ONE } };
0448     std::map<IDClass, ParticleEffFn> _effs_photon{ { IDClass::LOOSE, PARTICLE_EFF_ONE }, { IDClass::MEDIUM, PARTICLE_EFF_ONE }, { IDClass::TIGHT, PARTICLE_EFF_ONE } };
0449     std::map<IDClass, ParticleEffFn> _effs_tau{ { IDClass::LOOSE, PARTICLE_EFF_ONE }, { IDClass::MEDIUM, PARTICLE_EFF_ONE }, { IDClass::TIGHT, PARTICLE_EFF_ONE } };
0450     ParticleSmearFn _smear_electron{PARTICLE_SMEAR_IDENTITY}, _smear_muon{PARTICLE_SMEAR_IDENTITY}, _smear_photon{PARTICLE_SMEAR_IDENTITY}, _smear_tau{PARTICLE_SMEAR_IDENTITY};
0451     ParticleEffFn _eff_trk{PARTICLE_EFF_ONE};
0452     ParticleSmearFn _smear_trk{PARTICLE_SMEAR_IDENTITY};
0453     std::map<std::string, JetSmearFn> _smears_jet{ { "DEFAULT", JET_SMEAR_IDENTITY } };
0454     std::map<std::string, JetEffFn> _effs_btag{ { "DEFAULT", JET_BTAG_IDENTITY } };
0455     METSmearParamsFn _smearps_met{MET_SMEARPARAMS_IDENTITY};
0456     METSmearFn _smear_met{MET_SMEAR_IDENTITY};
0457     /// @}
0458 
0459     /// @name Kinematic requirements on physics objects
0460     /// @{
0461     Cut _acut = Cuts::abseta < 5.0; //< base acceptance, applied to all
0462     Cut _ecut = Cuts::pT > 10*GeV;
0463     Cut _mcut = Cuts::pT > 10*GeV;
0464     Cut _tcut = Cuts::pT > 10*GeV;
0465     Cut _trkcut = Cuts::abseta < 2.5;
0466     Cut _phocut = Cuts::pT > 10*GeV;
0467     std::map<std::string, Cut> _jcuts = {{ "DEFAULT",  Cuts::pT > 20*GeV && Cuts::abseta < 5.0 }};
0468     std::map<std::string, JetScheme> _jdefs = {{ "DEFAULT", JetScheme(JetAlg::ANTIKT, 0.4) }};
0469     /// @}
0470 
0471 
0472     /// @name Kinematic requirements on tagging particles
0473     ///
0474     /// @todo Make specific to jet name?
0475     /// @{
0476     Cut _bcut = Cuts::pT > 5*GeV && Cuts::abseta < 2.5;
0477     double _bdeltaR = 0.3;
0478     /// @}
0479 
0480 
0481     /// @name Physics-object collections
0482     ///
0483     /// @note Mutable to allow const-correct caching
0484     /// @{
0485 
0486     // Internal typedefs
0487     // using ParticlesMap = std::map<std::string, Particles>;
0488     // using JetsMap = std::map<std::string, Jets>;
0489 
0490     /// Named maps of prompt/inclusive particle collections
0491     mutable std::map<std::string, Particles> _electrons, _muons, _taus, _photons;
0492 
0493     /// Named maps of jet collections
0494     mutable std::map<std::string, Jets> _jets, _bjets;
0495 
0496     /// @}
0497 
0498 
0499     /// List of ID classes (and their string reprs) to iterate over
0500     //
0501     /// @note Following advice at https://qr.ae/p2EvHB
0502     const std::map<IDClass, std::string>& _idclasses() const {
0503       // static const std::vector<IDClass> _idclasses{ {IDClass::LOOSE, IDClass::MEDIUM, IDClass::TIGHT} };
0504       static const auto* map = new std::map<IDClass, std::string>{
0505         {IDClass::LOOSE, "Loose"}, {IDClass::MEDIUM, "Medium"}, {IDClass::TIGHT, "Tight"} };
0506       return *map;
0507     }
0508     const std::string& _idclassname(const IDClass& idc) const {
0509       return _idclasses().at(idc);
0510     }
0511 
0512     /// Create a promptness-IDclass string for keying maps
0513     std::string _pckey(bool include_nonprompt, IDClass idc, const std::string& prefix="") const {
0514       return prefix + (include_nonprompt ? "All" : "Prompt") + _idclassname(idc);
0515     }
0516 
0517   };
0518 
0519 
0520 }
0521 
0522 
0523 /// @def RIVET_DEFAULT_SIMPLEANALYSIS_CTOR
0524 ///
0525 /// Preprocessor define to prettify the awkward derived constructor, with a name-string argument
0526 #define RIVET_DEFAULT_SIMPLEANALYSIS_CTOR(clsname) clsname() : SimpleAnalysis(# clsname) {}
0527 
0528 
0529 /// @def setDetSmearing
0530 /// A macro for setting a whole family of eff/smearing functions at once
0531 ///
0532 /// @todo Specify electron medium/tight, book both to allow control, or generalise
0533 #define setDetSmearing(DET, BTAG) do {                                  \
0534   setElectronReco(ELECTRON_EFF_ ## DET ## _LOOSE, ELECTRON_SMEAR_ ## DET, IDClass::LOOSE) ; \
0535   setElectronReco(ELECTRON_EFF_ ## DET ## _MEDIUM, ELECTRON_SMEAR_ ## DET, IDClass::MEDIUM) ; \
0536   setElectronReco(ELECTRON_EFF_ ## DET ## _TIGHT, ELECTRON_SMEAR_ ## DET, IDClass::TIGHT) ; \
0537   setMuonReco(MUON_EFF_ ## DET ## _LOOSE, MUON_SMEAR_ ## DET, IDClass::LOOSE) ; \
0538   setMuonReco(MUON_EFF_ ## DET ## _MEDIUM, MUON_SMEAR_ ## DET, IDClass::MEDIUM) ; \
0539   setMuonReco(MUON_EFF_ ## DET ## _TIGHT, MUON_SMEAR_ ## DET, IDClass::TIGHT) ; \
0540   setPhotonReco(PHOTON_EFF_ ## DET ## _LOOSE, ELECTRON_SMEAR_ ## DET, IDClass::LOOSE) ; \
0541   setPhotonReco(PHOTON_EFF_ ## DET ## _MEDIUM, ELECTRON_SMEAR_ ## DET, IDClass::MEDIUM) ; \
0542   setPhotonReco(PHOTON_EFF_ ## DET ## _TIGHT, ELECTRON_SMEAR_ ## DET, IDClass::TIGHT) ; \
0543   setTauReco(TAU_EFF_ ## DET ## _LOOSE, TAU_SMEAR_ ## DET, IDClass::LOOSE) ; \
0544   setTauReco(TAU_EFF_ ## DET ## _MEDIUM, TAU_SMEAR_ ## DET, IDClass::MEDIUM) ; \
0545   setTauReco(TAU_EFF_ ## DET ## _TIGHT, TAU_SMEAR_ ## DET, IDClass::TIGHT) ; \
0546   setTrkReco(TRK_EFF_ ## DET, TRK_SMEAR_ ## DET) ;                      \
0547   for (const std::string& jname : jetnames())                           \
0548     setJetReco(JET_SMEAR_ ## DET, JET_BTAG_ ## DET ## _ ## BTAG, jname) ; \
0549   setMETReco(MET_SMEARPARAMS_ ## DET, MET_SMEAR_ ## DET) ;                                \
0550   } while (0)