EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Detector.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Detector.cxx
1 
11 
12 #include <algorithm>
13 #include <functional>
14 #include <list>
15 #include <memory>
16 #include <vector>
17 
22 #include "eicsmear/smear/Smearer.h"
24 
25 using std::cout;
26 using std::cerr;
27 using std::endl;
28 
29 namespace Smear {
30 
32 : useNM(false)
33 , useJB(false)
34 , useDA(false) {
35 }
36 
38 : TObject(other) {
39  useNM = other.useNM;
40  useJB = other.useJB;
41  useDA = other.useDA;
42  Devices = other.CopyDevices();
43  LegacyMode = other.GetLegacyMode();
44 }
45 
47  if (this != &that) {
48  useNM = that.useNM;
49  useJB = that.useJB;
50  useDA = that.useDA;
51  Devices = that.CopyDevices();
52  LegacyMode = that.GetLegacyMode();
53  } // if
54  return *this;
55 }
56 
59 }
60 
62  for (unsigned i(0); i < GetNDevices(); i++) {
63  delete Devices.at(i);
64  Devices.at(i) = NULL;
65  } // for
66  Devices.clear();
67 }
68 
70  Devices.push_back(dev.Clone());
71 }
72 
74  s.ToLower();
75  useNM = s.Contains("nm") || s.Contains("null");
76  useJB = s.Contains("jb") || s.Contains("jacquet");
77  useDA = s.Contains("da") || s.Contains("double");
78 }
79 
81  Smearer* smearer(NULL);
82  if (unsigned(n) < Devices.size()) {
83  smearer = Devices.at(n);
84  } // if
85  return smearer;
86 }
87 
89  if (!(useNM || useJB || useDA)) {
90  return;
91  } // if
92  // Need a bit of jiggery-pokery here, as the incident beam info isn't
93  // associated with the smeared event.
94  // So, get the beam info from the MC event, but replace the scattered
95  // electron with the smeared version.
96  // Then we can use the standard JB/DA algorithms on the smeared event.
97  const ParticleMCS* scattered = eventS->ScatteredLepton();
98  typedef std::unique_ptr<erhic::DisKinematics> KinPtr;
99  if (useNM && scattered) {
100  KinPtr kin(erhic::LeptonKinematicsComputer(*eventS).Calculate());
101  if (kin.get()) {
102  eventS->SetLeptonKinematics(*kin);
103  } // if
104  } else {
105  eventS->SetLeptonKinematics( erhic::DisKinematics(-1., -1., -1., -1., -1.));
106  } // if
107  if (useJB) {
108  KinPtr kin(erhic::JacquetBlondelComputer(*eventS).Calculate());
109  if (kin.get()) {
110  eventS->SetJacquetBlondelKinematics(*kin);
111  } // if
112  } // if
113  if (useDA && scattered) {
114  KinPtr kin(erhic::DoubleAngleComputer(*eventS).Calculate());
115  if (kin.get()) {
116  eventS->SetDoubleAngleKinematics(*kin);
117  } // if
118  } // if
119 }
120 
121 std::list<Smearer*> Detector::Accept(const erhic::VirtualParticle& p) const {
122  std::list<Smearer*> devices;
123  // Only accept final-state particles, so skip the check against each
124  // devices for non-final-state particles.
125  if (p.GetStatus() == 1) {
126  std::vector<Smearer*>::const_iterator iter;
127  for (iter = Devices.begin(); iter != Devices.end(); ++iter) {
128  // Store each device that accepts the particle.
129  if ((*iter)->Accept.Is(p)) {
130  devices.push_back(*iter);
131  } // if
132  } // for
133  } // if
134  return devices;
135 }
136 
138  // Does the particle fall in the acceptance of any device?
139  // If so, we smear it, if not, we skip it (store a NULL pointer).
140  std::list<Smearer*> devices = Accept(prt);
141  ParticleMCS* prtOut(NULL);
142  if (!devices.empty()) {
143  // It passes through at least one device, so smear it.
144  // Devices in which it doesn't pass won't smear it.
145  prtOut = new ParticleMCS();
146  prtOut->SetSmeared();
147  std::list<Smearer*>::iterator iter;
148  for (iter = devices.begin(); iter != devices.end(); ++iter) {
149  (*iter)->Smear(prt, *prtOut);
150  } // for
151 
152  if (LegacyMode){
153  // Compute derived momentum components.
154  prtOut->SetPx( prtOut->GetP() * sin(prtOut->GetTheta()) * cos(prtOut->GetPhi()));
155  prtOut->SetPy( prtOut->GetP() * sin(prtOut->GetTheta()) * sin(prtOut->GetPhi()));
156  prtOut->SetPt( sqrt(pow(prtOut->GetPx(), 2.) + pow(prtOut->GetPy(), 2.)));
157  prtOut->SetPz( prtOut->GetP() * cos(prtOut->GetTheta()));
158 
159  } else { // not in LegacyMode
160  // Sanity check and computation of derived momentum components.
161  // ---------------------------------------------------------------------
162  // - this cannot happen...
163  if ( !prtOut->IsSmeared() ) throw std::runtime_error ("particle seems to be not smeared?!");
164 
165  // Is momentum smeared at all?
166  int MomComponentsChanged = prtOut->IsPSmeared() + prtOut->IsPtSmeared() + prtOut->IsPxSmeared() + prtOut->IsPySmeared() + prtOut->IsPzSmeared();
167  int AngComponentsChanged = prtOut->IsPhiSmeared() + prtOut->IsThetaSmeared();
168 
169  if ( MomComponentsChanged==0 ){
170  // Momentum is untouched (pure calo measurement)
171  // all we have to do is ensure phi and theta are explicitly smeared
172  if ( !prtOut->IsPhiSmeared() ) {
173  cerr << "Phi always needs to be smeared (at least with sigma=0)" << endl;
174  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
175  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
176  }
177  if ( !prtOut->IsThetaSmeared() ){
178  cerr << "Theta always needs to be smeared (at least with sigma=0)" << endl;
179  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
180  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
181  }
182  } else if ( MomComponentsChanged + AngComponentsChanged != 3){
183  // - Momentum is exactly defined by three independent quantities, including theta and phi
184  cerr << "Expected 0 (excluding phi, theta) or exactly 3 (excluding phi, theta) smeared momentum quantities." << endl;
185  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
186  cerr << "MomComponentsChanged = " << MomComponentsChanged << endl;
187  cerr << prt.GetEta() << endl;
188  cerr << prtOut->IsPSmeared() << endl;
189  cerr << prtOut->IsPtSmeared() << endl;
190  cerr << prtOut->IsPxSmeared() << endl;
191  cerr << prtOut->IsPySmeared() << endl;
192  cerr << prtOut->IsPzSmeared() << endl;
193  cerr << "AngComponentsChanged = " << AngComponentsChanged << endl;
194  cerr << " pid : " << prt.Id() << endl;
195  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
196  } else {
197  // We now have exactly three out of P, px, py, pz, pt, phi, theta. Compute the rest.
198  // That's 35 total cases, but luckily many of them are not independent, or nonsensical in a detector.
199  // NOTE: We need p^2 >= pT^2, pz^2.
200  // Smearing (P and Pz) or (P and pT) is obscure enough to where we just make the ad-hoc decision
201  // to adjust P in such a case.
202 
203  // - px, py, pt, phi are intimately related. Let's condense.
204  if ( prtOut->IsPxSmeared() ^ prtOut->IsPySmeared() ) {// "^" = XOR
205  cerr << "Smearing only one out of px, py is not supported. Please contact the authors." << endl;
206  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
207  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
208  } // illegal px, py --> removes 2 * ( 5 choose 2 ) = 20 combinations
209 
210  if ( prtOut->IsPxSmeared() && prtOut->IsPySmeared() ) {
211  if ( prtOut->IsPhiSmeared() ) {
212  cerr << "Smearing px, py, and phi is inconsistent" << endl;
213  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
214  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
215  }// 1, running 21
216  if ( prtOut->IsPtSmeared() ) {
217  cerr << "Smearing px, py, and pt is inconsistent" << endl;
218  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
219  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
220  } // 1, running 22
221 
222  // Can set phi and pt now
223  prtOut->SetPhi( std::atan2( prtOut->GetPy(),prtOut->GetPx() ) );
224  prtOut->SetPt( std::sqrt( std::pow( prtOut->GetPx(),2) + std::pow( prtOut->GetPy(),2) ) );
225 
226  // legal options remaining: pz, P, theta
227  if ( prtOut->IsPzSmeared() ){ // pz is smeared
228  prtOut->SetTheta( std::atan2(prtOut->GetPt() ,prtOut->GetPz() ) );
229  prtOut->SetP( std::sqrt( std::pow( prtOut->GetPt(),2) + std::pow( prtOut->GetPz(),2) ) );
230  } // 1, running 23
231 
232  if ( prtOut->IsPSmeared() ){ // p is smeared
233  if ( prtOut->GetP() < prtOut->GetPt() ) prtOut->SetP(prtOut->GetPt(), false);
234  prtOut->SetTheta( std::atan2(prtOut->GetPt() ,prtOut->GetPz() ) );
235  prtOut->SetPz( std::sqrt( std::pow(prtOut->GetP(), 2.) - std::pow(prtOut->GetPt(), 2.)) );
236  } // 1, running 24
237 
238  if ( prtOut->IsThetaSmeared() ){ // theta is smeared
239  // Note: Here (as in other cases), we rely on the device to deliver physically sound
240  // numbers (see and adapt HandleBogusValues). But for now we explicitly fault on division by zero
241  assert ( fabs(std::tan(prtOut->GetTheta())) > 1e-8 );
242  prtOut->SetPz( prtOut->GetPt() / std::tan(prtOut->GetTheta()) );
243  prtOut->SetP( std::sqrt( std::pow( prtOut->GetPt(),2) + std::pow( prtOut->GetPz(),2) ) );
244  } // 1, running 25
245 
246  } // know both px and py --> knocked off 5 cases, 25 running
247 
248  // We're left with px and py unsmeared, and choose 3 out of P, pt, pz, phi, theta = 10 combinations.
249  // But we can reject the case of unsmeared phi since without px, py we can't reconstruct azimuth
250  // No need to protect with another if() because we touched Phi in the previous clause
251  if ( !prtOut->IsPhiSmeared() ) {
252  cerr << "Momentum components are smeared, but neither phi nor px and py are." << endl;
253  cerr << "For legacy smear scripts, use det.SetLegacyMode ( true );" << endl;
254  throw std::runtime_error ("Failed consistency check in Detector::Smear()");
255  } // 4 cases (4 choose 3), 29 running
256 
257  // Leaves (choose 2 out of P, pz, pt, theta) = 6 combinations, math checks out.
258  // Using a complementary if clause instead of another else for readability
259  if ( !prtOut->IsPxSmeared() && !prtOut->IsPySmeared() ) {
260  // NOTE: Currently, this is the only loop that should matter
261  // since we don't allow px and py smearing in the formulas,
262  // but there's no reason not to allow it in the future
263  if(prtOut->IsPSmeared() && prtOut->IsThetaSmeared() ) /*(1100)*/ { // P, theta
264 
265  prtOut->SetPt ( prtOut->GetP() * std::sin(prtOut->GetTheta()));
266  prtOut->SetPz ( prtOut->GetP() * std::cos(prtOut->GetTheta()));
267 
268  } else if( prtOut->IsPSmeared() && prtOut->IsPtSmeared() ) /*(1010)*/ { // P, pt
269  if ( prtOut->GetP() < prtOut->GetPt() ) prtOut->SetP(prtOut->GetPt(), false);
270  prtOut->SetPz( std::sqrt(std::pow(prtOut->GetP(), 2) - std::pow(prtOut->GetPt(), 2)));
271  prtOut->SetTheta ( std::atan2(prtOut->GetPt(),prtOut->GetPz()));
272 
273  } else if(prtOut->IsPzSmeared() && prtOut->IsPtSmeared() ) /*(0011)*/ { // pt, pz
274 
275  prtOut->SetP( std::sqrt(std::pow(prtOut->GetPt(), 2) + std::pow(prtOut->GetPz(), 2)));
276  prtOut->SetTheta ( std::atan2(prtOut->GetPt(),prtOut->GetPz()));
277 
278  } else if(prtOut->IsThetaSmeared() && prtOut->IsPtSmeared()) /*(0110)*/ { // pt, theta
279  // Note: Here (as in other cases), we rely on the device to deliver physically sound
280  // numbers (see and adapt HandleBogusValues). But for now we explicitly fault on division by zero
281  assert ( fabs(std::tan(prtOut->GetTheta())) > 1e-8 );
282  prtOut->SetPz( prtOut->GetPt() / std::tan(prtOut->GetTheta()) );
283  prtOut->SetP( std::sqrt(std::pow(prtOut->GetPt(), 2) + std::pow(prtOut->GetPz(), 2)));
284 
285  } else if(prtOut->IsPSmeared() && prtOut->IsPzSmeared()) /*(1001)*/ { // P, pz
286  if ( prtOut->GetP() < std::abs(prtOut->GetPz()) ) prtOut->SetP( std::abs(prtOut->GetPz()), false);
287  prtOut->SetPt( std::sqrt(std::pow(prtOut->GetP(), 2) - std::pow(prtOut->GetPz(), 2)));
288  prtOut->SetTheta( std::atan2(prtOut->GetPt() ,prtOut->GetPz() ) );
289 
290  } else if(prtOut->IsThetaSmeared() && prtOut->IsPzSmeared()) /*(0101)*/ { // theta, pz
291 
292  prtOut->SetPt( prtOut->GetPz() * std::tan(prtOut->GetTheta()));
293  prtOut->SetP( std::sqrt( std::pow( prtOut->GetPt(),2) + std::pow( prtOut->GetPz(),2) ) );
294  }
295 
296  prtOut->SetPx (prtOut->GetP() * std::sin(prtOut->GetTheta()) * std::cos(prtOut->GetPhi()));
297  prtOut->SetPy (prtOut->GetP() * std::sin(prtOut->GetTheta()) * std::sin(prtOut->GetPhi()));
298 
299  } // Know px and py are unsmeared, phi IS smeared
300 
301  } // case treatment for momentum components changed
302 
303  } // LegacyMode
304  } // if devices not empty
305 
306  // Done.
307  return prtOut;
308 
309 }
310 
311 std::vector<Smearer*> Detector::CopyDevices() const {
312  std::vector<Smearer*> copies;
313  for ( std::vector<Smearer*>::const_iterator it = Devices.begin();
314  it!=Devices.end();
315  ++it ){
316  copies.push_back ( (*it)->Clone(""));
317  }
318  return copies;
319 }
320 
321 void Detector::Print(Option_t* o) const {
322  for (unsigned i(0); i < GetNDevices(); ++i) {
323  Devices.at(i)->Print(o);
324  } // for
325 }
326 
327  void Detector::SetLegacyMode( const bool mode ){
328  LegacyMode = mode;
329  if ( LegacyMode ){
330  std::cout << "Warning: Turning on legacy mode, i.e. deactivating consistency checks and momentum regularization in Smear(). Use only for legacy smear scripts from earlier versions (<~1.0.4)" << endl;
331  }
332  }
333 
334  bool Detector::GetLegacyMode() const {
335  return LegacyMode;
336  }
337 
338 } // namespace Smear