Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:06:53

0001 #ifndef RIVET_PARTICLEUTILS_HH
0002 #define RIVET_PARTICLEUTILS_HH
0003 
0004 #include "Rivet/Particle.hh"
0005 #include "Rivet/Tools/ParticleBaseUtils.hh"
0006 #include "Rivet/Tools/ParticleIdUtils.hh"
0007 
0008 // Macros to map Rivet::Particle functions to PID:: functions of the same name
0009 #define PARTICLE_TO_PID_BOOLFN(fname) inline bool fname (const Particle& p) { return PID:: fname (p.pid()); }
0010 #define PARTICLE_TO_PID_INTFN(fname) inline int fname (const Particle& p) { return PID:: fname (p.pid()); }
0011 #define PARTICLE_TO_PID_DBLFN(fname) inline double fname (const Particle& p) { return PID:: fname (p.pid()); }
0012 
0013 namespace Rivet {
0014 
0015 
0016   /// @defgroup particleutils Functions for Particles
0017   /// @{
0018 
0019   /// @defgroup particleutils_class Particle classifier functions
0020   /// @{
0021 
0022   /// Unbound function access to PID code
0023   inline int pid(const Particle& p) { return p.pid(); }
0024 
0025   /// Unbound function access to abs PID code
0026   inline int abspid(const Particle& p) { return p.abspid(); }
0027 
0028 
0029   /// Is this particle species charged?
0030   PARTICLE_TO_PID_BOOLFN(isCharged)
0031 
0032   /// Is this particle species neutral?
0033   PARTICLE_TO_PID_BOOLFN(isNeutral)
0034 
0035 
0036   /// Is this a neutrino?
0037   PARTICLE_TO_PID_BOOLFN(isNeutrino)
0038 
0039   /// Determine if the PID is that of a charged lepton
0040   PARTICLE_TO_PID_BOOLFN(isChargedLepton)
0041 
0042   /// Determine if the PID is that of a lepton (charged or neutral)
0043   PARTICLE_TO_PID_BOOLFN(isLepton)
0044 
0045   /// Determine if the PID is that of a photon
0046   PARTICLE_TO_PID_BOOLFN(isPhoton)
0047 
0048   /// Determine if the PID is that of an electron or positron
0049   PARTICLE_TO_PID_BOOLFN(isElectron)
0050 
0051   /// Determine if the PID is that of an muon or antimuon
0052   PARTICLE_TO_PID_BOOLFN(isMuon)
0053 
0054   /// Determine if the PID is that of an tau or antitau
0055   PARTICLE_TO_PID_BOOLFN(isTau)
0056 
0057   /// Determine if the PID is that of a hadron
0058   PARTICLE_TO_PID_BOOLFN(isHadron)
0059 
0060   /// Determine if the PID is that of a meson
0061   PARTICLE_TO_PID_BOOLFN(isMeson)
0062 
0063   /// Determine if the PID is that of a baryon
0064   PARTICLE_TO_PID_BOOLFN(isBaryon)
0065 
0066   /// Determine if the PID is that of a quark
0067   PARTICLE_TO_PID_BOOLFN(isQuark)
0068 
0069   /// Determine if the PID is that of a parton (quark or gluon)
0070   PARTICLE_TO_PID_BOOLFN(isParton)
0071 
0072 
0073 
0074   /// Determine if the PID is that of a W+
0075   PARTICLE_TO_PID_BOOLFN(isWplus)
0076 
0077   /// Determine if the PID is that of a W-
0078   PARTICLE_TO_PID_BOOLFN(isWminus)
0079 
0080   /// Determine if the PID is that of a W+-
0081   PARTICLE_TO_PID_BOOLFN(isW)
0082 
0083   /// Determine if the PID is that of a Z0
0084   PARTICLE_TO_PID_BOOLFN(isZ)
0085 
0086   /// Determine if the PID is that of an SM/lightest SUSY Higgs
0087   PARTICLE_TO_PID_BOOLFN(isHiggs)
0088 
0089   /// Determine if the PID is that of an s/sbar
0090   PARTICLE_TO_PID_BOOLFN(isStrange)
0091 
0092   /// Determine if the PID is that of a c/cbar
0093   PARTICLE_TO_PID_BOOLFN(isCharm)
0094 
0095   /// Determine if the PID is that of a b/bbar
0096   PARTICLE_TO_PID_BOOLFN(isBottom)
0097 
0098   /// Determine if the PID is that of a t/tbar
0099   PARTICLE_TO_PID_BOOLFN(isTop)
0100 
0101 
0102   /// Determine if the particle is a heavy flavour hadron or parton
0103   PARTICLE_TO_PID_BOOLFN(isHeavyFlavour)
0104 
0105   /// Determine if the PID is that of a heavy parton (c,b,t)
0106   PARTICLE_TO_PID_BOOLFN(isHeavyParton)
0107 
0108   /// Determine if the PID is that of a light parton (u,d,s)
0109   PARTICLE_TO_PID_BOOLFN(isLightParton)
0110 
0111 
0112   /// Determine if the PID is that of a heavy flavour (b or c) meson
0113   PARTICLE_TO_PID_BOOLFN(isHeavyMeson)
0114 
0115   /// Determine if the PID is that of a heavy flavour (b or c) baryon
0116   PARTICLE_TO_PID_BOOLFN(isHeavyBaryon)
0117 
0118   /// Determine if the PID is that of a heavy flavour (b or c) hadron
0119   PARTICLE_TO_PID_BOOLFN(isHeavyHadron)
0120 
0121 
0122   /// Determine if the PID is that of a light flavour (not b or c) meson
0123   PARTICLE_TO_PID_BOOLFN(isLightMeson)
0124 
0125   /// Determine if the PID is that of a light flavour (not b or c) baryon
0126   PARTICLE_TO_PID_BOOLFN(isLightBaryon)
0127 
0128   /// Determine if the PID is that of a light flavour (not b or c) hadron
0129   PARTICLE_TO_PID_BOOLFN(isLightHadron)
0130 
0131 
0132   /// Determine if the PID is that of a b-meson.
0133   PARTICLE_TO_PID_BOOLFN(isBottomMeson)
0134 
0135   /// Determine if the PID is that of a b-baryon.
0136   PARTICLE_TO_PID_BOOLFN(isBottomBaryon)
0137 
0138   /// Determine if the PID is that of a b-hadron.
0139   PARTICLE_TO_PID_BOOLFN(isBottomHadron)
0140 
0141 
0142   /// @brief Determine if the PID is that of a c-meson.
0143   ///
0144   /// Specifically, the _heaviest_ quark is a c: a B_c is a b-meson and NOT a c-meson.
0145   /// Charmonia (closed charm) are counted as c-mesons here.
0146   PARTICLE_TO_PID_BOOLFN(isCharmMeson)
0147 
0148   /// @brief Determine if the PID is that of a c-baryon.
0149   ///
0150   /// Specifically, the _heaviest_ quark is a c: a baryon containing a b & c
0151   /// is a b-baryon and NOT a c-baryon. To test for the simpler case, just use
0152   /// a combination of hasCharm() and isBaryon().
0153   PARTICLE_TO_PID_BOOLFN(isCharmBaryon)
0154 
0155   /// Determine if the PID is that of a c-hadron.
0156   PARTICLE_TO_PID_BOOLFN(isCharmHadron)
0157 
0158 
0159   // /// Determine if the PID is that of a strange meson
0160   // PARTICLE_TO_PID_BOOLFN(isStrangeMeson)
0161 
0162   // /// Determine if the PID is that of a strange baryon
0163   // PARTICLE_TO_PID_BOOLFN(isStrangeBaryon)
0164 
0165   // /// Determine if the PID is that of a strange hadron
0166   // PARTICLE_TO_PID_BOOLFN(isStrangeHadron)
0167 
0168 
0169 
0170   /// Is this a pomeron, odderon, or generic reggeon?
0171   PARTICLE_TO_PID_BOOLFN(isReggeon)
0172 
0173   /// Determine if the PID is that of a diquark (used in hadronization models)
0174   PARTICLE_TO_PID_BOOLFN(isDiquark)
0175 
0176   /// Determine if the PID is that of a pentaquark (hypothetical hadron)
0177   PARTICLE_TO_PID_BOOLFN(isPentaquark)
0178 
0179   /// Is this a fundamental SUSY particle?
0180   PARTICLE_TO_PID_BOOLFN(isSUSY)
0181 
0182   /// Is this an R-hadron?
0183   PARTICLE_TO_PID_BOOLFN(isRhadron)
0184 
0185   /// Is this a technicolor particle?
0186   PARTICLE_TO_PID_BOOLFN(isTechnicolor)
0187 
0188   /// Is this an excited (composite) quark or lepton?
0189   PARTICLE_TO_PID_BOOLFN(isExcited)
0190 
0191   /// Is this a Kaluza-Klein excitation?
0192   PARTICLE_TO_PID_BOOLFN(isKK)
0193 
0194   /// Is this a graviton?
0195   PARTICLE_TO_PID_BOOLFN(isGraviton)
0196 
0197   /// Is this a BSM particle (including graviton)?
0198   PARTICLE_TO_PID_BOOLFN(isBSM)
0199 
0200 
0201 
0202   /// Determine if the PID is in the generator-specific range
0203   PARTICLE_TO_PID_BOOLFN(isGenSpecific)
0204 
0205   /// Determine if the PID is that of an EW scale resonance
0206   PARTICLE_TO_PID_BOOLFN(isResonance)
0207 
0208   /// Check the PID for usability in transport codes like Geant4
0209   PARTICLE_TO_PID_BOOLFN(isTransportable)
0210 
0211 
0212 
0213   /// Does this particle contain an up quark?
0214   PARTICLE_TO_PID_BOOLFN(hasUp)
0215 
0216   /// Does this particle contain a down quark?
0217   PARTICLE_TO_PID_BOOLFN(hasDown)
0218 
0219   /// Does this particle contain a strange quark?
0220   PARTICLE_TO_PID_BOOLFN(hasStrange)
0221 
0222   /// Does this particle contain a charm quark?
0223   PARTICLE_TO_PID_BOOLFN(hasCharm)
0224 
0225   /// Does this particle contain a bottom quark?
0226   PARTICLE_TO_PID_BOOLFN(hasBottom)
0227 
0228   /// Does this particle contain a top quark?
0229   PARTICLE_TO_PID_BOOLFN(hasTop)
0230 
0231 
0232 
0233   /// jSpin returns 2J+1, where J is the total spin
0234   PARTICLE_TO_PID_INTFN(jSpin)
0235 
0236   /// sSpin returns 2S+1, where S is the spin
0237   PARTICLE_TO_PID_INTFN(sSpin)
0238 
0239   /// lSpin returns 2L+1, where L is the orbital angular momentum
0240   PARTICLE_TO_PID_INTFN(lSpin)
0241 
0242 
0243   /// Return the charge
0244   PARTICLE_TO_PID_DBLFN(charge)
0245 
0246   /// Return 3 times the charge (3 x quark charge is an int)
0247   PARTICLE_TO_PID_INTFN(charge3)
0248 
0249   /// Return the absolute charge
0250   PARTICLE_TO_PID_DBLFN(abscharge)
0251 
0252   /// Return 3 times the abs charge (3 x quark charge is an int)
0253   PARTICLE_TO_PID_INTFN(abscharge3)
0254 
0255   /// Get the atomic number (number of protons) in a nucleus/ion
0256   PARTICLE_TO_PID_INTFN(nuclZ)
0257 
0258   /// Get the atomic weight (number of nucleons) in a nucleus/ion
0259   PARTICLE_TO_PID_INTFN(nuclA)
0260 
0261   /// If this is a nucleus (ion), get nLambda
0262   PARTICLE_TO_PID_INTFN(nuclNlambda)
0263 
0264   /// @}
0265 
0266 
0267   /// @defgroup particleutils_pairclass Particle pair classifiers
0268   /// @brief
0269   /// @todo Make versions that work on ParticlePair?
0270   /// @{
0271 
0272   inline bool isSameSign(const Particle& a, const Particle& b) { return PID::isSameSign(a.pid(), b.pid()); }
0273   inline bool isOppSign(const Particle& a, const Particle& b) { return PID::isOppSign(a.pid(), b.pid()); }
0274   inline bool isSameFlav(const Particle& a, const Particle& b) { return PID::isSameFlav(a.pid(), b.pid()); }
0275   inline bool isOppFlav(const Particle& a, const Particle& b) { return PID::isOppFlav(a.pid(), b.pid()); }
0276 
0277   inline bool isOSSF(const Particle& a, const Particle& b) { return PID::isOSSF(a.pid(), b.pid()); }
0278   inline bool isSSSF(const Particle& a, const Particle& b) { return PID::isSSSF(a.pid(), b.pid()); }
0279   inline bool isOSOF(const Particle& a, const Particle& b) { return PID::isOSOF(a.pid(), b.pid()); }
0280   inline bool isSSOF(const Particle& a, const Particle& b) { return PID::isSSOF(a.pid(), b.pid()); }
0281 
0282   /// @}
0283 
0284 
0285   /// @defgroup particleutils_charge Particle charge/sign comparison functions
0286   /// @{
0287 
0288   /// @brief Return true if Particles @a a and @a b have the opposite charge sign
0289   /// @note Two neutrals returns false
0290   inline bool oppSign(const Particle& a, const Particle& b) {
0291     return sign(a.charge3()) == -sign(b.charge3()) && sign(a.charge3()) != ZERO;
0292   }
0293 
0294   /// Return true if Particles @a a and @a b have the same charge sign
0295   /// @note Two neutrals returns true
0296   inline bool sameSign(const Particle& a, const Particle& b) {
0297     return sign(a.charge3()) == sign(b.charge3());
0298   }
0299 
0300   /// Return true if Particles @a a and @a b have the exactly opposite charge
0301   /// @note Two neutrals returns false
0302   inline bool oppCharge(const Particle& a, const Particle& b) {
0303     return a.charge3() == -b.charge3() && a.charge3() != 0;
0304   }
0305 
0306   /// Return true if Particles @a a and @a b have the same charge (including neutral)
0307   /// @note Two neutrals returns true
0308   inline bool sameCharge(const Particle& a, const Particle& b) {
0309     return a.charge3() == b.charge3();
0310   }
0311 
0312   /// Return true if Particles @a a and @a b have a different (not necessarily opposite) charge
0313   inline bool diffCharge(const Particle& a, const Particle& b) {
0314     return a.charge3() != b.charge3();
0315   }
0316 
0317   /// @}
0318 
0319 
0320 
0321   //////////////////////////////////////
0322 
0323 
0324 
0325   /// @defgroup particleutils_nonpid Non-PID particle properties, via unbound functions
0326   /// @{
0327 
0328   /// @brief Determine whether a particle is the first in a decay chain to meet the function requirement
0329   inline bool isFirstWith(const Particle& p, const ParticleSelector& f) {
0330     return p.isFirstWith(f);
0331   }
0332 
0333   /// @brief Determine whether a particle is the first in a decay chain not to meet the function requirement
0334   inline bool isFirstWithout(const Particle& p, const ParticleSelector& f) {
0335     return p.isFirstWithout(f);
0336   }
0337 
0338 
0339   /// @brief Determine whether a particle is the last in a decay chain to meet the function requirement
0340   inline bool isLastWith(const Particle& p, const ParticleSelector& f) {
0341     return p.isLastWith(f);
0342   }
0343 
0344   /// @brief Determine whether a particle is the last in a decay chain not to meet the function requirement
0345   inline bool isLastWithout(const Particle& p, const ParticleSelector& f) {
0346     return p.isLastWithout(f);
0347   }
0348 
0349 
0350 
0351   /// @brief Determine whether a particle has an ancestor which meets the function requirement
0352   inline bool hasAncestorWith(const Particle& p, const ParticleSelector& f, bool only_physical=true) {
0353     return p.hasAncestorWith(f, only_physical);
0354   }
0355 
0356   /// @brief Determine whether a particle has an ancestor which doesn't meet the function requirement
0357   inline bool hasAncestorWithout(const Particle& p, const ParticleSelector& f, bool only_physical=true) {
0358     return p.hasAncestorWithout(f, only_physical);
0359   }
0360 
0361 
0362   /// @brief Determine whether a particle has a parent which meets the function requirement
0363   inline bool hasParentWith(const Particle& p, const ParticleSelector& f) {
0364     return p.hasParentWith(f);
0365   }
0366 
0367   /// @brief Determine whether a particle has a parent which doesn't meet the function requirement
0368   inline bool hasParentWithout(const Particle& p, const ParticleSelector& f) {
0369     return p.hasParentWithout(f);
0370   }
0371 
0372 
0373   /// @brief Determine whether a particle has a child which meets the function requirement
0374   inline bool hasChildWith(const Particle& p, const ParticleSelector& f) {
0375     return p.hasChildWith(f);
0376   }
0377 
0378   /// @brief Determine whether a particle has a child which doesn't meet the function requirement
0379   inline bool hasChildWithout(const Particle& p, const ParticleSelector& f) {
0380     return p.hasChildWithout(f);
0381   }
0382 
0383 
0384   /// @brief Determine whether a particle has a descendant which meets the function requirement
0385   inline bool hasDescendantWith(const Particle& p, const ParticleSelector& f, bool remove_duplicates=true) {
0386     return p.hasDescendantWith(f, remove_duplicates);
0387   }
0388 
0389   /// @brief Determine whether a particle has a descendant which doesn't meet the function requirement
0390   inline bool hasDescendantWithout(const Particle& p, const ParticleSelector& f, bool remove_duplicates=true) {
0391     return p.hasDescendantWithout(f, remove_duplicates);
0392   }
0393 
0394 
0395   /// @brief Determine whether a particle has a stable descendant which meets the function requirement
0396   inline bool hasStableDescendantWith(const Particle& p, const ParticleSelector& f) {
0397     return p.hasStableDescendantWith(f);
0398   }
0399 
0400   /// @brief Determine whether a particle has a stable descendant which doesn't meet the function requirement
0401   inline bool hasStableDescendantWithout(const Particle& p, const ParticleSelector& f) {
0402     return p.hasStableDescendantWithout(f);
0403   }
0404 
0405 
0406 
0407   /// Is this particle potentially visible in a detector?
0408   inline bool isVisible(const Particle& p) { return p.isVisible(); }
0409 
0410   /// @brief Decide if a given particle is direct, via Particle::isDirect()
0411   ///
0412   /// A "direct" particle is one directly connected to the hard process. It is a
0413   /// preferred alias for "prompt", since it has no confusing implications about
0414   /// distinguishability by timing information.
0415   ///
0416   /// The boolean arguments allow a decay lepton to be considered direct if
0417   /// its parent was a "real" direct lepton.
0418   inline bool isDirect(const Particle& p, bool allow_from_direct_tau=false, bool allow_from_direct_mu=false) {
0419     return p.isDirect(allow_from_direct_tau, allow_from_direct_mu);
0420   }
0421 
0422   /// @brief Decide if a given particle is prompt, via Particle::isPrompt()
0423   ///
0424   /// The boolean arguments allow a decay lepton to be considered prompt if
0425   /// its parent was a "real" prompt lepton.
0426   inline bool isPrompt(const Particle& p, bool allow_from_prompt_tau=false, bool allow_from_prompt_mu=false) {
0427     return p.isPrompt(allow_from_prompt_tau, allow_from_prompt_mu);
0428   }
0429 
0430 
0431   /// Decide if a given particle is stable, via Particle::isStable()
0432   inline bool isStable(const Particle& p) { return p.isStable(); }
0433 
0434   /// Decide if a given particle decays hadronically
0435   inline bool hasHadronicDecay(const Particle& p) {
0436     if (p.isStable()) return false;
0437     if (p.hasChildWith(isHadron)) return true;
0438     return false;
0439   }
0440 
0441   /// Decide if a given particle decays leptonically (decays, and no hadrons)
0442   inline bool hasLeptonicDecay(const Particle& p) {
0443     if (p.isStable()) return false;
0444     if (p.hasChildWith(isHadron)) return false;
0445     return true;
0446   }
0447 
0448 
0449   /// Determine whether the particle is from a b-hadron decay
0450   inline bool fromBottom(const Particle& p) { return p.fromBottom(); }
0451 
0452   /// @brief Determine whether the particle is from a c-hadron decay
0453   inline bool fromCharm(const Particle& p) { return p.fromCharm(); }
0454 
0455   /// @brief Determine whether the particle is from a hadron decay
0456   inline bool fromHadron(const Particle& p) { return p.fromHadron(); }
0457 
0458   /// @brief Determine whether the particle is from a tau decay
0459   inline bool fromTau(const Particle& p, bool prompt_taus_only=false) {
0460     return p.fromTau(prompt_taus_only);
0461   }
0462 
0463   /// @brief Determine whether the particle is from a prompt tau decay
0464   inline bool fromPromptTau(const Particle& p) { return p.fromPromptTau(); }
0465 
0466   /// @}
0467 
0468 
0469   /// @defgroup particleutils_p2bool Particle classifier -> bool functors
0470   ///
0471   /// To be passed to any() or all() e.g. any(p.children(), HasPID(PID::MUON))
0472   /// @{
0473 
0474   /// Base type for Particle -> bool functors
0475   struct BoolParticleFunctor {
0476     virtual bool operator()(const Particle& p) const = 0;
0477     virtual ~BoolParticleFunctor() {}
0478   };
0479 
0480   /// Functor for and-combination of selector logic
0481   struct BoolParticleAND : public BoolParticleFunctor {
0482     BoolParticleAND(const std::vector<ParticleSelector>& sels) : selectors(sels) {}
0483     BoolParticleAND(const ParticleSelector& a, const ParticleSelector& b) : selectors({a,b}) {}
0484     BoolParticleAND(const ParticleSelector& a, const ParticleSelector& b, const ParticleSelector& c) : selectors({a,b,c}) {}
0485     bool operator()(const Particle& p) const {
0486       for (const ParticleSelector& sel : selectors) if (!sel(p)) return false;
0487       return true;
0488     }
0489     std::vector<ParticleSelector> selectors;
0490   };
0491   /// Operator syntactic sugar for AND construction
0492   inline BoolParticleAND operator && (const ParticleSelector& a, const ParticleSelector& b) {
0493     return BoolParticleAND(a, b);
0494   }
0495 
0496 
0497   /// Functor for or-combination of selector logic
0498   struct BoolParticleOR : public BoolParticleFunctor {
0499     BoolParticleOR(const std::vector<ParticleSelector>& sels) : selectors(sels) {}
0500     BoolParticleOR(const ParticleSelector& a, const ParticleSelector& b) : selectors({a,b}) {}
0501     BoolParticleOR(const ParticleSelector& a, const ParticleSelector& b, const ParticleSelector& c) : selectors({a,b,c}) {}
0502     bool operator()(const Particle& p) const {
0503       for (const ParticleSelector& sel : selectors) if (sel(p)) return true;
0504       return false;
0505     }
0506     std::vector<ParticleSelector> selectors;
0507   };
0508   /// Operator syntactic sugar for OR construction
0509   inline BoolParticleOR operator || (const ParticleSelector& a, const ParticleSelector& b) {
0510     return BoolParticleOR(a, b);
0511   }
0512 
0513   /// Functor for inverting selector logic
0514   struct BoolParticleNOT : public BoolParticleFunctor {
0515     BoolParticleNOT(const ParticleSelector& sel) : selector(sel) {}
0516     bool operator()(const Particle& p) const { return !selector(p); }
0517     ParticleSelector selector;
0518   };
0519   /// Operator syntactic sugar for NOT construction
0520   inline BoolParticleNOT operator ! (const ParticleSelector& a) {
0521     return BoolParticleNOT(a);
0522   }
0523 
0524 
0525   /// PID matching functor
0526   struct HasPID : public BoolParticleFunctor {
0527     HasPID(PdgId pid) : targetpids{pid} { }
0528     HasPID(vector<PdgId> pids) : targetpids{pids} { }
0529     HasPID(initializer_list<PdgId> pids) : targetpids{pids} { }
0530     bool operator()(const Particle& p) const { return contains(targetpids, p.pid()); }
0531     vector<PdgId> targetpids;
0532   };
0533   using hasPID = HasPID;
0534 
0535   /// |PID| matching functor
0536   struct HasAbsPID : public BoolParticleFunctor {
0537     HasAbsPID(PdgId pid) : targetapids{abs(pid)} { }
0538     HasAbsPID(vector<PdgId> pids) { for (PdgId pid : pids) targetapids.push_back(abs(pid)); }
0539     HasAbsPID(initializer_list<PdgId> pids) { for (PdgId pid : pids) targetapids.push_back(abs(pid)); }
0540     bool operator()(const Particle& p) const { return contains(targetapids, p.abspid()); }
0541     vector<PdgId> targetapids;
0542   };
0543   using hasAbsPID = HasAbsPID;
0544 
0545 
0546   /// Determine whether a particle is the first in a decay chain to meet the cut/function
0547   struct FirstParticleWith : public BoolParticleFunctor {
0548     FirstParticleWith(const ParticleSelector& f) : fn(f) { }
0549     FirstParticleWith(const Cut& c);
0550     bool operator()(const Particle& p) const { return isFirstWith(p, fn); }
0551     ParticleSelector fn;
0552   };
0553   using firstParticleWith = FirstParticleWith;
0554 
0555   /// Determine whether a particle is the first in a decay chain not to meet the cut/function
0556   struct FirstParticleWithout : public BoolParticleFunctor {
0557     FirstParticleWithout(const ParticleSelector& f) : fn(f) { }
0558     FirstParticleWithout(const Cut& c);
0559     bool operator()(const Particle& p) const { return isFirstWithout(p, fn); }
0560     ParticleSelector fn;
0561   };
0562   using firstParticleWithout = FirstParticleWithout;
0563 
0564 
0565   /// Determine whether a particle is the last in a decay chain to meet the cut/function
0566   struct LastParticleWith : public BoolParticleFunctor {
0567     template <typename FN>
0568     LastParticleWith(const FN& f) : fn(f) { }
0569     LastParticleWith(const Cut& c);
0570     bool operator()(const Particle& p) const { return isLastWith(p, fn); }
0571     std::function<bool(const Particle&)> fn;
0572   };
0573   using lastParticleWith = LastParticleWith;
0574 
0575   /// Determine whether a particle is the last in a decay chain not to meet the cut/function
0576   struct LastParticleWithout : public BoolParticleFunctor {
0577     LastParticleWithout(const ParticleSelector& f) : fn(f) { }
0578     LastParticleWithout(const Cut& c);
0579     bool operator()(const Particle& p) const { return isLastWithout(p, fn); }
0580     ParticleSelector fn;
0581   };
0582   using lastParticleWithout = LastParticleWithout;
0583 
0584 
0585   /// Determine whether a particle has an ancestor which meets the cut/function
0586   struct HasParticleAncestorWith : public BoolParticleFunctor {
0587     HasParticleAncestorWith(const ParticleSelector& f, bool only_physical=true) : fn(f), onlyphysical(only_physical) { }
0588     HasParticleAncestorWith(const Cut& c, bool only_physical=true);
0589     bool operator()(const Particle& p) const { return hasAncestorWith(p, fn, onlyphysical); }
0590     ParticleSelector fn;
0591     bool onlyphysical;
0592   };
0593   using hasParticleAncestorWith = HasParticleAncestorWith;
0594 
0595   /// Determine whether a particle has an ancestor which doesn't meet the cut/function
0596   struct HasParticleAncestorWithout : public BoolParticleFunctor {
0597     HasParticleAncestorWithout(const ParticleSelector& f, bool only_physical=true) : fn(f), onlyphysical(only_physical) { }
0598     HasParticleAncestorWithout(const Cut& c, bool only_physical=true);
0599     bool operator()(const Particle& p) const { return hasAncestorWithout(p, fn, onlyphysical); }
0600     ParticleSelector fn;
0601     bool onlyphysical;
0602   };
0603   using hasParticleAncestorWithout = HasParticleAncestorWithout;
0604 
0605 
0606   /// Determine whether a particle has an parent which meets the cut/function
0607   struct HasParticleParentWith : public BoolParticleFunctor {
0608     HasParticleParentWith(const ParticleSelector& f) : fn(f) { }
0609     HasParticleParentWith(const Cut& c);
0610     bool operator()(const Particle& p) const { return hasParentWith(p, fn); }
0611     ParticleSelector fn;
0612   };
0613   using hasParticleParentWith = HasParticleParentWith;
0614 
0615   /// Determine whether a particle has an parent which doesn't meet the cut/function
0616   struct HasParticleParentWithout : public BoolParticleFunctor {
0617     HasParticleParentWithout(const ParticleSelector& f) : fn(f) { }
0618     HasParticleParentWithout(const Cut& c);
0619     bool operator()(const Particle& p) const { return hasParentWithout(p, fn); }
0620     ParticleSelector fn;
0621   };
0622   using hasParticleParentWithout = HasParticleParentWithout;
0623 
0624 
0625   /// Determine whether a particle has a child which meets the cut/function
0626   struct HasParticleChildWith : public BoolParticleFunctor {
0627     HasParticleChildWith(const ParticleSelector& f) : fn(f) { }
0628     HasParticleChildWith(const Cut& c);
0629     bool operator()(const Particle& p) const { return hasChildWith(p, fn); }
0630     ParticleSelector fn;
0631   };
0632   using hasParticleChildWith = HasParticleChildWith;
0633 
0634   /// Determine whether a particle has a child which doesn't meet the cut/function
0635   struct HasParticleChildWithout : public BoolParticleFunctor {
0636     HasParticleChildWithout(const ParticleSelector& f) : fn(f) { }
0637     HasParticleChildWithout(const Cut& c);
0638     bool operator()(const Particle& p) const { return hasChildWithout(p, fn); }
0639     ParticleSelector fn;
0640   };
0641   using hasParticleChildWithout = HasParticleChildWithout;
0642 
0643 
0644   /// Determine whether a particle has a descendant which meets the cut/function
0645   struct HasParticleDescendantWith : public BoolParticleFunctor {
0646     HasParticleDescendantWith(const ParticleSelector& f, bool remove_duplicates=true) : fn(f), rmduplicates(remove_duplicates) { }
0647     HasParticleDescendantWith(const Cut& c, bool remove_duplicates=true);
0648     bool operator()(const Particle& p) const { return hasDescendantWith(p, fn, rmduplicates); }
0649     ParticleSelector fn;
0650     bool rmduplicates;
0651   };
0652   using hasParticleDescendantWith = HasParticleDescendantWith;
0653 
0654   /// Determine whether a particle has a descendant which doesn't meet the cut/function
0655   struct HasParticleDescendantWithout : public BoolParticleFunctor {
0656     HasParticleDescendantWithout(const ParticleSelector& f, bool remove_duplicates=true) : fn(f), rmduplicates(remove_duplicates) { }
0657     HasParticleDescendantWithout(const Cut& c, bool remove_duplicates=true);
0658     bool operator()(const Particle& p) const { return hasDescendantWithout(p, fn, rmduplicates); }
0659     ParticleSelector fn;
0660     bool rmduplicates;
0661   };
0662   using hasParticleDescendantWithout = HasParticleDescendantWithout;
0663 
0664   /// @}
0665 
0666 
0667   /// @defgroup particleutils_filt Unbound functions for filtering particles
0668   /// @{
0669 
0670   /// Filter a particle collection in-place to the subset that passes the supplied Cut
0671   Particles& iselect(Particles& particles, const Cut& c);
0672 
0673   /// Filter a particle collection in-place to the subset that passes the supplied Cut
0674   inline Particles select(const Particles& particles, const Cut& c) {
0675     Particles rtn = particles;
0676     return iselect(rtn, c);
0677   }
0678 
0679   /// Filter a particle collection in-place to the subset that passes the supplied Cut
0680   inline Particles select(const Particles& particles, const Cut& c, Particles& out) {
0681     out = select(particles, c);
0682     return out;
0683   }
0684 
0685    /// Filter a particle collection in-place to the subset that fails the supplied Cut
0686    Particles& idiscard(Particles& particles, const Cut& c);
0687 
0688   /// Filter a particle collection in-place to the subset that fails the supplied Cut
0689   inline Particles discard(const Particles& particles, const Cut& c) {
0690     Particles rtn = particles;
0691     return idiscard(rtn, c);
0692   }
0693 
0694   /// Filter a particle collection in-place to the subset that fails the supplied Cut
0695   inline Particles discard(const Particles& particles, const Cut& c, Particles& out) {
0696     out = discard(particles, c);
0697     return out;
0698   }
0699 
0700 
0701   // inline void ifilterIsolateDeltaR(Particles& particles, const FourMomenta& vecs) {
0702   //   ifilter_discard(particles,
0703   // }
0704 
0705 
0706   // inline Particles filterIsolateDeltaR(const Particles& particles, const FourMomenta& vecs) {
0707   // }
0708 
0709   /// @}
0710 
0711 
0712 
0713   /// @defgroup particleutils_pair Particle pair functions
0714   /// @{
0715 
0716   /// @brief Get the PDG ID codes of a ParticlePair
0717   inline PdgIdPair pids(const ParticlePair& pp) {
0718     return make_pair(pp.first.pid(), pp.second.pid());
0719   }
0720 
0721   namespace Kin {
0722   
0723     /// @brief Get the energies of a ParticlePair
0724     inline pair<double,double> energies(const ParticlePair& pp) {
0725       return make_pair(pp.first.E(), pp.second.E());
0726     }
0727    
0728     /// @brief Get the momenta of a ParticlePair
0729     inline pair<FourMomentum,FourMomentum> moms(const ParticlePair& pp) {
0730       return make_pair(pp.first.mom(), pp.second.mom());
0731     }
0732 
0733     /// @brief Get the mass of a ParticlePair
0734     inline double mass(const ParticlePair& pp) {
0735       return mass(pp.first, pp.second);
0736     }
0737 
0738     /// @brief Get the mass^2 of a ParticlePair
0739     inline double mass2(const ParticlePair& pp) {
0740       return mass2(pp.first, pp.second);
0741     }
0742 
0743     /// @brief Get the transverse mass of a ParticlePair
0744     inline double mT(const ParticlePair& pp) {
0745       return mT(pp.first, pp.second);
0746     }
0747 
0748     /// @brief Get the transverse momentum of a ParticlePair
0749     inline double pT(const ParticlePair& pp) {
0750       return pT(pp.first, pp.second);
0751     }
0752 
0753   }  
0754   
0755   /// @}
0756 
0757 
0758   /// @defgroup Specialised kinematics for Particle
0759   ///
0760   /// @todo Get rid of this, make it all work via the base class definitions and template matching
0761   ///
0762   /// @{
0763 
0764   namespace Kin {
0765 
0766     /// @brief Get the mass of a Particle and a P4
0767     inline double mass(const Particle& p, const FourMomentum& p4) {
0768       return mass(p.mom(), p4);
0769     }
0770 
0771     /// @brief Get the mass of a Particle and a P4
0772     inline double mass(const FourMomentum& p4, const Particle& p) {
0773       return mass(p4, p.mom());
0774     }
0775 
0776     /// @brief Get the mass of a pair of Particle (as separate args)
0777     inline double mass(const Particle& p1, const Particle& p2) {
0778       return mass(p1.mom(), p2.mom());
0779     }
0780 
0781     /// @brief Get the mass^2 of a Particle and a P4
0782     inline double mass2(const Particle& p, const P4& p4) {
0783       return mass2(p.mom(), p4);
0784     }
0785 
0786     /// @brief Get the mass^2 of a Particle and a P4
0787     inline double mass2(const P4& p4, const Particle& p) {
0788       return mass2(p4, p.mom());
0789     }
0790 
0791     /// @brief Get the mass^2 of a pair of Particle (as separate args)
0792     inline double mass2(const Particle& p1, const Particle& p2) {
0793       return mass2(p1.mom(), p2.mom());
0794     }
0795 
0796     /// @brief Get the transverse mass of a Particle and a P4
0797     ///
0798     /// @note This ignores the particle mass and just computes mT from the 3-vectors
0799     /// @todo Fix!!
0800     inline double mT(const Particle& p, const P4& p4) {
0801       return mT(p.mom(), p4);
0802     }
0803 
0804     /// @brief Get the transverse mass of a Particle and a P4
0805     ///
0806     /// @note This ignores the particle mass and just computes mT from the 3-vectors
0807     /// @todo Fix!!
0808     inline double mT(const P4& p4, const Particle& p) {
0809       return mT(p4, p.mom());
0810     }
0811 
0812     /// @brief Get the transverse mass of a pair of Particle (as separate args)
0813     ///
0814     /// @note This ignores the particle mass and just computes mT from the 3-vectors
0815     /// @todo Fix!!
0816     inline double mT(const Particle& p1, const Particle& p2) {
0817       return mT(p1.mom(), p2.mom());
0818     }
0819 
0820     /// @brief Get the transverse momentum of a Particle and a P4
0821     inline double pT(const Particle& p, const P4& p4) {
0822       return pT(p.mom(), p4);
0823     }
0824 
0825     /// @brief Get the transverse momentum of a Particle and a P4
0826     inline double pT(const P4& p4, const Particle& p) {
0827       return pT(p4, p.mom());
0828     }
0829 
0830     /// @brief Get the transverse momentum of a pair of Particle (as separate args)
0831     inline double pT(const Particle& p1, const Particle& p2) {
0832       return pT(p1.mom(), p2.mom());
0833     }
0834 
0835   }
0836 
0837   /// @}
0838 
0839 
0840   /// @defgroup particleutils_kin Operations on collections of Particle
0841   ///
0842   /// @note This can't be done on generic collections of ParticleBase -- thanks, C++ :-/
0843   ///
0844   /// @todo Or can't it? Try some of the metaprogramming that got closestMatchIndex working...
0845   ///
0846   /// @{
0847   namespace Kin {
0848 
0849     /// @todo This shouldn't be necessary, if the sum() function SFINAE picked up be ParticleBase versions...
0850     inline double pT(const Particle& p) {
0851       return p.pT();
0852     }
0853 
0854     inline double sumPt(const Particles& ps) {
0855       return sum(ps, Kin::pT, 0.0);
0856     }
0857 
0858     inline FourMomentum sumP4(const Particles& ps) {
0859       return sum(ps, Kin::p4, FourMomentum());
0860     }
0861 
0862     inline Vector3 sumP3(const Particles& ps) {
0863       return sum(ps, Kin::p3, Vector3());
0864     }
0865 
0866     /// @todo Min dPhi, min dR?
0867 
0868     /// @todo Isolation routines?
0869 
0870   }
0871 
0872 
0873   // Import Kin namespace into Rivet
0874   using namespace Kin;
0875 
0876 
0877   /// Check Particle equivalence
0878   inline bool isSame(const Particle& a, const Particle& b) {
0879     return a.isSame(b);
0880   }
0881   
0882   /// @}
0883 
0884 
0885   /// @brief Check for pid membership in a list of particles
0886   /// @note if abs is true, then only abs pids are checked.
0887   inline bool containsPID(const Particles& parts, int id, bool abs=false) {
0888     if (abs) return any(parts, HasAbsPID(id));
0889     return any(parts, HasPID(id));
0890   }
0891 
0892 
0893 
0894   /// @brief Check whether a particle is radiative.
0895   /// @note A particle is considered radiative if (1) it has only one mother particle and (2) one of its siblings has the same pid as the mother particle.
0896   inline bool isRadiative(const Particle& part) {
0897     const Particles& parents = part.parents();
0898     if (parents.size() != 1)
0899       return false;
0900 
0901     const Particle& mother = parents[0];
0902     return ( part.pid() != mother.pid() ) && ( containsPID(mother.children(), mother.pid()) );
0903   }
0904 
0905 
0906   /// @brief Check whether a set of particles' decay chains can contain the requested list of pids.
0907   /// @note if absolute is true, then only the absolute values of pids are compared.
0908   /// @note if ignorephoton is true, then photons are ignored when searching for the set of particles.
0909   bool cascadeContains( const Particles& parts,
0910             const vector<int>& pids,
0911                 bool absolute,
0912             bool ignorephoton);
0913 
0914   /// @}
0915 
0916 }
0917 
0918 #endif