File indexing completed on 2026-09-27 09:14:58
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #ifndef LAGER_CORE_PARTICLE_LOADED
0021 #define LAGER_CORE_PARTICLE_LOADED
0022
0023 #include <Math/LorentzRotation.h>
0024 #include <Math/Vector3D.h>
0025 #include <Math/Vector4D.h>
0026 #include <TRandom.h>
0027 #include <lager/core/assert.hh>
0028 #include <lager/core/interval.hh>
0029 #include <lager/core/pdg.hh>
0030 #include <memory>
0031
0032 namespace lager {
0033
0034
0035
0036
0037
0038
0039 class particle {
0040 public:
0041 using BetaVector = ROOT::Math::XYZTVector::BetaVector;
0042 using XYZTVector = ROOT::Math::XYZTVector;
0043 using Boost = ROOT::Math::Boost;
0044 using XYZVector = ROOT::Math::XYZVector;
0045 using Polar3DVector = ROOT::Math::Polar3DVector;
0046
0047
0048 enum class status_code : int {
0049 BEAM = 11,
0050 SECONDARY_BEAM = 12,
0051 SCAT = 13,
0052 RECOIL = 14,
0053 SPECTATOR = 15,
0054 DECAYED = 21,
0055 DECAYED_SCHC = 22,
0056 DECAYED_RADCOR_ONLY = 23,
0057 FINAL = 30,
0058 UNSTABLE = 31,
0059 UNSTABLE_SCHC = 32,
0060 UNSTABLE_RADCOR_ONLY = 33,
0061 INFO = 40,
0062 INFO_PARENT_CM = 41,
0063 OTHER = 99
0064 };
0065
0066
0067
0068
0069 particle(const particle&) = default;
0070 particle& operator=(const particle&) = default;
0071
0072
0073
0074 particle(const pdg_id id = pdg_id::unknown,
0075 const status_code status = status_code::FINAL);
0076 particle(const std::string& name,
0077 const status_code status = status_code::FINAL);
0078 particle(const int32_t id, const status_code status = status_code::FINAL);
0079
0080 template <class PdgId>
0081 particle(const PdgId& id, const XYZVector& p3,
0082 const status_code status = status_code::FINAL);
0083
0084
0085 template <class PdgId>
0086 particle(const PdgId& id, const XYZVector& direction, const double E,
0087 const status_code status = status_code::FINAL);
0088
0089
0090 template <class PdgId>
0091 particle(const PdgId& id, const XYZTVector& p,
0092 const status_code status = status_code::FINAL);
0093
0094
0095
0096
0097
0098 template <class PdgId>
0099 particle(const PdgId& id, std::shared_ptr<TRandom> rng);
0100 template <class PdgId>
0101 particle(const PdgId& id, const XYZVector& p3, std::shared_ptr<TRandom> rng);
0102
0103
0104 template <class Integer = pdg_id> Integer type() const {
0105 return static_cast<Integer>(type_);
0106 }
0107
0108
0109 int index() const { return index_; }
0110 void update_index(const int i) { index_ = i; };
0111
0112
0113 TParticlePDG* pdg() const { return pdg_; }
0114
0115
0116 template <class Integer = status_code> Integer status() const {
0117 return static_cast<Integer>(status_);
0118 }
0119 void update_status(const status_code ns) { status_ = ns; }
0120 bool stable() const {
0121 return (status_ != status_code::UNSTABLE &&
0122 status_ != status_code::UNSTABLE_SCHC &&
0123 status_ != status_code::UNSTABLE_RADCOR_ONLY &&
0124 status_ != status_code::DECAYED &&
0125 status_ != status_code::DECAYED_SCHC &&
0126 status_ != status_code::DECAYED_RADCOR_ONLY);
0127 }
0128 bool decayed() const {
0129 return (status_ == status_code::DECAYED ||
0130 status_ == status_code::DECAYED_SCHC ||
0131 status_ == status_code::DECAYED_RADCOR_ONLY);
0132 }
0133
0134
0135
0136 bool final_state() const {
0137 return (status_ == status_code::FINAL || status_ == status_code::SCAT ||
0138 status_ == status_code::SPECTATOR ||
0139 status_ == status_code::RECOIL ||
0140 status_ == status_code::UNSTABLE ||
0141 status_ == status_code::UNSTABLE_SCHC ||
0142 status_ == status_code::UNSTABLE_RADCOR_ONLY);
0143 }
0144
0145 bool documentation() const {
0146 return (status_ == status_code::INFO) ||
0147 (status_ == status_code::INFO_PARENT_CM);
0148 }
0149
0150
0151
0152
0153 int charge() const { return charge_; }
0154
0155 double mass() const { return mass_; }
0156 double mass2() const { return mass_ * mass_; }
0157
0158 double pole_mass() const { return pdg_->Mass(); }
0159
0160 double width() const { return width_; }
0161
0162 double lifetime() const { return lifetime_; }
0163
0164 interval<int> parent() const { return parent_; }
0165
0166 interval<int> daughter() const { return daughter_; }
0167
0168 int daughter_begin() const { return daughter_.min; }
0169 int daughter_end() const { return daughter_.max; }
0170 int n_daughters() const { return daughter_.max - daughter_.min; }
0171 int parent_first() const { return parent_.min; }
0172 int parent_second() const { return parent_.max; }
0173 int n_parents() const;
0174
0175 double momentum() const {
0176 return sqrt(p_.X() * p_.X() + p_.Y() * p_.Y() + p_.Z() * p_.Z());
0177 }
0178 double energy() const { return p_.E(); }
0179 double theta() const { return p_.theta(); }
0180 double phi() const { return p_.phi(); }
0181
0182 std::string name() const { return pdg_->GetName(); }
0183
0184
0185 XYZTVector& p() { return p_; };
0186 const XYZTVector& p() const { return p_; };
0187 XYZTVector& vertex() { return vertex_; }
0188 const XYZTVector& vertex() const { return vertex_; }
0189
0190
0191 void boost(const BetaVector& bv);
0192 void boost(const Boost& b) { p_ = b * p_; }
0193
0194
0195
0196 void rotate_uz(const XYZVector& v);
0197 void rotate_uz(const XYZTVector& v);
0198 void rotate_uz(const particle& pv);
0199
0200
0201 void add_daughter(const int index);
0202
0203 void add_parent(const int index);
0204
0205 void set_parents(const interval<int> indices) { parent_ = indices; }
0206
0207 private:
0208 void set_mass_lifetime(std::shared_ptr<TRandom> rng);
0209 XYZTVector make_4vector(const XYZVector v, const double t) {
0210 return {v.X(), v.Y(), v.Z(), t};
0211 }
0212
0213 int index_{-1};
0214 pdg_id type_{pdg_id::unknown};
0215 TParticlePDG* pdg_{nullptr};
0216 int charge_{0};
0217 double width_{0};
0218 status_code status_{status_code::OTHER};
0219
0220
0221
0222 double mass_{0};
0223 double lifetime_{0};
0224 XYZTVector p_{0, 0, 0, 0};
0225 XYZTVector vertex_{0, 0, 0, 0};
0226
0227
0228 interval<int> parent_{-1, -1};
0229
0230
0231 interval<int> daughter_{-1, -1};
0232 };
0233
0234
0235
0236
0237
0238 class detected_particle {
0239 public:
0240 using XYZTVector = particle::XYZTVector;
0241
0242
0243
0244
0245 detected_particle(const detected_particle&) = default;
0246 detected_particle& operator=(const detected_particle&) = default;
0247
0248 detected_particle(const particle& part, const XYZTVector& momentum,
0249 const XYZTVector vertex, const int status = 0)
0250 : status_{status}, generated_{&part}, p_{momentum}, vertex_{vertex} {}
0251 detected_particle(const particle& part, const XYZTVector& momentum,
0252 const int status = 0)
0253 : status_{status}
0254 , generated_{&part}
0255 , p_{momentum}
0256 , vertex_{part.vertex()} {}
0257 detected_particle(const particle& part, const int status = 0)
0258 : status_{status}
0259 , generated_{&part}
0260 , p_{part.p()}
0261 , vertex_{part.vertex()} {}
0262
0263
0264 int status() const { return status_; }
0265 void update_status(const int ns) { status_ = ns; }
0266
0267 const particle& generated() const {
0268 tassert(generated_, "Associated generated particle to this detected "
0269 "particle is a nullptr");
0270 return *generated_;
0271 }
0272
0273
0274 const XYZTVector& p() const { return p_; }
0275
0276 const XYZTVector& vertex() const { return vertex_; }
0277
0278
0279 double mass() const { return p_.M(); }
0280 double mass2() const { return p_.M2(); }
0281
0282 double momentum() const { return sqrt(p_.Vect().Mag2()); }
0283 double energy() const { return p_.E(); }
0284
0285 private:
0286 int status_{0};
0287 const particle* generated_{nullptr};
0288 XYZTVector p_;
0289 XYZTVector vertex_;
0290 };
0291
0292 }
0293
0294
0295
0296
0297
0298
0299
0300 namespace lager {
0301
0302
0303
0304
0305
0306 inline particle::particle(const pdg_id id, const status_code status)
0307 : type_{id}
0308 , pdg_{pdg_particle(id)}
0309 , charge_{static_cast<int>(pdg_->Charge() / 3)}
0310 , width_{pdg_->Width()}
0311 , status_{status}
0312 , mass_{pdg_->Mass()}
0313 , p_{0, 0, 0, mass_} {}
0314
0315 inline particle::particle(const std::string& name, const status_code status)
0316 : pdg_{pdg_particle(name)}
0317 , charge_{static_cast<int>(pdg_->Charge() / 3)}
0318 , width_{pdg_->Width()}
0319 , status_{status}
0320 , mass_{pdg_->Mass()}
0321 , p_{0, 0, 0, mass_} {
0322 type_ = static_cast<pdg_id>(pdg_->PdgCode());
0323 }
0324 inline particle::particle(const int32_t id, const status_code status)
0325 : particle(static_cast<pdg_id>(id), status) {}
0326
0327 template <class PdgId>
0328 particle::particle(const PdgId& id, const XYZVector& p3,
0329 const status_code status)
0330 : particle{id, status} {
0331 p_ = make_4vector(p3, sqrt(mass_ * mass_ + p3.Mag2()));
0332 }
0333
0334 template <class PdgId>
0335 particle::particle(const PdgId& id, const XYZVector& direction, const double E,
0336 const status_code status)
0337 : particle{id, status} {
0338 p_ = make_4vector(direction.Unit() * sqrt(E * E - mass_ * mass_), E);
0339 }
0340
0341 template <class PdgId>
0342 particle::particle(const PdgId& id, const XYZTVector& p,
0343 const status_code status)
0344 : particle{id, status} {
0345 mass_ = p.M();
0346 p_ = p;
0347 }
0348
0349
0350 template <class PdgId>
0351 particle::particle(const PdgId& id, std::shared_ptr<TRandom> rng)
0352 : particle(id) {
0353 set_mass_lifetime(std::move(rng));
0354 }
0355 template <class PdgId>
0356 particle::particle(const PdgId& id, const XYZVector& p3,
0357 std::shared_ptr<TRandom> rng)
0358 : particle{id} {
0359 set_mass_lifetime(std::move(rng));
0360 p_.SetXYZT(p3.X(), p3.Y(), p3.Z(), sqrt(mass_ * mass_ + p3.Mag2()));
0361 }
0362
0363
0364
0365 inline void particle::add_daughter(const int index) {
0366 if (daughter_.min > index || daughter_.min < 0) {
0367 daughter_.min = index;
0368 }
0369 if (daughter_.max >= index || daughter_.max < 0) {
0370 daughter_.max = index + 1;
0371 }
0372 }
0373 inline void particle::add_parent(const int index) {
0374 if (parent_.min < 0) {
0375 parent_.min = index;
0376 } else if (parent_.max < 0) {
0377 parent_.max = index;
0378 } else {
0379 tassert(false, "A particle can have only have up to 2 parents"
0380 "(tried to add additional parent to particle '" +
0381 name() + "')");
0382 }
0383 }
0384
0385 inline int particle::n_parents() const {
0386 if (parent_.min < 0) {
0387 return 0;
0388 } else if (parent_.max < 0) {
0389 return 1;
0390 } else {
0391 return 2;
0392 }
0393 }
0394
0395
0396
0397
0398
0399
0400 inline void particle::rotate_uz(const particle::XYZVector& v) {
0401 const auto uv = v.Unit();
0402 const double u1 = uv.X();
0403 const double u2 = uv.Y();
0404 const double u3 = uv.Z();
0405 double up = u1 * u1 + u2 * u2;
0406 if (up) {
0407 up = sqrt(up);
0408 const double px = p_.X();
0409 const double py = p_.Y();
0410 const double pz = p_.Z();
0411 p_ = {(u1 * u3 * px - u2 * py + u1 * up * pz) / up,
0412 (u2 * u3 * px + u1 * py + u2 * up * pz) / up,
0413 (u3 * u3 * px - px + u3 * up * pz) / up, p_.T()};
0414 } else if (u3 < 0) {
0415 p_ = {-p_.X(), p_.Y(), -p_.Z(), p_.T()};
0416 }
0417 }
0418 inline void particle::rotate_uz(const particle::XYZTVector& v) {
0419 rotate_uz(v.Vect());
0420 }
0421 inline void particle::rotate_uz(const particle& pv) {
0422 rotate_uz(pv.p().Vect());
0423 }
0424 inline void particle::boost(const particle::BetaVector& bv) {
0425 Boost b{bv};
0426 boost(b);
0427 }
0428
0429
0430
0431
0432 inline void particle::set_mass_lifetime(std::shared_ptr<TRandom> rng) {
0433 if (pdg_->Stable()) {
0434 mass_ = pdg_->Mass();
0435 lifetime_ = 0;
0436 status_ = status_code::FINAL;
0437 } else {
0438 mass_ = rng->BreitWigner(pdg_->Mass(), pdg_->Width());
0439 lifetime_ = rng->Exp(pdg_->Lifetime());
0440 status_ = status_code::UNSTABLE;
0441 }
0442 }
0443
0444 }
0445
0446 #endif