File indexing completed on 2025-12-16 09:23:02
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Definitions/ParticleData.hpp"
0010
0011 #include "Acts/Definitions/PdgParticle.hpp"
0012
0013 #include <algorithm>
0014 #include <array>
0015 #include <cassert>
0016 #include <cmath>
0017 #include <cstdint>
0018 #include <iterator>
0019 #include <limits>
0020 #include <optional>
0021 #include <ostream>
0022 #include <type_traits>
0023 #include <utility>
0024
0025 #include "ParticleDataTable.hpp"
0026
0027 namespace {
0028
0029 enum class Type {
0030 Charge,
0031 Mass,
0032 Name,
0033 };
0034
0035
0036
0037 template <typename T, Acts::PdgParticle pdg, Type type>
0038 std::optional<T> findCachedImpl(const std::map<std::int32_t, T>& map) {
0039 const static std::optional<T> value = [&map]() -> std::optional<T> {
0040 const auto it = map.find(static_cast<std::int32_t>(pdg));
0041 if (it == map.end()) {
0042 return std::nullopt;
0043 }
0044 return it->second;
0045 }();
0046
0047 return value;
0048 }
0049
0050
0051
0052 template <typename T, Type type>
0053 std::optional<T> findCached(Acts::PdgParticle pdg,
0054 const std::map<std::int32_t, T>& map) {
0055 using enum Acts::PdgParticle;
0056 switch (pdg) {
0057 case eElectron:
0058 return findCachedImpl<T, eElectron, type>(map);
0059 case ePositron:
0060 return findCachedImpl<T, ePositron, type>(map);
0061 case eMuon:
0062 return findCachedImpl<T, eMuon, type>(map);
0063 case eAntiMuon:
0064 return findCachedImpl<T, eAntiMuon, type>(map);
0065 case eTau:
0066 return findCachedImpl<T, eTau, type>(map);
0067 case eAntiTau:
0068 return findCachedImpl<T, eAntiTau, type>(map);
0069 case eGamma:
0070 return findCachedImpl<T, eGamma, type>(map);
0071 case ePionZero:
0072 return findCachedImpl<T, ePionZero, type>(map);
0073 case ePionPlus:
0074 return findCachedImpl<T, ePionPlus, type>(map);
0075 case ePionMinus:
0076 return findCachedImpl<T, ePionMinus, type>(map);
0077 case eKaonPlus:
0078 return findCachedImpl<T, eKaonPlus, type>(map);
0079 case eKaonMinus:
0080 return findCachedImpl<T, eKaonMinus, type>(map);
0081 case eNeutron:
0082 return findCachedImpl<T, eNeutron, type>(map);
0083 case eAntiNeutron:
0084 return findCachedImpl<T, eAntiNeutron, type>(map);
0085 case eProton:
0086 return findCachedImpl<T, eProton, type>(map);
0087 case eAntiProton:
0088 return findCachedImpl<T, eAntiProton, type>(map);
0089 case eLead:
0090 return findCachedImpl<T, eLead, type>(map);
0091 default:
0092 return std::nullopt;
0093 }
0094 }
0095
0096 }
0097
0098 std::optional<float> Acts::findCharge(Acts::PdgParticle pdg) {
0099 if (auto cached = findCached<float, Type::Charge>(pdg, kParticlesMapCharge);
0100 cached) {
0101 return cached;
0102 }
0103
0104 const auto it = kParticlesMapCharge.find(pdg);
0105 if (it == kParticlesMapCharge.end()) {
0106 if (isNucleus(pdg)) {
0107 return Acts::findChargeOfNucleus(pdg);
0108 } else {
0109 return std::nullopt;
0110 }
0111 }
0112 return it->second;
0113 }
0114
0115 float Acts::findChargeOfNucleus(Acts::PdgParticle pdg) {
0116 if (!isNucleus(pdg)) {
0117 throw std::invalid_argument("PDG must represent a nucleus");
0118 }
0119 auto pdgGround = makeNucleusGroundState(pdg);
0120 const auto it = kParticlesMapCharge.find(pdgGround);
0121 if (it == kParticlesMapCharge.end()) {
0122
0123 auto ZandA = extractNucleusZandA(pdg);
0124 return static_cast<float>(ZandA.first);
0125 }
0126 return it->second;
0127 }
0128
0129 std::optional<float> Acts::findMass(Acts::PdgParticle pdg) {
0130 if (auto cached = findCached<float, Type::Mass>(pdg, kParticlesMapMass);
0131 cached) {
0132 return cached;
0133 }
0134
0135 const auto it = kParticlesMapMass.find(pdg);
0136 if (it == kParticlesMapMass.end()) {
0137 if (isNucleus(pdg)) {
0138 return Acts::findMassOfNucleus(pdg);
0139 } else {
0140 return std::nullopt;
0141 }
0142 }
0143 return it->second;
0144 }
0145
0146 float Acts::findMassOfNucleus(Acts::PdgParticle pdg) {
0147 if (!isNucleus(pdg)) {
0148 throw std::invalid_argument("PDG must represent a nucleus");
0149 }
0150 auto pdgGround = makeNucleusGroundState(pdg);
0151 const auto it = kParticlesMapMass.find(pdgGround);
0152 if (it == kParticlesMapMass.end()) {
0153
0154 return calculateNucleusMass(pdg);
0155 }
0156 return it->second;
0157 }
0158
0159 float Acts::calculateNucleusMass(Acts::PdgParticle pdg) {
0160 if (!isNucleus(pdg)) {
0161 throw std::invalid_argument("PDG must represent a nucleus");
0162 }
0163
0164 auto ZandA = extractNucleusZandA(pdg);
0165 int Z = std::abs(ZandA.first);
0166 int A = ZandA.second;
0167
0168 float a_Vol = 15.260f * Acts::UnitConstants::MeV;
0169 float a_Surf = 16.267f * Acts::UnitConstants::MeV;
0170 float a_Col = 0.689f * Acts::UnitConstants::MeV;
0171 float a_Sym = 22.209f * Acts::UnitConstants::MeV;
0172 float a_Pair =
0173 (1 - 2 * (Z % 2)) * (1 - A % 2) * 10.076f * Acts::UnitConstants::MeV;
0174
0175 float massP = 0.938272f * Acts::UnitConstants::GeV;
0176 float massN = 0.939565f * Acts::UnitConstants::GeV;
0177
0178 float bindEnergy = 0.f;
0179 bindEnergy += a_Vol * A;
0180 bindEnergy -= a_Surf * std::pow(A, 2. / 3.);
0181 bindEnergy -= a_Col * Z * (Z - 1) * std::pow(A, -1. / 3.);
0182 bindEnergy -= a_Sym * std::pow(A - 2 * Z, 2.) / A;
0183 bindEnergy -= a_Pair * std::pow(A, -1. / 2.);
0184
0185 return massP * Z + massN * (A - Z) - bindEnergy;
0186 }
0187
0188 std::optional<std::string_view> Acts::findName(Acts::PdgParticle pdg) {
0189 if (auto cached =
0190 findCached<const char* const, Type::Name>(pdg, kParticlesMapName);
0191 cached) {
0192 return cached;
0193 }
0194
0195 const auto it = kParticlesMapName.find(pdg);
0196 if (it == kParticlesMapName.end()) {
0197 if (isNucleus(pdg)) {
0198 return Acts::findNameOfNucleus(pdg);
0199 } else {
0200 return std::nullopt;
0201 }
0202 }
0203 return it->second;
0204 }
0205
0206 std::optional<std::string_view> Acts::findNameOfNucleus(Acts::PdgParticle pdg) {
0207 if (!isNucleus(pdg)) {
0208 throw std::invalid_argument("PDG must represent a nucleus");
0209 }
0210
0211 auto pdgGround = makeNucleusGroundState(pdg);
0212 const auto it = kParticlesMapName.find(pdgGround);
0213 if (it == kParticlesMapName.end()) {
0214 return std::nullopt;
0215 }
0216 return it->second;
0217 }
0218
0219 std::optional<Acts::ParticleData> Acts::findParticleData(PdgParticle pdg) {
0220 auto charge = findCharge(pdg);
0221 auto mass = findMass(pdg);
0222 auto name = findName(pdg);
0223
0224 if (charge.has_value() && mass.has_value() && name.has_value()) {
0225 return ParticleData{charge.value(), mass.value(), name.value()};
0226 }
0227
0228 return std::nullopt;
0229 }
0230
0231 std::ostream& Acts::operator<<(std::ostream& os, Acts::PdgParticle pdg) {
0232 const auto name = Acts::findName(pdg);
0233 if (name) {
0234 os << *name;
0235 } else {
0236 os << static_cast<std::int32_t>(pdg);
0237 }
0238 return os;
0239 }
0240
0241 std::optional<std::string_view> Acts::pdgToShortAbsString(PdgParticle pdg) {
0242 pdg = makeAbsolutePdgParticle(pdg);
0243 if (pdg == eElectron) {
0244 return "e";
0245 }
0246 if (pdg == eMuon) {
0247 return "mu";
0248 }
0249 if (pdg == eTau) {
0250 return "t";
0251 }
0252 if (pdg == eGamma) {
0253 return "g";
0254 }
0255 if (pdg == ePionZero) {
0256 return "pi0";
0257 }
0258 if (pdg == ePionPlus) {
0259 return "pi";
0260 }
0261 if (pdg == eKaonPlus) {
0262 return "K";
0263 }
0264 if (pdg == eNeutron) {
0265 return "n";
0266 }
0267 if (pdg == eProton) {
0268 return "p";
0269 }
0270 if (pdg == eLead) {
0271 return "lead";
0272 }
0273 return std::nullopt;
0274 }