Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // -*- C++ -*-
0002 //
0003 // This file is part of MCUtils -- https://gitlab.com/hepcedar/mcutils/
0004 // Copyright (C) 2013-2022 Andy Buckley <andy.buckley@cern.ch>
0005 //
0006 // Embedding of MCUtils code in other projects is permitted provided this
0007 // notice is retained and the MCUtils namespace and include path are changed.
0008 //
0009 #ifndef RIVET_PARTICLEIDUTILS_HH
0010 #define RIVET_PARTICLEIDUTILS_HH
0011 
0012 /// @file Utility functions for querying PDG ID codes (many from HepPID)
0013 /// @author Andy Buckley <andy.buckley@cern.ch>
0014 
0015 #include "Rivet/Tools/ParticleName.hh"
0016 #include "Rivet/Math/MathUtils.hh"
0017 #include <cassert>
0018 
0019 namespace Rivet {
0020   namespace PID {
0021 
0022 
0023     /// @defgroup mcutils_utils Utility functions
0024     /// @{
0025 
0026     // /// Compile-time int^int power-raising function
0027     // template <size_t N>
0028     // inline int _intpow(int x) { return x * _intpow<N-1>(x); }
0029     // template <>
0030     // inline int _intpow<0>(int x) { return 1; }
0031 
0032     /// Raise 10 to an integer power (terrible)
0033     // inline size_t _pow10(unsigned int power) {
0034     //   return (size_t) std::pow(10.0, power);
0035     // }
0036     /// Raise 10 to an integer power (LUT, tested and found faster than constexpr version)
0037     inline size_t _pow10(unsigned int power) {
0038       //assert(power >= 0 && "_pow10 only defined for positive powers");
0039       assert(power < 16 && "_pow10 only defined for powers < 16");
0040       static const size_t POWS10[] = {1, 10, 100, 1000, 10000, 100000, 1000000,
0041                                       10000000, 100000000, 1000000000, 10000000000,
0042                                       100000000000, 1000000000000, 10000000000000,
0043                                       100000000000000, 1000000000000000, 10000000000000000};
0044       return POWS10[power];
0045     }
0046     // /// Raise 10 to an integer power (constexpr)
0047     // inline constexpr size_t _pow10(unsigned int power) {
0048     //   assert(power >= 0 && "_pow10 only defined for positive powers");
0049     //   return (power > 0) ? 10*_pow10(power-1) : 1;
0050     // }
0051 
0052     ///  PID digits (base 10) are: n nr nl nq1 nq2 nq3 nj
0053     ///  The Location enum provides a convenient index into the PID.
0054     enum Location { nj=1, nq3, nq2, nq1, nl, nr, n, n8, n9, n10 };
0055 
0056     /// Split the PID into constituent integers
0057     ///
0058     ///  PID digits (base 10) are: n nr nl nq1 nq2 nq3 nj (cf. Location)
0059     ///
0060     /// @note Right-to-left nunbering, RHS-digit index = 1
0061     inline unsigned short _digit(Location loc, int pid) {
0062       const int div = _pow10(loc-1);
0063       return (abs(pid)/div) % 10;
0064     }
0065 
0066     /// Returns everything beyond the 7th digit (e.g. outside the numbering scheme)
0067     inline int _extraBits(int pid) {
0068       return abs(pid)/10000000;
0069     }
0070 
0071     /// @brief Return the first two digits if this is a "fundamental" particle
0072     /// @note ID = 100 is a special case (internal generator ID's are 81-100)
0073     inline int _fundamentalID(int pid) {
0074       if (_extraBits(pid) > 0) return 0;
0075       if (_digit(nq2,pid) == 0 && _digit(nq1,pid) == 0) {
0076         return abs(pid) % 10000;
0077       } else if (abs(pid) <= 100) {
0078         return abs(pid);
0079       } else {
0080         return 0;
0081       }
0082     }
0083 
0084     /// @}
0085 
0086 
0087     // Forward declaration
0088     inline bool isBSM(int pid);
0089 
0090 
0091     /// @defgroup mcutils_nucleus_ion Nucleus/ion functions
0092     /// @{
0093 
0094     /// @brief Is this a nucleus PID?
0095     ///
0096     /// This implements the 2006 Monte Carlo nuclear code scheme.
0097     /// Ion numbers are +/- 10LZZZAAAI.
0098     /// AAA is A - total baryon number
0099     /// ZZZ is Z - total charge
0100     /// L is the total number of strange quarks.
0101     /// I is the isomer number, with I=0 corresponding to the ground state.
0102     inline bool isNucleus(int pid) {
0103       // A proton can also be a Hydrogen nucleus
0104       if (abs(pid) == 2212) return true;
0105       // New standard: +/- 10LZZZAAAI
0106       if (_digit(n10,pid) == 1 && _digit(n9,pid) == 0) {
0107         // charge should always be less than or equal to baryon number
0108         // the following line is A >= Z
0109         if ((abs(pid)/10) % 1000 >= (abs(pid)/10000) % 1000) return true;
0110       }
0111       return false;
0112     }
0113 
0114     /// Get the atomic number (number of protons) in a nucleus/ion
0115     /// @note Ion numbers are +/- 10LZZZAAAI.
0116     inline int nuclZ(int pid) {
0117       // A proton can also be a Hydrogen nucleus
0118       if (abs(pid) == 2212) {
0119         return 1;
0120       }
0121       if (isNucleus(pid)) return (abs(pid)/10000) % 1000;
0122       return 0;
0123     }
0124 
0125     /// Get the atomic weight (number of nucleons) in a nucleus/ion
0126     /// @note Ion numbers are +/- 10LZZZAAAI.
0127     inline int nuclA(int pid) {
0128       // a proton can also be a Hydrogen nucleus
0129       if (abs(pid) == 2212) {
0130         return 1;
0131       }
0132       if (isNucleus(pid)) return (abs(pid)/10) % 1000;
0133       return 0;
0134     }
0135 
0136     /// If this is a nucleus (ion), get nLambda
0137     /// @note Ion numbers are +/- 10LZZZAAAI.
0138     inline int nuclNlambda(int pid) {
0139       // a proton can also be a Hydrogen nucleus
0140       if (abs(pid) == 2212) {
0141         return 0;
0142       }
0143       if (isNucleus(pid)) return _digit(n8,pid);
0144       return 0;
0145     }
0146 
0147     /// @}
0148 
0149 
0150     /// @defgroup mcutils_fundamental Fundamental particles
0151     /// @{
0152 
0153     /// Determine if the PID is that of a quark
0154     inline bool isQuark(int pid) {
0155       return in_closed_range(abs(pid), 1, 8);
0156     }
0157 
0158     /// Determine if the PID is that of a gluon
0159     inline bool isGluon(int pid) {
0160       return pid == GLUON;
0161     }
0162 
0163     /// Determine if the PID is that of a parton (quark or gluon)
0164     inline bool isParton(int pid) {
0165       return isGluon(pid) || isQuark(pid);
0166     }
0167 
0168 
0169     /// Determine if the PID is that of a photon
0170     inline bool isPhoton(int pid) {
0171       return pid == PHOTON;
0172     }
0173 
0174     /// Determine if the PID is that of an electron or positron
0175     inline bool isElectron(int pid) {
0176       return abs(pid) == ELECTRON;
0177     }
0178 
0179     /// Determine if the PID is that of an muon or antimuon
0180     inline bool isMuon(int pid) {
0181       return abs(pid) == MUON;
0182     }
0183 
0184     /// Determine if the PID is that of an tau or antitau
0185     inline bool isTau(int pid) {
0186       return abs(pid) == TAU;
0187     }
0188 
0189     /// Determine if the PID is that of a charged lepton
0190     inline bool isChargedLepton(int pid) {
0191       const long apid = abs(pid);
0192       return apid == 11 || apid == 13 || apid == 15 || apid == 17;
0193     }
0194 
0195     /// Determine if the PID is that of a neutrino
0196     inline bool isNeutrino(int pid) {
0197       const long apid = abs(pid);
0198       return apid == 12 || apid == 14 || apid == 16 || apid == 18;
0199     }
0200 
0201 
0202     /// Determine if the PID is that of a W+
0203     inline bool isWplus(int pid) {
0204       return pid == WPLUSBOSON;
0205     }
0206 
0207     /// Determine if the PID is that of a W-
0208     inline bool isWminus(int pid) {
0209       return pid == WMINUSBOSON;
0210     }
0211 
0212     /// Determine if the PID is that of a W+-
0213     inline bool isW(int pid) {
0214       return abs(pid) == WPLUSBOSON;
0215     }
0216 
0217     /// Determine if the PID is that of a Z0
0218     inline bool isZ(int pid) {
0219       return pid == Z0BOSON;
0220     }
0221 
0222     /// Determine if the PID is that of an SM/lightest SUSY Higgs
0223     inline bool isHiggs(int pid) {
0224       return pid == HIGGSBOSON || pid == 26; //< @todo Check on 26 still needed? (used in HERWIG SUSY, for example)
0225     }
0226 
0227     /// @todo isSUSYHiggs?
0228 
0229 
0230     /// Is this a graviton?
0231     inline bool isGraviton(int pid) {
0232       return pid == GRAVITON;
0233     }
0234 
0235 
0236     // /// Determine if the PID is that of a d/dbar
0237     // inline bool isDown(int pid) { return abs(pid) == DQUARK; }
0238 
0239     // /// Determine if the PID is that of a u/ubar
0240     // inline bool isUp(int pid) { return abs(pid) == UQUARK; }
0241 
0242     /// Determine if the PID is that of an s/sbar
0243     inline bool isStrange(int pid) {
0244       return abs(pid) == SQUARK;
0245     }
0246 
0247     /// Determine if the PID is that of a c/cbar
0248     inline bool isCharm(int pid) {
0249       return abs(pid) == CQUARK;
0250     }
0251 
0252     /// Determine if the PID is that of a b/bbar
0253     inline bool isBottom(int pid) {
0254       return abs(pid) == BQUARK;
0255     }
0256 
0257     /// Determine if the PID is that of a t/tbar
0258     inline bool isTop(int pid) {
0259       return abs(pid) == TQUARK;
0260     }
0261 
0262     /// @}
0263 
0264 
0265     /// @defgroup mcutils_qcomp Quark composite functions
0266     /// @{
0267 
0268     /// Is this a pomeron, odderon, or generic reggeon?
0269     inline bool isReggeon(int pid) {
0270       return pid == 110 || pid == 990 || pid == 9990;
0271     }
0272 
0273     /// Check to see if this is a valid meson
0274     inline bool isMeson(int pid) {
0275       if (_extraBits(pid) > 0) return false;
0276       if (isBSM(pid)) return false;
0277       const int aid = abs(pid);
0278       if (aid == 130 || aid == 310 || aid == 210) return true; //< special cases for kaons
0279       if (aid <= 100) return false;
0280       if (_digit(nq1,pid) != 0) return false;
0281       if (_digit(nq2,pid) == 0) return false;
0282       if (_digit(nq3,pid) == 0) return false;
0283       if (_digit(nq2,pid) < _digit(nq3,pid)) return false;
0284       // EvtGen uses some odd numbers
0285       /// @todo Remove special-casing for EvtGen
0286       if (aid == 150 || aid == 350 || aid == 510 || aid == 530) return true;
0287       // Pomeron, Reggeon, etc.
0288       if (isReggeon(pid)) return false; //true; //< WTF?
0289       // Check for illegal antiparticles
0290       if (_digit(nj,pid) > 0 && _digit(nq3,pid) > 0 && _digit(nq2,pid) > 0 && _digit(nq1,pid) == 0) {
0291         return !(_digit(nq3,pid) == _digit(nq2,pid) && pid < 0);
0292       }
0293       return false;
0294     }
0295 
0296     /// Check to see if this is a valid baryon
0297     inline bool isBaryon(int pid) {
0298       if (_extraBits(pid) > 0) return false;
0299       if (isBSM(pid)) return false;
0300       if (abs(pid) <= 100) return false;
0301       if (_fundamentalID(pid) <= 100 && _fundamentalID(pid) > 0) return false;
0302       if (abs(pid) == 2110 || abs(pid) == 2210) return true; ///< @todo Why this special case with nJ = 0? What are these? Not listed in RPP MC doc...
0303       if (_digit(nj,pid) == 0) return false;
0304       if (_digit(nq1,pid) == 0 || _digit(nq2,pid) == 0 || _digit(nq3,pid) == 0) return false;
0305       return true;
0306       /// @todo This is more correct by the definition, but the PDG's entries 1212, 1214, 1216, 1218 and 2122, 2124, 2126, 2128 come out as invalid
0307       // if ((_digit(nq1,pid) >= _digit(nq2,pid) && _digit(nq2,pid) >= _digit(nq3,pid)) ||
0308       //     (_digit(nq1,pid) > _digit(nq3,pid) && _digit(nq3,pid) > _digit(nq2,pid)) || //< case 6b for lighter quarks in J=1
0309       //     (_digit(nq3,pid) > _digit(nq1,pid) && _digit(nq1,pid) > _digit(nq2,pid))) //< case 6e for extra states in excited multiplets
0310       //   return true;
0311       // return false;
0312     }
0313 
0314     // Check to see if this is a valid diquark
0315     inline bool isDiquark(int pid) {
0316       if (_extraBits(pid) > 0) return false;
0317       if (isBSM(pid)) return false;
0318       if (abs(pid) <= 100) return false;
0319       if (_fundamentalID(pid) <= 100 && _fundamentalID(pid) > 0) return false;
0320       if (_digit(nq1,pid) == 0) return false;
0321       if (_digit(nq2,pid) == 0) return false;
0322       if (_digit(nq3,pid) != 0) return false;
0323       if (_digit(nq1,pid) < _digit(nq2,pid)) return false;
0324       if (_digit(nj,pid) > 0  && _digit(nq3,pid) == 0 && _digit(nq2,pid) > 0 && _digit(nq1,pid) > 0) return true; // diquark signature
0325       // EvtGen uses the diquarks for quark pairs, so, for instance, 5501 is a valid "diquark" for EvtGen
0326       // if (_digit(nj) == 1 && _digit(nq2) == _digit(nq1)) {    // illegal
0327       //    return false;
0328       // } else {
0329       //  return true;
0330       // }
0331       return false;
0332     }
0333 
0334     /// Check to see if this is a valid pentaquark
0335     inline bool isPentaquark(int pid) {
0336       // a pentaquark is of the form 9abcdej,
0337       // where j is the spin and a, b, c, d, and e are quarks
0338       if (_extraBits(pid) > 0) return false;
0339       if (isBSM(pid)) return false;
0340       if (_digit(n,pid) != 9)  return false;
0341       if (_digit(nr,pid) == 9 || _digit(nr,pid) == 0)  return false;
0342       if (_digit(nj,pid) == 9 || _digit(nl,pid) == 0)  return false;
0343       if (_digit(nq1,pid) == 0)  return false;
0344       if (_digit(nq2,pid) == 0)  return false;
0345       if (_digit(nq3,pid) == 0)  return false;
0346       if (_digit(nj,pid) == 0)  return false;
0347       // check ordering
0348       if (_digit(nq2,pid) > _digit(nq1,pid))  return false;
0349       if (_digit(nq1,pid) > _digit(nl,pid))  return false;
0350       if (_digit(nl,pid) > _digit(nr,pid))  return false;
0351       return true;
0352     }
0353 
0354     /// Is this a valid hadron ID?
0355     ///
0356     /// @note BSM hadrons, e.g. R-hadrons, don't count
0357     inline bool isHadron(int pid) {
0358       if (_extraBits(pid) > 0) return false;
0359       if (isBSM(pid) > 0) return false;
0360       if (isMeson(pid)) return true;
0361       if (isBaryon(pid)) return true;
0362       if (isPentaquark(pid)) return true;
0363       return false;
0364     }
0365 
0366     /// @}
0367 
0368 
0369     /// @defgroup mcutils_idclasses More general particle class identification functions
0370     /// @{
0371 
0372     /// Is this a valid lepton ID?
0373     ///
0374     /// @note BSM "leptons" don't count
0375     inline bool isLepton(int pid) {
0376       if (_extraBits(pid) > 0) return false;
0377       if (isBSM(pid) > 0) return false;
0378       if (_fundamentalID(pid) >= 11 && _fundamentalID(pid) <= 18) return true;
0379       return false;
0380     }
0381 
0382     /// Is this a valid BSM boson (SUSY Higgs, W', Z')?
0383     inline bool isBSMBoson(int pid) {
0384       return in_closed_range(abs(pid), 32, 37);
0385     }
0386 
0387     /// Is this an SM fundamental particle?
0388     inline bool isSMFundamental(int pid) {
0389       return isQuark(pid) || isLepton(pid) ||
0390         isGluon(pid) || isPhoton(pid) || isW(pid) || isZ(pid) || isHiggs(pid) ||
0391         isBSMBoson(pid) || isGraviton(pid);
0392     }
0393 
0394     /// @brief Is this a fundamental SUSY particle?
0395     ///
0396     /// The MSSM extended Higgs sector is not counted as 'SUSY' particles, since they are not superpartners.
0397     inline bool isSUSY(int pid) {
0398       // Fundamental SUSY particles have n = 1 or 2
0399       if (_extraBits(pid) > 0) return false;
0400       if (_digit(n,pid) != 1 && _digit(n,pid) != 2)  return false;
0401       if (_digit(nr,pid) != 0)  return false;
0402       // Check fundamental part for SM PID on which it is based
0403       const int fundId = _fundamentalID(pid);
0404       if (fundId == 0) return false;
0405       if (_digit(n,pid) == 1) { // most superpartners, incl LH sfermions
0406         return isSMFundamental(fundId);
0407       } else if (_digit(n,pid) == 2) { // RH sfermions
0408         return isQuark(fundId) || isChargedLepton(fundId);
0409       }
0410       return true;
0411     }
0412 
0413     /// Is this an R-hadron?
0414     inline bool isRHadron(int pid) {
0415       // An R-hadron is of the form 10abcdj,
0416       // where j is the spin and a, b, c, and d are quarks or gluons
0417       if (_extraBits(pid) > 0) return false;
0418       if (_digit(n,pid) != 1)  return false;
0419       if (_digit(nr,pid) != 0)  return false;
0420       // Make sure this isn't a SUSY particle
0421       if (isSUSY(pid)) return false;
0422       // All R-hadrons have at least 3 core digits
0423       if (_digit(nq2,pid) == 0)  return false;
0424       if (_digit(nq3,pid) == 0)  return false;
0425       if (_digit(nj,pid) == 0)  return false;
0426       return true;
0427     }
0428     /// Alias
0429     inline bool isRhadron(int pid) {
0430       return isRHadron(pid);
0431     }
0432 
0433     /// Is this a technicolor particle?
0434     inline bool isTechnicolor(int pid) {
0435       if (_extraBits(pid) > 0) return false;
0436       return _digit(n,pid) == 3;
0437     }
0438 
0439     /// Is this an excited (composite) quark or lepton?
0440     inline bool isExcited(int pid) {
0441       if (_extraBits(pid) > 0) return false;
0442       return _digit(n,pid) == 4 && _digit(nr,pid) == 0;
0443     }
0444 
0445     /// Is this a Kaluza-Klein excitation?
0446     inline bool isKK(int pid) {
0447       if (_extraBits(pid) > 0) return false;
0448       const int ndigit = _digit(n,pid);
0449       return ndigit == 5 || ndigit == 6;
0450     }
0451 
0452     /// Is this a lepto-quark?
0453     inline bool isLeptoQuark(int pid) {
0454       // Many UFO models are extending the PDG standards... is this going to be official?
0455       return abs(pid) == 42;
0456     }
0457 
0458     /// Is this a generic Dark Matter particle?
0459     /// @note DM particles, including mediators, get the range 51-60
0460     /// @note Also covers other cases: Heavy neutral leptons (50), Light pseudo-scalar A in 2HDM (55), Z' scalar UFO models (56)
0461     /// @todo Give a more explicit name to clarify that this does not cover all DM particles, e.g. LSP?
0462     inline bool isDarkMatter(int pid) {
0463       const int ndigit = _digit(n,pid);
0464       const int nrdigit = _digit(nr,pid);
0465       if ((ndigit == 0 && nrdigit == 0) || (ndigit == 5 && nrdigit == 9))
0466         return in_closed_range(abs(_fundamentalID(pid)),50,60);
0467       return false;
0468     }
0469     /// Convenience alias
0470     inline bool isDM(int pid) {
0471       return isDarkMatter(pid);
0472     }
0473 
0474     /// Is this a Hidden Valley particle?
0475     inline bool isHiddenValley(int pid) {
0476       return (_digit(n,pid) == 4 && _digit(nr,pid) == 9);
0477     }
0478 
0479     /// Is this an exotic particle?
0480     inline bool isExotic(int pid) {
0481       // From the PDG definition, 40-80 reserved for exotic particles
0482       // Some overlap with ranges from other functions (e.g. isDM)
0483       // Also covers R0 (41)
0484       return in_closed_range(abs(pid),40,80);
0485     }
0486 
0487     /// Is this a 4th generation particle?
0488     inline bool isFourthGen(int pid) {
0489       return abs(pid) == BPRIME || abs(pid) == TPRIME || abs(pid) == LPRIME || abs(pid) == NUPRIME;
0490     }
0491 
0492     /// Is this from a magnetic monopole or dyon?
0493     inline bool isMagMonopole(int pid) {
0494       if (_digit(n,pid) != 4) return false;
0495       if (_digit(nr,pid) != 1) return false;
0496       if (_digit(nl,pid) != 1 && _digit(nl,pid) != 2) return false;
0497       // Require at least 1 core digit
0498       // NOT TRUE! Electrically neutral monopoles are possible
0499       // if (_digit(nq3,pid) == 0)  return false;
0500       // Always have spin zero for now
0501       if (_digit(nj,pid) != 0) return false;
0502       return true;
0503     }
0504     /// Just treat a dyon as an alias for magmonopole for now
0505     inline bool isDyon(int pid) {
0506       return isMagMonopole(pid);
0507     }
0508 
0509     /// Is this a Q-ball?
0510     /// @note Ad-hoc numbering for such particles is 100xxxx0, where xxxx is the charge in tenths.
0511     inline bool isQBall(int pid) {
0512       if (_extraBits(pid) != 1) return false;
0513       if (_digit(n,pid) != 0) return false;
0514       if (_digit(nr,pid) != 0) return false;
0515       // Check the core number
0516       if ((abs(pid)/10) % 10000 == 0) return false;
0517       // These particles have spin zero for now
0518       if (_digit(nj,pid) != 0) return false;
0519       return true;
0520     }
0521     /// Alias
0522     inline bool isQball(int pid) {
0523       return isQBall(pid);
0524     }
0525 
0526     /// Is this an excited lepton?
0527     inline bool isExcitedLepton(int pid) {
0528       if (!isExcited(pid)) return false;
0529       return isLepton( _fundamentalID(pid) );
0530     }
0531 
0532     /// Is this a black hole?
0533     inline bool isBlackHole(int pid) {
0534       if (_digit(n,pid) != 5 && _digit(n,pid) != 6) return false;
0535       if (_digit(nl,pid) != 0) return false;
0536       return _fundamentalID(pid)==40;
0537     }
0538 
0539     /// Is this an anomalously electrically charged particle (AECO)?
0540     inline bool isAECO(int pid) {
0541       if (_digit( n,pid) != 1) return false;
0542       if (_digit(nr,pid) != 0) return false;
0543       if (_digit(nl,pid) != 0) return false;
0544       if (_digit(nj,pid) != 0) return false;
0545       return true;
0546     }
0547 
0548     /// Is this a BSM particle (including graviton)?
0549     inline bool isBSM(int pid) {
0550       return isSUSY(pid) || isRHadron(pid) || isTechnicolor(pid) ||
0551         isExcited(pid) || isKK(pid) || isGraviton(pid) ||
0552         isBSMBoson(pid) || isLeptoQuark(pid) || isDM(pid) || isHiddenValley(pid) ||
0553         isExotic(pid) || isFourthGen(pid) || isBlackHole(pid) ||
0554         isDyon(pid) || isQball(pid) || isAECO(pid);
0555     }
0556 
0557     /// Check to see if this is a valid PID (i.e. matches any known scheme)
0558     inline bool _isValid(int pid) {
0559       // Starting with 99 means anything goes (but nothing is known)
0560       if (_digit(n, pid) == 9 && _digit(nr, pid) == 9) return true;
0561       // Check that extra bits are only used for nuclei
0562       if (_extraBits(pid) > 0) return (isNucleus(pid) || isQball(pid));
0563       // Check that it fits into a standard non-nucleus convention
0564       if (isBSM(pid)) return true;
0565       if (isHadron(pid)) return true;
0566       if (_digit(n,pid) == 9 && _digit(nr,pid) == 0) return false; // could only have been a tentative hadron, but !isHadron
0567       if (isDiquark(pid)) return true;
0568       if (isPentaquark(pid)) return true;
0569       if (isReggeon(pid)) return true;
0570       // // Quark digit orderings required by the standard
0571       // if (_digit(nq1,pid) != 0 && _digit(nq1,pid) < _digit(nq2,pid)) return false;
0572       // if (_digit(nq2,pid) != 0 && _digit(nq2,pid) < _digit(nq3,pid)) return false;
0573       // Final check on fundamental ID
0574       return (_fundamentalID(pid) > 0);
0575     }
0576     inline bool isValid(int pid) {
0577       return _isValid(pid);
0578     }
0579 
0580     /// @}
0581 
0582 
0583     /// @defgroup mcutils_partoncontent Parton content functions
0584     /// @{
0585 
0586     inline bool _hasQ(int pid, int q) {
0587       if (abs(pid) == q) return true; //< trivial case!
0588       if (!_isValid(pid)) return false;
0589       // if (_extraBits(pid) > 0) return false;
0590       // if (_fundamentalID(pid) > 0) return false;
0591       if (isMagMonopole(pid)) return false;
0592       if (isRHadron(pid)) {
0593         int iz = 7;
0594         for (int i = 6; i > 1; --i) {
0595           if (_digit(Location(i), pid) == 0) {
0596             iz = i;
0597           } else if ( i == iz-1 ) {
0598             // ignore squark or gluino
0599           } else {
0600             if (_digit(Location(i),pid) == q) return true;
0601           }
0602         }
0603         return false;
0604       }
0605       if (_digit(nq3,pid) == q || _digit(nq2,pid) == q || _digit(nq1,pid) == q ) return true;
0606       if (isPentaquark(pid)) {
0607         if (_digit(nl,pid) == q || _digit(nr,pid) == q) return true;
0608       }
0609       return false;
0610     }
0611 
0612     /// Does this particle contain a down quark?
0613     inline bool hasDown(int pid) {
0614       return (isHadron(pid) || isQuark(pid)) && _hasQ(pid, 1);
0615     }
0616     /// Does this particle contain an up quark?
0617     inline bool hasUp(int pid) {
0618       return (isHadron(pid) || isQuark(pid)) && _hasQ(pid, 2);
0619     }
0620     /// Does this particle contain a strange quark?
0621     inline bool hasStrange(int pid) {
0622       return (isHadron(pid) || isQuark(pid)) && _hasQ(pid, 3);
0623     }
0624     /// Does this particle contain a charm quark?
0625     inline bool hasCharm(int pid) {
0626       return (isHadron(pid) || isQuark(pid)) && _hasQ(pid, 4);
0627     }
0628     /// Does this particle contain a bottom quark?
0629     inline bool hasBottom(int pid) {
0630       return (isHadron(pid) || isQuark(pid)) && _hasQ(pid, 5);
0631     }
0632     /// Does this particle contain a top quark?
0633     inline bool hasTop(int pid) {
0634       return (isHadron(pid) || isQuark(pid)) && _hasQ(pid, 6);
0635     }
0636 
0637     /// @}
0638 
0639 
0640     /// @defgroup mcutils_parton_classes Parton content classification
0641     /// @{
0642 
0643     /// Determine if the particle is a heavy flavour hadron or parton
0644     inline bool isHeavyFlavor(int pid) {
0645       if (!isHadron(pid) && !isQuark(pid)) return false;
0646       return hasCharm(pid) || hasBottom(pid) || hasTop(pid);
0647     }
0648     /// British-spelling alias for isHeavyFlavor
0649     inline bool isHeavyFlavour(int pid) {
0650       return isHeavyFlavor(pid);
0651     }
0652 
0653 
0654     // /// Determine if the particle is a light-flavour hadron or parton
0655     // inline bool isLightFlavor(int pid) {
0656     //   return !isHeavyFlavor();
0657     // }
0658 
0659 
0660     /// Determine if the PID is that of a heavy parton (c,b,t)
0661     inline bool isHeavyParton(int pid) {
0662       return isParton(pid) && isHeavyFlavor(pid);
0663     }
0664 
0665     /// Determine if the PID is that of a light parton (u,d,s)
0666     inline bool isLightParton(int pid) {
0667       return isParton(pid) && !isHeavyFlavor(pid);
0668     }
0669 
0670 
0671     /// Determine if the PID is that of a heavy flavour (b or c) meson
0672     inline bool isHeavyMeson(int pid) {
0673       return isMeson(pid) && isHeavyFlavor(pid);
0674     }
0675 
0676     /// Determine if the PID is that of a heavy flavour (b or c) baryon
0677     inline bool isHeavyBaryon(int pid) {
0678       return isBaryon(pid) && isHeavyFlavor(pid);
0679     }
0680 
0681     /// Determine if the PID is that of a heavy flavour (b or c) hadron
0682     inline bool isHeavyHadron(int pid) {
0683       return isHadron(pid) && isHeavyFlavor(pid);
0684     }
0685 
0686     /// Determine if the PID is that of a light flavour (not b or c) meson
0687     inline bool isLightMeson(int pid) {
0688       return isMeson(pid) && !isHeavyFlavor(pid);
0689     }
0690 
0691     /// Determine if the PID is that of a light flavour (not b or c) baryon
0692     inline bool isLightBaryon(int pid) {
0693       return isBaryon(pid) && !isHeavyFlavor(pid);
0694     }
0695 
0696     /// Determine if the PID is that of a light flavour (not b or c) hadron
0697     inline bool isLightHadron(int pid) {
0698       return isHadron(pid) && !isHeavyFlavor(pid);
0699     }
0700 
0701 
0702     /// Determine if the PID is that of a b-meson.
0703     inline bool isBottomMeson(int pid) {
0704       return hasBottom(pid) && isMeson(pid);
0705     }
0706 
0707     /// Determine if the PID is that of a b-baryon.
0708     inline bool isBottomBaryon(int pid) {
0709       return hasBottom(pid) && isBaryon(pid);
0710     }
0711 
0712     /// Determine if the PID is that of a b-hadron.
0713     inline bool isBottomHadron(int pid) {
0714       return hasBottom(pid) && isHadron(pid);
0715     }
0716 
0717 
0718     /// @brief Determine if the PID is that of a c-meson.
0719     ///
0720     /// @note Specifically, the _heaviest_ quark is a c: a B_c is a b-meson and NOT a c-meson.
0721     /// Charmonia (closed charm) are counted as c-mesons here.
0722     inline bool isCharmMeson(int pid) {
0723       return isMeson(pid) && hasCharm(pid) &&
0724         !hasBottom(pid);
0725     }
0726 
0727     /// @brief Determine if the PID is that of a c-baryon.
0728     ///
0729     /// @note Specifically, the _heaviest_ quark is a c: a baryon containing a b & c
0730     /// is a b-baryon and NOT a c-baryon. To test for the simpler case, just use
0731     /// a combination of hasCharm() and isBaryon().
0732     inline bool isCharmBaryon(int pid) {
0733       return isBaryon(pid) && hasCharm(pid) &&
0734         !hasBottom(pid);
0735     }
0736 
0737     /// Determine if the PID is that of a c-hadron.
0738     ///
0739     /// @note Specifically, the _heaviest_ quark is a c: a baryon containing a b & c
0740     /// is a b-baryon and NOT a c-baryon. To test for the simpler case, just use
0741     /// a combination of hasCharm() and isBaryon().
0742     inline bool isCharmHadron(int pid) {
0743       return isHadron(pid) && hasCharm(pid) &&
0744         !hasBottom(pid);
0745     }
0746 
0747 
0748     /// Determine if the PID is that of a strange meson
0749     ///
0750     /// @note Specifically, the _heaviest_ quark is an s: if it also contains
0751     /// either charm or bottom, it is not considered to be a strange hadron.
0752     inline bool isStrangeMeson(int pid) {
0753       return isMeson(pid) && hasStrange(pid) &&
0754         !(hasBottom(pid) || hasCharm(pid));
0755     }
0756 
0757     /// Determine if the PID is that of a strange baryon
0758     ///
0759     /// @note Specifically, the _heaviest_ quark is an s: if it also contains
0760     /// either charm or bottom, it is not considered to be a strange hadron.
0761     inline bool isStrangeBaryon(int pid) {
0762       return isBaryon(pid) && hasStrange(pid) &&
0763         !(hasBottom(pid) || hasCharm(pid));
0764     }
0765 
0766     /// Determine if the PID is that of a strange hadron
0767     ///
0768     /// @note Specifically, the _heaviest_ quark is an s: if it also contains
0769     /// either charm or bottom, it is not considered to be a strange hadron.
0770     inline bool isStrangeHadron(int pid) {
0771       return isHadron(pid) && hasStrange(pid) &&
0772         !(hasBottom(pid) || hasCharm(pid));
0773     }
0774 
0775     /// @}
0776 
0777 
0778 
0779     /// @defgroup mcutils_angmom Angular momentum functions
0780     /// @{
0781 
0782     /// jSpin returns 2J+1, where J is the total spin
0783     inline int jSpin(int pid) {
0784       const int fund = _fundamentalID(pid);
0785       if (fund > 0) {
0786         // some of these are known
0787         if (fund > 0 && fund < 7) return 2;
0788         if (fund == 9) return 3;
0789         if (fund > 10 && fund < 17) return 2;
0790         if (fund > 20 && fund < 25) return 3;
0791         return 0;
0792       } else if (_extraBits(pid) > 0) {
0793         return 0;
0794       }
0795       return abs(pid) % 10;
0796     }
0797 
0798     /// sSpin returns 2S+1, where S is the spin
0799     inline int sSpin(int pid) {
0800       // Handle invalid cases first
0801       if (!isMeson(pid)) return 0;
0802       if (_digit(n,pid) == 9 && _digit(nr,pid) == 0) return 0; // tentative ID
0803       // Special generic DM particles with defined spins
0804       const int fund = _fundamentalID(pid);
0805       if (fund == 51 || fund == 54) return 1;
0806       if (fund == 52) return 2;
0807       if (fund == 53 || fund == 55) return 3;
0808       // Calculate from nl and nj digits
0809       const int inl = _digit(nl,pid);
0810       const int js = _digit(nj,pid);
0811       if (inl == 0 && js >= 3) return 1;
0812       else if (inl == 0  && js == 1) return 0;
0813       else if (inl == 1  && js >= 3) return 0;
0814       else if (inl == 2  && js >= 3) return 1;
0815       else if (inl == 1  && js == 1) return 1;
0816       else if (inl == 3  && js >= 3) return 1;
0817       // Default to zero
0818       return 0;
0819     }
0820 
0821     /// lSpin returns 2L+1, where L is the orbital angular momentum
0822     inline int lSpin(int pid) {
0823       // Handle invalid cases first
0824       if (!isMeson(pid)) return 0;
0825       if (_digit(n,pid) == 9 && _digit(nr,pid) == 0) return 0; // tentative ID
0826       // Calculate from nl and nj digits
0827       const int inl = _digit(nl,pid);
0828       const int js = _digit(nj,pid);
0829       if (inl == 0 && js == 3) return 0;
0830       else if (inl == 0 && js == 5) return 1;
0831       else if (inl == 0 && js == 7) return 2;
0832       else if (inl == 0 && js == 9) return 3;
0833       else if (inl == 0 && js == 1) return 0;
0834       else if (inl == 1 && js == 3) return 1;
0835       else if (inl == 1 && js == 5) return 2;
0836       else if (inl == 1 && js == 7) return 3;
0837       else if (inl == 1 && js == 9) return 4;
0838       else if (inl == 2 && js == 3) return 1;
0839       else if (inl == 2 && js == 5) return 2;
0840       else if (inl == 2 && js == 7) return 3;
0841       else if (inl == 2 && js == 9) return 4;
0842       else if (inl == 1 && js == 1) return 1;
0843       else if (inl == 3 && js == 3) return 2;
0844       else if (inl == 3 && js == 5) return 3;
0845       else if (inl == 3 && js == 7) return 4;
0846       else if (inl == 3 && js == 9) return 5;
0847       // Default to zero
0848       return 0;
0849     }
0850 
0851     /// @}
0852 
0853 
0854     /// @defgroup mcutils_charge Charge functions
0855     /// @{
0856 
0857     /// Three times the EM charge (as integer)
0858     inline int charge3(int pid) {
0859       static int ch100[100] = { -1,  2, -1, 2, -1, 2, -1, 2, 0, 0,
0860                                 -3,  0, -3, 0, -3, 0, -3, 0, 0, 0,
0861                                  0,  0,  0, 3,  0, 0,  0, 0, 0, 0,
0862                                  0,  0,  0, 3,  0, 0,  3, 0, 0, 0,
0863                                  0, -1,  0, 0,  0, 0,  0, 0, 0, 0,
0864                                  0,  6,  3, 6,  0, 0,  0, 0, 0, 0,
0865                                  0,  0,  0, 0,  0, 0,  0, 0, 0, 0,
0866                                  0,  0,  0, 0,  0, 0,  0, 0, 0, 0,
0867                                  0,  0,  0, 0,  0, 0,  0, 0, 0, 0,
0868                                  0,  0,  0, 0,  0, 0,  0, 0, 0, 0 };
0869       // Shortcuts for common particles
0870       const int ida = abs(pid);
0871       if (pid == 21 || pid == 22) return 0; // gluon and photon
0872       if (ida == 211) return std::signbit(pid) ? -3 : 3; // charged pion
0873       if (pid == 111) return 0; // neutral pion
0874       // if (ida == 12 || ida == 14 || ida == 16) return 0; // neutrinos
0875       // if (ida == 11 || ida == 13 || ida == 15) return std::signbit(pid) ? +3 : -3; // leptons
0876       // if (ida == 1 || ida == 3 || ida == 5) return std::signbit(pid) ? +1 : -1; // quarks
0877       // if (ida == 2 || ida == 4 || ida == 6) return std::signbit(pid) ? -2 : +2; // quarks
0878       // Standard decoding
0879       const unsigned short q1 = _digit(nq1,pid);
0880       const unsigned short q2 = _digit(nq2,pid);
0881       const unsigned short q3 = _digit(nq3,pid);
0882       const unsigned short ql = _digit(nl,pid);
0883       const int sid = _fundamentalID(pid);
0884       int ch3 = 0;
0885       if (ida == 0 || _extraBits(pid) > 0) { // ion or illegal
0886         return 0;
0887       } else if (sid > 0 && sid <= 100) { // Use table
0888         if (ida == 1000017 || ida == 1000018 || ida == 1000034) ch3 = 0;
0889         else if (ida > 1000050 && ida <= 1000060) ch3 = 0; // ?
0890         else if (ida > 50 && ida <= 60) ch3 = 0; // Generic DM
0891         else if (ida == 5100061 || ida == 5100062) ch3 = 6;
0892         else ch3 = ch100[sid-1];
0893       } else if (_digit(nj,pid) == 0) { // KL, Ks, or undefined
0894         return 0;
0895       } else if (isMeson(pid)) { // Mesons
0896         ch3 = ((q2 == 3 || q2 == 5) ? -1 : 1) * (ch100[q2-1] - ch100[q3-1]);
0897       } else if (isBaryon(pid)) { // Baryons
0898         ch3 = ch100[q3-1] + ch100[q2-1] + ch100[q1-1];
0899       } else if (isQBall(pid) ) { // QBall
0900         ch3 = 3*( (ida/10) % 10000);
0901       } else if (isHiddenValley(pid) ) { // Hidden Valley
0902         return 0;
0903       } else if (isDyon(pid) ) { // Dyon
0904         ch3 = 3*( (ida/10) %  1000) * (ql == 2 ? -1 : 1); //< NB. charge is flipped at the end if pid < 0
0905       } else if (isRHadron(pid) ) { // R-hadron
0906         /// @todo Is this sufficiently general? Why only gluino in g+q+qbar? Better to recurse to the related SM hadron code?
0907         if (q1 == 0 || q1 == 9) { //< gluino+q+qbar
0908           if (q2 == 3 || q2 == 5) {
0909             ch3 = ch100[q3-1] - ch100[q2-1];
0910           } else {
0911             ch3 = ch100[q2-1] - ch100[q3-1];
0912           }
0913         } else if (ql == 0) { //< squark+q+q
0914           ch3 = ch100[q3-1] + ch100[q2-1] + ch100[q1-1];
0915         } else if (_digit(nr,pid) == 0) { //< squark+q+q+q
0916           ch3 = ch100[q3-1] + ch100[q2-1] + ch100[q1-1] + ch100[ql-1];
0917         }
0918       } else if (isDiquark(pid)) { // Diquarks
0919         ch3 = ch100[q2-1] + ch100[q1-1];
0920       } else { // Unknown
0921         return 0;
0922       }
0923       if (pid < 0) ch3 *= -1;
0924       return ch3;
0925     }
0926 
0927     /// Return the absolute value of 3 times the EM charge
0928     inline int abscharge3(int pid) {
0929       return std::abs(charge3(pid));
0930     }
0931 
0932     /// Return the EM charge (as floating point)
0933     inline double charge(int pid) {
0934       return charge3(pid)/3.0;
0935     }
0936 
0937     /// Return the EM charge (as floating point)
0938     inline double abscharge(int pid) {
0939       return std::abs(charge(pid));
0940     }
0941 
0942     /// @}
0943 
0944 
0945     /// @defgroup mcutils_charge_classes General PID-based classifier functions
0946     /// @{
0947 
0948     /// Determine if the particle is electrically charged
0949     inline bool isCharged(int pid) {
0950       /// @todo What if PID = 0? Applies to everything... should we throw an exception?
0951       if (pid >= -8 && pid <= 8) return true; // quarks and anti-quarks
0952       return charge3(pid) != 0; //< pions, photons and gluons already fast in here
0953     }
0954 
0955     /// Determine if the particle is electrically neutral
0956     inline bool isNeutral(int pid) {
0957       return !isCharged(pid);
0958     }
0959 
0960     /// @}
0961 
0962 
0963     /// @defgroup mcutils_interactions Interaction classifiers
0964     /// @{
0965 
0966     /// Determine if the PID is that of a strongly interacting particle
0967     inline bool isStrongInteracting(int pid) {
0968       return isParton(pid) || isHadron(pid);
0969     }
0970 
0971     /// Determine if the PID is that of a electromagnetically interacting particle
0972     inline bool isEMInteracting(int pid) {
0973       return isCharged(pid) || isPhoton(pid);
0974     }
0975 
0976     /// Determine if the PID is that of a weakly interacting particle
0977     ///
0978     /// @note Photons are considered weak-interacting, as are all hadrons and
0979     /// leptons (we can't distinguish between L and R fermions at physical particle level).
0980     inline bool isWeakInteracting(int pid) {
0981       return !isGluon(pid) && !isGraviton(pid);
0982     }
0983 
0984     /// @}
0985 
0986 
0987     /// @defgroup mcutils_other Other classifiers
0988     /// @{
0989 
0990     /// Determine if the PID is in the generator-specific range
0991     inline bool isGenSpecific(int pid) {
0992       return in_range(pid, 80, 101);
0993     }
0994 
0995     /// Determine if the PID is that of an EW scale resonance
0996     ///
0997     /// @todo Also include SUSY, technicolor, etc. etc.?
0998     /// Maybe via a isStandardModel(pid) function, but there are stable BSM particles (in principle)
0999     inline bool isResonance(int pid) {
1000       return isW(pid) || isZ(pid) || isHiggs(pid) || isTop(pid);
1001     }
1002 
1003     /// Check the PID for usability in transport codes like Geant4
1004     ///
1005     /// @todo Should exclude neutrinos/LSP, since the ATLAS G4 interface deletes them immediately?
1006     /// @todo What about long-lived particles... could be BSM but need to be transported
1007     inline bool isTransportable(int pid) {
1008       // return !isResonance(pid) && !isParton(pid) && !isGenSpecific(pid);
1009       return isPhoton(pid) || isHadron(pid) || isLepton(pid);
1010     }
1011 
1012     /// @}
1013 
1014 
1015     /// @defgroup ppair_class Particle pair classifiers
1016     /// @todo Make versions that work on PdgIdPair?
1017     /// @{
1018 
1019     inline bool isSameSign(PdgId a, PdgId b) { return a*b >= 0; }
1020     inline bool isOppSign(PdgId a, PdgId b) { return !isSameSign(a, b); }
1021     inline bool isSameFlav(PdgId a, PdgId b) { return abs(a) == abs(b); }
1022     inline bool isOppFlav(PdgId a, PdgId b) { return !isSameFlav(a, b); }
1023 
1024     inline bool isOSSF(PdgId a, PdgId b) { return isOppSign(a, b) && isSameFlav(a, b); }
1025     inline bool isSSSF(PdgId a, PdgId b) { return isSameSign(a, b) && isSameFlav(a, b); }
1026     inline bool isOSOF(PdgId a, PdgId b) { return isOppSign(a, b) && isOppFlav(a, b); }
1027     inline bool isSSOF(PdgId a, PdgId b) { return isSameSign(a, b) && isOppFlav(a, b); }
1028 
1029     /// @}
1030 
1031 
1032   }
1033 }
1034 
1035 #endif