EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SmearMatrixDetector_0_1_FF.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SmearMatrixDetector_0_1_FF.cxx
1 // Based on
2 // https://physdiv.jlab.org/DetectorMatrix/
3 // From June 16 2020
4 
5 // Here added devices for the far forward region on request by the WG
6 
11 #include "eicsmear/smear/Smearer.h"
14 #include <eicsmear/smear/Smear.h>
16 #include "Math/Vector4D.h"
17 
18 #include <string>
19 using std::string;
20 
21 // declare static --> local to this file, won't clash with others
22 static double ThetaFromEta( const double eta );
23 
28 Smear::Detector BuildMatrixDetector_0_1_FF( const int beam_mom_nn ) {
29  gSystem->Load("libeicsmear");
30 
31  // Create the detector object to hold all devices
32  Smear::Detector det;
33 
34  // The framework provides implementations of three kinematic calculation methods
35  // from smeared values
36  // NM - "Null method" uses the scattered lepton.
37  // DA - Double Angle method
38  // JB - Jacquet-Blondel method
39  // Important Notes:
40  // - All methods rely on measured energy and momentum components (lepton for NM, hadrons for DA, JB).
41  // In order to rely on these methods, you therefore _need_ to cover the relevant
42  // regions with smearers for momentum, angles and energy.
43  // - This is exacerbated by the fact that in the current implementation a smearing of e.g. P
44  // may fool the framework into assuming theta is measured to be the initialization value 0,
45  // leading to nan or inf or otherwise wrong calculations.
46  // NM catches P=0 and replaces it in calculations with E,
47  // but JB and DA either don't work at all or report nonsenical values.
48  // - It may be advantageous to use a P measurement in place of E in a more general case.
49  // In the future, we plan to change to a weighted mean approach.
50  // The summary of the above is that the user should be mindful of the limitations and assumptions in
51  // the calculation of smeared kinematics. Sophisticated calculations are best redone in user code
52  // from the smeared particles directly.
53  det.SetEventKinematicsCalculator("NM DA JB");
54 
55  // IMPORTANT: There are two traps (will be addressed in future releases):
56  // 1) If you smear E but don't provide phi, theta smearers, those values will be
57  // set to 0, not to a fault value and not to the truth level
58  // 2) If you do provide such a smearer, pt and pz will be changed
59  // by a consistency enforcer in Detector::Smear()
60 
61 
62  // Perfect phi and theta for all particles
63  // ---------------------------------------
64  // total coverage of the handbook for tracker and hcal is -3.5 < eta < 3.5
65  Smear::Acceptance::Zone AngleZoneHadronic(ThetaFromEta ( 3.5 ),ThetaFromEta ( -3.5 ));
66  Smear::Device SmearThetaHadronic(Smear::kTheta, "0.0");
67  SmearThetaHadronic.Accept.AddZone(AngleZoneHadronic);
68  SmearThetaHadronic.Accept.SetGenre(Smear::kHadronic);
69  det.AddDevice(SmearThetaHadronic);
70 
71  Smear::Device SmearPhiHadronic(Smear::kPhi, "0.0");
72  SmearPhiHadronic.Accept.AddZone(AngleZoneHadronic);
73  SmearPhiHadronic.Accept.SetGenre(Smear::kHadronic);
74  det.AddDevice(SmearPhiHadronic);
75 
76  // muons are neither hadrons nor electromgnetic
77  Smear::Acceptance::Zone AngleZoneMuon(ThetaFromEta ( 3.5 ),ThetaFromEta ( -3.5 ));
78  Smear::Device SmearThetaMuon(Smear::kTheta, "0.0");
79  SmearThetaMuon.Accept.AddZone(AngleZoneMuon);
80  SmearThetaMuon.Accept.AddParticle(13);
81  SmearThetaMuon.Accept.AddParticle(-13);
82  det.AddDevice(SmearThetaMuon);
83 
84  Smear::Device SmearPhiMuon(Smear::kPhi, "0.0");
85  SmearPhiMuon.Accept.AddZone(AngleZoneMuon);
86  SmearPhiMuon.Accept.AddParticle(13);
87  SmearPhiMuon.Accept.AddParticle(-13);
88  det.AddDevice(SmearPhiMuon);
89 
90  // emcal stretches to -4.5 < eta < 4.5
91  Smear::Acceptance::Zone AngleZoneEmcal(ThetaFromEta ( 4.5 ),ThetaFromEta ( -4.5 ));
92  Smear::Device SmearThetaEmcal(Smear::kTheta, "0.0");
93  SmearThetaEmcal.Accept.AddZone(AngleZoneEmcal);
94  SmearThetaEmcal.Accept.SetGenre(Smear::kElectromagnetic);
95  det.AddDevice(SmearThetaEmcal);
96 
97  Smear::Device SmearPhiEmcal(Smear::kPhi, "0.0");
98  SmearPhiEmcal.Accept.AddZone(AngleZoneEmcal);
100  det.AddDevice(SmearPhiEmcal);
101 
102  // Tracking
103  // --------
104  // Note: Smear::kCharged checks pdg charge, so includes muons (good)
105  // eta = -3.5 -- -2.0
106  // sigma_p/p ~ 0.1% p+0.5%
107  Smear::Acceptance::Zone TrackBack1Zone(ThetaFromEta ( -2.5 ),ThetaFromEta ( -3.5 ));
108  Smear::Device TrackBack1P(Smear::kP, "sqrt( pow ( 0.001*P*P, 2) + pow ( 0.005*P, 2) )");
109  TrackBack1P.Accept.AddZone(TrackBack1Zone);
110  TrackBack1P.Accept.SetCharge(Smear::kCharged);
111  det.AddDevice(TrackBack1P);
112 
113  // eta = -2.0 -- -1
114  // sigma_p/p ~ 0.05% p+ 0.5%
115  Smear::Acceptance::Zone TrackBack2Zone(ThetaFromEta ( -1 ),ThetaFromEta ( -2.5 ));
116  Smear::Device TrackBack2P(Smear::kP, "sqrt( pow ( 0.0005*P*P, 2) + pow ( 0.005*P, 2) )");
117  TrackBack2P.Accept.AddZone(TrackBack2Zone);
118  TrackBack2P.Accept.SetCharge(Smear::kCharged);
119  det.AddDevice(TrackBack2P);
120 
121  // eta = -1 -- +1
122  // sigma_p/p ~ 0.05% p+0.5%
123  Smear::Acceptance::Zone TrackBarrelZone(ThetaFromEta ( 1 ),ThetaFromEta ( -1 ));
124  Smear::Device TrackBarrelP(Smear::kP, "sqrt( pow ( 0.0005*P*P, 2) + pow ( 0.005*P, 2) )");
125  TrackBarrelP.Accept.AddZone(TrackBarrelZone);
126  TrackBarrelP.Accept.SetCharge(Smear::kCharged);
127  det.AddDevice(TrackBarrelP);
128 
129  // eta = 1 -- 2.5
130  // sigma_p/p ~ 0.05% p+1.0%
131  Smear::Acceptance::Zone TrackFwd2Zone(ThetaFromEta ( 2.5 ),ThetaFromEta ( 1 ));
132  Smear::Device TrackFwd2P(Smear::kP, "sqrt( pow ( 0.0005*P*P, 2) + pow ( 0.01*P, 2) )");
133  TrackFwd2P.Accept.AddZone(TrackFwd2Zone);
134  TrackFwd2P.Accept.SetCharge(Smear::kCharged);
135  det.AddDevice(TrackFwd2P);
136 
137  // eta = 2.5 -- 3.5
138  // sigma_p/p ~ 0.1% p+2.0%
139  Smear::Acceptance::Zone TrackFwd1Zone(ThetaFromEta ( 3.5 ),ThetaFromEta ( 2.5 ));
140  Smear::Device TrackFwd1P(Smear::kP, "sqrt( pow ( 0.001*P*P, 2) + pow ( 0.02*P, 2) )");
141  TrackFwd1P.Accept.AddZone(TrackFwd1Zone);
142  TrackFwd1P.Accept.SetCharge(Smear::kCharged);
143  det.AddDevice(TrackFwd1P);
144 
145  // EM Calorimeters
146  // ---------------
147  // Note: Smear::kElectromagnetic == gamma + e. Does not include muons (good)
148 
149  // Calorimeter resolution usually given as sigma_E/E = const% + stochastic%/Sqrt{E}
150  // EIC Smear needs absolute sigma: sigma_E = Sqrt{const*const*E*E + stoc*stoc*E}
151 
152  // Back
153  // eta = -4.5 -- -2
154  // stoch. = 2%
155  Smear::Acceptance::Zone EmcalBackZone(ThetaFromEta ( -2 ),ThetaFromEta ( -4.5 ));
156  Smear::Device EmcalBack(Smear::kE, "sqrt( pow ( 0.0*E,2 ) + pow( 0.02,2)*E)");
157  EmcalBack.Accept.AddZone(EmcalBackZone);
159  det.AddDevice(EmcalBack);
160 
161  // MidBack
162  // eta = -2 -- -1
163  // stoch. = 7%
164  Smear::Acceptance::Zone EmcalMidBackZone(ThetaFromEta ( -1 ),ThetaFromEta ( -2 ));
165  Smear::Device EmcalMidBack(Smear::kE, "sqrt( pow ( 0.0*E,2 ) + pow( 0.07,2)*E)");
166  EmcalMidBack.Accept.AddZone(EmcalMidBackZone);
168  det.AddDevice(EmcalMidBack);
169 
170  // Forward
171  // eta = -1 -- 4.5
172  // stoch. = 10-12%, use 12%
173  Smear::Acceptance::Zone EmcalFwdZone(ThetaFromEta ( 4.5 ),ThetaFromEta ( -1 ));
174  Smear::Device EmcalFwd(Smear::kE, "sqrt( pow ( 0.0*E,2 ) + pow( 0.12,2)*E)");
175  EmcalFwd.Accept.AddZone(EmcalFwdZone);
177  det.AddDevice(EmcalFwd);
178 
179  // TODO: Add PID
180  // Could turn on perfect PID
181  // Make sure to not cover more than is covered by the other detectors.
182  // Using the smallest region here, but we could add a second one for
183  // the extended EmCal range
184  // Smear::Acceptance::Zone acceptpid(ThetaFromEta(3.5),ThetaFromEta(-3.5));
185  // Smear::PerfectID pid;
186  // pid.Accept.AddZone(acceptpid);
187  // det.AddDevice( pid );
188 
189  // Hadronic Calorimeters
190  // ----------------------
191  // Note: kHadronic == |pdg|>110.
192 
193  // Back
194  // eta = -3.5 -- -1
195  // stoch. = 50%
196  Smear::Acceptance::Zone HcalBackZone(ThetaFromEta ( -1 ),ThetaFromEta ( -3.5 ));
197  Smear::Device HcalBack(Smear::kE, "sqrt(pow( 0.0*E, 2) + pow ( 0.5,2) *E)");
198  HcalBack.Accept.AddZone(HcalBackZone);
199  HcalBack.Accept.SetGenre(Smear::kHadronic);
200  det.AddDevice(HcalBack);
201 
202  // Barrel
203  // eta = -1 -- 1
204  // The matrix has nothing. As examples, one could turn to
205  // ~CMS
206  // Smear::Device HcalBarrel(Smear::kE, "sqrt( pow( 0.07*E, 2) + pow( 0.85, 2)*E)");
207  // ~Zeus
208  // Smear::Device HcalBarrel(Smear::kE, "sqrt( pow( 0.02*E, 2) + pow( 0.35,2) *E)");
209 
210  // Smear::Acceptance::Zone HcalBarrelZone(ThetaFromEta ( 1 ),ThetaFromEta ( -1 ));
211  // HcalBarrel.Accept.AddZone(HcalBarrelZone);
212  // HcalBarrel.Accept.SetGenre(Smear::kHadronic);
213  // det.AddDevice(HcalBarrel);
214 
215  // Forward
216  // eta = 1 -- 3.5
217  // stoch. = 50%
218  Smear::Acceptance::Zone HcalFwdZone(ThetaFromEta ( 3.5 ),ThetaFromEta ( 1 ));
219  Smear::Device HcalFwd(Smear::kE, "sqrt(pow( 0.0*E, 2) + pow ( 0.5,2) *E)");
220  HcalFwd.Accept.AddZone(HcalFwdZone);
222  det.AddDevice(HcalFwd);
223 
224  // Far forward
225  // -----------
226  // Neutrons:
227  // - Assume uniform acceptancefor 0<theta< 4.5 mrad
228  // - Assume an overall energy resolution sigmaE/E = 50% / sqrt (E) oplus 5%
229  // - Assume angular resolution of sigmaTheta = 3 mrad/rootE
230  // sigma_E/E = stochastic%/Sqrt{E} + const%
231  // EIC Smear needs absolute sigma: sigma_E = Sqrt{const*const*E*E + stoc*stoc*E}
232  // Note: In principle, ZDC{|theta|phi}.Accept.SetCharge(Smear::kNeutral); is the more correct
233  // way - which would also accept K0L for example.
234  // Anticipating the actual needs of users, we're going to only accept neutrons and gammas for now
235  Smear::Acceptance::Zone ZDCzone( 1e-7, 4.5e-3 );
236  Smear::Device ZDC(Smear::kE, "sqrt(pow( 0.05*E, 2) + pow ( 0.5,2) *E)");
237  ZDC.Accept.AddZone(ZDCzone);
238  ZDC.Accept.AddParticle(2112);
239  ZDC.Accept.AddParticle(22);
240  det.AddDevice(ZDC);
241 
242  Smear::Device ZDCtheta(Smear::kTheta, "3e-3 / sqrt(E)");
243  ZDCtheta.Accept.AddZone(ZDCzone);
244  ZDCtheta.Accept.AddParticle(2112);
245  ZDCtheta.Accept.AddParticle(22);
246  det.AddDevice(ZDCtheta);
247 
248  Smear::Device ZDCphi(Smear::kPhi, "0");
249  ZDCphi.Accept.AddZone(ZDCzone);
250  ZDCphi.Accept.AddParticle(2112);
251  ZDCphi.Accept.AddParticle(22);
252  det.AddDevice(ZDCphi);
253 
254  // Protons
255  // All detectors: Reasonable to assume sigma_p/p= 0.5% sigmaPt/Pt = 3%
256  // CHANGE on September 10, 2020: 5% was a typo in the source, it should be 0.5%
257  std::string pformula = "0.005*P";
258  std::string ptformula = "0.03*pT";
259 
260  // Assume uniform acceptance for 6<theta <20 mrad – "B0 spectrometer"
261  // Note that anti-protons bend the other way
262  Smear::Acceptance::Zone B0zone( 6e-3, 20e-3 );
263  Smear::Device B0P(Smear::kP, pformula);
264  B0P.Accept.AddZone(B0zone);
265  B0P.Accept.AddParticle(2212);
266  B0P.Accept.AddParticle(22);
267  B0P.Accept.AddParticle(211);
268  B0P.Accept.AddParticle(-211);
269  B0P.Accept.AddParticle(111);
270  B0P.Accept.AddParticle(321);
271  B0P.Accept.AddParticle(-321);
272  det.AddDevice(B0P);
273 
274  Smear::Device B0Pt(Smear::kPt, ptformula);
275  B0Pt.Accept.AddZone(B0zone);
276  B0Pt.Accept.AddParticle(2212);
277  B0Pt.Accept.AddParticle(22);
278  B0Pt.Accept.AddParticle(211);
279  B0Pt.Accept.AddParticle(-211);
280  B0Pt.Accept.AddParticle(111);
281  B0Pt.Accept.AddParticle(321);
282  B0Pt.Accept.AddParticle(-321);
283  det.AddDevice(B0Pt);
284 
285  Smear::Device B0phi(Smear::kPhi, "0");
286  B0phi.Accept.AddZone(B0zone);
287  B0phi.Accept.AddParticle(2212);
288  B0phi.Accept.AddParticle(22);
289  B0phi.Accept.AddParticle(211);
290  B0phi.Accept.AddParticle(-211);
291  B0phi.Accept.AddParticle(111);
292  B0phi.Accept.AddParticle(321);
293  B0phi.Accept.AddParticle(-321);
294  det.AddDevice(B0phi);
295 
296  // For protons with p_z/(beam momentum / nucleus )>.6 – "Roman pots"
297  auto RP_minpz = 0.6 * beam_mom_nn;
298  // 275 GeV -or- 135 GeV/n deuterons: Assume uniform acceptance for .5<theta<5.0 mrad
299  // 100 GeV: Assume uniform acceptance for .2<theta<5.0 mrad
300  // 41 GeV: Assume uniform acceptance for 1.0<theta<4.5 mrad
301  //
302  // for protons from nuclear breakup, the TOTAL momentum of the beam must be specified
303  // so 41 GeV/n He-3 is 61 GeV total, and 41 GeV/n deuteron is 82 GeV total
304  float thetamin = 0;
305  float thetamax = 0;
306  switch ( beam_mom_nn ){ // switch needs an int. add a little to avoid rounding problems
307  case 275 : // e+P
308  case 135 : // e+D
309  thetamin = 0.5e-3;
310  thetamax = 5e-3;
311  break;
312  case 110 :
313  case 100 :
314  thetamin = 0.2e-3;
315  thetamax = 5e-3;
316  break;
317  case 41 :
318  thetamin = 1.0e-3;
319  thetamax = 4.5e-3;
320  break;
321  case 61 : // 41 GeV/n He-3 beam setting
322  case 82 : // 41 GeV/n deuteron beam setting
323  case 165 : // 110 GeV/n He-3 beam setting
324  case 220 : // 110 GeV/n deuteron beam setting
325  thetamin = 1.0e-6;
326  thetamax = 5.0e-3;
327  break;
328  default :
329  throw std::runtime_error ( "Unsupported beam momentum for far forward detectors");
330  }
331 
332  Smear::Acceptance::Zone RPzone( thetamin, thetamax,
333  0, TMath::TwoPi(), // phi
334  0., TMath::Infinity(), // E
335  0., TMath::Infinity(), // p
336  0., TMath::Infinity(), // pt
337  RP_minpz, TMath::Infinity() // pz
338  );
339 
340  Smear::Device RPP(Smear::kP, pformula);
341  RPP.Accept.AddZone(RPzone);
342  RPP.Accept.AddParticle(2212);
343  det.AddDevice(RPP);
344 
345  Smear::Device RPPt(Smear::kPt, ptformula);
346  RPPt.Accept.AddZone(RPzone);
347  RPPt.Accept.AddParticle(2212);
348  det.AddDevice(RPPt);
349 
350  Smear::Device RPphi(Smear::kPhi, "0");
351  RPphi.Accept.AddZone(RPzone);
352  RPphi.Accept.AddParticle(2212);
353  det.AddDevice(RPphi);
354 
355  // For protons with .25<p_z/(beam momentum)<.6 – "Off-momentum Detectors"
356  auto OM_minpz = 0.25 * beam_mom_nn;
357  auto OM_maxpz = RP_minpz;
358 
359  // Assume uniform acceptance for 0.0<theta<2.0 mrad
360  Smear::Acceptance::Zone OMfullzone( 1e-7, 2e-3,
361  0, TMath::TwoPi(), // phi
362  0., TMath::Infinity(), // E
363  0., TMath::Infinity(), // p
364  0., TMath::Infinity(), // pt
365  OM_minpz, OM_maxpz // pz
366  );
367 
368 
369  Smear::Device OMfullP(Smear::kP, pformula);
370  OMfullP.Accept.AddZone(OMfullzone);
371  OMfullP.Accept.AddParticle(2212);
372  det.AddDevice(OMfullP);
373 
374  Smear::Device OMfullPt(Smear::kPt, ptformula);
375  OMfullPt.Accept.AddZone(OMfullzone);
376  OMfullPt.Accept.AddParticle(2212);
377  det.AddDevice(OMfullPt);
378 
379  Smear::Device OMfullphi(Smear::kPhi, "0");
380  OMfullphi.Accept.AddZone(OMfullzone);
381  OMfullphi.Accept.AddParticle(2212);
382  det.AddDevice(OMfullphi);
383 
384  // for 2.0<theta<5.0 mrad, only accepted for |phi|>1 radian
385  Smear::Acceptance::Zone OMpartialzone( 2e-3, 5e-3,
386  1, TMath::TwoPi()-1, // phi
387  0., TMath::Infinity(), // E
388  0., TMath::Infinity(), // p
389  0., TMath::Infinity(), // pt
390  OM_minpz, OM_maxpz // pz
391  );
392 
393  Smear::Device OMpartialP(Smear::kP, pformula);
394  OMpartialP.Accept.AddZone(OMpartialzone);
395  OMpartialP.Accept.AddParticle(2212);
396  det.AddDevice(OMpartialP);
397 
398  Smear::Device OMpartialPt(Smear::kPt, ptformula);
399  OMpartialPt.Accept.AddZone(OMpartialzone);
400  OMpartialPt.Accept.AddParticle(2212);
401  det.AddDevice(OMpartialPt);
402 
403  Smear::Device OMpartialphi(Smear::kPhi, "0");
404  OMpartialphi.Accept.AddZone(OMpartialzone);
405  OMpartialphi.Accept.AddParticle(2212);
406  det.AddDevice(OMpartialphi);
407 
408  // Not covered:
409  // Tracker material budget X/X0 <~5%
410  // Low-Q^2 tagger: -6.9<eta<-5.8: Delta_theta/theta < 1.5%; 10^-6 < Q2 < 10^-2 GeV2
411  // Barrel vertexing: sigma_xyz ~ 20 microns, d0(z) ~ d0(r phi) ~ (20 microns)/(pT [GeV]) + 5 microns
412  // Central muon detection
413 
414  return det;
415 }
416 
417 // -------------------------------------------------------------------
418 double ThetaFromEta( const double eta ) {
419  if ( !std::isnan(eta) && !std::isinf(eta) ) {
420  return 2.0 * atan( exp( -eta ));
421  }
422  throw std::runtime_error("ThetaFromEta called with NaN or Inf");
423  return -1;
424 }