Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-13 10:26:19

0001 // -*- C++ -*-
0002 #ifndef RIVET_Particle_HH
0003 #define RIVET_Particle_HH
0004 
0005 #include "Rivet/Particle.fhh"
0006 #include "Rivet/ParticleBase.hh"
0007 #include "Rivet/Config/RivetCommon.hh"
0008 #include "Rivet/Tools/Cuts.hh"
0009 #include "Rivet/Tools/Utils.hh"
0010 #include "Rivet/Tools/RivetFastJet.hh"
0011 #include "Rivet/Math/LorentzTrans.hh"
0012 // NOTE: Rivet/Tools/ParticleUtils.hh included at the end
0013 
0014 namespace Rivet {
0015 
0016 
0017   /// @brief Specialised vector of Particle objects.
0018   ///
0019   /// A specialised version of vector<Particle> which is able to implicitly and
0020   /// explicitly convert to a vector of FourMomentum.
0021   class Particles : public std::vector<Particle> {
0022   public:
0023     using base = std::vector<Particle>; //< using-declarations don't like template syntax
0024     using base::base; //< import base-class constructors
0025     Particles();
0026     Particles(const std::vector<Particle>& vps);
0027     FourMomenta moms() const;
0028     PseudoJets pseudojets() const;
0029     operator FourMomenta () const { return moms(); }
0030     operator PseudoJets () const { return pseudojets(); }
0031     Particles& operator += (const Particle& p);
0032     Particles& operator += (const Particles& ps);
0033   };
0034 
0035   Particles operator + (const Particles& a, const Particles& b);
0036 
0037   /// Typedef for a pair of Particle objects.
0038   typedef std::pair<Particle, Particle> ParticlePair;
0039 
0040 
0041   /////////////////////
0042 
0043 
0044   /// Particle representation, either from a HepMC::GenEvent or reconstructed.
0045   class Particle : public ParticleBase {
0046   public:
0047 
0048     /// @name Constructors
0049     /// @{
0050 
0051     /// Default constructor.
0052     /// @note A particle without info is useless. This only exists to keep STL containers happy.
0053     Particle()
0054       : ParticleBase(),
0055         _original(nullptr), _id(PID::ANY), _isDirect(4, std::make_pair(false,false))
0056     {   }
0057 
0058     /// Constructor from PID and momentum.
0059     Particle(PdgId pid, const FourMomentum& mom, const FourVector& pos=FourVector(), ConstGenParticlePtr gp=nullptr)
0060       : ParticleBase(),
0061         _original(gp), _id(pid),
0062         _momentum(mom), _origin(pos),
0063         _isDirect(4, std::make_pair(false,false))
0064     {   }
0065 
0066     /// Constructor from PID, momentum, and a GenParticle for relational links.
0067     Particle(PdgId pid, const FourMomentum& mom, ConstGenParticlePtr gp, const FourVector& pos=FourVector())
0068       : Particle(pid, mom, pos, gp)
0069     {   }
0070 
0071     /// Constructor from a HepMC GenParticle pointer.
0072     Particle(ConstGenParticlePtr gp)
0073       : ParticleBase(),
0074         _original(gp), _id(gp->pdg_id()),
0075         _momentum(gp->momentum()),
0076         _isDirect(4, std::make_pair(false,false))
0077     {
0078       ConstGenVertexPtr vprod = gp->production_vertex();
0079       if (vprod != nullptr) {
0080         setOrigin(vprod->position().t(), vprod->position().x(), vprod->position().y(), vprod->position().z());
0081       }
0082     }
0083 
0084     /// Constructor from a HepMC GenParticle reference.
0085     Particle(const RivetHepMC::GenParticle& gp)
0086       : Particle(HepMCUtils::getParticlePtr(gp))
0087     {   }
0088 
0089     /// @}
0090 
0091 
0092     /// @name Kinematic properties
0093     /// @{
0094 
0095     /// The momentum.
0096     const FourMomentum& momentum() const {
0097       return _momentum;
0098     }
0099 
0100     /// Set the momentum.
0101     Particle& setMomentum(const FourMomentum& momentum) {
0102       _momentum = momentum;
0103       return *this;
0104     }
0105 
0106     /// Set the momentum via components.
0107     Particle& setMomentum(double E, double px, double py, double pz) {
0108       _momentum = FourMomentum(E, px, py, pz);
0109       return *this;
0110     }
0111 
0112     /// Apply an active Lorentz transform to this particle
0113     Particle& transformBy(const LorentzTransform& lt);
0114 
0115     /// @}
0116 
0117 
0118     /// @name Positional properties
0119     /// @{
0120 
0121     /// The origin position (and time).
0122     const FourVector& origin() const {
0123       return _origin;
0124     }
0125     /// Set the origin position.
0126     Particle& setOrigin(const FourVector& position) {
0127       _origin = position;
0128       return *this;
0129     }
0130     /// Set the origin position via components.
0131     Particle& setOrigin(double t, double x, double y, double z) {
0132       _origin = FourMomentum(t, x, y, z);
0133       return *this;
0134     }
0135 
0136     /// @}
0137 
0138 
0139     /// @name Displacement-projection properties
0140     /// @{
0141 
0142     /// Find the point of closest approach to the primary vertex
0143     Vector3 closestApproach() const {
0144       const FourVector& v0 = origin();
0145       /// @todo Check that this works with all angles
0146       const double rho0 = origin().perp() / sin(this->phi() - origin().phi());
0147       const double phi0 = M_PI/2 - this->phi();
0148       const double x0 = rho0 * cos(phi0);
0149       const double y0 = rho0 * sin(phi0);
0150       const double z0 = origin().z() - v0.perp()/tan(this->theta());
0151       return Vector3(x0, y0, z0);
0152     }
0153 
0154     /// @}
0155 
0156 
0157     /// @name Other representations and implicit casts to momentum-like objects
0158     /// @{
0159 
0160     /// Converter to FastJet3 PseudoJet
0161     virtual fastjet::PseudoJet pseudojet() const {
0162       return fastjet::PseudoJet(mom().px(), mom().py(), mom().pz(), mom().E());
0163     }
0164 
0165     /// Cast operator to FastJet3 PseudoJet
0166     operator PseudoJet () const { return pseudojet(); }
0167 
0168 
0169     /// Set a const pointer to the original GenParticle
0170     Particle& setGenParticle(ConstGenParticlePtr gp) {
0171       _original = gp;
0172       return *this;
0173     }
0174 
0175     /// Get a const pointer to the original GenParticle
0176     ConstGenParticlePtr genParticle() const {
0177       return _original;
0178     }
0179 
0180     /// Cast operator for conversion to GenParticle*
0181     /// @note Not implicit since that would enable accidental Particle::operator== comparisons
0182     explicit operator ConstGenParticlePtr () const { return genParticle(); }
0183 
0184     /// @}
0185 
0186 
0187     /// @name Particle ID code accessors
0188     /// @{
0189 
0190     /// This Particle's PDG ID code.
0191     PdgId pid() const { return _id; }
0192     /// Absolute value of the PDG ID code.
0193     PdgId abspid() const { return std::abs(_id); }
0194 
0195     /// @}
0196 
0197 
0198     /// @name Charge
0199     /// @{
0200 
0201     /// The charge of this Particle.
0202     double charge() const { return PID::charge(pid()); }
0203 
0204     /// The absolute charge of this Particle.
0205     double abscharge() const { return PID::abscharge(pid()); }
0206 
0207     /// Three times the charge of this Particle (i.e. integer multiple of smallest quark charge).
0208     int charge3() const { return PID::charge3(pid()); }
0209 
0210     /// Three times the absolute charge of this Particle (i.e. integer multiple of smallest quark charge).
0211     int abscharge3() const { return PID::abscharge3(pid()); }
0212 
0213     /// Is this Particle charged?
0214     bool isCharged() const { return charge3() != 0; }
0215 
0216     /// @}
0217 
0218 
0219     /// @name Particle species
0220     /// @{
0221 
0222     /// Is this a hadron?
0223     bool isHadron() const { return PID::isHadron(pid()); }
0224 
0225     /// Is this a meson?
0226     bool isMeson() const { return PID::isMeson(pid()); }
0227 
0228     /// Is this a baryon?
0229     bool isBaryon() const { return PID::isBaryon(pid()); }
0230 
0231     /// Is this a lepton?
0232     bool isLepton() const { return PID::isLepton(pid()); }
0233 
0234     /// Is this a charged lepton?
0235     bool isChargedLepton() const { return PID::isChargedLepton(pid()); }
0236 
0237     /// Is this a neutrino?
0238     bool isNeutrino() const { return PID::isNeutrino(pid()); }
0239 
0240     /// Does this (hadron) contain a b quark?
0241     bool hasBottom() const { return PID::hasBottom(pid()); }
0242 
0243     /// Does this (hadron) contain a c quark?
0244     bool hasCharm() const { return PID::hasCharm(pid()); }
0245 
0246     // /// Does this (hadron) contain an s quark?
0247     // bool hasStrange() const { return PID::hasStrange(pid()); }
0248 
0249     /// Is this particle potentially visible in a detector?
0250     bool isVisible() const;
0251 
0252     /// Is this a parton? (Hopefully not very often... fiducial FTW)
0253     bool isParton() const { return PID::isParton(pid()); }
0254 
0255     /// @}
0256 
0257 
0258     /// @name Constituents (for composite particles)
0259     /// @{
0260 
0261     /// Set direct constituents of this particle
0262     virtual void setConstituents(const Particles& cs, bool setmom=false);
0263 
0264     /// Add a single direct constituent to this particle
0265     virtual void addConstituent(const Particle& c, bool addmom=false);
0266 
0267     /// Add direct constituents to this particle
0268     virtual void addConstituents(const Particles& cs, bool addmom=false);
0269 
0270 
0271     /// Determine if this Particle is a composite of other Rivet Particles
0272     bool isComposite() const { return !constituents().empty(); }
0273 
0274 
0275     /// @brief Direct constituents of this particle, returned by reference
0276     ///
0277     /// The returned vector will be empty if this particle is non-composite,
0278     /// and its entries may themselves be composites.
0279     const Particles& constituents() const { return _constituents; }
0280 
0281     /// @brief Direct constituents of this particle, sorted by a functor
0282     /// @note Returns a copy, thanks to the sorting
0283     const Particles constituents(const ParticleSorter& sorter) const {
0284       return sortBy(constituents(), sorter);
0285     }
0286 
0287     /// @brief Direct constituents of this particle, filtered by a Cut
0288     /// @note Returns a copy, thanks to the filtering
0289     const Particles constituents(const Cut& c) const {
0290       return select(constituents(), c);
0291     }
0292 
0293     /// @brief Direct constituents of this particle, sorted by a functor
0294     /// @note Returns a copy, thanks to the filtering and sorting
0295     const Particles constituents(const Cut& c, const ParticleSorter& sorter) const {
0296       return sortBy(constituents(c), sorter);
0297     }
0298 
0299     /// @brief Direct constituents of this particle, filtered by a selection functor
0300     /// @note Returns a copy, thanks to the filtering
0301     const Particles constituents(const ParticleSelector& selector) const {
0302       return select(constituents(), selector);
0303     }
0304 
0305     /// @brief Direct constituents of this particle, filtered and sorted by functors
0306     /// @note Returns a copy, thanks to the filtering and sorting
0307     const Particles constituents(const ParticleSelector& selector, const ParticleSorter& sorter) const {
0308       return sortBy(constituents(selector), sorter);
0309     }
0310 
0311 
0312     /// @brief Fundamental constituents of this particle
0313     /// @note Returns {{*this}} if this particle is non-composite.
0314     Particles rawConstituents() const;
0315 
0316     /// @brief Fundamental constituents of this particle, sorted by a functor
0317     /// @note Returns a copy, thanks to the sorting
0318     const Particles rawConstituents(const ParticleSorter& sorter) const {
0319       return sortBy(rawConstituents(), sorter);
0320     }
0321 
0322     /// @brief Fundamental constituents of this particle, filtered by a Cut
0323     /// @note Returns a copy, thanks to the filtering
0324     const Particles rawConstituents(const Cut& c) const {
0325       return select(rawConstituents(), c);
0326     }
0327 
0328     /// @brief Fundamental constituents of this particle, sorted by a functor
0329     /// @note Returns a copy, thanks to the filtering and sorting
0330     const Particles rawConstituents(const Cut& c, const ParticleSorter& sorter) const {
0331       return sortBy(rawConstituents(c), sorter);
0332     }
0333 
0334     /// @brief Fundamental constituents of this particle, filtered by a selection functor
0335     /// @note Returns a copy, thanks to the filtering
0336     const Particles rawConstituents(const ParticleSelector& selector) const {
0337       return select(rawConstituents(), selector);
0338     }
0339 
0340     /// @brief Fundamental constituents of this particle, filtered and sorted by functors
0341     /// @note Returns a copy, thanks to the filtering and sorting
0342     const Particles rawConstituents(const ParticleSelector& selector, const ParticleSorter& sorter) const {
0343       return sortBy(rawConstituents(selector), sorter);
0344     }
0345 
0346     /// @}
0347 
0348 
0349     /// @name Ancestry (for fundamental particles with a HepMC link)
0350     /// @{
0351 
0352     /// Get a list of the direct parents of the current particle (with optional selection Cut)
0353     ///
0354     /// @note This is valid in MC, but may not be answerable
0355     /// experimentally -- use this function with care when replicating
0356     /// experimental analyses!
0357     Particles parents(const Cut& c=Cuts::OPEN) const;
0358 
0359     /// Get a list of the direct parents of the current particle (with selector function)
0360     ///
0361     /// @note This is valid in MC, but may not be answerable
0362     /// experimentally -- use this function with care when replicating
0363     /// experimental analyses!
0364     Particles parents(const ParticleSelector& f) const {
0365       return select(parents(), f);
0366     }
0367 
0368     /// Check whether any particle in the particle's parent list has the requested property
0369     ///
0370     /// @note This question is valid in MC, but may not be answerable
0371     /// experimentally -- use this function with care when replicating
0372     /// experimental analyses!
0373     bool hasParentWith(const ParticleSelector& f) const {
0374       return !parents(f).empty();
0375     }
0376     /// Check whether any particle in the particle's parent list has the requested property
0377     ///
0378     /// @note This question is valid in MC, but may not be answerable
0379     /// experimentally -- use this function with care when replicating
0380     /// experimental analyses!
0381     bool hasParentWith(const Cut& c) const;
0382 
0383     /// Check whether any particle in the particle's parent list does not have the requested property
0384     ///
0385     /// @note This question is valid in MC, but may not be answerable
0386     /// experimentally -- use this function with care when replicating
0387     /// experimental analyses!
0388     bool hasParentWithout(const ParticleSelector& f) const {
0389       return hasParentWith([&](const Particle& p){ return !f(p); });
0390     }
0391     /// Check whether any particle in the particle's parent list does not have the requested property
0392     ///
0393     /// @note This question is valid in MC, but may not be answerable
0394     /// experimentally -- use this function with care when replicating
0395     /// experimental analyses!
0396     bool hasParentWithout(const Cut& c) const;
0397 
0398 
0399     /// Get a list of the ancestors of the current particle (with optional selection Cut)
0400     ///
0401     /// @note By default only physical ancestors, with status=2, are returned.
0402     ///
0403     /// @note This is valid in MC, but may not be answerable experimentally --
0404     /// use this function with care when replicating experimental analyses!
0405     Particles ancestors(const Cut& c=Cuts::OPEN, bool only_physical=true) const;
0406 
0407     /// Get a list of the direct parents of the current particle (with selector function)
0408     ///
0409     /// @note By default only physical ancestors, with status=2, are returned.
0410     ///
0411     /// @note This is valid in MC, but may not be answerable experimentally --
0412     /// use this function with care when replicating experimental analyses!
0413     Particles ancestors(const ParticleSelector& f, bool only_physical=true) const {
0414       return select(ancestors(Cuts::OPEN, only_physical), f);
0415     }
0416 
0417     /// Check whether any particle in the particle's ancestor list has the requested property
0418     ///
0419     /// @note This question is valid in MC, but may not be answerable
0420     /// experimentally -- use this function with care when replicating
0421     /// experimental analyses!
0422     bool hasAncestorWith(const ParticleSelector& f, bool only_physical=true) const {
0423       return !ancestors(f, only_physical).empty();
0424     }
0425     /// Check whether any particle in the particle's ancestor list has the requested property
0426     ///
0427     /// @note This question is valid in MC, but may not be answerable
0428     /// experimentally -- use this function with care when replicating
0429     /// experimental analyses!
0430     bool hasAncestorWith(const Cut& c, bool only_physical=true) const;
0431 
0432     /// Check whether any particle in the particle's ancestor list does not have the requested property
0433     ///
0434     /// @note This question is valid in MC, but may not be answerable
0435     /// experimentally -- use this function with care when replicating
0436     /// experimental analyses!
0437     bool hasAncestorWithout(const ParticleSelector& f, bool only_physical=true) const {
0438       return hasAncestorWith([&](const Particle& p){ return !f(p); }, only_physical);
0439     }
0440     /// Check whether any particle in the particle's ancestor list does not have the requested property
0441     ///
0442     /// @note This question is valid in MC, but may not be answerable
0443     /// experimentally -- use this function with care when replicating
0444     /// experimental analyses!
0445     bool hasAncestorWithout(const Cut& c, bool only_physical=true) const;
0446 
0447     /// @brief Determine whether the particle is from a b-hadron decay
0448     ///
0449     /// @note This question is valid in MC, but may not be perfectly answerable
0450     /// experimentally -- use this function with care when replicating
0451     /// experimental analyses!
0452     bool fromBottom() const;
0453 
0454     /// @brief Determine whether the particle is from a c-hadron decay
0455     ///
0456     /// @note If a hadron contains b and c quarks it is considered a bottom
0457     /// hadron and NOT a charm hadron.
0458     ///
0459     /// @note This question is valid in MC, but may not be perfectly answerable
0460     /// experimentally -- use this function with care when replicating
0461     /// experimental analyses!
0462     bool fromCharm() const;
0463 
0464     // /// @brief Determine whether the particle is from a s-hadron decay
0465     // ///
0466     // /// @note If a hadron contains b or c quarks as well as strange it is
0467     // /// considered a b or c hadron, but NOT a strange hadron.
0468     // ///
0469     // /// @note This question is valid in MC, but may not be perfectly answerable
0470     // /// experimentally -- use this function with care when replicating
0471     // /// experimental analyses!
0472     // bool fromStrange() const;
0473 
0474     /// @brief Determine whether the particle is from a hadron decay
0475     ///
0476     /// @note This question is valid in MC, but may not be perfectly answerable
0477     /// experimentally -- use this function with care when replicating
0478     /// experimental analyses!
0479     bool fromHadron() const;
0480 
0481     /// @brief Determine whether the particle is from a tau decay
0482     ///
0483     /// @note This question is valid in MC, but may not be perfectly answerable
0484     /// experimentally -- use this function with care when replicating
0485     /// experimental analyses!
0486     bool fromTau(bool prompt_taus_only=false) const;
0487 
0488     /// @brief Determine whether the particle is from a prompt tau decay
0489     ///
0490     /// @note This question is valid in MC, but may not be perfectly answerable
0491     /// experimentally -- use this function with care when replicating
0492     /// experimental analyses!
0493     bool fromPromptTau() const { return fromTau(true); }
0494 
0495     /// @brief Determine whether the particle is from a tau which decayed hadronically
0496     ///
0497     /// @note This question is valid in MC, but may not be perfectly answerable
0498     /// experimentally -- use this function with care when replicating
0499     /// experimental analyses!
0500     bool fromHadronicTau(bool prompt_taus_only=false) const;
0501 
0502     /// @brief Shorthand definition of 'promptness' based on set definition flags
0503     ///
0504     /// A "direct" particle is one directly connected to the hard process. It is a
0505     /// preferred alias for "prompt", since it has no confusing implications about
0506     /// distinguishability by timing information.
0507     ///
0508     /// The boolean arguments allow a decay lepton to be considered direct if
0509     /// its parent was a "real" direct lepton.
0510     ///
0511     /// @note This one doesn't make any judgements about final-stateness
0512     bool isDirect(bool allow_from_direct_tau=false, bool allow_from_direct_mu=false) const;
0513 
0514     /// Alias for isDirect
0515     bool isPrompt(bool allow_from_prompt_tau=false, bool allow_from_prompt_mu=false) const {
0516       return isDirect(allow_from_prompt_tau, allow_from_prompt_mu);
0517     }
0518 
0519     /// @}
0520 
0521 
0522     /// @name Decay info
0523     /// @{
0524 
0525     /// Whether this particle is stable according to the generator
0526     bool isStable() const;
0527 
0528     /// @todo isDecayed? How to restrict to physical particles?
0529 
0530 
0531     /// Get a list of the direct descendants from the current particle (with optional selection Cut)
0532     Particles children(const Cut& c=Cuts::OPEN) const;
0533 
0534     /// Get a list of the direct descendants from the current particle (with selector function)
0535     Particles children(const ParticleSelector& f) const {
0536       return select(children(), f);
0537     }
0538 
0539     /// Check whether any direct child of this particle has the requested property
0540     ///
0541     /// @note This question is valid in MC, but may not be answerable
0542     /// experimentally -- use this function with care when replicating
0543     /// experimental analyses!
0544     bool hasChildWith(const ParticleSelector& f) const {
0545       return !children(f).empty();
0546     }
0547     /// Check whether any direct child of this particle has the requested property
0548     ///
0549     /// @note This question is valid in MC, but may not be answerable
0550     /// experimentally -- use this function with care when replicating
0551     /// experimental analyses!
0552     bool hasChildWith(const Cut& c) const;
0553 
0554     /// Check whether any direct child of this particle does not have the requested property
0555     ///
0556     /// @note This question is valid in MC, but may not be answerable
0557     /// experimentally -- use this function with care when replicating
0558     /// experimental analyses!
0559     bool hasChildWithout(const ParticleSelector& f) const {
0560       return hasChildWith([&](const Particle& p){ return !f(p); });
0561     }
0562     /// Check whether any direct child of this particle does not have the requested property
0563     ///
0564     /// @note This question is valid in MC, but may not be answerable
0565     /// experimentally -- use this function with care when replicating
0566     /// experimental analyses!
0567     bool hasChildWithout(const Cut& c) const;
0568 
0569 
0570     /// Get a list of all the descendants from the current particle (with optional selection Cut)
0571     Particles allDescendants(const Cut& c=Cuts::OPEN, bool remove_duplicates=true) const;
0572 
0573     /// Get a list of all the descendants from the current particle (with selector function)
0574     Particles allDescendants(const ParticleSelector& f, bool remove_duplicates=true) const {
0575       return select(allDescendants(Cuts::OPEN, remove_duplicates), f);
0576     }
0577 
0578     /// Check whether any descendant of this particle has the requested property
0579     ///
0580     /// @note This question is valid in MC, but may not be answerable
0581     /// experimentally -- use this function with care when replicating
0582     /// experimental analyses!
0583     bool hasDescendantWith(const ParticleSelector& f, bool remove_duplicates=true) const {
0584       return !allDescendants(f, remove_duplicates).empty();
0585     }
0586     /// Check whether any descendant of this particle has the requested property
0587     ///
0588     /// @note This question is valid in MC, but may not be answerable
0589     /// experimentally -- use this function with care when replicating
0590     /// experimental analyses!
0591     bool hasDescendantWith(const Cut& c, bool remove_duplicates=true) const;
0592 
0593     /// Check whether any descendant of this particle does not have the requested property
0594     ///
0595     /// @note This question is valid in MC, but may not be answerable
0596     /// experimentally -- use this function with care when replicating
0597     /// experimental analyses!
0598     bool hasDescendantWithout(const ParticleSelector& f, bool remove_duplicates=true) const {
0599       return hasDescendantWith([&](const Particle& p){ return !f(p); }, remove_duplicates);
0600     }
0601     /// Check whether any descendant of this particle does not have the requested property
0602     ///
0603     /// @note This question is valid in MC, but may not be answerable
0604     /// experimentally -- use this function with care when replicating
0605     /// experimental analyses!
0606     bool hasDescendantWithout(const Cut& c, bool remove_duplicates=true) const;
0607 
0608 
0609     /// Get a list of all the stable descendants from the current particle (with optional selection Cut)
0610     ///
0611     /// @todo Use recursion through replica-avoiding MCUtils functions to avoid bookkeeping duplicates
0612     /// @todo Insist that the current particle is post-hadronization, otherwise throw an exception?
0613     Particles stableDescendants(const Cut& c=Cuts::OPEN) const;
0614 
0615     /// Get a list of all the stable descendants from the current particle (with selector function)
0616     Particles stableDescendants(const ParticleSelector& f) const {
0617       return select(stableDescendants(), f);
0618     }
0619 
0620     /// Check whether any stable descendant of this particle has the requested property
0621     ///
0622     /// @note This question is valid in MC, but may not be answerable
0623     /// experimentally -- use this function with care when replicating
0624     /// experimental analyses!
0625     bool hasStableDescendantWith(const ParticleSelector& f) const {
0626       return !stableDescendants(f).empty();
0627     }
0628     /// Check whether any stable descendant of this particle has the requested property
0629     ///
0630     /// @note This question is valid in MC, but may not be answerable
0631     /// experimentally -- use this function with care when replicating
0632     /// experimental analyses!
0633     bool hasStableDescendantWith(const Cut& c) const;
0634 
0635     /// Check whether any stable descendant of this particle does not have the requested property
0636     ///
0637     /// @note This question is valid in MC, but may not be answerable
0638     /// experimentally -- use this function with care when replicating
0639     /// experimental analyses!
0640     bool hasStableDescendantWithout(const ParticleSelector& f) const {
0641       return hasStableDescendantWith([&](const Particle& p){ return !f(p); });
0642     }
0643     /// Check whether any stable descendant of this particle does not have the requested property
0644     ///
0645     /// @note This question is valid in MC, but may not be answerable
0646     /// experimentally -- use this function with care when replicating
0647     /// experimental analyses!
0648     bool hasStableDescendantWithout(const Cut& c) const;
0649 
0650 
0651     /// Flight length of the particle from origin to decay
0652     ///
0653     /// @note Divide by mm or cm as usual to get the appropriate units.
0654     double flightLength() const;
0655 
0656     /// @}
0657 
0658 
0659     /// @name Duplicate testing
0660     /// @{
0661 
0662     /// @brief Determine whether a particle is the first in a decay chain to meet the function requirement
0663     inline bool isFirstWith(const ParticleSelector& f) const {
0664       if (!f(*this)) return false; //< This doesn't even meet f, let alone being the last to do so
0665       if (any(parents(), f)) return false; //< If a direct parent has this property, this isn't the first
0666       return true;
0667     }
0668 
0669     /// @brief Determine whether a particle is the first in a decay chain not to meet the function requirement
0670     inline bool isFirstWithout(const ParticleSelector& f) const {
0671       return isFirstWith([&](const Particle& p){ return !f(p); });
0672     }
0673 
0674     /// @brief Determine whether a particle is the last in a decay chain to meet the function requirement
0675     inline bool isLastWith(const ParticleSelector& f) const {
0676       if (!f(*this)) return false; //< This doesn't even meet f, let alone being the last to do so
0677       if (any(children(), f)) return false; //< If a child has this property, this isn't the last
0678       return true;
0679     }
0680 
0681     /// @brief Determine whether a particle is the last in a decay chain not to meet the function requirement
0682     inline bool isLastWithout(const ParticleSelector& f) const {
0683       return isLastWith([&](const Particle& p){ return !f(p); });
0684     }
0685 
0686     /// @}
0687 
0688 
0689     /// @name Comparison
0690     /// @{
0691 
0692     /// Compare particles, based on "external" characteristics, with a little angular tolerance
0693     ///
0694     /// @note Not a deep comparison: GenParticle ptr and constituents are not used in the comparison
0695     bool isSame(const Particle& other) const {
0696       if (pid() != other.pid()) return false;
0697       if (!isZero((mom() - other.mom()).mod())) return false;
0698       if (!isZero((origin() - other.origin()).mod())) return false;
0699       return true;
0700     }
0701 
0702     /// @}
0703 
0704 
0705   protected:
0706 
0707     /// A pointer to the original GenParticle from which this Particle is projected.
0708     ConstGenParticlePtr _original;
0709 
0710     /// Constituent particles if this is a composite (may be empty)
0711     Particles _constituents;
0712 
0713     /// The PDG ID code for this Particle.
0714     PdgId _id;
0715 
0716     /// The momentum of this particle.
0717     FourMomentum _momentum;
0718 
0719     /// The creation position of this particle.
0720     FourVector _origin;
0721 
0722     /// Cached computation of directness, via ancestry. Second element is cache status
0723     /// @todo Replace this awkward caching with C++17 std::optional
0724     mutable std::vector<std::pair<bool,bool> > _isDirect;
0725 
0726   };
0727 
0728 
0729   /// @name String representation and streaming support
0730   /// @{
0731 
0732   /// Allow a Particle to be passed to an ostream.
0733   std::ostream& operator << (std::ostream& os, const Particle& p);
0734 
0735   /// Allow ParticlePair to be passed to an ostream.
0736   std::ostream& operator << (std::ostream& os, const ParticlePair& pp);
0737 
0738   /// @}
0739 
0740 
0741 }
0742 
0743 
0744 #include "Rivet/Tools/ParticleUtils.hh"
0745 
0746 #endif