eic-smear  1.0.3
A collection of ROOT classes for Monte Carlo events and a fast-smearing code simulating detector effects for the Electron-Ion Collider task force
Kinematics.cxx
Go to the documentation of this file.
1 
11 
12 #include <algorithm>
13 #include <cmath>
14 #include <functional>
15 #include <iostream>
16 #include <list>
17 #include <memory>
18 #include <numeric> // For std::accumulate
19 #include <stdexcept>
20 #include <utility>
21 #include <vector>
22 
23 #include <TDatabasePDG.h>
24 #include <TParticlePDG.h>
25 #include <TVector3.h>
26 
29 
30 namespace {
31 
32 const double chargedPionMass =
33 TDatabasePDG::Instance()->GetParticle(211)->Mass();
34 
36 
37 // ==========================================================================
38 // Helper function returning W^2 from x, Q^2 and mass.
39 // ==========================================================================
40 double computeW2FromXQ2M(double x, double Q2, double m) {
41  if (x > 0.) {
42  return std::pow(m, 2.) + (1. - x) * Q2 / x;
43  } // if
44  return 0.;
45 }
46 
47 // ==========================================================================
48 // Returns the value x bounded by [minimum, maximum].
49 // ==========================================================================
50 double bounded(double x, double minimum, double maximum) {
51  return std::max(minimum, std::min(x, maximum));
52 }
53 
54 // ==========================================================================
55 // Returns the energy of a particle based on either its stored energy or
56 // its total momentum and its mass.
57 // If the particle's momentum is greater than zero, tracking information is
58 // assumed to have been present and the particle's momentum is used to
59 // calculate energy, via E^2 = p^2 + m^2.
60 // For mass, if the particle ID is available, use the mass of that particle.
61 // If not, assume the charged pion mass.
62 // If momentum is not greater than zero, returns the stored energy of the
63 // particle (only calorimeter information is assumed to be available).
64 // ==========================================================================
65 struct EnergyFromMomentumAndId : public std::unary_function<const Particle*,
66  double> {
67  double operator()(const Particle* p) const {
68  if (!p) {
69  return 0.;
70  } // if
71  // Use the stored energy and zero mass as the default.
72  double energy(p->GetE());
73  double mass(0.);
74  // If momentum greater than zero, we assume the particle represents
75  // data where tracking information was available, and we use the
76  // momentum to compute energy.
77  if (p->GetP() > 0.) {
78  TParticlePDG* pdg = p->Id().Info();
79  // Skip pid of 0 (the default) as this is a dummy "ROOTino".
80  if (pdg && p->Id() != 0) {
81  mass = pdg->Mass();
82  } else {
83  // The particle must be charged to have tracking information
84  // so set the assumed mass to that of a charged pion.
85  mass = chargedPionMass;
86  } // if
87  // Compute energy from p and m
88  energy = sqrt(pow(p->GetP(), 2.) + pow(mass, 2.));
89  } // if
90  return energy;
91  }
92 };
93 
94 /*
95  Calculates kinematic information that is knowable by a detector.
96 
97  This takes account whether p, E and particle id are known or not, and
98  computes assumed values for variables that aren't explicitly known.
99  As a simple example: if p is not known, but E is, assume p == E.
100  Returns a ParticleMC with these "best-guess" values, specifically p, E,
101  ID, mass, pz, px, py, pt.
102  */
103 using erhic::ParticleMC;
105 class MeasuredParticle {
106  public:
107  static ParticleMC* Create(const erhic::VirtualParticle* particle) {
108  if (!particle) {
109  throw std::invalid_argument("MeasuredParticle given NULL pointer");
110  } // if
111  ParticleMC* measured = new ParticleMC;
112  // Copy ID from the input particle or guess it if not known.
113  measured->SetId(CalculateId(particle));
114  // Set mass from known/guessed ID.
115  TParticlePDG* pdg = measured->Id().Info();
116  if (pdg) {
117  measured->SetM(pdg->Mass());
118  } // if
119  std::pair<double, double> ep =
120  CalculateEnergyMomentum(particle, pdg->Mass());
121  TLorentzVector vec(0., 0., ep.second, ep.first);
122  vec.SetTheta(particle->GetTheta());
123  vec.SetPhi(particle->GetPhi());
124  measured->Set4Vector(vec);
125  return measured;
126  }
127  /*
128  Determine the particle ID.
129  If the input particle ID is known, returns the same value.
130  If the input ID is not known, return an assumed ID based on available
131  information.
132  */
133  static int CalculateId(const erhic::VirtualParticle* particle) {
134  int id(0);
135  TParticlePDG* pdg = particle->Id().Info();
136  // Skip pid of 0 (the default) as this is a dummy "ROOTino".
137  if (pdg && particle->Id() != 0) {
138  id = particle->Id();
139  } else if (particle->GetP() > 0.) {
140  // The particle ID is unknown.
141  // If there is momentum information, it must be charged so
142  // assume it is a pi+. We don't currently track whether charge info
143  // is known so we can't distinguish pi+ and pi- here.
144  // If there is no momentum information, assume it is a photon.
145  // *This is not really correct, but currently we don't track
146  // whether a particle is known to be EM or hadronic, so we can't
147  // e.g. assume neutron for hadronic particles.
148  id = 211;
149  } else {
150  id = 22;
151  } // if
152  return id;
153  }
154  /*
155  Calculate energy, using momentum and mass information if available.
156  If mass is greater than zero, use this as the assumed mass when
157  calculating via momentum, otherwise calculate
158  mass from scratch via either the known or assumed ID returned by
159  CalculateID(). Use this argument if you already know this mass and
160  don't want to repeat the calculation.
161  */
162  static std::pair<double, double> CalculateEnergyMomentum(
163  const erhic::VirtualParticle* particle, double mass = -1.) {
164  if (mass < 0.) {
165  int id = CalculateId(particle);
166  TParticlePDG* pdg = TDatabasePDG::Instance()->GetParticle(id);
167  if (pdg) {
168  mass = pdg->Mass();
169  } else {
170  mass = 0.;
171  } // if
172  } // if
173  // If momentum greater than zero, we assume the particle represents
174  // data where tracking information was available, and we use the
175  // momentum to compute energy.
176  std::pair<double, double> ep(0., 0.);
177  if (particle->GetP() > 0.) {
178  ep.first = sqrt(pow(particle->GetP(), 2.) + pow(mass, 2.));
179  ep.second = particle->GetP();
180  } else if (particle->GetE() > 0.) {
181  ep.first = particle->GetE();
182  ep.second = sqrt(pow(particle->GetE(), 2.) - pow(mass, 2.));
183  } // if
184  return ep;
185  }
186 };
187 
188 // ==========================================================================
189 // Returns the energy-momentum 4-vector of a particle.
190 // The returned energy is that computed using EnergyFromMomentumAndId, not
191 // the stored energy of the particle.
192 // ==========================================================================
193 struct EnergyMomentum4Vector : public std::unary_function<const Particle*,
194  TLorentzVector> {
195  TLorentzVector operator()(const Particle* p) const {
196  TLorentzVector ep;
197  if (p) {
198  ep = p->Get4Vector();
199  // Attempt to calculate a (hopefully more precise) energy
200  // using momentum and mass.
201  ep.SetE(EnergyFromMomentumAndId()(p));
202  } // if
203  return ep;
204  }
205 };
206 
207 // ==========================================================================
208 // Helper functor for calculating E - p_z of a particle.
209 // ==========================================================================
210 struct EMinusPz : public std::unary_function<const Particle*, double> {
211  double operator()(const Particle* p) const {
212  TLorentzVector fourMomentum(0., 0., 0., 0.);
213  if (p) {
214  fourMomentum = EnergyMomentum4Vector()(p);
215  } // if
216  return fourMomentum.E() - fourMomentum.Pz();
217  }
218 };
219 
220 } // anonymous namespace
221 
222 namespace erhic {
223 
225 : mX(0.)
226 , mQ2(0.)
227 , mW2(0.)
228 , mNu(0.)
229 , mY(0) {
230 }
231 
232 DisKinematics::DisKinematics(double x, double y, double nu,
233  double Q2, double W2)
234 : mX(x)
235 , mQ2(Q2)
236 , mW2(W2)
237 , mNu(nu)
238 , mY(y) {
239 }
240 
241 // ==========================================================================
242 // ==========================================================================
244  // ParticleIdentifier::IdentifyBeams(event, mBeams);
245  mBeams.push_back(event.BeamLepton());
246  mBeams.push_back(event.BeamHadron());
247  mBeams.push_back(event.ExchangeBoson());
248  mBeams.push_back(event.ScatteredLepton());
249 }
250 
251 // ==========================================================================
252 // ==========================================================================
253 DisKinematics* LeptonKinematicsComputer::Calculate() {
254  // Create kinematics with default values.
255  DisKinematics* kin = new DisKinematics;
256  try {
257  // Use E, p and ID of scattered lepton to create "best-guess" kinematics.
258  // MeasuredParticle::Create will throw an exception in case of a NULL
259  // pointer argument.
260  std::auto_ptr<const VirtualParticle> scattered(
261  MeasuredParticle::Create(mBeams.at(3)));
262  // If there is no measurement of theta of the scattered lepton we
263  // cannot calculate kinematics. Note that via MeasuredParticle the momentum
264  // may actually be derived from measured energy instead of momentum.
265  if (scattered->GetTheta() > 0. && scattered->GetP() > 0.) {
266  const TLorentzVector& l = mBeams.at(0)->Get4Vector();
267  const TLorentzVector& h = mBeams.at(1)->Get4Vector();
268  // Calculate kinematic quantities, making sure to bound
269  // the results by physical limits.
270  // First, Q-squared.
271  double Q2 = 2. * l.E() * scattered->GetE() *
272  (1. + cos(scattered->GetTheta()));
273  kin->mQ2 = std::max(0., Q2);
274  // Find scattered lepton energy boosted to the rest
275  // frame of the hadron beam. Calculate nu from this.
276  double gamma = mBeams.at(1)->GetE() / mBeams.at(1)->GetM();
277  double beta = mBeams.at(1)->GetP() / mBeams.at(1)->GetE();
278  double ELeptonInNucl = gamma * (l.P() - beta * l.Pz());
279  double ELeptonOutNucl = gamma *
280  (scattered->GetP() - beta * scattered->GetPz());
281  kin->mNu = std::max(0., ELeptonInNucl - ELeptonOutNucl);
282  // Calculate y using the exchange boson.
283  // Determine the exchange boson 4-vector from the scattered lepton, as
284  // this will then be valid for smeared event input also (where the
285  // exchange boson is not recorded).
286  const TLorentzVector q = l - scattered->Get4Vector();
287  double y = (q.Dot(h)) / (l.Dot(h));
288  kin->mY = bounded(y, 0., 1.);
289  // Calculate x from Q^2 = sxy
290  double cme = (l + h).M2();
291  double x = kin->mQ2 / kin->mY / cme;
292  kin->mX = bounded(x, 0., 1.);
293  kin->mW2 = computeW2FromXQ2M(kin->mX, kin->mQ2, h.M());
294  } // if
295  } // try
296  catch(std::invalid_argument& except) {
297  // In case of no scattered lepton return default values.
298  std::cerr << "No lepton for kinematic calculations" << std::endl;
299  } // catch
300  return kin;
301 }
302 
303 // ==========================================================================
304 // ==========================================================================
305 JacquetBlondelComputer::~JacquetBlondelComputer() {
306  // Delete all "measureable" particles.
307  typedef std::vector<const VirtualParticle*>::iterator Iter;
308  for (Iter i = mParticles.begin(); i != mParticles.end(); ++i) {
309  if (*i) {
310  delete *i;
311  *i = NULL;
312  } // if
313  } // for
314  mParticles.clear();
315 }
316 
317 // ==========================================================================
318 // ==========================================================================
320 : mEvent(event) {
321  // Get the full list of final-state particles in the event.
322  std::vector<const erhic::VirtualParticle*> final;
323  mEvent.HadronicFinalState(final);
324  // Populate the stored particle list with "measurable" versions of
325  // each final-state particle.
326  std::transform(final.begin(), final.end(), std::back_inserter(mParticles),
327  std::ptr_fun(&MeasuredParticle::Create));
328 }
329 
330 // ==========================================================================
331 // ==========================================================================
332 DisKinematics* JacquetBlondelComputer::Calculate() {
333  // Get all the final-state particles except the scattered lepton.
334  DisKinematics* kin = new DisKinematics;
335  kin->mY = ComputeY();
336  kin->mQ2 = ComputeQSquared();
337  kin->mX = ComputeX();
338  kin->mW2 = computeW2FromXQ2M(kin->mX, kin->mQ2,
339  mEvent.BeamHadron()->GetM());
340  return kin;
341 }
342 
343 // ==========================================================================
344 // ==========================================================================
345 Double_t JacquetBlondelComputer::ComputeY() const {
346  double y(0.);
347  // Calculate y, as long as we have beam information.
348  const VirtualParticle* hadron = mEvent.BeamHadron();
349  const VirtualParticle* lepton = mEvent.BeamLepton();
350  if (hadron && lepton) {
351  // Sum the energies of the final-state hadrons
352  std::list<double> E;
353  std::transform(mParticles.begin(), mParticles.end(),
354  std::back_inserter(E),
355  std::mem_fun(&VirtualParticle::GetE));
356  const double sumEh = std::accumulate(E.begin(), E.end(), 0.);
357  // Sum the pz of the final-state hadrons
358  std::list<double> pz;
359  std::transform(mParticles.begin(), mParticles.end(),
360  std::back_inserter(pz),
361  std::mem_fun(&VirtualParticle::GetPz));
362  const double sumPzh = std::accumulate(pz.begin(), pz.end(), 0.);
363  // Compute y.
364  // This expression seems more accurate at small y than the usual
365  // (sumE - sumPz) / 2E_lepton.
366  y = (hadron->GetE() * sumEh -
367  hadron->GetPz() * sumPzh -
368  pow(hadron->GetM(), 2.)) /
369  (lepton->GetE() * hadron->GetE() - lepton->GetPz() * hadron->GetPz());
370  } // if
371  // Return y, bounding it by the range [0, 1]
372  return bounded(y, 0., 1.);
373 }
374 
375 // ==========================================================================
376 // We use the following exact expression:
377 // 2(E_p * sum(E_h) - pz_p * sum(pz_h)) - sum(E_h)^2 + sum(p_h)^2 - M_p^2
378 // ==========================================================================
379 Double_t JacquetBlondelComputer::ComputeQSquared() const {
380  double Q2(0.);
381  // Calculate Q^2, as long as we have beam information.
382  const VirtualParticle* hadron = mEvent.BeamHadron();
383  if (hadron) {
384  // Get the px of each particle:
385  std::list<double> px;
386  std::transform(mParticles.begin(),
387  mParticles.end(),
388  std::back_inserter(px),
389  std::mem_fun(&VirtualParticle::GetPx));
390  // Get the py of each particle:
391  std::list<double> py;
392  std::transform(mParticles.begin(),
393  mParticles.end(),
394  std::back_inserter(py),
395  std::mem_fun(&VirtualParticle::GetPy));
396  double sumPx = std::accumulate(px.begin(), px.end(), 0.);
397  double sumPy = std::accumulate(py.begin(), py.end(), 0.);
398  double y = ComputeY();
399  if (y < 1.) {
400  Q2 = (pow(sumPx, 2.) + pow(sumPy, 2.)) / (1. - y);
401  } // if
402  } // if
403  return std::max(0., Q2);
404 }
405 
406 // ==========================================================================
407 // x_JB = Q2_JB / (y_JB * s)
408 // ==========================================================================
409 Double_t JacquetBlondelComputer::ComputeX() const {
410  double x(0.);
411  // Calculate x, as long as we have beam information.
412  const VirtualParticle* hadron = mEvent.BeamHadron();
413  const VirtualParticle* lepton = mEvent.BeamLepton();
414  if (hadron && lepton) {
415  double y = ComputeY();
416  double s = (hadron->Get4Vector() + lepton->Get4Vector()).M2();
417  // Use the approximate relation Q^2 = sxy to calculate x.
418  if (y > 0.0) {
419  x = ComputeQSquared() / y / s;
420  } // if
421  } // if
422  return bounded(x, 0., 1.);
423 }
424 
425 // ==========================================================================
426 // ==========================================================================
427 DoubleAngleComputer::~DoubleAngleComputer() {
428  // Delete all "measureable" particles.
429  typedef std::vector<const VirtualParticle*>::iterator Iter;
430  for (Iter i = mParticles.begin(); i != mParticles.end(); ++i) {
431  if (*i) {
432  delete *i;
433  *i = NULL;
434  } // if
435  } // for
436  mParticles.clear();
437 }
438 
439 // ==========================================================================
440 // ==========================================================================
442 : mEvent(event) {
443  // Get the full list of final-state particles in the event.
444  std::vector<const erhic::VirtualParticle*> final;
445  mEvent.HadronicFinalState(final);
446  // Populate the stored particle list with "measurable" versions of
447  // each final-state particle.
448  std::transform(final.begin(), final.end(), std::back_inserter(mParticles),
449  std::ptr_fun(&MeasuredParticle::Create));
450 }
451 
452 // ==========================================================================
453 // Formulae used are from F.D. Aaron et al., JHEP01 (2010) 109.
454 // ==========================================================================
455 DisKinematics* DoubleAngleComputer::Calculate() {
456  mHasChanged = true;
457  DisKinematics* kin = new DisKinematics;
458  kin->mQ2 = ComputeQSquared();
459  kin->mX = ComputeX();
460  kin->mY = ComputeY();
461  // Calculate W^2 from M^2 + (1 - x) * Q^2 / x
462  kin->mW2 = computeW2FromXQ2M(kin->mX, kin->mQ2,
463  mEvent.BeamHadron()->GetM());
464  return kin;
465 }
466 
467 // ==========================================================================
468 // Scattering angle of struck quark
469 // cos(angle) = A / B
470 // where A = (sum of px_h)^2 + (sum of py_h)^2 - (sum of [E_h - pz_h])^2
471 // and B = (sum of px_h)^2 + (sum of py_h)^2 + (sum of [E_h - pz_h])^2
472 // This is a computationally expensive operation that is called a lot,
473 // so cashe the result until the particle list changes.
474 // ==========================================================================
476  // Return the cached value if no changes have occurred since
477  // the last call to computeQuarkAngle().
478  if (!mHasChanged) {
479  return mAngle;
480  } // if
481  std::list<TLorentzVector> hadrons;
482  std::transform(mParticles.begin(),
483  mParticles.end(),
484  std::back_inserter(hadrons),
485  std::mem_fun(&VirtualParticle::Get4Vector));
486  TLorentzVector h = std::accumulate(hadrons.begin(),
487  hadrons.end(),
488  TLorentzVector(0., 0., 0., 0.));
489  mAngle = 2. * TMath::ATan((h.E() - h.Pz()) / h.Pt());
490  mHasChanged = false;
491  return mAngle;
492 }
493 
494 // ==========================================================================
495 // ==========================================================================
496 Double_t DoubleAngleComputer::ComputeY() const {
497  double y(0.);
498  const VirtualParticle* scattered = mEvent.ScatteredLepton();
499  if (scattered) {
500  double theta = scattered->GetTheta();
501  double gamma = ComputeQuarkAngle();
502  double denominator = tan(theta / 2.) + tan(gamma / 2.);
503  if (denominator > 0.) {
504  y = tan(gamma / 2.) / denominator;
505  } // if
506  } // if
507  // Return y bounded by the range [0, 1].
508  return bounded(y, 0., 1.);
509 }
510 
511 // ==========================================================================
512 // ==========================================================================
513 Double_t DoubleAngleComputer::ComputeQSquared() const {
514  double Q2(0.);
515  const VirtualParticle* lepton = mEvent.BeamLepton();
516  const VirtualParticle* scattered = mEvent.ScatteredLepton();
517  if (lepton && scattered) {
518  double theta = scattered->GetTheta();
519  double gamma = ComputeQuarkAngle();
520  double denominator = tan(theta / 2.) + tan(gamma / 2.);
521  if (denominator > 0.) {
522  Q2 = 4. * pow(lepton->GetE(), 2.) / tan(theta / 2.) / denominator;
523  } // if
524  } // if
525  // Return Q^2, requiring it to be positive.
526  return std::max(0., Q2);
527 }
528 
529 // ==========================================================================
530 // ==========================================================================
531 Double_t DoubleAngleComputer::ComputeX() const {
532  double x(0.);
533  const VirtualParticle* lepton = mEvent.BeamLepton();
534  const VirtualParticle* hadron = mEvent.BeamHadron();
535  if (lepton && hadron) {
536  double s = (lepton->Get4Vector() + hadron->Get4Vector()).M2();
537  double y = ComputeY();
538  if (s > 0. && y > 0.) {
539  x = ComputeQSquared() / y / s;
540  } // if
541  } // if
542  // Return x bounded by the range [0, 1].
543  return bounded(x, 0., 1.);
544 }
545 
546 } // namespace erhic
virtual Double_t GetPhi() const =0
virtual Double_t GetP() const =0
DoubleAngleComputer(const EventDis &)
Definition: Kinematics.cxx:441
virtual Double_t GetM() const =0
virtual Double_t GetE() const =0
virtual Double_t GetPz() const =0
LeptonKinematicsComputer(const EventDis &)
Definition: Kinematics.cxx:243
Double_t mAngle
Caches the quark angle.
Definition: Kinematics.h:145
virtual TLorentzVector Get4Vector() const
Definition: ParticleMC.cxx:176
virtual Double_t GetPy() const =0
virtual Double_t GetP() const
Definition: ParticleMC.h:460
virtual Double_t GetPx() const =0
const EventDis & mEvent
The event for which kinematics are being calculated.
Definition: Kinematics.h:106
virtual const VirtualParticle * BeamLepton() const =0
virtual void HadronicFinalState(ParticlePtrList &) const
Definition: VirtualEvent.h:57
JacquetBlondelComputer(const EventDis &)
Definition: Kinematics.cxx:319
std::vector< const VirtualParticle * > mParticles
Array of final-state particles used in computing kinematics.
Definition: Kinematics.h:108
virtual Double_t ComputeQuarkAngle() const
Definition: Kinematics.cxx:475
virtual const VirtualParticle * ScatteredLepton() const =0
virtual TLorentzVector Get4Vector() const =0
TParticlePDG * Info() const
Definition: Pid.cxx:31
virtual Pid Id() const
Definition: ParticleMC.h:496
virtual Pid Id() const =0
virtual Double_t GetTheta() const =0
virtual Double_t GetE() const
Definition: ParticleMC.h:506
virtual const VirtualParticle * BeamHadron() const =0
Abstract base class for a general particle.
virtual const VirtualParticle * ExchangeBoson() const =0