diff --git a/include/Rivet/Jet.hh b/include/Rivet/Jet.hh --- a/include/Rivet/Jet.hh +++ b/include/Rivet/Jet.hh @@ -1,271 +1,265 @@ // -*- C++ -*- #ifndef RIVET_Jet_HH #define RIVET_Jet_HH #include "Rivet/Config/RivetCommon.hh" #include "Rivet/Jet.fhh" #include "Rivet/Particle.hh" #include "Rivet/Tools/Cuts.hh" #include "Rivet/Tools/Utils.hh" #include "Rivet/Tools/RivetFastJet.hh" #include "Rivet/Math/LorentzTrans.hh" #include <numeric> namespace Rivet { /// @brief Representation of a clustered jet of particles. class Jet : public ParticleBase { public: /// @name Constructors //@{ /// Constructor from a FastJet PseudoJet, with optional full particle constituents information. Jet(const fastjet::PseudoJet& pj, const Particles& particles=Particles(), const Particles& tags=Particles()) { setState(pj, particles, tags); } /// Set the jet data, with optional full particle information. Jet(const FourMomentum& pjet, const Particles& particles=Particles(), const Particles& tags=Particles()) { setState(pjet, particles, tags); } /// Default constructor -- only for STL storability Jet() { clear(); } //@} /// @name Access jet constituents //@{ /// Number of particles in this jet. size_t size() const { return _particles.size(); } /// Get the particles in this jet. Particles& particles() { return _particles; } /// Get the particles in this jet (const version) const Particles& particles() const { return _particles; } /// Get the particles in this jet which pass a cut (const) const Particles particles(const Cut& c) const { return filter_select(_particles, c); } /// Get the particles in this jet which pass a filtering functor (const) const Particles particles(const ParticleSelector& s) const { return filter_select(_particles, s); } /// Get the particles in this jet (FastJet-like alias) Particles& constituents() { return particles(); } /// Get the particles in this jet (FastJet-like alias, const version) const Particles& constituents() const { return particles(); } /// Get the particles in this jet which pass a cut (FastJet-like alias, const) const Particles constituents(const Cut& c) const { return particles(c); } /// Get the particles in this jet which pass a filtering functor (FastJet-like alias, const) const Particles constituents(const ParticleSelector& s) const { return particles(s); } /// Check whether this jet contains a particular particle. bool containsParticle(const Particle& particle) const; /// Nicer alias for containsParticleId bool containsPID(const Particle& particle) const { return containsParticle(particle); } /// Check whether this jet contains a certain particle type. bool containsParticleId(PdgId pid) const; /// Nicer alias for containsParticleId bool containsPID(PdgId pid) const { return containsParticleId(pid); } /// Check whether this jet contains at least one of certain particle types. bool containsParticleId(const vector<PdgId>& pids) const; /// Nicer alias for containsParticleId bool containsPID(const vector<PdgId>& pids) const { return containsParticleId(pids); } //@} /// @name Tagging /// /// @note General sources of tag particles are planned. The default jet finding /// adds b-hadron, c-hadron, and tau tags by ghost association. //@{ /// @brief Particles which have been tag-matched to this jet Particles& tags() { return _tags; } /// @brief Particles which have been tag-matched to this jet (const version) const Particles& tags() const { return _tags; } /// @brief Particles which have been tag-matched to this jet _and_ pass a selector function /// /// @note Note the less efficient return by value, due to the filtering. Particles tags(const ParticleSelector& f) const { return filter_select(tags(), f); } /// @brief Particles which have been tag-matched to this jet _and_ pass a Cut /// /// @note Note the less efficient return by value, due to the cut-pass filtering. Particles tags(const Cut& c) const; /// @brief b particles which have been tag-matched to this jet (and pass an optional Cut) /// /// The default jet finding adds b-hadron tags by ghost association. Particles bTags(const Cut& c=Cuts::open()) const; /// @brief b particles which have been tag-matched to this jet _and_ pass a selector function Particles bTags(const ParticleSelector& f) const { return filter_select(bTags(), f); } /// Does this jet have at least one b-tag (that passes an optional Cut)? bool bTagged(const Cut& c=Cuts::open()) const { return !bTags(c).empty(); } /// Does this jet have at least one b-tag (that passes the supplied selector function)? bool bTagged(const ParticleSelector& f) const { return !bTags(f).empty(); } /// @brief c (and not b) particles which have been tag-matched to this jet (and pass an optional Cut) /// /// The default jet finding adds c-hadron tags by ghost association. Particles cTags(const Cut& c=Cuts::open()) const; /// @brief c (and not b) particles which have been tag-matched to this jet and pass a selector function Particles cTags(const ParticleSelector& f) const { return filter_select(cTags(), f); } /// Does this jet have at least one c-tag (that passes an optional Cut)? bool cTagged(const Cut& c=Cuts::open()) const { return !cTags(c).empty(); } /// Does this jet have at least one c-tag (that passes the supplied selector function)? bool cTagged(const ParticleSelector& f) const { return !cTags(f).empty(); } /// @brief Tau particles which have been tag-matched to this jet (and pass an optional Cut) /// /// The default jet finding adds tau tags by ghost association. Particles tauTags(const Cut& c=Cuts::open()) const; /// @brief Tau particles which have been tag-matched to this jet and pass a selector function Particles tauTags(const ParticleSelector& f) const { return filter_select(tauTags(), f); } /// Does this jet have at least one tau-tag (that passes an optional Cut)? bool tauTagged(const Cut& c=Cuts::open()) const { return !tauTags(c).empty(); } /// Does this jet have at least one tau-tag (that passes the supplied selector function)? bool tauTagged(const ParticleSelector& f) const { return !tauTags(f).empty(); } /// @brief Check whether this jet contains a bottom-flavoured hadron. /// /// @deprecated The bTags() or bTagged() function is probably what you want /// for tagging. This one ignores the tags() list and draws conclusions /// based directly on the jet constituents; the other gives a much better match /// to typical experimental methods. /// /// @note The decision is made by first trying to find a bottom-flavoured particle /// in the particles list. Most likely this will fail unless bottom hadrons /// are set stable. If @a include_decay_products is true (the default), a /// fallback is attempted, using the post-hadronization ancestor history of /// all constituents. DEPRECATED("Prefer the bTags() or bTagged() function") bool containsBottom(bool include_decay_products=true) const; /// @brief Check whether this jet contains a charm-flavoured hadron. /// /// @deprecated The cTags() or cTagged() function is probably what you want /// for tagging. This one ignores the tags() list and draws conclusions /// based directly on the jet constituents; the other gives a much better match /// to typical experimental methods. /// /// @note The decision is made by first trying to find a charm-flavoured particle /// in the particles list. Most likely this will fail unless charmed hadrons /// are set stable. If @a include_decay_products is true (the default), a /// fallback is attempted, using the post-hadronization ancestor history of /// all constituents. DEPRECATED("Prefer the cTags() or cTagged() function") bool containsCharm(bool include_decay_products=true) const; //@} /// @name Effective jet 4-vector properties //@{ /// Get equivalent single momentum four-vector. const FourMomentum& momentum() const { return _momentum; } /// Apply an active Lorentz transform to this jet /// @note The Rivet jet momentum, constituent particles, and tag particles will be modified. /// @warning The FastJet cluster sequence and pseudojets will not be modified: don't use them after transformation! Jet& transformBy(const LorentzTransform& lt); /// Get the total energy of this jet. double totalEnergy() const { return momentum().E(); } /// Get the energy carried in this jet by neutral particles. double neutralEnergy() const; /// Get the energy carried in this jet by hadrons. double hadronicEnergy() const; //@} /// @name Interaction with FastJet //@{ /// Access the internal FastJet3 PseudoJet (as a const reference) const fastjet::PseudoJet& pseudojet() const { return _pseudojet; } /// Cast operator to FastJet3 PseudoJet (as a const reference) operator const fastjet::PseudoJet& () const { return pseudojet(); } //@} /// @name Set the jet constituents and properties //@{ /// @brief Set the jet data from a FastJet PseudoJet, with optional particle constituents and tags lists. /// /// @note The particles() list will be extracted from PseudoJet constituents /// by default, making use of an attached user info if one is found. Jet& setState(const fastjet::PseudoJet& pj, const Particles& particles=Particles(), const Particles& tags=Particles()); /// Set all the jet data, with optional full particle constituent and tag information. Jet& setState(const FourMomentum& mom, const Particles& particles, const Particles& tags=Particles()); /// @brief Set the particles collection with full particle information. /// /// If set, this overrides particle info extracted from the PseudoJet Jet& setParticles(const Particles& particles); Jet& setConstituents(const Particles& particles) { return setParticles(particles); } /// Reset this jet as empty. Jet& clear(); //@} private: /// FJ3 PseudoJet member to unify PseudoJet and Jet fastjet::PseudoJet _pseudojet; /// Full constituent particle information. (Filled from PseudoJet if possible.) /// @todo Make these mutable or similar? Add a flag to force a cache rebuild? Particles _particles; /// Particles used to tag this jet (can be anything, but c and b hadrons are the most common) Particles _tags; /// Effective jet 4-vector (just for caching) mutable FourMomentum _momentum; }; /// @name String representation and streaming support //@{ - /// Represent a Jet as a string. - std::string to_str(const Jet& j); - /// Allow a Jet to be passed to an ostream. - inline std::ostream& operator<<(std::ostream& os, const Jet& j) { - os << to_str(j); - return os; - } + std::ostream& operator << (std::ostream& os, const Jet& j); //@} } #include "Rivet/Tools/JetUtils.hh" #endif diff --git a/include/Rivet/Particle.hh b/include/Rivet/Particle.hh --- a/include/Rivet/Particle.hh +++ b/include/Rivet/Particle.hh @@ -1,616 +1,603 @@ // -*- C++ -*- #ifndef RIVET_Particle_HH #define RIVET_Particle_HH #include "Rivet/Particle.fhh" #include "Rivet/ParticleBase.hh" #include "Rivet/Config/RivetCommon.hh" #include "Rivet/Tools/Cuts.hh" #include "Rivet/Tools/Utils.hh" #include "Rivet/Math/LorentzTrans.hh" // NOTE: Rivet/Tools/ParticleUtils.hh included at the end #include "fastjet/PseudoJet.hh" namespace Rivet { /// Particle representation, either from a HepMC::GenEvent or reconstructed. class Particle : public ParticleBase { public: /// @name Constructors //@{ /// Default constructor. /// @note A particle without info is useless. This only exists to keep STL containers happy. Particle() : ParticleBase(), _original(nullptr), _id(PID::ANY) { } /// Constructor without GenParticle. Particle(PdgId pid, const FourMomentum& mom, const FourVector& pos=FourVector()) : ParticleBase(), _original(nullptr), _id(pid), _momentum(mom), _origin(pos) { } /// Constructor from a HepMC GenParticle pointer. Particle(const GenParticle* gp) : ParticleBase(), _original(gp), _id(gp->pdg_id()), _momentum(gp->momentum()) { const GenVertex* vprod = gp->production_vertex(); if (vprod != nullptr) { setOrigin(vprod->position().t(), vprod->position().x(), vprod->position().y(), vprod->position().z()); } } /// Constructor from a HepMC GenParticle. Particle(const GenParticle& gp) : ParticleBase(), _original(&gp), _id(gp.pdg_id()), _momentum(gp.momentum()) { const GenVertex* vprod = gp.production_vertex(); if (vprod != nullptr) { setOrigin(vprod->position().t(), vprod->position().x(), vprod->position().y(), vprod->position().z()); } } //@} /// @name Other representations and implicit casts //@{ /// Converter to FastJet3 PseudoJet virtual fastjet::PseudoJet pseudojet() const { return fastjet::PseudoJet(mom().px(), mom().py(), mom().pz(), mom().E()); } /// Cast operator to FastJet3 PseudoJet operator PseudoJet () const { return pseudojet(); } /// Get a const pointer to the original GenParticle. const GenParticle* genParticle() const { return _original; } /// Cast operator for conversion to GenParticle* operator const GenParticle* () const { return genParticle(); } //@} /// @name Kinematic properties //@{ /// The momentum. const FourMomentum& momentum() const { return _momentum; } /// Set the momentum. Particle& setMomentum(const FourMomentum& momentum) { _momentum = momentum; return *this; } /// Set the momentum via components. Particle& setMomentum(double E, double px, double py, double pz) { _momentum = FourMomentum(E, px, py, pz); return *this; } /// Apply an active Lorentz transform to this particle Particle& transformBy(const LorentzTransform& lt); //@ /// @name Positional properties //@{ /// The origin position. const FourVector& origin() const { return _origin; } /// Set the origin position. Particle& setOrigin(const FourVector& position) { _origin = position; return *this; } /// Set the origin position via components. Particle& setOrigin(double t, double x, double y, double z) { _origin = FourMomentum(t, x, y, z); return *this; } //@} /// @name Particle ID code accessors //@{ /// This Particle's PDG ID code. PdgId pid() const { return _id; } /// Absolute value of the PDG ID code. PdgId abspid() const { return std::abs(_id); } /// This Particle's PDG ID code (alias). /// @deprecated Prefer the pid/abspid form PdgId pdgId() const { return _id; } //@} /// @name Particle species properties //@{ /// The charge of this Particle. double charge() const { return PID::charge(pid()); } /// The absolute charge of this Particle. double abscharge() const { return PID::abscharge(pid()); } /// Three times the charge of this Particle (i.e. integer multiple of smallest quark charge). int charge3() const { return PID::charge3(pid()); } /// Alias for charge3 /// @deprecated Use charge3 int threeCharge() const { return PID::threeCharge(pid()); } /// Three times the absolute charge of this Particle (i.e. integer multiple of smallest quark charge). int abscharge3() const { return PID::abscharge3(pid()); } /// Is this a hadron? bool isHadron() const { return PID::isHadron(pid()); } /// Is this a meson? bool isMeson() const { return PID::isMeson(pid()); } /// Is this a baryon? bool isBaryon() const { return PID::isBaryon(pid()); } /// Is this a lepton? bool isLepton() const { return PID::isLepton(pid()); } /// Is this a neutrino? bool isNeutrino() const { return PID::isNeutrino(pid()); } /// Does this (hadron) contain a b quark? bool hasBottom() const { return PID::hasBottom(pid()); } /// Does this (hadron) contain a c quark? bool hasCharm() const { return PID::hasCharm(pid()); } // /// Does this (hadron) contain an s quark? // bool hasStrange() const { return PID::hasStrange(pid()); } /// Is this particle potentially visible in a detector? bool isVisible() const; //@} /// @name Ancestry properties //@{ /// Get a list of the direct parents of the current particle (with optional selection Cut) /// /// @note This is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! Particles parents(const Cut& c=Cuts::OPEN) const; /// Get a list of the direct parents of the current particle (with selector function) /// /// @note This is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! Particles parents(const ParticleSelector& f) const { return filter_select(parents(), f); } /// Check whether any particle in the particle's parent list has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasParentWith(const ParticleSelector& f) const { return !parents(f).empty(); } /// Check whether any particle in the particle's parent list has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasParentWith(const Cut& c) const; /// Check whether any particle in the particle's parent list does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasParentWithout(const ParticleSelector& f) const { return hasParentWith([&](const Particle& p){ return !f(p); }); } /// Check whether any particle in the particle's parent list does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasParentWithout(const Cut& c) const; /// Check whether a given PID is found in the particle's parent list /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! /// /// @deprecated Prefer e.g. hasParentWith(Cut::pid == 123) //DEPRECATED("Prefer e.g. hasParentWith(Cut::pid == 123)"); bool hasParent(PdgId pid) const; /// Get a list of the ancestors of the current particle (with optional selection Cut) /// /// @note By default only physical ancestors, with status=2, are returned. /// /// @note This is valid in MC, but may not be answerable experimentally -- /// use this function with care when replicating experimental analyses! Particles ancestors(const Cut& c=Cuts::OPEN, bool only_physical=true) const; /// Get a list of the direct parents of the current particle (with selector function) /// /// @note By default only physical ancestors, with status=2, are returned. /// /// @note This is valid in MC, but may not be answerable experimentally -- /// use this function with care when replicating experimental analyses! Particles ancestors(const ParticleSelector& f, bool only_physical=true) const { return filter_select(ancestors(Cuts::OPEN, only_physical), f); } /// Check whether any particle in the particle's ancestor list has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasAncestorWith(const ParticleSelector& f, bool only_physical=true) const { return !ancestors(f, only_physical).empty(); } /// Check whether any particle in the particle's ancestor list has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasAncestorWith(const Cut& c, bool only_physical=true) const; /// Check whether any particle in the particle's ancestor list does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasAncestorWithout(const ParticleSelector& f, bool only_physical=true) const { return hasAncestorWith([&](const Particle& p){ return !f(p); }, only_physical); } /// Check whether any particle in the particle's ancestor list does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasAncestorWithout(const Cut& c, bool only_physical=true) const; /// Check whether a given PID is found in the particle's ancestor list /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! /// /// @deprecated Prefer hasAncestorWith(Cuts::pid == pid) etc. //DEPRECATED("Prefer e.g. hasAncestorWith(Cut::pid == 123)"); bool hasAncestor(PdgId pid, bool only_physical=true) const; /// @brief Determine whether the particle is from a b-hadron decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromBottom() const; /// @brief Determine whether the particle is from a c-hadron decay /// /// @note If a hadron contains b and c quarks it is considered a bottom /// hadron and NOT a charm hadron. /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromCharm() const; // /// @brief Determine whether the particle is from a s-hadron decay // /// // /// @note If a hadron contains b or c quarks as well as strange it is // /// considered a b or c hadron, but NOT a strange hadron. // /// // /// @note This question is valid in MC, but may not be perfectly answerable // /// experimentally -- use this function with care when replicating // /// experimental analyses! // bool fromStrange() const; /// @brief Determine whether the particle is from a hadron decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromHadron() const; /// @brief Determine whether the particle is from a tau decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromTau(bool prompt_taus_only=false) const; /// @brief Determine whether the particle is from a prompt tau decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromPromptTau() const { return fromTau(true); } /// @brief Determine whether the particle is from a tau which decayed hadronically /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromHadronicTau(bool prompt_taus_only=false) const; /// @brief Determine whether the particle is from a hadron or tau decay /// /// Specifically, walk up the ancestor chain until a status 2 hadron or /// tau is found, if at all. /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! /// /// @deprecated Too vague: use fromHadron or fromHadronicTau bool fromDecay() const { return fromHadron() || fromPromptTau(); } /// @brief Shorthand definition of 'promptness' based on set definition flags /// /// A "direct" particle is one directly connected to the hard process. It is a /// preferred alias for "prompt", since it has no confusing implications about /// distinguishability by timing information. /// /// The boolean arguments allow a decay lepton to be considered direct if /// its parent was a "real" direct lepton. /// /// @note This one doesn't make any judgements about final-stateness bool isDirect(bool allow_from_direct_tau=false, bool allow_from_direct_mu=false) const; /// Alias for isDirect bool isPrompt(bool allow_from_prompt_tau=false, bool allow_from_prompt_mu=false) const { return isDirect(allow_from_prompt_tau, allow_from_prompt_mu); } //@} /// @name Decay info //@{ /// Whether this particle is stable according to the generator bool isStable() const; /// @todo isDecayed? How to restrict to physical particles? /// Get a list of the direct descendants from the current particle (with optional selection Cut) Particles children(const Cut& c=Cuts::OPEN) const; /// Get a list of the direct descendants from the current particle (with selector function) Particles children(const ParticleSelector& f) const { return filter_select(children(), f); } /// Check whether any direct child of this particle has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasChildWith(const ParticleSelector& f) const { return !children(f).empty(); } /// Check whether any direct child of this particle has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasChildWith(const Cut& c) const; /// Check whether any direct child of this particle does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasChildWithout(const ParticleSelector& f) const { return hasChildWith([&](const Particle& p){ return !f(p); }); } /// Check whether any direct child of this particle does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasChildWithout(const Cut& c) const; /// Get a list of all the descendants from the current particle (with optional selection Cut) Particles allDescendants(const Cut& c=Cuts::OPEN, bool remove_duplicates=true) const; /// Get a list of all the descendants from the current particle (with selector function) Particles allDescendants(const ParticleSelector& f, bool remove_duplicates=true) const { return filter_select(allDescendants(Cuts::OPEN, remove_duplicates), f); } /// Check whether any descendant of this particle has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasDescendantWith(const ParticleSelector& f, bool remove_duplicates=true) const { return !allDescendants(f, remove_duplicates).empty(); } /// Check whether any descendant of this particle has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasDescendantWith(const Cut& c, bool remove_duplicates=true) const; /// Check whether any descendant of this particle does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasDescendantWithout(const ParticleSelector& f, bool remove_duplicates=true) const { return hasDescendantWith([&](const Particle& p){ return !f(p); }, remove_duplicates); } /// Check whether any descendant of this particle does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasDescendantWithout(const Cut& c, bool remove_duplicates=true) const; /// Get a list of all the stable descendants from the current particle (with optional selection Cut) /// /// @todo Use recursion through replica-avoiding MCUtils functions to avoid bookkeeping duplicates /// @todo Insist that the current particle is post-hadronization, otherwise throw an exception? Particles stableDescendants(const Cut& c=Cuts::OPEN) const; /// Get a list of all the stable descendants from the current particle (with selector function) Particles stableDescendants(const ParticleSelector& f) const { return filter_select(stableDescendants(), f); } /// Check whether any stable descendant of this particle has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasStableDescendantWith(const ParticleSelector& f) const { return !stableDescendants(f).empty(); } /// Check whether any stable descendant of this particle has the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasStableDescendantWith(const Cut& c) const; /// Check whether any stable descendant of this particle does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasStableDescendantWithout(const ParticleSelector& f) const { return hasStableDescendantWith([&](const Particle& p){ return !f(p); }); } /// Check whether any stable descendant of this particle does not have the requested property /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasStableDescendantWithout(const Cut& c) const; /// Flight length (divide by mm or cm to get the appropriate units) double flightLength() const; //@} /// @name Duplicate testing //@{ /// @brief Determine whether a particle is the first in a decay chain to meet the function requirement inline bool isFirstWith(const ParticleSelector& f) const { if (!f(*this)) return false; //< This doesn't even meet f, let alone being the last to do so if (any(parents(), f)) return false; //< If a direct parent has this property, this isn't the first return true; } /// @brief Determine whether a particle is the first in a decay chain not to meet the function requirement inline bool isFirstWithout(const ParticleSelector& f) const { return isFirstWith([&](const Particle& p){ return !f(p); }); } /// @brief Determine whether a particle is the last in a decay chain to meet the function requirement inline bool isLastWith(const ParticleSelector& f) const { if (!f(*this)) return false; //< This doesn't even meet f, let alone being the last to do so if (any(children(), f)) return false; //< If a child has this property, this isn't the last return true; } /// @brief Determine whether a particle is the last in a decay chain not to meet the function requirement inline bool isLastWithout(const ParticleSelector& f) const { return isLastWith([&](const Particle& p){ return !f(p); }); } //@} protected: /// A pointer to the original GenParticle from which this Particle is projected. const GenParticle* _original; /// The PDG ID code for this Particle. PdgId _id; /// The momentum of this particle. FourMomentum _momentum; /// The creation position of this particle. FourVector _origin; }; /// @name String representation and streaming support //@{ - /// Represent a Particle as a string. - std::string to_str(const Particle& p); - /// Allow a Particle to be passed to an ostream. - inline std::ostream& operator<<(std::ostream& os, const Particle& p) { - os << to_str(p); - return os; - } - - - /// Represent a ParticlePair as a string. - std::string to_str(const ParticlePair& pair); + std::ostream& operator << (std::ostream& os, const Particle& p); /// Allow ParticlePair to be passed to an ostream. - inline std::ostream& operator<<(std::ostream& os, const ParticlePair& pp) { - os << to_str(pp); - return os; - } + std::ostream& operator << (std::ostream& os, const ParticlePair& pp); //@} } #include "Rivet/Tools/ParticleUtils.hh" #endif diff --git a/src/Core/Jet.cc b/src/Core/Jet.cc --- a/src/Core/Jet.cc +++ b/src/Core/Jet.cc @@ -1,192 +1,202 @@ #include "Rivet/Jet.hh" #include "Rivet/Tools/Cuts.hh" #include "Rivet/Tools/ParticleName.hh" #include "Rivet/Tools/Logging.hh" #include "Rivet/Tools/ParticleIdUtils.hh" namespace Rivet { Jet& Jet::clear() { _momentum = FourMomentum(); _pseudojet.reset(0,0,0,0); _particles.clear(); return *this; } Jet& Jet::setState(const FourMomentum& mom, const Particles& particles, const Particles& tags) { clear(); _momentum = mom; _pseudojet = fastjet::PseudoJet(mom.px(), mom.py(), mom.pz(), mom.E()); _particles = particles; _tags = tags; return *this; } Jet& Jet::setState(const fastjet::PseudoJet& pj, const Particles& particles, const Particles& tags) { clear(); _pseudojet = pj; _momentum = FourMomentum(pj.e(), pj.px(), pj.py(), pj.pz()); _particles = particles; _tags = tags; // if (_particles.empty()) { // foreach (const fastjet::PseudoJet pjc, _pseudojet.constituents()) { // // If there is no attached user info, we can't create a meaningful particle, so skip // if (!pjc.has_user_info<RivetFJInfo>()) continue; // const RivetFJInfo& fjinfo = pjc.user_info<RivetFJInfo>(); // // Don't add ghosts to the particles list // if (fjinfo.isGhost) continue; // // Otherwise construct a Particle from the PseudoJet, preferably from an associated GenParticle // ?if (fjinfo.genParticle != NULL) { // _particles.push_back(Particle(fjinfo.genParticle)); // } else { // if (fjinfo.pid == 0) continue; // skip if there is a null PID entry in the FJ info // const FourMomentum pjcmom(pjc.e(), pjc.px(), pjc.py(), pjc.pz()); // _particles.push_back(Particle(fjinfo.pid, pjcmom)); // } // } // } return *this; } Jet& Jet::setParticles(const Particles& particles) { _particles = particles; return *this; } bool Jet::containsParticle(const Particle& particle) const { const int barcode = particle.genParticle()->barcode(); foreach (const Particle& p, particles()) { if (p.genParticle()->barcode() == barcode) return true; } return false; } bool Jet::containsParticleId(PdgId pid) const { foreach (const Particle& p, particles()) { if (p.pid() == pid) return true; } return false; } bool Jet::containsParticleId(const vector<PdgId>& pids) const { foreach (const Particle& p, particles()) { foreach (PdgId pid, pids) { if (p.pid() == pid) return true; } } return false; } /// @todo Jet::containsMatch(Matcher m) { ... if m(pid) return true; ... } Jet& Jet::transformBy(const LorentzTransform& lt) { _momentum = lt.transform(_momentum); for (Particle& p : _particles) p.transformBy(lt); for (Particle& t : _tags) t.transformBy(lt); _pseudojet.reset(_momentum.px(), _momentum.py(), _momentum.pz(), _momentum.E()); //< lose ClusterSeq etc. return *this; } double Jet::neutralEnergy() const { double e_neutral = 0.0; foreach (const Particle& p, particles()) { const PdgId pid = p.pid(); if (PID::threeCharge(pid) == 0) { e_neutral += p.E(); } } return e_neutral; } double Jet::hadronicEnergy() const { double e_hadr = 0.0; foreach (const Particle& p, particles()) { const PdgId pid = p.pid(); if (PID::isHadron(pid)) { e_hadr += p.E(); } } return e_hadr; } bool Jet::containsCharm(bool include_decay_products) const { foreach (const Particle& p, particles()) { const PdgId pid = p.pid(); if (abs(pid) == PID::CQUARK) return true; if (PID::isHadron(pid) && PID::hasCharm(pid)) return true; if (include_decay_products) { const HepMC::GenVertex* gv = p.genParticle()->production_vertex(); if (gv) { foreach (const GenParticle* pi, Rivet::particles(gv, HepMC::ancestors)) { const PdgId pid2 = pi->pdg_id(); if (PID::isHadron(pid2) && PID::hasCharm(pid2)) return true; } } } } return false; } bool Jet::containsBottom(bool include_decay_products) const { foreach (const Particle& p, particles()) { const PdgId pid = p.pid(); if (abs(pid) == PID::BQUARK) return true; if (PID::isHadron(pid) && PID::hasBottom(pid)) return true; if (include_decay_products) { const HepMC::GenVertex* gv = p.genParticle()->production_vertex(); if (gv) { foreach (const GenParticle* pi, Rivet::particles(gv, HepMC::ancestors)) { const PdgId pid2 = pi->pdg_id(); if (PID::isHadron(pid2) && PID::hasBottom(pid2)) return true; } } } } return false; } Particles Jet::tags(const Cut& c) const { return filter_select(tags(), c); } Particles Jet::bTags(const Cut& c) const { Particles rtn; for (const Particle& tp : tags()) { if (hasBottom(tp) && c->accept(tp)) rtn.push_back(tp); } return rtn; } Particles Jet::cTags(const Cut& c) const { Particles rtn; for (const Particle& tp : tags()) { /// @todo Is making b and c tags exclusive the right thing to do? if (hasCharm(tp) && !hasBottom(tp) && c->accept(tp)) rtn.push_back(tp); } return rtn; } Particles Jet::tauTags(const Cut& c) const { Particles rtn; for (const Particle& tp : tags()) { if (isTau(tp) && c->accept(tp)) rtn.push_back(tp); } return rtn; } + /// Allow a Jet to be passed to an ostream. + std::ostream& operator << (std::ostream& os, const Jet& j) { + os << "Jet<" << j.mom()/GeV << " GeV; Nparticles=" << j.size() << "; "; + os << "bTag=" << boolalpha << j.bTagged() << ", "; + os << "cTag=" << boolalpha << j.cTagged() << ", "; + os << "tauTag=" << boolalpha << j.tauTagged() << ">"; + return os; + } + + } diff --git a/src/Core/Particle.cc b/src/Core/Particle.cc --- a/src/Core/Particle.cc +++ b/src/Core/Particle.cc @@ -1,263 +1,257 @@ #include "Rivet/Particle.hh" #include "Rivet/Tools/Cuts.hh" #include "Rivet/Tools/ParticleIdUtils.hh" namespace Rivet { Particle& Particle::transformBy(const LorentzTransform& lt) { _momentum = lt.transform(_momentum); return *this; } bool Particle::isVisible() const { // Charged particles are visible if ( PID::threeCharge(pid()) != 0 ) return true; // Neutral hadrons are visible if ( PID::isHadron(pid()) ) return true; // Photons are visible if ( pid() == PID::PHOTON ) return true; // Gluons are visible (for parton level analyses) if ( pid() == PID::GLUON ) return true; // Everything else is invisible return false; } bool Particle::isStable() const { return genParticle() != NULL && genParticle()->status() == 1 && genParticle()->end_vertex() == NULL; } vector<Particle> Particle::ancestors(const Cut& c, bool physical_only) const { vector<Particle> rtn; /// @todo Remove this const mess crap when HepMC doesn't suck GenVertexPtr gv = const_cast<GenVertexPtr>( genParticle()->production_vertex() ); if (gv == NULL) return rtn; /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticlePtr gp, gv->particles(HepMC::children)) // rtn += Particle(gp); for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::ancestors); it != gv->particles_end(HepMC::ancestors); ++it) { if (physical_only && (*it)->status() != 1 && (*it)->status() != 2) continue; const Particle p(*it); if (c != Cuts::OPEN && !c->accept(p)) continue; rtn += p; } return rtn; } vector<Particle> Particle::parents(const Cut& c) const { vector<Particle> rtn; /// @todo Remove this const mess crap when HepMC doesn't suck GenVertexPtr gv = const_cast<GenVertexPtr>( genParticle()->production_vertex() ); if (gv == NULL) return rtn; /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticlePtr gp, gv->particles(HepMC::children)) // rtn += Particle(gp); for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::parents); it != gv->particles_end(HepMC::parents); ++it) { const Particle p(*it); if (c != Cuts::OPEN && !c->accept(p)) continue; rtn += p; } return rtn; } vector<Particle> Particle::children(const Cut& c) const { vector<Particle> rtn; if (isStable()) return rtn; /// @todo Remove this const mess crap when HepMC doesn't suck GenVertexPtr gv = const_cast<GenVertexPtr>( genParticle()->end_vertex() ); if (gv == NULL) return rtn; /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticlePtr gp, gv->particles(HepMC::children)) // rtn += Particle(gp); for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::children); it != gv->particles_end(HepMC::children); ++it) { const Particle p(*it); if (c != Cuts::OPEN && !c->accept(p)) continue; rtn += p; } return rtn; } /// @todo Insist that the current particle is post-hadronization, otherwise throw an exception? /// @todo Use recursion through replica-avoiding functions to avoid bookkeeping duplicates vector<Particle> Particle::allDescendants(const Cut& c, bool remove_duplicates) const { vector<Particle> rtn; if (isStable()) return rtn; /// @todo Remove this const mess crap when HepMC doesn't suck GenVertexPtr gv = const_cast<GenVertexPtr>( genParticle()->end_vertex() ); if (gv == NULL) return rtn; /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticlePtr gp, gv->particles(HepMC::descendants)) for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::descendants); it != gv->particles_end(HepMC::descendants); ++it) { const Particle p(*it); if (c != Cuts::OPEN && !c->accept(p)) continue; if (remove_duplicates && (*it)->end_vertex() != NULL) { // size_t n = 0; ///< @todo Only remove 1-to-1 duplicates? bool dup = false; /// @todo Yuck, HepMC for (GenVertex::particle_iterator it2 = (*it)->end_vertex()->particles_begin(HepMC::children); it2 != (*it)->end_vertex()->particles_end(HepMC::children); ++it2) { // n += 1; if (n > 1) break; if ((*it)->pdg_id() == (*it2)->pdg_id()) { dup = true; break; } } if (dup) continue; } rtn += p; } return rtn; } /// @todo Insist that the current particle is post-hadronization, otherwise throw an exception? vector<Particle> Particle::stableDescendants(const Cut& c) const { vector<Particle> rtn; if (isStable()) return rtn; /// @todo Remove this const mess crap when HepMC doesn't suck GenVertexPtr gv = const_cast<GenVertexPtr>( genParticle()->end_vertex() ); if (gv == NULL) return rtn; /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticlePtr gp, gv->particles(HepMC::descendants)) for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::descendants); it != gv->particles_end(HepMC::descendants); ++it) { // if ((*it)->status() != 1 || (*it)->end_vertex() != NULL) continue; const Particle p(*it); if (!p.isStable()) continue; if (c != Cuts::OPEN && !c->accept(p)) continue; rtn += p; } return rtn; } double Particle::flightLength() const { if (isStable()) return -1; if (genParticle() == NULL) return 0; if (genParticle()->production_vertex() == NULL) return 0; const HepMC::FourVector v1 = genParticle()->production_vertex()->position(); const HepMC::FourVector v2 = genParticle()->end_vertex()->position(); return sqrt(sqr(v2.x()-v1.x()) + sqr(v2.y()-v1.y()) + sqr(v2.z()-v1.z())); } bool Particle::hasParent(PdgId pid) const { return hasParentWith(hasPID(pid)); } bool Particle::hasParentWith(const Cut& c) const { return hasParentWith([&](const Particle& p){return c->accept(p);}); } bool Particle::hasAncestor(PdgId pid, bool only_physical) const { return hasAncestorWith(hasPID(pid), only_physical); } bool Particle::hasAncestorWith(const Cut& c, bool only_physical) const { return hasAncestorWith([&](const Particle& p){return c->accept(p);}, only_physical); } bool Particle::hasChildWith(const Cut& c) const { return hasChildWith([&](const Particle& p){return c->accept(p);}); } bool Particle::hasDescendantWith(const Cut& c, bool remove_duplicates) const { return hasDescendantWith([&](const Particle& p){return c->accept(p);}, remove_duplicates); } bool Particle::hasStableDescendantWith(const Cut& c) const { return hasStableDescendantWith([&](const Particle& p){return c->accept(p);}); } bool Particle::fromBottom() const { return hasAncestorWith([](const Particle& p){ return p.genParticle()->status() == 2 && p.isHadron() && p.hasBottom(); }); } bool Particle::fromCharm() const { return hasAncestorWith([](const Particle& p){ return p.genParticle()->status() == 2 && p.isHadron() && p.hasCharm(); }); } bool Particle::fromHadron() const { return hasAncestorWith([](const Particle& p){ return p.genParticle()->status() == 2 && p.isHadron(); }); } bool Particle::fromTau(bool prompt_taus_only) const { if (prompt_taus_only && fromHadron()) return false; return hasAncestorWith([](const Particle& p){ return p.genParticle()->status() == 2 && isTau(p); }); } bool Particle::fromHadronicTau(bool prompt_taus_only) const { return hasAncestorWith([&](const Particle& p){ return p.genParticle()->status() == 2 && isTau(p) && (!prompt_taus_only || p.isPrompt()) && hasHadronicDecay(p); }); } bool Particle::isDirect(bool allow_from_direct_tau, bool allow_from_direct_mu) const { if (genParticle() == NULL) return false; // no HepMC connection, give up! Throw UserError exception? const GenVertexPtr prodVtx = genParticle()->production_vertex(); if (prodVtx == NULL) return false; // orphaned particle, has to be assume false const pair<GenParticlePtr, GenParticlePtr> beams = prodVtx->parent_event()->beam_particles(); /// @todo Would be nicer to be able to write this recursively up the chain, exiting as soon as a parton or string/cluster is seen for (const GenParticlePtr ancestor : Rivet::particles(prodVtx, HepMC::ancestors)) { const PdgId pid = ancestor->pdg_id(); if (ancestor->status() != 2) continue; // no non-standard statuses or beams to be used in decision making if (ancestor == beams.first || ancestor == beams.second) continue; // PYTHIA6 uses status 2 for beams, I think... (sigh) if (PID::isParton(pid)) continue; // PYTHIA6 also uses status 2 for some partons, I think... (sigh) if (PID::isHadron(pid)) return false; // direct particles can't be from hadron decays if (abs(pid) == PID::TAU && abspid() != PID::TAU && !allow_from_direct_tau) return false; // allow or ban particles from tau decays (permitting tau copies) if (abs(pid) == PID::MUON && abspid() != PID::MUON && !allow_from_direct_mu) return false; // allow or ban particles from muon decays (permitting muon copies) } return true; } /////////////////////// - - string to_str(const Particle& p) { + /// Allow a Particle to be passed to an ostream. + std::ostream& operator << (std::ostream& os, const Particle& p) { string pname; try { pname = PID::toParticleName(p.pid()); } catch (...) { pname = "PID=" + to_str(p.pid()); } - stringstream out; - out << pname << " @ " << p.momentum() << " GeV"; - return out.str(); + os << "Particle<" << pname << " @ " << p.momentum()/GeV << " GeV>"; + return os; } - string to_str(const ParticlePair& pair) { - stringstream out; - out << "[" << pair.first << ", " << pair.second << "]"; - // out << "[" - // << PID::toParticleName(pair.first.pid()) << " @ " - // << pair.first.momentum().E()/GeV << " GeV, " - // << PID::toParticleName(pair.second.pid()) << " @ " - // << pair.second.momentum().E()/GeV << " GeV]"; - return out.str(); + /// Allow ParticlePair to be passed to an ostream. + std::ostream& operator << (std::ostream& os, const ParticlePair& pp) { + os << "[" << pp.first << ", " << pp.second << "]"; + return os; } }