Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-27 09:14:58

0001 // lAger: General Purpose l/A-event Generator
0002 // Copyright (C) 2016-2021 Sylvester Joosten <sjoosten@anl.gov>
0003 //
0004 // This file is part of lAger.
0005 //
0006 // lAger is free software: you can redistribute it and/or modify
0007 // it under the terms of the GNU General Public License as published by
0008 // the Free Shoftware Foundation, either version 3 of the License, or
0009 // (at your option) any later version.
0010 //
0011 // lAger is distributed in the hope that it will be useful,
0012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 // GNU General Public License for more details.
0015 //
0016 // You should have received a copy of the GNU General Public License
0017 // along with lAger.  If not, see <https://www.gnu.org/licenses/>.
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 // particle
0036 //
0037 // cornerstone class containing particle info
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   // particle status
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   // constructors
0067   //
0068   // default
0069   particle(const particle&) = default;
0070   particle& operator=(const particle&) = default;
0071   // particle with a given id or name or int
0072   // All other constructors are templates that will resolve to one
0073   // of these types
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   // particle with a given momentum 3-vector
0080   template <class PdgId>
0081   particle(const PdgId& id, const XYZVector& p3,
0082            const status_code status = status_code::FINAL);
0083 
0084   // particle in a given direction with a given energy
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   // particle with a given 4-momentum (can be off-shell)
0090   template <class PdgId>
0091   particle(const PdgId& id, const XYZTVector& p,
0092            const status_code status = status_code::FINAL);
0093 
0094   // RNG constructors usefull in particular for unstable particles with a mass
0095   // and lifetime that needs to be generated
0096   // status will be auto-set to UNSTABLE for unstable particles, and FINAL for
0097   // stable particles
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   // particle ID code
0104   template <class Integer = pdg_id> Integer type() const {
0105     return static_cast<Integer>(type_);
0106   }
0107 
0108   // particle index, -1 if not a part of a structured event
0109   int index() const { return index_; }
0110   void update_index(const int i) { index_ = i; };
0111 
0112   // full PDG info from the TDatabasePDG
0113   TParticlePDG* pdg() const { return pdg_; }
0114 
0115   // particle status
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   // final state particles (labeled FINAL or SCAT, RECOIL or SPECTATOR)
0135   // Also includes undecayed unstable particles
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   // particle properties
0151   //
0152   // charge
0153   int charge() const { return charge_; }
0154   // mass and mass squared, will be taken from 4-vector of RNG if requested
0155   double mass() const { return mass_; }
0156   double mass2() const { return mass_ * mass_; }
0157   // pole mass (differs from mass for unstable particles
0158   double pole_mass() const { return pdg_->Mass(); }
0159   // width for unstable particles
0160   double width() const { return width_; }
0161   // actual generated lifetime for unstable particles
0162   double lifetime() const { return lifetime_; }
0163   // parent or parents
0164   interval<int> parent() const { return parent_; }
0165   // first and last+1 index of the daughters
0166   interval<int> daughter() const { return daughter_; }
0167   // other parent-daughter accessors
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   // momentum and energy
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   // name
0182   std::string name() const { return pdg_->GetName(); }
0183 
0184   // get a reference to the momentum/vertex 4-vector
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   // transformations etc.
0191   void boost(const BetaVector& bv);
0192   void boost(const Boost& b) { p_ = b * p_; }
0193   // rotate from a coordate system where v moved along the z-axis, to the
0194   // coordate system of v
0195   // (algorithm taken from TVector3::RotateUz)
0196   void rotate_uz(const XYZVector& v);
0197   void rotate_uz(const XYZTVector& v);
0198   void rotate_uz(const particle& pv);
0199 
0200   // add a daughter track
0201   void add_daughter(const int index);
0202   // add a parent track
0203   void add_parent(const int index);
0204   // set the parent tracks
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   // actual mass and lifetime for this particle, can deviate from pole values
0221   // for unstable particles!
0222   double mass_{0};
0223   double lifetime_{0};
0224   XYZTVector p_{0, 0, 0, 0};
0225   XYZTVector vertex_{0, 0, 0, 0};
0226   // parent indices store the first (and optional second) parent of a
0227   // particle. -1 if not stored
0228   interval<int> parent_{-1, -1};
0229   // daughter indices are encoded from [begin, end) where begin is the
0230   // first index and end one past the last index. (-1, -1) if none
0231   interval<int> daughter_{-1, -1};
0232 };
0233 // =============================================================================
0234 // DETECTED PARTICLE
0235 //
0236 // small utility class for detected particles
0237 // =============================================================================
0238 class detected_particle {
0239 public:
0240   using XYZTVector = particle::XYZTVector;
0241 
0242   // constructors
0243   //
0244   // default
0245   detected_particle(const detected_particle&) = default;
0246   detected_particle& operator=(const detected_particle&) = default;
0247   // from a particle with a given index
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   // particle status
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   // detected 4-momentum
0274   const XYZTVector& p() const { return p_; }
0275   // detected vertex
0276   const XYZTVector& vertex() const { return vertex_; }
0277 
0278   // mass
0279   double mass() const { return p_.M(); }
0280   double mass2() const { return p_.M2(); }
0281   // momentum and energy
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 } // namespace lager
0293 
0294 // =============================================================================
0295 // Particle implementation
0296 //
0297 // This is a work-horse class called, and therefore all its members are defined
0298 // inline
0299 // =============================================================================
0300 namespace lager {
0301 
0302 //
0303 // CONSTRUCTORS
0304 //
0305 // particle with a given id
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 // particle with a given name
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 // particle with a given momentum 3-vector
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 // particle in a given direction with a given energy
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 // particle with a given 4-momentum (can be off-shell)
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 // RNG constructors usefull in particular for unstable particles with a mass
0349 // and lifetime that needs to be generated
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 // parent/daughter modifiers
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 // momentum transformations
0396 //
0397 // rotate from a coordate system where v moved along the z-axis, to the
0398 // coordate system of v
0399 // (algorithm taken from TVector3::RotateUz)
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) { // phi = 0, theta = pi
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 // private utility functions
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 } // namespace lager
0445 
0446 #endif