GENIEGenerator
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
HNLFluxCreator.cxx
Go to the documentation of this file.
1 //____________________________________________________________________________
2 /*
3  Copyright (c) 2003-2024, The GENIE Collaboration
4  For the full text of the license visit http://copyright.genie-mc.org
5 
6  Author: John Plows <komninos-john.plows \at physics.ox.ac.uk>
7  University of Oxford
8 */
9 //____________________________________________________________________________
10 
12 
13 using namespace genie;
14 using namespace genie::hnl;
15 using namespace genie::hnl::enums;
16 
17 //----------------------------------------------------------------------------
18 FluxCreator::FluxCreator() :
19  FluxRecordVisitorI("genie::hnl::FluxCreator")
20 {
21 
22 }
23 //----------------------------------------------------------------------------
25  FluxRecordVisitorI(name)
26 {
27 
28 }
29 //----------------------------------------------------------------------------
30 FluxCreator::FluxCreator(string name, string config) :
31  FluxRecordVisitorI(name, config)
32 {
33 
34 }
35 //----------------------------------------------------------------------------
37 {
38 
39 }
40 //----------------------------------------------------------------------------
42 {
43  // Adds the inital state HNL at the event record.
44  // Also assigns the production vertex to evrec (this will be overwritten by subsequent modules)
45  // Also adds (acceptance*nimpwt)^(-1) component of weight
46 
47  this->SetCurrentEntry( evrec->XSec() );
48 
49  if( !fIsUsingRootGeom ){
50  this->SetUsingRootGeom(true); // must always be true
51 
52  gGeoManager = TGeoManager::Import( fGeomFile.c_str() );
53 
54  TGeoVolume * top_volume = gGeoManager->GetTopVolume();
55  assert( top_volume );
56  TGeoShape * ts = top_volume->GetShape();
57  TGeoBBox * box = (TGeoBBox *) ts;
58 
59  this->ImportBoundingBox( box );
60  }
61 
62  if( fUsingDk2nu ){
63 
65 
66  if( iCurrEntry >= fFirstEntry ) {
67 
68  if( iCurrEntry == fFirstEntry ){
69  FluxContainer * pfGnmf = new FluxContainer();
70  fGnmf = *pfGnmf;
71  delete pfGnmf;
72  }
73 
75 
76  if( std::abs(fGnmf.pdg) == genie::kPdgHNL ){ // only add particle if parent is valid
77 
78  LOG( "HNL", pDEBUG ) << fGnmf;
79 
80  double invAccWeight = fGnmf.nimpwt * fGnmf.acceptance;
81  evrec->SetWeight( evrec->Weight() / invAccWeight );
82 
83  // scale by how many POT it takes to make the appropriate parent
84  /*
85  * To incorporate populations of parents, we take the cumulative multiplicity
86  * i.e. HNL light enough to be made by every parent get scaled by
87  * n1 = \sigma(p + target) / \sigma(p + target ; parent-producing)
88  * For HNL that are heavier than a muon, we don't take muons into account. So
89  * we up the scaling to incorporate their dropping out as
90  * n2 = \sigma(p + target) / \sigma(p + target ; parent-producing ; no muon) - etc.
91  */
92  evrec->SetWeight( evrec->Weight() * POTScaleWeight );
93 
94  // set prod-vertex in cm, ns, NEAR coords
95  TVector3 xVtxNear = fGnmf.startPoint; // in m, NEAR
96  double tVtx = fGnmf.delay; // ns
97  TLorentzVector pVtx( xVtxNear.X() * units::m / units::cm,
98  xVtxNear.Y() * units::m / units::cm,
99  xVtxNear.Z() * units::m / units::cm, tVtx ); // cm ns NEAR
100  evrec->SetVertex( pVtx ); // HNL production vertex. NOT where HNL decays to visible FS.
101 
102  // construct Particle(0). Don't worry about daughter links at this stage.
103  // this must be in USER coords
104  TLorentzVector probeP4 = fGnmf.p4User; // USER
105  GHepParticle ptHNL( fGnmf.pdg, kIStInitialState, -1, -1, -1, -1, probeP4, pVtx );
106  evrec->AddParticle( ptHNL );
107  }
108 
109  if( fGnmf.acceptance >= 0.0 ){
110  // set some event information where subsequent events can see it.
111  // Use the x4 position of the HNL. First, ensure the Vertex() is correctly set.
112  TLorentzVector * vx4 = evrec->Particle(0)->GetX4();
113  evrec->SetVertex( *vx4 );
114  TLorentzVector tmpx4( fLPx, fLPy, fGnmf.parPdg, fGnmf.lepPdg );
115  if( fLPz >= 0.0 ) evrec->SetXSec( 1.0 );
116  else evrec->SetXSec( -1.0 );
117  evrec->Particle(0)->SetPosition( tmpx4 );
118 
119  } // if( fGnmf.fgXYWgt >= 0 )
120  } // if( iCurrEntry > fFirstEntry )
121  } else {
122  LOG( "HNL", pFATAL )
123  << "No input dk2nu flux detected. Cannot proceed.";
124  exit(1);
125  }
126 }
127 //----------------------------------------------------------------------------
128 void FluxCreator::SetInputFluxPath(std::string finpath) const
129 {
130  LOG( "HNL", pDEBUG ) << "Setting input path to " << finpath;
131  fCurrPath = finpath;
132 }
133 //----------------------------------------------------------------------------
135 {
136  if( fNEntries <= 0 ){
137  this->OpenFluxInput( fCurrPath );
138  }
139  return fNEntries;
140 }
141 //----------------------------------------------------------------------------
142 void FluxCreator::SetCurrentEntry( int iCurr ) const
143 {
144  iCurrEntry = iCurr;
145 }
146 //----------------------------------------------------------------------------
148 {
149  return fGnmf;
150 }
151 //----------------------------------------------------------------------------
152 void FluxCreator::SetUsingRootGeom( bool IsUsingRootGeom ) const
153 {
154  fIsUsingRootGeom = IsUsingRootGeom;
155 }
156 //____________________________________________________________________________
157 FluxContainer FluxCreator::MakeTupleFluxEntry( int iEntry, std::string finpath ) const
158 {
159  // This method creates 1 HNL from the flux info and saves the information
160  // Essentially, it replaces a SMv with an HNL
161  FluxContainer * tmpGnmf = new FluxContainer();
162  FluxContainer gnmf = *tmpGnmf;
163  delete tmpGnmf;
164 
165  // Open flux input and initialise trees
166  if( iEntry == fFirstEntry ){
167  this->OpenFluxInput( finpath );
168  this->InitialiseTree();
169  this->InitialiseMeta();
170  if(fDoingOldFluxCalc) this->MakeBBox();
171  } else if( iEntry < fFirstEntry ){
172  this->FillNonsense( iEntry, gnmf );
173  return gnmf;
174  }
175 
176  TVector3 dumori( 0.0, 0.0, 0.0 ); // use to rotate VECTORS
177  TVector3 originPoint( -(fCx + fDetOffset.at(0)),
178  -(fCy + fDetOffset.at(1)),
179  -(fCz + fDetOffset.at(2)) ); // use to rotate POINTS. m
180 
181  // All these in m
182  TVector3 fCvec_beam( fCx, fCy, fCz );
183  TVector3 fCvec = this->ApplyUserRotation( fCvec_beam ); // in NEAR coords
184  fLepPdg = 0;
185 
186  ctree->GetEntry(iEntry);
187 
188  // explicitly check if there are any allowed decays for this parent
189  bool canGoForward = true;
190  switch( std::abs( decay_ptype ) ){
191  case kPdgPiP:
192  canGoForward =
195  case kPdgKP:
196  canGoForward =
201  case kPdgMuon:
202  canGoForward =
203  utils::hnl::IsProdKinematicallyAllowed( kHNLProdMuon3Nue ); break; // SM nus are massless so doesn't matter
204  case kPdgK0L:
205  canGoForward =
208  }
209 
210  if( !canGoForward ){
211  this->FillNonsense( iEntry, gnmf ); return gnmf;
212  }
213 
214  // turn cm to m and make origin wrt detector
215  fDx = decay_vx * units::cm / units::m; // BEAM, m
218 
219  if( !fSupplyingBEAM ){
220  TVector3 tmpVec( fDx, fDy, fDz ); // NEAR
221  tmpVec = this->ApplyUserRotation( tmpVec, false ); // BEAM
222  fDx = tmpVec.X(); fDy = tmpVec.Y(); fDz = tmpVec.Z();
223  }
224 
225  TVector3 fDvec( fDx, fDy, fDz ); // in BEAM coords
226  TVector3 fDvec_beam = this->ApplyUserRotation( fDvec, true ); // in NEAR coords
227 
228  TVector3 fDvec_user( fDvec_beam.X() - fCx, fDvec_beam.Y() - fCy, fDvec_beam.Z() - fCz ); // in USER coords
229  fDvec_user = this->ApplyUserRotation( fDvec_user, originPoint, fDetRotation, false );
230 
231  LOG( "HNL", pDEBUG )
232  << "\nIn BEAM coords, fDvec = " << utils::print::Vec3AsString( &fDvec )
233  << "\nIn NEAR coords, fDvec = " << utils::print::Vec3AsString( &fDvec_beam );
234 
235  TVector3 detO_beam( fCvec_beam.X() - fDvec_beam.X(),
236  fCvec_beam.Y() - fDvec_beam.Y(),
237  fCvec_beam.Z() - fDvec_beam.Z() ); // separation in NEAR coords
238  TVector3 detO( fCvec.X() - fDvec.X(),
239  fCvec.Y() - fDvec.Y(),
240  fCvec.Z() - fDvec.Z() ); // separation in BEAM coords
241  TVector3 detO_user( detO_beam.X(), detO_beam.Y(), detO_beam.Z() );
242 
243  detO_user = this->ApplyUserRotation( detO_user, dumori, fDetRotation, false ); // tgt-hall --> det
244 
245  double acc_saa = this->CalculateDetectorAcceptanceSAA( detO_user );
246 
247  // set parent mass
248 
249  double dpdpx = decay_pdpx, dpdpy = decay_pdpy, dpdpz = decay_pdpz; // BEAM GeV
250  if( !fSupplyingBEAM ){
251  TVector3 tmpVec( dpdpx, dpdpy, dpdpz ); // NEAR
252  tmpVec = this->ApplyUserRotation( tmpVec, false ); // BEAM
253  dpdpx = tmpVec.X(); dpdpy = tmpVec.Y(); dpdpz = tmpVec.Z();
254  }
255 
256  switch( std::abs( decay_ptype ) ){
257  case kPdgPiP: case kPdgKP: case kPdgMuon: case kPdgK0L:
258  parentMass = PDGLibrary::Instance()->Find(decay_ptype)->Mass(); break;
259  default:
260  LOG( "HNL", pERROR ) << "Parent with PDG code " << decay_ptype << " not handled!"
261  << "\n\tProceeding, but results are possibly unphysical.";
262  parentMass = PDGLibrary::Instance()->Find(decay_ptype)->Mass(); break;
263  }
264  parentMomentum = std::sqrt( dpdpx*dpdpx + dpdpy*dpdpy + dpdpz*dpdpz );
266 
267  TLorentzVector p4par = ( isParentOnAxis ) ?
268  TLorentzVector( parentMomentum * (detO.Unit()).X(),
269  parentMomentum * (detO.Unit()).Y(),
270  parentMomentum * (detO.Unit()).Z(),
271  parentEnergy ) :
272  TLorentzVector( dpdpx, dpdpy, dpdpz, parentEnergy ); // in BEAM coords
273 
274  TLorentzVector p4par_beam( dpdpx, dpdpy, dpdpz, parentEnergy ); // in BEAM coords
275  TVector3 p3par_beam = p4par_beam.Vect();
276  TVector3 p3par_near = this->ApplyUserRotation( p3par_beam, true );
277  TLorentzVector p4par_near( p3par_near.X(), p3par_near.Y(), p3par_near.Z(), parentEnergy ); // in NEAR coords
278  LOG( "HNL", pDEBUG )
279  << "\nIn BEAM coords: p3par_beam = " << utils::print::Vec3AsString( &p3par_beam )
280  << "\nIn NEAR coords: p3par_near = " << utils::print::Vec3AsString( &p3par_near );
281  if( !isParentOnAxis ){
282  // rotate p4par to NEAR coordinates
283  TVector3 tmpv3 = ApplyUserRotation( p4par.Vect(), true );
284  p4par.SetPxPyPzE( tmpv3.Px(), tmpv3.Py(), tmpv3.Pz(), p4par.E() );
285  }
286 
287  TLorentzVector p4par_user = p4par_near;
288  // rotate it to user coords
289  TVector3 ppar_user = p4par_user.Vect();
290  ppar_user = this->ApplyUserRotation( ppar_user, dumori, fDetRotation, false ); // tgt-hall --> det
291  p4par_user.SetPxPyPzE( ppar_user.Px(), ppar_user.Py(), ppar_user.Pz(), p4par_user.E() );
292 
293  TVector3 boost_beta = p4par_beam.BoostVector(); // in BEAM coords
294 
295  // now calculate which decay channel produces the HNL.
297  assert( dynamicScores.size() > 0 );
298 
299  if( dynamicScores.find( kHNLProdNull ) != dynamicScores.end() ){ // exists kin allowed channel but 0 coupling
300  this->FillNonsense( iEntry, gnmf ); return gnmf;
301  }
302 
303  RandomGen * rnd = RandomGen::Instance();
304  double score = rnd->RndGen().Uniform( 0.0, 1.0 );
305  HNLProd_t prodChan;
306  // compare with cumulative prob. If < 1st in map, pick 1st chan. If >= 1st and < (1st+2nd), pick 2nd, etc
307 
308  unsigned int imap = 0; double s1 = 0.0;
309  std::map< HNLProd_t, double >::iterator pdit = dynamicScores.begin();
310  while( score >= s1 && pdit != dynamicScores.end() ){
311  s1 += (*pdit).second;
312  if( parentMass > 0.495 ){
313  LOG( "HNL", pDEBUG )
314  << "(*pdit).first = " << utils::hnl::ProdAsString( (*pdit).first )
315  << " : (*pdit).second = " << (*pdit).second;
316  }
317  if( score >= s1 ){
318  imap++; pdit++;
319  }
320  }
321  assert( imap < dynamicScores.size() ); // should have decayed to *some* HNL
322  prodChan = (*pdit).first;
323 
324  // bookkeep this
325  fProdChan = static_cast<int>(prodChan);
326  switch( prodChan ){
327  case kHNLProdNeuk3Electron: fNuProdChan = 1; fNuPdg = kPdgNuE; break;
328  case kHNLProdNeuk3Muon: fNuProdChan = 2; fNuPdg = kPdgNuMu; break;
329  case kHNLProdKaon2Electron: fNuProdChan = 4; fNuPdg = kPdgNuE; break;
330  case kHNLProdKaon2Muon: fNuProdChan = 3; fNuPdg = kPdgNuMu; break;
331  case kHNLProdKaon3Electron: fNuProdChan = 6; fNuPdg = kPdgNuE; break;
332  case kHNLProdKaon3Muon: fNuProdChan = 5; fNuPdg = kPdgNuMu; break;
333  case kHNLProdMuon3Nue:
334  case kHNLProdMuon3Numu:
335  case kHNLProdMuon3Nutau: fNuProdChan = 9; fNuPdg = kPdgNuE; break;
336  case kHNLProdPion2Electron: fNuProdChan = 8; fNuPdg = kPdgNuE; break;
337  case kHNLProdPion2Muon: fNuProdChan = 7; fNuPdg = kPdgNuMu; break;
338  default: fNuProdChan = -999; fNuPdg = -999; break;
339  }
340 
341  LOG( "HNL", pDEBUG )
342  << "Selected channel: " << utils::hnl::ProdAsString( prodChan );
343 
344  // decay channel specified, now time to make kinematics
345  TLorentzVector p4HNL_rest = HNLEnergy( prodChan, p4par );
346  // this is a random direction rest-frame HNL.
347  fECM = p4HNL_rest.E();
348 
349  // we will now boost detO into rest frame, force rest to point to the new direction, boost the result, and compare the boost corrections
350  double boost_correction_two = 0.0;
351 
352  // 17-Jun-22: Notice the time component needs to be nonzero to get this to work!
353 
354  // first guess: betaHNL ~= 1 . Do the Lorentz boosts knocking betaHNL downwards until we hit det centre
355  double betaMag = boost_beta.Mag();
356  double gamma = std::sqrt( 1.0 / ( 1.0 - betaMag * betaMag ) );
357  double betaLab = 1.0; // first guess
358 
359  // now make a TLorentzVector in lab frame to boost back to rest.
360  double timeBit = detO.Mag(); // / units::kSpeedOfLight ; // s
361  TLorentzVector detO_4v( detO.X(), detO.Y(), detO.Z(), timeBit ); detO_4v.Boost( -boost_beta ); // BEAM with BEAM
362  TVector3 detO_rest_unit = (detO_4v.Vect()).Unit();
363  TLorentzVector p4HNL_rest_good( p4HNL_rest.P() * detO_rest_unit.X(),
364  p4HNL_rest.P() * detO_rest_unit.Y(),
365  p4HNL_rest.P() * detO_rest_unit.Z(),
366  p4HNL_rest.E() );
367 
368  double pLep_rest = std::sqrt( fLPx*fLPx + fLPy*fLPy + fLPz*fLPz );
369  TLorentzVector p4Lep_rest_good( -1.0 * pLep_rest * detO_rest_unit.X(),
370  -1.0 * pLep_rest * detO_rest_unit.Y(),
371  -1.0 * pLep_rest * detO_rest_unit.Z(),
372  fLPE );
373 
374  // boost HNL into lab frame!
375 
376  TLorentzVector p4HNL_good = p4HNL_rest_good;
377  p4HNL_good.Boost( boost_beta );
378  boost_correction_two = p4HNL_good.E() / p4HNL_rest.E();
379 
380  TVector3 detO_unit = detO.Unit(); // BEAM
381 
382  TVector3 p4HNL_good_vect = p4HNL_good.Vect();
383  TVector3 p4HNL_good_unit = p4HNL_good_vect.Unit();
384 
385  // now calculate how far away from target point we are.
386  // dist = || detO x p4HNL || / || p4HNL_good || where x == cross product
387  TVector3 distNum = detO.Cross( p4HNL_good_unit );
388  double dist = distNum.Mag(); // m
389 
390  double prevDist = 2.0 * dist;
391 
392  while( betaLab > 0.0 && ( dist > 1.0e-3 && dist < prevDist &&
393  std::abs(dist - prevDist) > 1.0e-3 * prevDist ) ){ // 1mm tolerance
394 
395  // that didn't work. Knock betaLab down a little bit and try again.
396  prevDist = dist;
397  betaLab -= 1.0e-4;
398  timeBit = detO.Mag() / ( betaLab );
399  detO_4v.SetXYZT( detO.X(), detO.Y(), detO.Z(), timeBit );
400  detO_4v.Boost( -boost_beta );
401  detO_rest_unit = (detO_4v.Vect()).Unit();
402  p4HNL_rest_good.SetPxPyPzE( p4HNL_rest.P() * detO_rest_unit.X(),
403  p4HNL_rest.P() * detO_rest_unit.Y(),
404  p4HNL_rest.P() * detO_rest_unit.Z(),
405  p4HNL_rest.E() );
406 
407  // boost into lab frame
408  p4HNL_good = p4HNL_rest_good;
409  p4HNL_good.Boost( boost_beta );
410 
411  detO_unit = detO.Unit();
412  p4HNL_good_vect = p4HNL_good.Vect();
413  p4HNL_good_unit = p4HNL_good_vect.Unit();
414 
415  distNum = detO.Cross( p4HNL_good_unit );
416  dist = distNum.Mag(); // m
417  }
418 
419  // but we don't care about that. We just want to obtain a proxy for betaHNL in lab frame.
420  // Then we can use the dk2nu-style formula modified for betaHNL!
421 
422  /*
423  * it is NOT sufficient to boost this into lab frame!
424  * Only a small portion of the CM decays can possibly reach the detector,
425  * imposing a constraint on the allowed directions of p4HNL_rest.
426  * You will miscalculate the HNL energy if you just Boost here.
427  */
428  // explicitly calculate the boost correction to lab-frame energy
429  // in a dk2nu-like fashion. See bsim::CalcEnuWgt()
430  //double betaHNL = p4HNL_rest.P() / p4HNL_rest.E();
431  double betaHNL = p4HNL_good.P() / p4HNL_good.E();
432  double costh_pardet = 0.0;
433  double boost_correction = 0.0;
434  if( parentMomentum > 0.0 ){
435  costh_pardet = ( p4par_beam.X() * detO.X() +
436  p4par_beam.Y() * detO.Y() +
437  p4par_beam.Z() * detO.Z() ) / ( parentMomentum * detO.Mag() );
438  if( costh_pardet < -1.0 ) costh_pardet = -1.0;
439  if( costh_pardet > 1.0 ) costh_pardet = 1.0;
440  boost_correction = 1.0 / ( gamma * ( 1.0 - betaMag * betaHNL * costh_pardet ) );
441  // assume boost is on z' direction where z' = parent momentum direction, subbing betaMag ==> betaMag * costh_pardet
442  if( true && boost_correction * p4HNL_rest.E() > p4HNL_rest.M() ) {
443  boost_correction = 1.0 / ( gamma * ( 1.0 - betaMag * betaHNL * costh_pardet ) );
444  } else {
445  boost_correction = p4HNL_good.E() / p4HNL_rest_good.E();
446  }
447  }
448 
449  assert( boost_correction > 0.0 && boost_correction_two > 0.0 );
450 
451  // so now we have the random decay. Direction = parent direction, energy = what we calculated
452  double EHNL = p4HNL_rest.E() * boost_correction;
453  double MHNL = p4HNL_rest.M();
454  double PHNL = std::sqrt( EHNL * EHNL - MHNL * MHNL );
455  TVector3 pdu = ( p4par.Vect() ).Unit(); // in BEAM coords
456  TLorentzVector p4HNL_rand( PHNL * pdu.X(), PHNL * pdu.Y(), PHNL * pdu.Z(), EHNL );
457 
458  // find random point in BBox and force momentum to point to that point
459 
460  double FDx = fDvec_beam.X();
461  double FDy = fDvec_beam.Y();
462  double FDz = fDvec_beam.Z();
463 
464  TVector3 absolutePoint = this->PointToRandomPointInBBox( ); // in NEAR coords, m
465  TVector3 fRVec_beam( absolutePoint.X() - FDx, absolutePoint.Y() - FDy, absolutePoint.Z() - FDz ); // NEAR, m
466  // rotate it and get unit
467  TVector3 fRVec_unit = (this->ApplyUserRotation( fRVec_beam )).Unit(); // BEAM, m/m
468  TVector3 fRVec_actualBeam = this->ApplyUserRotation( fRVec_beam ); // BEAM, m
469  TVector3 fRVec_user = this->ApplyUserRotation( fRVec_beam, dumori, fDetRotation, false ); // USER m
470 
471 
472  TVector3 rRVec_near( fRVec_beam.X() + FDx, fRVec_beam.Y() + FDy, fRVec_beam.Z() + FDz );
473  TVector3 rRVec_beam = this->ApplyUserRotation( rRVec_near );
474  TVector3 rRVec_user = this->ApplyUserRotation( rRVec_near, dumori, fDetRotation, false );
475  /*
476  rRVec_user.SetXYZ( rRVec_user.X() + (fCx + fDetOffset.at(0)),
477  rRVec_user.Y() + (fCy + fDetOffset.at(1)),
478  rRVec_user.Z() + (fCz + fDetOffset.at(2)) );
479  */
480  rRVec_user.SetXYZ( rRVec_user.X() + (fCx),
481  rRVec_user.Y() + (fCy),
482  rRVec_user.Z() + (fCz) );
483 
484  // force HNL to point along this direction
485  TLorentzVector p4HNL( p4HNL_rand.P() * fRVec_unit.X(),
486  p4HNL_rand.P() * fRVec_unit.Y(),
487  p4HNL_rand.P() * fRVec_unit.Z(),
488  p4HNL_rand.E() ); // in BEAM coords
489 
490  TVector3 pHNL_near = this->ApplyUserRotation( p4HNL.Vect(), true ); // BEAM --> NEAR coords
491  TLorentzVector p4HNL_near( pHNL_near.X(), pHNL_near.Y(), pHNL_near.Z(), p4HNL.E() );
492 
493  LOG( "HNL", pDEBUG )
494  << "\nRandom: " << utils::print::P4AsString( &p4HNL_rand )
495  << "\nPointed [NEAR]: " << utils::print::P4AsString( &p4HNL_near )
496  << "\nRest: " << utils::print::P4AsString( &p4HNL_rest );
497 
498  // update polarisation
499  TLorentzVector p4Lep_good = p4Lep_rest_good; // in parent rest frame
500  p4Lep_good.Boost( boost_beta ); // in lab frame
501  TVector3 boost_beta_HNL = p4HNL_near.BoostVector();
502  p4Lep_good.Boost( -boost_beta_HNL ); // in HNL rest frame
503 
504  fLPx = ( fixPol ) ? fFixedPolarisation.at(0) : p4Lep_good.Px() / p4Lep_good.P();
505  fLPy = ( fixPol ) ? fFixedPolarisation.at(1) : p4Lep_good.Py() / p4Lep_good.P();
506  fLPz = ( fixPol ) ? fFixedPolarisation.at(2) : p4Lep_good.Pz() / p4Lep_good.P();
507 
508  // calculate acceptance correction
509  // first, get minimum and maximum deviation from parent momentum to hit detector in degrees
510  double zm = 0.0, zp = 0.0;
511  if( fIsUsingRootGeom ){
512  this->GetAngDeviation( p4par_near, detO_beam, zm, zp ); // using NEAR and NEAR
513  } else { // !fIsUsingRootGeom
514  zm = ( isParentOnAxis ) ? 0.0 : this->GetAngDeviation( p4par_near, detO_beam, false );
515  zp = this->GetAngDeviation( p4par_near, detO_beam, true );
516  }
517 
518  if( zm == -999.9 && zp == 999.9 ){
519  this->FillNonsense( iEntry, gnmf ); return gnmf;
520  }
521 
522  if( isParentOnAxis ){
523  double tzm = zm, tzp = zp;
524  zm = 0.0;
525  zp = (tzp - tzm)/2.0; // 1/2 * angular opening
526  }
527 
528  fSMECM = decay_necm;
529  fZm = zm; fZp = zp;
530  double accCorr = this->CalculateAcceptanceCorrection( p4par, p4HNL_rest, decay_necm, zm, zp );
531  //if( !fDoingOldFluxCalc ){
532  if( fRerollPoints ){
533  // if accCorr == 0 then we must ~bail and find the next event.~
534  // We must reroll the point a bunch of times. Then we can skip.
535  // Ideally we'd be able to tell how much detector lives within the reachable region [zm, zp]
536  int iAccFail = 0; const int iAccFailBail = 10;
537  while( iAccFail < iAccFailBail && accCorr == 0.0 ){
538  LOG( "HNL", pNOTICE )
539  << "Point with separation " << utils::print::Vec3AsString( &fRVec_beam ) << " is unreachable "
540  << "by HNL from parent with momentum " << utils::print::P4AsString( &p4par ) << " !"
541  << "\nRerolling point. This is the " << iAccFail << "th try out of " << iAccFailBail;
542 
543  // find random point in BBox and force momentum to point to that point
544  // first, separation in beam frame
545  absolutePoint = this->PointToRandomPointInBBox( ); // always NEAR, m
546  /*
547  fRVec_beam.SetXYZ( absolutePoint.X() - (fCx + fDetOffset.at(0)),
548  absolutePoint.Y() - (fCy + fDetOffset.at(1)),
549  absolutePoint.Z() - (fCz + fDetOffset.at(2)) );
550  */
551  fRVec_beam.SetXYZ( absolutePoint.X() - (fCx),
552  absolutePoint.Y() - (fCy),
553  absolutePoint.Z() - (fCz) );
554  // rotate it and get unit
555  fRVec_unit = (this->ApplyUserRotation( fRVec_beam )).Unit(); // BEAM
556  // force HNL to point along this direction
557  p4HNL.SetPxPyPzE( p4HNL_rand.P() * fRVec_unit.X(),
558  p4HNL_rand.P() * fRVec_unit.Y(),
559  p4HNL_rand.P() * fRVec_unit.Z(),
560  p4HNL_rand.E() ); // BEAM
561 
562  pHNL_near = this->ApplyUserRotation( p4HNL.Vect(), true ); // NEAR
563  p4HNL_near.SetPxPyPzE( pHNL_near.X(), pHNL_near.Y(), pHNL_near.Z(), p4HNL.E() );
564 
565  LOG( "HNL", pDEBUG )
566  << "\nRandom: " << utils::print::P4AsString( &p4HNL_rand )
567  << "\nPointed: " << utils::print::P4AsString( &p4HNL )
568  << "\nRest: " << utils::print::P4AsString( &p4HNL_rest );
569 
570  // update polarisation
571  p4Lep_good = p4Lep_rest_good; // in parent rest frame
572  p4Lep_good.Boost( boost_beta ); // in lab frame
573  boost_beta_HNL = p4HNL_near.BoostVector(); // NEAR coords
574  p4Lep_good.Boost( -boost_beta_HNL ); // in HNL rest frame
575 
576  fLPx = ( fixPol ) ? fFixedPolarisation.at(0) : p4Lep_good.Px() / p4Lep_good.P();
577  fLPy = ( fixPol ) ? fFixedPolarisation.at(1) : p4Lep_good.Py() / p4Lep_good.P();
578  fLPz = ( fixPol ) ? fFixedPolarisation.at(2) : p4Lep_good.Pz() / p4Lep_good.P();
579 
580  // calculate acceptance correction
581  // first, get minimum and maximum deviation from parent momentum to hit detector in degrees
582  zm = 0.0; zp = 0.0;
583  if( fIsUsingRootGeom ){
584  this->GetAngDeviation( p4par_near, detO_beam, zm, zp ); // NEAR and NEAR
585  } else { // !fIsUsingRootGeom
586  zm = ( isParentOnAxis ) ? 0.0 : this->GetAngDeviation( p4par_near, detO_beam, false );
587  zp = this->GetAngDeviation( p4par_near, detO_beam, true );
588  }
589 
590  if( zm == -999.9 && zp == 999.9 ){
591  this->FillNonsense( iEntry, gnmf ); return gnmf;
592  }
593 
594  if( isParentOnAxis ){
595  double tzm = zm, tzp = zp;
596  zm = 0.0;
597  zp = (tzp - tzm)/2.0; // 1/2 * angular opening
598  }
599 
600  accCorr = this->CalculateAcceptanceCorrection( p4par_near, p4HNL_rest, decay_necm, zm, zp );
601  iAccFail++;
602  }
603  }
604  if( accCorr == 0.0 ){ // NOW we can give up and return.
605  this->FillNonsense( iEntry, gnmf ); return gnmf;
606  }
607 
608  // also have to factor in boost correction itself... that's same as energy boost correction squared
609  // which means a true acceptance of...
610  double acceptance = acc_saa * boost_correction * boost_correction * accCorr;
611 
612  // finally, a delay calculation
613  // if SMv arrives at t=0, then HNL arrives at t = c * ( 1 - beta_HNL ) / L
614 
615  double detDist = std::sqrt( detO.X() * detO.X() +
616  detO.Y() * detO.Y() +
617  detO.Z() * detO.Z() ); // m
618  const double kSpeedOfLightNs = units::kSpeedOfLight * units::ns / units::s; // m / ns
619  double delay = detDist / kSpeedOfLightNs * ( 1.0 / betaHNL - 1.0 );
620  delay *= units::ns / units::s;
621 
622  /*
623  LOG( "HNL", pDEBUG )
624  << "\ndetDist = " << detDist << " [m]"
625  << "\nbetaHNL = " << betaHNL
626  << "\ndelay = " << delay << " [ns]";
627  */
628 
629  // write 4-position all this happens at
630 
631  double dxvx = decay_vx, dxvy = decay_vy, dxvz = decay_vz;
632 
633  if( !fSupplyingBEAM ){
634  TVector3 tmpVec( dxvx, dxvy, dxvz ); // NEAR
635  tmpVec = this->ApplyUserRotation( tmpVec, false ); // BEAM
636  dxvx = tmpVec.X(); dxvy = tmpVec.Y(); dxvz = tmpVec.Z();
637  }
638 
639  TLorentzVector x4HNL_beam( dxvx, dxvy, dxvz, delay ); // in cm, ns, BEAM coords
640  TVector3 x3HNL_beam = x4HNL_beam.Vect();
641  TVector3 x3HNL_near = this->ApplyUserRotation( x3HNL_beam, true );
642  TLorentzVector x4HNL_near( x3HNL_near.X(), x3HNL_near.Y(), x3HNL_near.Z(), delay );
643 
644  TLorentzVector x4HNL( fDvec_user.X(), fDvec_user.Y(), fDvec_user.Z(), delay ); // in m, ns, USER coords
645  TLorentzVector x4HNL_cm( units::m / units::cm * x4HNL.X(),
646  units::m / units::cm * x4HNL.Y(),
647  units::m / units::cm * x4HNL.Z(), delay ); // in cm, ns, USER
648 
649  LOG( "HNL", pDEBUG ) << "Filling gnmf...";
650 
651  // fill all the flux stuff now!
652 
653  int typeMod = 1;
654  if( !fIsMajorana ) typeMod = ( decay_ptype > 0 ) ? 1 : -1;
655  // fix for muons which are backwards...
656  int parPdg = decay_ptype;
657  if( std::abs(parPdg) == kPdgMuon ) typeMod *= -1;
658 
659  gnmf.evtno = iEntry;
660 
661  gnmf.pdg = typeMod * kPdgHNL;
662  gnmf.parPdg = parPdg;
663  gnmf.lepPdg = fLepPdg;
664  gnmf.nuPdg = typeMod * fNuPdg;
665 
666  gnmf.prodChan = fProdChan;
667  gnmf.nuProdChan = fNuProdChan;
668 
669  gnmf.startPoint.SetXYZ( fDvec_beam.X(), fDvec_beam.Y(), fDvec_beam.Z() ); // NEAR m
670  gnmf.targetPoint.SetXYZ( fTargetPoint.X(), fTargetPoint.Y(), fTargetPoint.Z() ); // NEAR m
671  gnmf.startPointUser.SetXYZ( fDvec_user.X() - fCx, fDvec_user.Y() - fCy, fDvec_user.Z() - fCz ); // USER m
672  gnmf.targetPointUser.SetXYZ( fTargetPoint.X() - fCx, fTargetPoint.Y() - fCy, fTargetPoint.Z() - fCz ); // USER m
673  gnmf.delay = delay; // ns
674 
675  gnmf.polz.SetXYZ( fLPx, fLPy, fLPz );
676 
677  TLorentzVector p4HNL_user = p4HNL_near;
678  // rotate it to user coords
679  TVector3 pHNL_user = p4HNL_user.Vect();
680  pHNL_user = this->ApplyUserRotation( pHNL_user, dumori, fDetRotation, false ); // tgt-hall --> det
681  p4HNL_user.SetPxPyPzE( pHNL_user.Px(), pHNL_user.Py(), pHNL_user.Pz(), p4HNL_user.E() );
682 
683  gnmf.p4 = p4HNL_near;
684  gnmf.parp4 = p4par_near;
685  gnmf.p4User = p4HNL_user;
686  gnmf.parp4User = p4par_user;
687 
688  gnmf.Ecm = fECM;
689  gnmf.nuEcm = fSMECM;
690 
691  gnmf.XYWgt = acc_saa;
692  gnmf.boostCorr = p4HNL.E() / fECM;
693  gnmf.accCorr = accCorr;
694  gnmf.zetaMinus = fZm;
695  gnmf.zetaPlus = fZp;
696  gnmf.acceptance = acceptance;
697  gnmf.nimpwt = decay_nimpwt;
698 
699  return gnmf;
700 
701 }
702 //----------------------------------------------------------------------------
703 void FluxCreator::FillNonsense( int iEntry, FluxContainer & gnmf ) const
704 {
705  gnmf.evtno = iEntry;
706 
707  gnmf.pdg = -9999;
708  gnmf.parPdg = -9999;
709  gnmf.lepPdg = -9999;
710  gnmf.nuPdg = -9999;
711 
712  gnmf.prodChan = -9999;
713  gnmf.nuProdChan = -9999;
714 
715  gnmf.startPoint.SetXYZ(-9999.9, -9999.9, -9999.9);
716  gnmf.targetPoint.SetXYZ(-9999.9, -9999.9, -9999.9);
717  gnmf.startPointUser.SetXYZ(-9999.9, -9999.9, -9999.9);
718  gnmf.targetPointUser.SetXYZ(-9999.9, -9999.9, -9999.9);
719 
720  gnmf.polz.SetXYZ(-9999.9, -9999.9, -9999.9);
721 
722  gnmf.p4.SetPxPyPzE(-9999.9, -9999.9, -9999.9, -9999.9);
723  gnmf.parp4.SetPxPyPzE(-9999.9, -9999.9, -9999.9, -9999.9);
724  gnmf.p4User.SetPxPyPzE(-9999.9, -9999.9, -9999.9, -9999.9);
725  gnmf.parp4User.SetPxPyPzE(-9999.9, -9999.9, -9999.9, -9999.9);
726 
727  gnmf.Ecm = -9999.9;
728  gnmf.nuEcm = -9999.9;
729  gnmf.XYWgt = -9999.9;
730  gnmf.boostCorr = -9999.9;
731  gnmf.accCorr = -9999.9;
732  gnmf.zetaMinus = -9999.9;
733  gnmf.zetaPlus = -9999.9;
734  gnmf.acceptance = -9999.9;
735 
736  gnmf.nimpwt = -9999.9;
737 
738  return;
739 }
740 //----------------------------------------------------------------------------
741 void FluxCreator::OpenFluxInput( std::string finpath ) const
742 {
743  //if( std::strcmp( finpath.c_str(), fCurrPath.c_str() ) == 0 ) return;
744 
746  fCurrPath = finpath;
747  finpath.append("/");
748 
749  LOG( "HNL", pDEBUG )
750  << "Getting flux input from finpath = " << finpath.c_str();
751 
752  // recurse over files in this directory and add to chain
753  if(!ctree){
754  ctree = new TChain( "dkTree" );
755  cmeta = new TChain( "dkMeta" );
756  }
757 
758  if( fPathLoaded ) return;
759 
760  TSystemDirectory dir( finpath.c_str(), finpath.c_str() );
761  TList * files = dir.GetListOfFiles(); int nFiles = 0;
762  assert( files );
763  files->Sort();
764 
765  TSystemFile * file;
766  TString fname;
767  TIter next(files);
768 
769  while( (file=( TSystemFile * ) next()) && !fPathLoaded ){
770  fname = file->GetName();
771  if( !file->IsDirectory() ){
772  TString fullpath = TString( finpath.c_str() ) + fname;
773  nFiles++;
774  ctree->Add( fullpath );
775  cmeta->Add( fullpath );
776  }
777  }
778 
779  if( !ctree ){ LOG( "HNL", pFATAL ) << "Could not open flux tree!"; }
780  if( !cmeta ){ LOG( "HNL", pFATAL ) << "Could not open meta tree!"; }
781  assert( ctree && cmeta );
782 
783  const int nEntriesInMeta = cmeta->GetEntries();
784  int nEntries = ctree->GetEntries();
785 
786  fNEntries = nEntries;
787 
788  LOG( "HNL", pDEBUG )
789  << "\nThere were " << nEntriesInMeta << " entries in meta with " << nEntries << " total nus"
790  << "\n got from " << nFiles << " files";
791 
792  fPathLoaded = true;
793 
794  delete file;
795  delete files;
796 }
797 //----------------------------------------------------------------------------
799 {
800  potnum = 0.0;
801  decay_ptype = 0;
802  decay_vx = 0.0; decay_vy = 0.0; decay_vz = 0.0;
803  decay_pdpx = 0.0; decay_pdpy = 0.0; decay_pdpz = 0.0;
804  decay_nimpwt = 0.0;
805 
806  arSize = 0, anArSize = 0, trArSize = 0;
807  djob = -9999;
808  ppvx = -9999.9, ppvy = -9999.9, ppvz = -9999.9;
809  decay_norig = -9999, decay_ndecay = -9999, decay_ntype = -9999;
810  decay_ppdxdz = -9999.9, decay_ppdydz = -9999.9, decay_pppz = -9999.9, decay_ppenergy = -9999.9;
811  decay_ppmedium = -9999;
812  decay_muparpx = -9999.9, decay_muparpy = -9999.9, decay_muparpz = -9999.9, decay_mupare = -9999.9;
813 
814  tgtexit_tvx = -9999.9, tgtexit_tvy = -9999.9, tgtexit_tvz = -9999.9;
815  tgtexit_tpx = -9999.9, tgtexit_tpy = -9999.9, tgtexit_tpz = -9999.9;
816  tgtexit_tptype = -9999, tgtexit_tgen = -9999;
817 
818  for( int i = 0; i < maxArray; i++ ){
819  nuray_px[i] = -9999.9;
820  nuray_py[i] = -9999.9;
821  nuray_pz[i] = -9999.9;
822  nuray_E[i] = -9999.9;
823  nuray_wgt[i] = -9999.9;
824 
825  ancestor_pdg[i] = -9999;
826  ancestor_startx[i] = -9999.9;
827  ancestor_starty[i] = -9999.9;
828  ancestor_startz[i] = -9999.9;
829  ancestor_startpx[i] = -9999.9;
830  ancestor_startpy[i] = -9999.9;
831  ancestor_startpz[i] = -9999.9;
832  ancestor_stoppx[i] = -9999.9;
833  ancestor_stoppy[i] = -9999.9;
834  ancestor_stoppz[i] = -9999.9;
835  ancestor_polx[i] = -9999.9;
836  ancestor_poly[i] = -9999.9;
837  ancestor_polz[i] = -9999.9;
838  ancestor_pprodpx[i] = -9999.9;
839  ancestor_pprodpy[i] = -9999.9;
840  ancestor_pprodpz[i] = -9999.9;
841  ancestor_nucleus[i] = -9999;
842 
843  traj_trkx[i] = -9999.9;
844  traj_trky[i] = -9999.9;
845  traj_trkz[i] = -9999.9;
846  traj_trkpx[i] = -9999.9;
847  traj_trkpy[i] = -9999.9;
848  traj_trkpz[i] = -9999.9;
849 
850  for( int j = 0; j < maxC; j++ ){
851  ancestor_proc[i*maxC+j] = 0;
852  ancestor_ivol[i*maxC+j] = 0;
853  ancestor_imat[i*maxC+j] = 0;
854  }
855  }
856 
857  // necessary branches
858  ctree->SetBranchAddress( "potnum", &potnum );
859  ctree->SetBranchAddress( "decay_ptype", &decay_ptype );
860  ctree->SetBranchAddress( "decay_vx", &decay_vx );
861  ctree->SetBranchAddress( "decay_vy", &decay_vy );
862  ctree->SetBranchAddress( "decay_vz", &decay_vz );
863  ctree->SetBranchAddress( "decay_pdpx", &decay_pdpx );
864  ctree->SetBranchAddress( "decay_pdpy", &decay_pdpy );
865  ctree->SetBranchAddress( "decay_pdpz", &decay_pdpz );
866  ctree->SetBranchAddress( "decay_necm", &decay_necm );
867  ctree->SetBranchAddress( "decay_nimpwt", &decay_nimpwt );
868 
869  // extra branches
870  if( ctree->GetBranch( "job" ) ) ctree->SetBranchAddress( "job", &djob );
871  if( ctree->GetBranch( "decay_norig" ) ) ctree->SetBranchAddress( "decay_norig", &decay_norig );
872  if( ctree->GetBranch( "decay_ndecay" ) ) ctree->SetBranchAddress( "decay_ndecay", &decay_ndecay );
873  if( ctree->GetBranch( "decay_ntype" ) ) ctree->SetBranchAddress( "decay_ntype", &decay_ntype );
874  if( ctree->GetBranch( "decay_ppdxdz" ) ) ctree->SetBranchAddress( "decay_ppdxdz", &decay_ppdxdz );
875  if( ctree->GetBranch( "decay_ppdydz" ) ) ctree->SetBranchAddress( "decay_ppdydz", &decay_ppdydz );
876  if( ctree->GetBranch( "decay_pppz" ) ) ctree->SetBranchAddress( "decay_pppz", &decay_pppz );
877  if( ctree->GetBranch( "decay_ppenergy" ) ) ctree->SetBranchAddress( "decay_ppenergy", &decay_ppenergy );
878  if( ctree->GetBranch( "decay_ppmedium" ) ) ctree->SetBranchAddress( "decay_ppmedium", &decay_ppmedium );
879  if( ctree->GetBranch( "decay_ptype" ) ) ctree->SetBranchAddress( "decay_ptype", &decay_ptype );
880  if( ctree->GetBranch( "decay_muparpx" ) ) ctree->SetBranchAddress( "decay_muparpx", &decay_muparpx );
881  if( ctree->GetBranch( "decay_muparpy" ) ) ctree->SetBranchAddress( "decay_muparpy", &decay_muparpy );
882  if( ctree->GetBranch( "decay_muparpz" ) ) ctree->SetBranchAddress( "decay_muparpz", &decay_muparpz );
883  if( ctree->GetBranch( "decay_mupare" ) ) ctree->SetBranchAddress( "decay_mupare", &decay_mupare );
884 
885  if( ctree->GetBranch( "nuray_size" ) ) ctree->SetBranchAddress( "nuray_size", &arSize );
886  if( ctree->GetBranch( "nuray_px" ) ) ctree->SetBranchAddress( "nuray_px", nuray_px );
887  if( ctree->GetBranch( "nuray_py" ) ) ctree->SetBranchAddress( "nuray_py", nuray_py );
888  if( ctree->GetBranch( "nuray_pz" ) ) ctree->SetBranchAddress( "nuray_pz", nuray_pz );
889  if( ctree->GetBranch( "nuray_E" ) ) ctree->SetBranchAddress( "nuray_E", nuray_E );
890  if( ctree->GetBranch( "nuray_wgt" ) ) ctree->SetBranchAddress( "nuray_wgt", nuray_wgt );
891 
892  if( ctree->GetBranch( "ancestor_size" ) ) ctree->SetBranchAddress( "ancestor_size", &anArSize );
893  if( ctree->GetBranch( "ancestor_pdg" ) ) ctree->SetBranchAddress( "ancestor_pdg", ancestor_pdg );
894  if( ctree->GetBranch( "ancestor_startx" ) ) ctree->SetBranchAddress( "ancestor_startx", ancestor_startx );
895  if( ctree->GetBranch( "ancestor_starty" ) ) ctree->SetBranchAddress( "ancestor_starty", ancestor_starty );
896  if( ctree->GetBranch( "ancestor_startz" ) ) ctree->SetBranchAddress( "ancestor_startz", ancestor_startz );
897  if( ctree->GetBranch( "ancestor_startpx" ) ) ctree->SetBranchAddress( "ancestor_startpx", ancestor_startpx );
898  if( ctree->GetBranch( "ancestor_startpy" ) ) ctree->SetBranchAddress( "ancestor_startpy", ancestor_starty );
899  if( ctree->GetBranch( "ancestor_startpz" ) ) ctree->SetBranchAddress( "ancestor_startpz", ancestor_startpz );
900  if( ctree->GetBranch( "ancestor_stoppx" ) ) ctree->SetBranchAddress( "ancestor_stoppx", ancestor_stoppx );
901  if( ctree->GetBranch( "ancestor_stoppy" ) ) ctree->SetBranchAddress( "ancestor_stoppy", ancestor_stoppy );
902  if( ctree->GetBranch( "ancestor_stoppz" ) ) ctree->SetBranchAddress( "ancestor_stoppz", ancestor_stoppz );
903  if( ctree->GetBranch( "ancestor_polx" ) ) ctree->SetBranchAddress( "ancestor_polx", ancestor_polx );
904  if( ctree->GetBranch( "ancestor_poly" ) ) ctree->SetBranchAddress( "ancestor_poly", ancestor_poly );
905  if( ctree->GetBranch( "ancestor_polz" ) ) ctree->SetBranchAddress( "ancestor_polz", ancestor_polz );
906  if( ctree->GetBranch( "ancestor_pprodpx" ) ) ctree->SetBranchAddress( "ancestor_pprodpx", ancestor_pprodpx );
907  if( ctree->GetBranch( "ancestor_pprodpy" ) ) ctree->SetBranchAddress( "ancestor_pprodpy", ancestor_pprodpy );
908  if( ctree->GetBranch( "ancestor_pprodpz" ) ) ctree->SetBranchAddress( "ancestor_pprodpz", ancestor_pprodpz );
909  if( ctree->GetBranch( "ancestor_proc" ) ) ctree->SetBranchAddress( "ancestor_proc", ancestor_proc );
910  if( ctree->GetBranch( "ancestor_ivol" ) ) ctree->SetBranchAddress( "ancestor_ivol", ancestor_ivol );
911  if( ctree->GetBranch( "ancestor_imat" ) ) ctree->SetBranchAddress( "ancestor_imat", ancestor_imat );
912 
913  if( ctree->GetBranch( "ppvx" ) ) ctree->SetBranchAddress( "ppvx", &ppvx );
914  if( ctree->GetBranch( "ppvy" ) ) ctree->SetBranchAddress( "ppvy", &ppvy );
915  if( ctree->GetBranch( "ppvz" ) ) ctree->SetBranchAddress( "ppvz", &ppvz );
916 
917  if( ctree->GetBranch( "tgtexit_tvx" ) ) ctree->SetBranchAddress( "tgtexit_tvx", &tgtexit_tvx );
918  if( ctree->GetBranch( "tgtexit_tvy" ) ) ctree->SetBranchAddress( "tgtexit_tvy", &tgtexit_tvy );
919  if( ctree->GetBranch( "tgtexit_tvz" ) ) ctree->SetBranchAddress( "tgtexit_tvz", &tgtexit_tvz );
920 
921  if( ctree->GetBranch( "tgtexit_tpx" ) ) ctree->SetBranchAddress( "tgtexit_tpx", &tgtexit_tpx );
922  if( ctree->GetBranch( "tgtexit_tpy" ) ) ctree->SetBranchAddress( "tgtexit_tpy", &tgtexit_tpy );
923  if( ctree->GetBranch( "tgtexit_tpz" ) ) ctree->SetBranchAddress( "tgtexit_tpz", &tgtexit_tpz );
924 
925  if( ctree->GetBranch( "tgtexit_tptype" ) ) ctree->SetBranchAddress( "tgtexit_tptype", &tgtexit_tptype );
926  if( ctree->GetBranch( "tgtexit_tgen" ) ) ctree->SetBranchAddress( "tgtexit_tgen", &tgtexit_tgen );
927 
928  if( ctree->GetBranch( "traj_size" ) ) ctree->SetBranchAddress( "traj_size", &trArSize );
929  if( ctree->GetBranch( "traj_trkx" ) ) ctree->SetBranchAddress( "traj_trkx", traj_trkx );
930  if( ctree->GetBranch( "traj_trky" ) ) ctree->SetBranchAddress( "traj_trky", traj_trky );
931  if( ctree->GetBranch( "traj_trkz" ) ) ctree->SetBranchAddress( "traj_trkz", traj_trkz );
932  if( ctree->GetBranch( "traj_trkpx" ) ) ctree->SetBranchAddress( "traj_trkpx", traj_trkpx );
933  if( ctree->GetBranch( "traj_trkpy" ) ) ctree->SetBranchAddress( "traj_trkpy", traj_trkpy );
934  if( ctree->GetBranch( "traj_trkpz" ) ) ctree->SetBranchAddress( "traj_trkpz", traj_trkpz );
935 
936 }
937 //----------------------------------------------------------------------------
939 {
940  job = 0;
941  pots = 0.0;
942 
943  beam0x = -9999.9;
944  beam0y = -9999.9;
945  beam0z = -9999.9;
946  beamhwidth = -9999.9;
947  beamvwidth = -9999.9;
948  beamdxdz = -9999.9;
949  beamdydz = -9999.9;
950  mArSize = 0;
951 
952  for( int i = 0; i < maxC; i++ ){
953  beamsim[i] = 0;
954  physics[i] = 0;
955  physcuts[i] = 0;
956  tgtcfg[i] = 0;
957  horncfg[i] = 0;
958  dkvolcfg[i] = 0;
959  }
960 
961  for( int i = 0; i < maxArray; i++ ){
962  location_x[i] = -9999.9;
963  location_y[i] = -9999.9;
964  location_z[i] = -9999.9;
965 
966  for( int j = 0; j < maxC; j++ ){ location_name[i*maxC+j] = 0; }
967  }
968 
969  // necessary branches
970  cmeta->SetBranchAddress( "job", &job );
971  cmeta->SetBranchAddress( "pots", &pots );
972 
973  // extra branches
974  if( cmeta->GetBranch( "beamsim" ) ) cmeta->SetBranchAddress( "beamsim", beamsim );
975  if( cmeta->GetBranch( "physics" ) ) cmeta->SetBranchAddress( "physics", physics );
976  if( cmeta->GetBranch( "physcuts" ) ) cmeta->SetBranchAddress( "physcuts", physcuts );
977  if( cmeta->GetBranch( "tgtcfg" ) ) cmeta->SetBranchAddress( "tgtcfg", tgtcfg );
978  if( cmeta->GetBranch( "horncfg" ) ) cmeta->SetBranchAddress( "horncfg", horncfg );
979  if( cmeta->GetBranch( "dkvolcfg" ) ) cmeta->SetBranchAddress( "dkvolcfg", dkvolcfg );
980  if( cmeta->GetBranch( "beam0x" ) ) cmeta->SetBranchAddress( "beam0x", &beam0x );
981  if( cmeta->GetBranch( "beam0y" ) ) cmeta->SetBranchAddress( "beam0y", &beam0y );
982  if( cmeta->GetBranch( "beam0z" ) ) cmeta->SetBranchAddress( "beam0z", &beam0z );
983  if( cmeta->GetBranch( "beamhwidth" ) ) cmeta->SetBranchAddress( "beamhwidth", &beamhwidth );
984  if( cmeta->GetBranch( "beamvwidth" ) ) cmeta->SetBranchAddress( "beamvwidth", &beamvwidth );
985  if( cmeta->GetBranch( "beamdxdz" ) ) cmeta->SetBranchAddress( "beamdxdz", &beamdxdz );
986  if( cmeta->GetBranch( "beamdydz" ) ) cmeta->SetBranchAddress( "beamdydz", &beamdydz );
987  if( cmeta->GetBranch( "arSize" ) ) cmeta->SetBranchAddress( "arSize", &mArSize );
988  if( cmeta->GetBranch( "location_x" ) ) cmeta->SetBranchAddress( "location_x", location_x );
989  if( cmeta->GetBranch( "location_y" ) ) cmeta->SetBranchAddress( "location_y", location_y );
990  if( cmeta->GetBranch( "location_z" ) ) cmeta->SetBranchAddress( "location_z", location_z );
991  if( cmeta->GetBranch( "location_name" ) ) cmeta->SetBranchAddress( "location_name", location_name );
992 }
993 //----------------------------------------------------------------------------
995 {
996  TParticlePDG * pionParticle = PDGLibrary::Instance()->Find( kPdgPiP );
997  TParticlePDG * kaonParticle = PDGLibrary::Instance()->Find( kPdgKP );
998  TParticlePDG * neukParticle = PDGLibrary::Instance()->Find( kPdgK0L );
999 
1000  TObjArray * pionDecayList = pionParticle->DecayList();
1001  TObjArray * kaonDecayList = kaonParticle->DecayList();
1002  TObjArray * neukDecayList = neukParticle->DecayList();
1003 
1004  TDecayChannel * pion2muChannel = ( TDecayChannel * ) pionDecayList->At(0);
1005  TDecayChannel * pion2elChannel = ( TDecayChannel * ) pionDecayList->At(1);
1006 
1007  TDecayChannel * kaon2muChannel = ( TDecayChannel * ) kaonDecayList->At(0);
1008  //TDecayChannel * kaon2elChannel = 0; // tiny BR, not in genie_pdg_table.txt
1009  TDecayChannel * kaon3muChannel = ( TDecayChannel * ) kaonDecayList->At(5);
1010  TDecayChannel * kaon3elChannel = ( TDecayChannel * ) kaonDecayList->At(4);
1011 
1012  TDecayChannel * neuk3muChannel = ( TDecayChannel * ) neukDecayList->At(4);
1013  TDecayChannel * neuk3elChannel = ( TDecayChannel * ) neukDecayList->At(2);
1014 
1015  BR_pi2mu = pion2muChannel->BranchingRatio();
1016  BR_pi2e = pion2elChannel->BranchingRatio();
1017 
1018  BR_K2mu = kaon2muChannel->BranchingRatio();
1019  BR_K2e = 1.6e-5; // From PDG 2021
1020  BR_K3mu = kaon3muChannel->BranchingRatio();
1021  BR_K3e = kaon3elChannel->BranchingRatio();
1022 
1023  BR_K03mu = 2.0 * neuk3muChannel->BranchingRatio(); // one from K0L-->mu+ and one from -->mu-
1024  BR_K03e = 2.0 * neuk3elChannel->BranchingRatio();
1025 }
1026 //----------------------------------------------------------------------------
1027 std::map< HNLProd_t, double > FluxCreator::GetProductionProbs( int parPDG ) const
1028 {
1029  // check if we've calculated scores before
1030  switch( std::abs( parPDG ) ){
1031  case kPdgPiP : if( dynamicScores_pion.size() > 0 ) return dynamicScores_pion; break;
1032  case kPdgKP : if( dynamicScores_kaon.size() > 0 ) return dynamicScores_kaon; break;
1033  case kPdgMuon: if( dynamicScores_muon.size() > 0 ) return dynamicScores_muon; break;
1034  case kPdgK0L : if( dynamicScores_neuk.size() > 0 ) return dynamicScores_neuk; break;
1035  default: LOG( "HNL", pWARN ) << "Unknown parent. Proceeding, but results may be unphysical"; break;
1036  }
1037 
1038  std::map< HNLProd_t, double > dynScores;
1039 
1040  // first get branching ratios to SM
1041  ReadBRs();
1042  // then get HNL parameter space
1043 
1044  double Ue42 = fU4l2s.at(0);
1045  double Um42 = fU4l2s.at(1);
1046  double Ut42 = fU4l2s.at(2);
1047 
1048  // also, construct an BRCalculator * object to handle the scalings.
1049  const Algorithm * algBRCalc = AlgFactory::Instance()->GetAlgorithm("genie::hnl::BRCalculator", "Default");
1050  const BRCalculator * BRCalc = dynamic_cast< const BRCalculator * >( algBRCalc );
1051 
1052  // first get pure kinematic part of the BRs
1053  double KScale[4] = { -1.0, -1.0, -1.0, -1.0 }, mixScale[4] = { -1.0, -1.0, -1.0, -1.0 };
1054  double totalMix = 0.0;
1055  switch( std::abs( parPDG ) ){
1056  case genie::kPdgMuon:
1057  KScale[0] = BRCalc->KinematicScaling( kHNLProdMuon3Numu );
1058  KScale[1] = BRCalc->KinematicScaling( kHNLProdMuon3Nue ); // same, convenience for later
1059  KScale[2] = BRCalc->KinematicScaling( kHNLProdMuon3Nutau ); // same, convenience for later
1060  mixScale[0] = 1.0 * Um42 * KScale[0]; totalMix += mixScale[0];
1061  mixScale[1] = 1.0 * Ue42 * KScale[1]; totalMix += mixScale[1];
1062  mixScale[2] = 1.0 * Ut42 * KScale[2]; totalMix += mixScale[2];
1063 
1064  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdMuon3Numu, mixScale[0] / totalMix } ) );
1065  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdMuon3Nue, mixScale[1] / totalMix } ) );
1066  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdMuon3Nutau, mixScale[2] / totalMix } ) );
1067 
1068  // it can happen that HNL is not coupled to the only kinematically available channel.
1069  // Return bogus map if that happens
1070  if( totalMix <= 0.0 ){
1071  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdNull, -999.9 } ) );
1072  dynamicScores_muon = dynScores;
1073  return dynScores;
1074  }
1075 
1076  dynamicScores_muon = dynScores;
1077  break;
1078  case genie::kPdgKP:
1079  KScale[0] = BRCalc->KinematicScaling( kHNLProdKaon2Muon );
1080  KScale[1] = BRCalc->KinematicScaling( kHNLProdKaon2Electron );
1081  KScale[2] = BRCalc->KinematicScaling( kHNLProdKaon3Muon );
1082  KScale[3] = BRCalc->KinematicScaling( kHNLProdKaon3Electron );
1083  mixScale[0] = BR_K2mu * Um42 * KScale[0]; totalMix += mixScale[0];
1084  mixScale[1] = BR_K2e * Ue42 * KScale[1]; totalMix += mixScale[1];
1085  mixScale[2] = BR_K3mu * Um42 * KScale[2]; totalMix += mixScale[2];
1086  mixScale[3] = BR_K3e * Ue42 * KScale[3]; totalMix += mixScale[3];
1087 
1088  if( totalMix <= 0.0 ){
1089  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdNull, -999.9 } ) );
1090  dynamicScores_pion = dynScores;
1091  return dynScores;
1092  }
1093 
1094  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdKaon2Muon, mixScale[0] / totalMix } ) );
1095  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdKaon2Electron, mixScale[1] / totalMix } ) );
1096  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdKaon3Muon, mixScale[2] / totalMix } ) );
1097  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdKaon3Electron, mixScale[3] / totalMix } ) );
1098 
1099  dynamicScores_kaon = dynScores;
1100  break;
1101  case genie::kPdgPiP:
1102 
1103  KScale[0] = BRCalc->KinematicScaling( kHNLProdPion2Muon );
1104  KScale[1] = BRCalc->KinematicScaling( kHNLProdPion2Electron );
1105  mixScale[0] = BR_pi2mu * Um42 * KScale[0]; totalMix += mixScale[0];
1106  mixScale[1] = BR_pi2e * Ue42 * KScale[1]; totalMix += mixScale[1];
1107 
1108  if( totalMix <= 0.0 ){
1109  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdNull, -999.9 } ) );
1110  dynamicScores_pion = dynScores;
1111  return dynScores;
1112  }
1113 
1114  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdPion2Muon, mixScale[0] / totalMix } ) );
1115  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdPion2Electron, mixScale[1] / totalMix } ) );
1116 
1117  dynamicScores_pion = dynScores;
1118  break;
1119  case genie::kPdgK0L:
1120 
1121  KScale[0] = BRCalc->KinematicScaling( kHNLProdNeuk3Muon );
1122  KScale[1] = BRCalc->KinematicScaling( kHNLProdNeuk3Electron );
1123  mixScale[0] = BR_K03mu * Um42 * KScale[0]; totalMix += mixScale[0];
1124  mixScale[1] = BR_K03e * Ue42 * KScale[1]; totalMix += mixScale[1];
1125 
1126  if( totalMix <= 0.0 ){
1127  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdNull, -999.9 } ) );
1128  dynamicScores_neuk = dynScores;
1129  return dynScores;
1130  }
1131 
1132  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdNeuk3Muon, mixScale[0] / totalMix } ) );
1133  dynScores.insert( std::pair< HNLProd_t, double >( { kHNLProdNeuk3Electron, mixScale[1] / totalMix } ) );
1134 
1135  dynamicScores_neuk = dynScores;
1136  break;
1137  default:
1138  LOG( "HNL", pERROR )
1139  << "Unknown parent particle. Cannot make scales, exiting."; exit(1);
1140  }
1141 
1142  LOG( "HNL", pDEBUG )
1143  << "Score map now has " << dynScores.size() << " elements. Returning.";
1144  return dynScores;
1145 
1146 }
1147 //----------------------------------------------------------------------------
1148 TLorentzVector FluxCreator::HNLEnergy( HNLProd_t hnldm, TLorentzVector p4par ) const
1149 {
1150  // first boost to parent rest frame
1151  TLorentzVector p4par_rest = p4par;
1152  TVector3 boost_beta = p4par.BoostVector();
1153  p4par_rest.Boost( -boost_beta );
1154 
1155  LOG( "HNL", pDEBUG )
1156  << "Attempting to decay rest-system p4 = " << utils::print::P4AsString(&p4par_rest)
1157  << " as " << utils::hnl::ProdAsString( hnldm );
1158 
1159  // get PDGCodeList and truncate 1st member
1160  PDGCodeList fullList = utils::hnl::ProductionProductList( hnldm );
1161  bool allow_duplicate = true;
1162  PDGCodeList decayList( allow_duplicate );
1163  double * mass = new double[decayList.size()];
1164  double sum = 0.0;
1165 
1166  for( std::vector<int>::const_iterator pdg_iter = fullList.begin(); pdg_iter != fullList.end(); ++pdg_iter )
1167  {
1168  if( pdg_iter != fullList.begin() ){
1169  int pdgc = *pdg_iter;
1170  decayList.push_back( pdgc );
1171  }
1172  }
1173 
1174  int iv = 0;
1175  for( std::vector<int>::const_iterator pdg_iter = decayList.begin(); pdg_iter != decayList.end(); ++pdg_iter )
1176  {
1177  int pdgc = *pdg_iter;
1178  double m = PDGLibrary::Instance()->Find(pdgc)->Mass();
1179  mass[iv++] = m; sum += m;
1180  }
1181 
1182  // Set the decay
1183  TGenPhaseSpace fPhaseSpaceGenerator;
1184  bool permitted = fPhaseSpaceGenerator.SetDecay( p4par_rest, decayList.size(), mass );
1185  if(!permitted) {
1186  LOG("HNL", pERROR)
1187  << " *** Phase space decay is not permitted \n"
1188  << " Total particle mass = " << sum << "\n"
1189  << " Decaying system p4 = " << utils::print::P4AsString(&p4par_rest);
1190  // clean-up
1191  delete [] mass;
1192  // throw exception
1194  exception.SetReason("Decay not permitted kinematically");
1195  exception.SwitchOnFastForward();
1196  throw exception;
1197  }
1198 
1199  // Get the maximum weight
1200  double wmax = -1;
1201  for(int idec=0; idec<200; idec++) {
1202  double w = fPhaseSpaceGenerator.Generate();
1203  wmax = TMath::Max(wmax,w);
1204  }
1205  assert(wmax>0);
1206  wmax *= 2;
1207 
1208  LOG("HNL", pNOTICE)
1209  << "Max phase space gen. weight @ current HNL system: " << wmax;
1210 
1211  // Generate an unweighted decay
1212  RandomGen * rnd = RandomGen::Instance();
1213 
1214  bool accept_decay=false;
1215  unsigned int itry=0;
1216  while(!accept_decay)
1217  {
1218  itry++;
1219 
1221  // report, clean-up and return
1222  LOG("HNL", pWARN)
1223  << "Couldn't generate an unweighted phase space decay after "
1224  << itry << " attempts";
1225  // clean up
1226  delete [] mass;
1227  // throw exception
1229  exception.SetReason("Couldn't select decay after N attempts");
1230  exception.SwitchOnFastForward();
1231  throw exception;
1232  }
1233  double w = fPhaseSpaceGenerator.Generate();
1234  if(w > wmax) {
1235  LOG("HNL", pWARN)
1236  << "Decay weight = " << w << " > max decay weight = " << wmax;
1237  }
1238  double gw = wmax * rnd->RndHadro().Rndm();
1239  accept_decay = (gw<=w);
1240 
1241  LOG("HNL", pINFO)
1242  << "Decay weight = " << w << " / R = " << gw
1243  << " - accepted: " << accept_decay;
1244 
1245  } //!accept_decay
1246 
1247  // Grab 0th entry energy and return that
1248  int idp = 0; TLorentzVector p4HNL, p4HNL_rest;
1249  // search for the charged lepton in this stack, get the 4-vector in parent rest frame
1250  TLorentzVector p4Lep, p4Lep_HNLRest;
1251 
1252  p4HNL.SetPxPyPzE( 0.0, 0.0, 0.0, 0.0 );
1253  p4HNL_rest.SetPxPyPzE( 0.0, 0.0, 0.0, 0.0 );
1254  p4Lep.SetPxPyPzE( 0.0, 0.0, 0.0, 0.0 );
1255  p4Lep_HNLRest.SetPxPyPzE( 0.0, 0.0, 0.0, 0.0 );
1256 
1257  for(std::vector<int>::const_iterator pdg_iter = decayList.begin(); pdg_iter != decayList.end(); ++pdg_iter) {
1258  int pdgc = *pdg_iter;
1259  TLorentzVector * p4fin = fPhaseSpaceGenerator.GetDecay(idp);
1260 
1261  if( std::abs( pdgc ) == kPdgHNL ) p4HNL = *p4fin;
1262  if( std::abs( pdgc ) == kPdgElectron ||
1263  std::abs( pdgc ) == kPdgMuon ||
1264  std::abs( pdgc ) == kPdgTau ){
1265  p4Lep = *p4fin;
1266  fLepPdg = pdgc;
1267  }
1268  idp++;
1269  }
1270 
1271  if( doPol ){
1272  // boost this to HNL rest frame.
1273  TVector3 boostHNL = p4HNL.BoostVector();
1274  p4Lep_HNLRest = p4Lep; fLPE = p4Lep_HNLRest.E(); // still in parent rest frame here
1275  p4Lep_HNLRest.Boost( boostHNL );
1276  // save this
1277  fLPx = ( fixPol ) ? fFixedPolarisation.at(0) : p4Lep.Px() / p4Lep.P(); // note that this is for a true random decay.
1278  fLPy = ( fixPol ) ? fFixedPolarisation.at(1) : p4Lep.Py() / p4Lep.P(); // We still need to take the geometrical
1279  fLPz = ( fixPol ) ? fFixedPolarisation.at(2) : p4Lep.Pz() / p4Lep.P(); // constraint into account.
1280  } else {
1281  fLPx = 0.0;
1282  fLPy = 0.0;
1283  fLPz = 0.0;
1284  }
1285 
1286  delete [] mass;
1287  return p4HNL; // rest frame momentum!
1288 }
1289 //----------------------------------------------------------------------------
1291 {
1292  RandomGen * rnd = RandomGen::Instance();
1293  /*
1294  double ox = fCx + fDetOffset.at(0), oy = fCy + fDetOffset.at(1), oz = fCz + fDetOffset.at(2); // NEAR, m
1295  */
1296  double ox = fCx, oy = fCy, oz = fCz; // NEAR, m
1297 
1298  double rx = (rnd->RndGen()).Uniform( -fLx/2.0, fLx/2.0 ),
1299  ry = (rnd->RndGen()).Uniform( -fLy/2.0, fLy/2.0 ),
1300  rz = (rnd->RndGen()).Uniform( -fLz/2.0, fLz/2.0 ); // USER, m
1301 
1302  double ux = (rx + fDetOffset.at(0)) * units::m / units::cm;
1303  double uy = (ry + fDetOffset.at(1)) * units::m / units::cm;
1304  double uz = (rz + fDetOffset.at(2)) * units::m / units::cm;
1305  TVector3 checkPoint( ux, uy, uz ); // USER, cm
1306 
1307  TVector3 originPoint( -ox, -oy, -oz );
1308  TVector3 dumori( 0.0, 0.0, 0.0 );
1309  if( !fDoingOldFluxCalc ){
1310  // user-coordinates of this point. [cm]
1311  //double ux = rx - ox, uy = ry - oy, uz = rz - oz;
1312 
1313  LOG( "HNL", pDEBUG )
1314  << "\nChecking point " << utils::print::Vec3AsString(&checkPoint) << " [m, user]";
1315 
1316  // check if the point is inside the geometry, otherwise do it again
1317  std::string pathString = this->CheckGeomPoint( ux, uy, uz ); int iNode = 1; // 1 past beginning
1318  int iBad = 0;
1319  while( pathString.find( "/", iNode ) == string::npos && iBad < 10 ){
1320  rx = (rnd->RndGen()).Uniform( -fLx/2.0, fLx/2.0 ); ux = (rx + fDetOffset.at(0)) * units::m / units::cm;
1321  ry = (rnd->RndGen()).Uniform( -fLy/2.0, fLy/2.0 ); uy = (ry + fDetOffset.at(1)) * units::m / units::cm;
1322  rz = (rnd->RndGen()).Uniform( -fLz/2.0, fLz/2.0 ); uz = (rz + fDetOffset.at(2)) * units::m / units::cm;
1323  checkPoint.SetXYZ( ux, uy, uz );
1324  pathString = this->CheckGeomPoint( ux, uy, uz ); iNode = 1;
1325  iBad++;
1326  }
1327  assert( pathString.find( "/", iNode ) != string::npos );
1328  }
1329 
1330  // turn u back into [m] from [cm]
1331  ux *= units::cm / units::m; uy *= units::cm / units::m; uz *= units::cm / units::m;
1332  // return the absolute point in space [NEAR, m] that we're pointing to!
1333  checkPoint.SetXYZ( ux, uy, uz );
1334  checkPoint = this->ApplyUserRotation( checkPoint, dumori, fDetRotation, true ); // det --> tgt-hall
1335  checkPoint.SetXYZ( checkPoint.X() + ox, checkPoint.Y() + oy, checkPoint.Z() + oz );
1336 
1337  TVector3 vec( ux, uy, uz ); // USER m
1338  LOG( "HNL", pDEBUG )
1339  << "\nPointing to this point in BBox (USER coords): " << utils::print::Vec3AsString( &vec ) << "[m]"
1340  << "\nIn NEAR coords this is " << utils::print::Vec3AsString( &checkPoint ) << "[m]";
1341 
1342  // update bookkeeping
1343  fTargetPoint = checkPoint;
1344 
1345  return checkPoint;
1346 }
1347 //----------------------------------------------------------------------------
1348 double FluxCreator::GetAngDeviation( TLorentzVector p4par, TVector3 detO, bool seekingMax ) const
1349 {
1350  TVector3 ppar = p4par.Vect(); assert( ppar.Mag() > 0.0 );
1351  TVector3 pparUnit = ppar.Unit();
1352  // let face be planar and perpendicular to vector Q
1353  // assuming Q = ( 0, 0, 1 ) == face perpendicular to z
1354  double Qx = 0.0, Qy = 0.0, Qz = 1.0;
1355  // plane: Qx . (x-xC) + Qy . (y-yC) + Qz . (z-zC) = 0
1356  // line: r(t) - r(D) = t * ppar
1357  // V0 \in plane && line
1358  // ==> Qx * ( x(t) - x(C) ) + Qy * ( y(t) - y(C) ) + Qz * ( z(t) - z(C) ) = 0
1359  // ==> t = ( \vec{Q} \cdot \vec{detO} ) / ( \vec{Q} \cdot \vec{ppar} )
1360  double nterm = Qx * detO.X() + Qy * detO.Y() + Qz * detO.Z();
1361  double dterm = Qx * ppar.X() + Qy * ppar.Y() + Qz * ppar.Z();
1362  double t = nterm / dterm;
1363  double x_incp = t * pparUnit.X(), y_incp = t * pparUnit.Y(), z_incp = t * pparUnit.Z();
1364 
1365  // sweep over plane perp to ppar, passing through centre, and calc intersection point
1366  // special case: parent is perfectly on axis so hits detector centre
1367  TVector3 IPdev( detO.X() - x_incp, detO.Y() - y_incp, detO.Z() - z_incp );
1368  bool parentHitsCentre = ( IPdev.Mag() < controls::kASmallNum );
1369 
1370  // see assumption about Q
1371  // to fix probably with a rotation of fLx, fLy by Euler angles onto Q-plane?
1372  // line: r(t) - r(incp) = t * IPdev
1373  // assume square face
1374  double ttx = ( IPdev.X() != 0.0 ) ? fLx / std::abs( IPdev.X() ) : 99999.9;
1375  double tty = ( IPdev.Y() != 0.0 ) ? fLy / std::abs( IPdev.Y() ) : 99999.9;
1376  double tt = std::max( ttx, tty ); // this defines how much the least sweep goes
1377  TVector3 atilde( tt * IPdev.X(), tt * IPdev.Y(), tt * IPdev.Z() );
1378 
1379  double fLT = IPdev.Mag();
1380  double dist = atilde.Mag();
1381 
1382  assert( fLT > 0.0 );
1383  double detRadius = std::max( fLx, fLy ) / 2.0;
1384 
1385  if( parentHitsCentre ){
1386  // calculate angles for four points and return largest (smallest) of them
1387 
1388  // randomly select a phi to go with, make 2 perpendicular vectors from it
1389  double phi = ( RandomGen::Instance()->RndGen() ).Uniform( 0., 2.0 * constants::kPi );
1390  TVector3 r1VecPrim( -pparUnit.Y(), pparUnit.X(), 0.0 ); // perp to ppar == on plane
1391  TVector3 r1Vec = r1VecPrim.Unit();
1392  r1Vec.Rotate( phi, pparUnit );
1393  TVector3 r2Vec( r1Vec ); r2Vec.Rotate( 0.5 * constants::kPi, pparUnit );
1394 
1395  double rprod = r1Vec.X() * r2Vec.X() + r1Vec.Y() * r2Vec.Y() + r1Vec.Z() * r2Vec.Z();
1396 
1397  assert( std::abs( rprod ) < controls::kASmallNum );
1398 
1399  // four IP with det. All have distance detRadius from centre.
1400  TVector3 p1( detO.X() + detRadius*r1Vec.X(),
1401  detO.Y() + detRadius*r1Vec.Y(),
1402  detO.Z() + detRadius*r1Vec.Z() );
1403  TVector3 p2( detO.X() - detRadius*r1Vec.X(),
1404  detO.Y() - detRadius*r1Vec.Y(),
1405  detO.Z() - detRadius*r1Vec.Z() );
1406  TVector3 p3( detO.X() + detRadius*r2Vec.X(),
1407  detO.Y() + detRadius*r2Vec.Y(),
1408  detO.Z() + detRadius*r2Vec.Z() );
1409  TVector3 p4( detO.X() - detRadius*r2Vec.X(),
1410  detO.Y() - detRadius*r2Vec.Y(),
1411  detO.Z() - detRadius*r2Vec.Z() );
1412 
1413  // Return largest(smallest) angle using inner product magic
1414  double thLarge = -999.9; double thSmall = 999.9;
1415  double th1 = TMath::ACos( ( p1.X()*pparUnit.X() + p1.Y()*pparUnit.Y() + p1.Z()*pparUnit.Z() ) / p1.Mag() ); if( th1 > thLarge ){ thLarge = th1; } else if( th1 < thSmall ){ thSmall = th1; }
1416  double th2 = TMath::ACos( ( p2.X()*pparUnit.X() + p2.Y()*pparUnit.Y() + p2.Z()*pparUnit.Z() ) / p2.Mag() ); if( th2 > thLarge ){ thLarge = th2; } else if( th2 < thSmall ){ thSmall = th2; }
1417  double th3 = TMath::ACos( ( p3.X()*pparUnit.X() + p3.Y()*pparUnit.Y() + p3.Z()*pparUnit.Z() ) / p3.Mag() ); if( th3 > thLarge ){ thLarge = th3; } else if( th3 < thSmall ){ thSmall = th3; }
1418  double th4 = TMath::ACos( ( p4.X()*pparUnit.X() + p4.Y()*pparUnit.Y() + p4.Z()*pparUnit.Z() ) / p4.Mag() ); if( th4 > thLarge ){ thLarge = th4; } else if( th4 < thSmall ){ thSmall = th4; }
1419 
1420  return ( seekingMax ) ? thLarge * 180.0 / constants::kPi : thSmall * 180.0 / constants::kPi;
1421  } else {
1422  // find direction from IP to det centre.
1423  TVector3 rVec = IPdev.Unit();
1424  // two IP with det. Closer(farther) has distance detRadius -(+) d( IP, detO )
1425  // actually, if IPdev endpoint lies inside detector have to go other way
1426  double dh = fLT + dist, dl = fLT - dist;
1427  // get those vectors and do inner product magic
1428  TVector3 ph( x_incp + dh * rVec.X(), y_incp + dh * rVec.Y(), z_incp + dh * rVec.Z() );
1429  TVector3 pl( x_incp - dl * rVec.X(), y_incp - dl * rVec.Y(), z_incp - dl * rVec.Z() );
1430  double thh = TMath::ACos( ( ph.X()*pparUnit.X() + ph.Y()*pparUnit.Y() + ph.Z()*pparUnit.Z() ) / ph.Mag() );
1431  double thl = TMath::ACos( ( pl.X()*pparUnit.X() + pl.Y()*pparUnit.Y() + pl.Z()*pparUnit.Z() ) / pl.Mag() );
1432 
1433  return ( seekingMax ) ? thh * 180.0 / constants::kPi : thl * 180.0 / constants::kPi;
1434  }
1435 
1436  LOG( "HNL", pERROR )
1437  << "Could not calculate the angle range for detector at ( " << detO.X() << ", "
1438  << detO.Y() << ", " << detO.Z() << " ) [m] from HNL production point with parent momentum = ( "
1439  << ppar.X() << ", " << ppar.Y() << ", " << ppar.Z() << " ) [GeV]. Returning zero.";
1440  return 0.0;
1441 }
1442 //----------------------------------------------------------------------------
1443 void FluxCreator::GetAngDeviation( TLorentzVector p4par, TVector3 detO, double &zm, double &zp ) const
1444 {
1445  // implementation of GetAngDeviation that uses ROOT geometry. More robust than analytical geom
1446  // (fewer assumptions about detector position)
1447 
1448  TVector3 ppar = p4par.Vect(); assert( ppar.Mag() > 0.0 );
1449  TVector3 pparUnit = ppar.Unit();
1450 
1451  const double sx1 = TMath::Sin(fBx1), cx1 = TMath::Cos(fBx1);
1452  const double sz = TMath::Sin(fBz), cz = TMath::Cos(fBz);
1453  const double sx2 = TMath::Sin(fBx2), cx2 = TMath::Cos(fBx2);
1454 
1455  const double xun[3] = { cz, -cx1*sz, sx1*sz };
1456  const double yun[3] = { sz*cx2, cx1*cz*cx2 - sx1*sx2, -sx1*cz*cx2 - cx1*sx2 };
1457  const double zun[3] = { sz*sx2, cx1*cz*sx2 + sx1*cx2, -sx1*cz*sx2 + cx1*cx2 };
1458 
1459  LOG("HNL", pDEBUG)
1460  << "\nxun = ( " << xun[0] << ", " << xun[1] << ", " << xun[2] << " )"
1461  << "\nyun = ( " << yun[0] << ", " << yun[1] << ", " << yun[2] << " )"
1462  << "\nzun = ( " << zun[0] << ", " << zun[1] << ", " << zun[2] << " )";
1463 
1464  /*
1465  TVector3 detO_cm( (detO.X() + fDetOffset.at(0)) * units::m / units::cm,
1466  (detO.Y() + fDetOffset.at(1)) * units::m / units::cm,
1467  (detO.Z() + fDetOffset.at(2)) * units::m / units::cm );
1468  */
1469  TVector3 detO_cm( (detO.X()) * units::m / units::cm,
1470  (detO.Y()) * units::m / units::cm,
1471  (detO.Z()) * units::m / units::cm );
1472  double inProd = zun[0] * detO_cm.X() + zun[1] * detO_cm.Y() + zun[2] * detO_cm.Z(); // cm
1473  assert( pparUnit.X() * zun[0] + pparUnit.Y() * zun[1] + pparUnit.Z() * zun[2] != 0.0 );
1474  inProd /= ( pparUnit.X() * zun[0] + pparUnit.Y() * zun[1] + pparUnit.Z() * zun[2] );
1475 
1476  // using vector formulation, find point of closest approach between parent momentum from
1477  // parent decay point, and detector centre.
1478 
1479  TVector3 dumori(0.0, 0.0, 0.0); // tgt-hall frame origin is 0
1480  TVector3 detori( (fDetOffset.at(0)) * units::m / units::cm,
1481  (fDetOffset.at(1)) * units::m / units::cm,
1482  (fDetOffset.at(2)) * units::m / units::cm ); // for rotations of the detector
1483 
1484  // Do all this in NEAR coords and m to avoid ambiguity.
1485 
1486  TVector3 fCvec_near( fCx, fCy, fCz ); // NEAR m
1487  TVector3 fDvec( fDx, fDy, fDz ); // in BEAM coords, m
1488  TVector3 fDvec_beam = this->ApplyUserRotation( fDvec, true ); // in NEAR coords, m
1489  TVector3 detO_near( fCvec_near.X() - fDvec_beam.X(),
1490  fCvec_near.Y() - fDvec_beam.Y(),
1491  fCvec_near.Z() - fDvec_beam.Z() );
1492  TVector3 detO_user = this->ApplyUserRotation( detO_near, dumori, fDetRotation, false ); // tgt-hall --> det
1493 
1494  const double aConst[3] = { fDvec_beam.X(), fDvec_beam.Y(), fDvec_beam.Z() };
1495  const double aConstUser[3] = { -detO_user.X(), -detO_user.Y(), -detO_user.Z() };
1496  /*
1497  const double dConst[3] = { fCx + fDetOffset.at(0), fCy + fDetOffset.at(1), fCz + fDetOffset.at(2) };
1498  */
1499  const double dConst[3] = { fCx, fCy, fCz };
1500  const double nConst[3] = { pparUnit.X(), pparUnit.Y(), pparUnit.Z() };
1501 
1502  // formula for POCA is \vec{a} + \vec{n} * ( (\vec{d} - \vec{a}) \cdot \vec{n} )
1503  const double nMult = nConst[0] * (dConst[0] - aConst[0]) +
1504  nConst[1] * (dConst[1] - aConst[1]) +
1505  nConst[2] * (dConst[2] - aConst[2]);
1506 
1507  // don't use the actual POCA, force calculation from that point V0 such that z(V0) = z(C)
1508  // and V0 lies on line joining decay point and POCA
1509 
1510  const double POCA_m[3] = { aConst[0] + nMult * nConst[0],
1511  aConst[1] + nMult * nConst[1],
1512  aConst[2] + nMult * nConst[2] }; // NEAR
1513  /*
1514  const double zConstMult = ((fCz + fDetOffset.at(2)) - aConst[2]) / (POCA_m[2] - aConst[2]);
1515  */
1516  const double zConstMult = ((fCz) - aConst[2]) / (POCA_m[2] - aConst[2]);
1517  const double startPoint_m[3] = { aConst[0] + nMult * zConstMult * nConst[0],
1518  aConst[1] + nMult * zConstMult * nConst[1],
1519  aConst[2] + nMult * zConstMult * nConst[2] }; // NEAR
1520 
1521  LOG( "HNL", pDEBUG )
1522  << "\ndetO_cm = " << utils::print::Vec3AsString( &detO_cm )
1523  << "\npparUnit = " << utils::print::Vec3AsString( &pparUnit );
1524 
1525  const double startPoint[3] = { startPoint_m[0] * units::m / units::cm,
1526  startPoint_m[1] * units::m / units::cm,
1527  startPoint_m[2] * units::m / units::cm }; // NEAR, cm
1528 
1529  /*
1530  const double sweepVect[3] = { (fCx + fDetOffset.at(0)) * units::m / units::cm - startPoint[0],
1531  (fCy + fDetOffset.at(1)) * units::m / units::cm - startPoint[1],
1532  (fCz + fDetOffset.at(2)) * units::m / units::cm - startPoint[2] }; // NEAR, cm
1533  */
1534  const double sweepVect[3] = { (fCx) * units::m / units::cm - startPoint[0],
1535  (fCy) * units::m / units::cm - startPoint[1],
1536  (fCz) * units::m / units::cm - startPoint[2] }; // NEAR, cm
1537  const double swvMag = std::sqrt( sweepVect[0]*sweepVect[0] + sweepVect[1]*sweepVect[1] + sweepVect[2]*sweepVect[2] ); assert( swvMag > 0.0 );
1538 
1539  // Note the geometry manager works in the *detector frame*. Transform to that.
1540  /*
1541  TVector3 detStartPoint( startPoint[0] - (fCx + fDetOffset.at(0)) * units::m / units::cm,
1542  startPoint[1] - (fCy + fDetOffset.at(1)) * units::m / units::cm,
1543  startPoint[2] - (fCz + fDetOffset.at(2)) * units::m / units::cm );
1544  */
1545  TVector3 detStartPoint( startPoint[0] - (fCx) * units::m / units::cm,
1546  startPoint[1] - (fCy) * units::m / units::cm,
1547  startPoint[2] - (fCz) * units::m / units::cm );
1548  TVector3 detSweepVect( sweepVect[0], sweepVect[1], sweepVect[2] );
1549 
1550  TVector3 detPpar = ppar;
1551 
1552  LOG( "HNL", pDEBUG )
1553  << "\nStartPoint = " << utils::print::Vec3AsString( &detStartPoint )
1554  << "\nSweepVect = " << utils::print::Vec3AsString( &detSweepVect )
1555  << "\nparent p3 = " << utils::print::Vec3AsString( &detPpar );
1556 
1557  detStartPoint = this->ApplyUserRotation( detStartPoint, detori, fDetRotation, false ); // passive transformation
1558  detSweepVect = this->ApplyUserRotation( detSweepVect, dumori, fDetRotation, true );
1559  detPpar = this->ApplyUserRotation( detPpar, dumori, fDetRotation, true );
1560 
1561  // first check that detStartPoint is not already in the detector! If it is, we should flag this now.
1562  std::string detPathString = this->CheckGeomPoint( detStartPoint.X(), detStartPoint.Y(), detStartPoint.Z() ); int iDNode = 1; // 1 past beginning
1563  bool startsInsideDet = ( detPathString.find("/", iDNode) != string::npos );
1564 
1565  TLorentzVector detPpar_4v( detPpar.X(), detPpar.Y(), detPpar.Z(), p4par.E() );
1566 
1567  // now sweep along sweepVect until we hit either side of the detector.
1568  // This will give us two points in space
1569 
1570  double minusPoint[3] = { 0.0, 0.0, 0.0 }; double plusPoint[3] = { 0.0, 0.0, 0.0 }; // USER, cm
1571  if( startsInsideDet ){
1572  minusPoint[0] = (gGeoManager->GetCurrentPoint())[0];
1573  minusPoint[1] = (gGeoManager->GetCurrentPoint())[1];
1574  minusPoint[2] = (gGeoManager->GetCurrentPoint())[2];
1575  }
1576 
1577  gGeoManager->SetCurrentPoint( detStartPoint.X(), detStartPoint.Y(), detStartPoint.Z() );
1578  gGeoManager->SetCurrentDirection( detSweepVect.X() / swvMag, detSweepVect.Y() / swvMag, detSweepVect.Z() / swvMag );
1579  const double sStepSize = 0.05 * std::min( std::min( fLx, fLy ), fLz );
1580 
1581  // start stepping. Let's do this manually cause FindNextBoundaryAndStep() can be finicky.
1582  // if start inside detector, then only find exit point, otherwise find entry point.
1583  if( startsInsideDet ){ // only find exit
1584 
1585  // to avoid large time inefficiencies just go to the edge of the box and step back by 10% of the size
1586  // this is *roughly correct* if not hugely correct
1587 
1588  double currx = (gGeoManager->GetCurrentPoint())[0];
1589  double curry = (gGeoManager->GetCurrentPoint())[1];
1590  double currz = (gGeoManager->GetCurrentPoint())[2];
1591  double currDist = std::sqrt( currx*currx + curry*curry + currz*currz ); // cm
1592 
1593  double curdx = (gGeoManager->GetCurrentDirection())[0];
1594  double curdy = (gGeoManager->GetCurrentDirection())[1];
1595  double curdz = (gGeoManager->GetCurrentDirection())[2];
1596 
1597  double stepMod = 0.99;
1598  double boxSize = std::sqrt( fLxR*fLxR + fLyR*fLyR + fLzR*fLzR )/2.0; // m
1599  double halfBoxSize = boxSize/2.0;
1600  double desiredDist = stepMod * halfBoxSize * units::m / units::cm; // cm
1601 
1602  // now we're at distance d on the one side of our detector centre
1603  // we want to get to distance D on the other side of our detector centre
1604  // meaning we should step by d + D
1605  double largeStep = currDist + desiredDist;
1606 
1607  gGeoManager->SetCurrentPoint( currx + largeStep * curdx,
1608  curry + largeStep * curdy,
1609  currz + largeStep * curdz );
1610 
1611  /* // this is the very correct, very slow way of doing it
1612  while( detPathString.find("/", iDNode) != string::npos ){
1613  gGeoManager->SetCurrentPoint( (gGeoManager->GetCurrentPoint())[0] + (gGeoManager->GetCurrentDirection())[0] * sStepSize,
1614  (gGeoManager->GetCurrentPoint())[1] + (gGeoManager->GetCurrentDirection())[1] * sStepSize,
1615  (gGeoManager->GetCurrentPoint())[2] + (gGeoManager->GetCurrentDirection())[2] * sStepSize );
1616  detPathString = this->CheckGeomPoint( (gGeoManager->GetCurrentPoint())[0], (gGeoManager->GetCurrentPoint())[1], (gGeoManager->GetCurrentPoint())[2] );
1617  }
1618  */
1619 
1620  plusPoint[0] = (gGeoManager->GetCurrentPoint())[0];
1621  plusPoint[1] = (gGeoManager->GetCurrentPoint())[1];
1622  plusPoint[2] = (gGeoManager->GetCurrentPoint())[2];
1623  } else { // find entry and exit
1624  // first, entry
1625 
1626  // to avoid large time inefficiencies just go to the edge of the box and step back by 10% of the size
1627  // this is *roughly correct* if not hugely correct
1628 
1629  // find that point that lies on the line along direction from current point that is 90% of box size from box centre
1630  double currx = (gGeoManager->GetCurrentPoint())[0];
1631  double curry = (gGeoManager->GetCurrentPoint())[1];
1632  double currz = (gGeoManager->GetCurrentPoint())[2];
1633  double currDist = std::sqrt( currx*currx + curry*curry + currz*currz );
1634 
1635  double stepMod = 0.99;
1636  double boxSize = std::sqrt( fLxR*fLxR + fLyR*fLyR + fLzR*fLzR )/2.0; // m
1637  double halfBoxSize = boxSize / 2.0;
1638  double desiredDist = stepMod * halfBoxSize * units::m / units::cm;
1639 
1640  // we're at some distance D and want to get to distance d, which means we step forward by
1641  // R = D-d = D(1-d/D)
1642  double largeStep = currDist - desiredDist;
1643 
1644  double curdx = (gGeoManager->GetCurrentDirection())[0];
1645  double curdy = (gGeoManager->GetCurrentDirection())[1];
1646  double curdz = (gGeoManager->GetCurrentDirection())[2];
1647 
1648  gGeoManager->SetCurrentPoint( currx + largeStep * curdx,
1649  curry + largeStep * curdy,
1650  currz + largeStep * curdz );
1651 
1652  /* // slow but correct
1653  while( detPathString.find("/", iDNode) == string::npos ){
1654  gGeoManager->SetCurrentPoint( (gGeoManager->GetCurrentPoint())[0] + (gGeoManager->GetCurrentDirection())[0] * sStepSize,
1655  (gGeoManager->GetCurrentPoint())[1] + (gGeoManager->GetCurrentDirection())[1] * sStepSize,
1656  (gGeoManager->GetCurrentPoint())[2] + (gGeoManager->GetCurrentDirection())[2] * sStepSize );
1657  detPathString = this->CheckGeomPoint( (gGeoManager->GetCurrentPoint())[0], (gGeoManager->GetCurrentPoint())[1], (gGeoManager->GetCurrentPoint())[2] );
1658  }
1659  */
1660 
1661  minusPoint[0] = (gGeoManager->GetCurrentPoint())[0];
1662  minusPoint[1] = (gGeoManager->GetCurrentPoint())[1];
1663  minusPoint[2] = (gGeoManager->GetCurrentPoint())[2];
1664 
1665  // then, exit
1666 
1667  // quick and dirty way: reflect about the origin
1668 
1669  currx = (gGeoManager->GetCurrentPoint())[0];
1670  curry = (gGeoManager->GetCurrentPoint())[1];
1671  currz = (gGeoManager->GetCurrentPoint())[2];
1672  /*
1673  double cdx = fDetOffset.at(0) * units::m / units::cm - currx; // cm
1674  double cdy = fDetOffset.at(1) * units::m / units::cm - curry; // cm
1675  double cdz = fDetOffset.at(2) * units::m / units::cm - currz; // cm
1676 
1677  gGeoManager->SetCurrentPoint( fDetOffset.at(0) * units::m / units::cm + cdx,
1678  fDetOffset.at(1) * units::m / units::cm + cdy,
1679  fDetOffset.at(2) * units::m / units::cm + cdz );
1680  */
1681 
1682  gGeoManager->SetCurrentPoint( -currx, -curry, -currz );
1683 
1684  /* // slow but correct
1685  while( detPathString.find("/", iDNode) != string::npos ){
1686  gGeoManager->SetCurrentPoint( (gGeoManager->GetCurrentPoint())[0] + (gGeoManager->GetCurrentDirection())[0] * sStepSize,
1687  (gGeoManager->GetCurrentPoint())[1] + (gGeoManager->GetCurrentDirection())[1] * sStepSize,
1688  (gGeoManager->GetCurrentPoint())[2] + (gGeoManager->GetCurrentDirection())[2] * sStepSize );
1689  detPathString = this->CheckGeomPoint( (gGeoManager->GetCurrentPoint())[0], (gGeoManager->GetCurrentPoint())[1], (gGeoManager->GetCurrentPoint())[2] );
1690  }
1691  */
1692 
1693  plusPoint[0] = (gGeoManager->GetCurrentPoint())[0];
1694  plusPoint[1] = (gGeoManager->GetCurrentPoint())[1];
1695  plusPoint[2] = (gGeoManager->GetCurrentPoint())[2];
1696  }
1697 
1698  /*
1699  int bdIdx = 0; const int bdIdxMax = 1e+4;
1700  if(!startsInsideDet){
1701  while( gGeoManager->FindNextBoundaryAndStep() && bdIdx < bdIdxMax ){
1702  bdIdx++;
1703  if( bdIdx % 100 == 0 )
1704  LOG( "HNL", pDEBUG ) << "bdIdx = " << bdIdx;
1705  if( std::abs( (gGeoManager->GetCurrentPoint())[0] ) != fLxR/2.0 * units::m / units::cm &&
1706  std::abs( (gGeoManager->GetCurrentPoint())[1] ) != fLyR/2.0 * units::m / units::cm &&
1707  std::abs( (gGeoManager->GetCurrentPoint())[2] ) != fLzR/2.0 * units::m / units::cm ){
1708  plusPoint[0] = (gGeoManager->GetCurrentPoint())[0];
1709  plusPoint[1] = (gGeoManager->GetCurrentPoint())[1];
1710  plusPoint[2] = (gGeoManager->GetCurrentPoint())[2];
1711  }
1712  }
1713  }
1714  */
1715 
1716  TVector3 originPoint( -(fCx + fDetOffset.at(0)), -(fCy + fDetOffset.at(1)), -(fCz + fDetOffset.at(2)) );
1717 
1718  TVector3 vMUser( minusPoint[0] * units::cm / units::m,
1719  minusPoint[1] * units::cm / units::m,
1720  minusPoint[2] * units::cm / units::m ); // USER, m
1721  TVector3 vPUser( plusPoint[0] * units::cm / units::m,
1722  plusPoint[1] * units::cm / units::m,
1723  plusPoint[2] * units::cm / units::m ); // USER, m
1724 
1725  // rotate to NEAR frame
1726  TVector3 vMNear = this->ApplyUserRotation( vMUser, originPoint, fDetRotation, true ); // det --> tgt-hall
1727  /*
1728  vMNear.SetXYZ( vMNear.X() + (fCx + fDetOffset.at(0)),
1729  vMNear.Y() + (fCy + fDetOffset.at(1)),
1730  vMNear.Z() + (fCz + fDetOffset.at(2)) );
1731  */
1732  vMNear.SetXYZ( vMNear.X() + (fCx),
1733  vMNear.Y() + (fCy),
1734  vMNear.Z() + (fCz) );
1735  TVector3 vPNear = this->ApplyUserRotation( vPUser, originPoint, fDetRotation, true ); // det --> tgt-hall
1736  /*
1737  vPNear.SetXYZ( vPNear.X() + (fCx + fDetOffset.at(0)),
1738  vPNear.Y() + (fCy + fDetOffset.at(1)),
1739  vPNear.Z() + (fCz + fDetOffset.at(2)) );
1740  */
1741  vPNear.SetXYZ( vPNear.X() + (fCx),
1742  vPNear.Y() + (fCy),
1743  vPNear.Z() + (fCz) );
1744 
1745  // with 3 points and 1 vector we calculate the angles.
1746  // Points: D(decay), E(entry), X(exit) [all in local, cm]. Vector: detPpar [local, GeV/GeV]
1747  // angles are <DE, detPpar> and <DX, detPpar>
1748 
1749  // now obtain the angles themselves and return in deg.
1750  /*
1751  TVector3 decayPoint_user( (fDx - (fCx + fDetOffset.at(0))) * units::m / units::cm,
1752  (fDy - (fCy + fDetOffset.at(1))) * units::m / units::cm,
1753  (fDz - (fCz + fDetOffset.at(2))) * units::m / units::cm ); // USER, cm
1754  */
1755  TVector3 decayPoint_user( aConstUser[0] * units::m / units::cm,
1756  aConstUser[1] * units::m / units::cm,
1757  aConstUser[2] * units::m / units::cm );
1758  TVector3 decayPoint_near = this->ApplyUserRotation( decayPoint_user, detori, fDetRotation, false ); // NEAR, cm
1759  /*
1760  decayPoint_near.SetXYZ( decayPoint_near.X() + (fCx + fDetOffset.at(0)) * units::m / units::cm,
1761  decayPoint_near.Y() + (fCy + fDetOffset.at(1)) * units::m / units::cm,
1762  decayPoint_near.Z() + (fCz + fDetOffset.at(2)) * units::m / units::cm );
1763  */
1764  decayPoint_near.SetXYZ( decayPoint_near.X() + (fCx) * units::m / units::cm,
1765  decayPoint_near.Y() + (fCy) * units::m / units::cm,
1766  decayPoint_near.Z() + (fCz) * units::m / units::cm );
1767 
1768  TVector3 minusVec( minusPoint[0] - decayPoint_user.X(), minusPoint[1] - decayPoint_user.Y(), minusPoint[2] - decayPoint_user[2] ); // USER, cm
1769  TVector3 plusVec( plusPoint[0] - decayPoint_user.X(), plusPoint[1] - decayPoint_user.Y(), plusPoint[2] - decayPoint_user[2] ); // USER, cm
1770  TVector3 startVec( detStartPoint[0] - decayPoint_user.X(), detStartPoint[1] - decayPoint_user.Y(), detStartPoint[2] - decayPoint_user.Z() ); // USER, cm
1771 
1772  TVector3 minusVec_near = this->ApplyUserRotation( minusVec, detori, fDetRotation, false ); // NEAR, cm
1773  TVector3 plusVec_near = this->ApplyUserRotation( plusVec, detori, fDetRotation, false ); // NEAR, cm
1774  TVector3 startVec_near = this->ApplyUserRotation( startVec, detori, fDetRotation, false ); // NEAR, cm
1775 
1776  double minusNum = startVec.X() * minusVec.X() + startVec.Y() * minusVec.Y() + startVec.Z() * minusVec.Z(); // USER AND USER
1777  double minusDen = startVec.Mag() * minusVec.Mag(); assert( minusDen > 0.0 ); // USER AND USER
1778 
1779  zm = TMath::ACos( minusNum / minusDen ) * TMath::RadToDeg();
1780 
1781  double plusNum = startVec.X() * plusVec.X() + startVec.Y() * plusVec.Y() + startVec.Z() * plusVec.Z(); // USER AND USER
1782  double plusDen = startVec.Mag() * plusVec.Mag(); assert( plusDen > 0.0 ); // USER AND USER
1783 
1784  zp = TMath::ACos( plusNum / plusDen ) * TMath::RadToDeg();
1785 
1786  if( zm > zp ){
1787  double tmpzp = zp;
1788  zp = zm;
1789  zm = tmpzp;
1790  }
1791 
1792  /*
1793  LOG( "HNL", pDEBUG )
1794  << "\nIn DETECTOR coordinates:"
1795  << "\nentered at ( " << minusPoint[0] << ", " << minusPoint[1] << ", " << minusPoint[2] << " ) [cm]"
1796  << "\nexited at ( " << plusPoint[0] << ", " << plusPoint[1] << ", " << plusPoint[2] << " ) [cm]"
1797  << "\nstarted at ( " << decVec.X() << ", " << decVec.Y() << ", " << decVec.Z() << " ) [cm]"
1798  << "\nmomentum ( " << detPpar.X() << ", " << detPpar.Y() << ", " << detPpar.Z() << " )"
1799  << "\nmeaning zm = " << zm << ", zp = " << zp << " [deg]";
1800  */
1801 }
1802 //----------------------------------------------------------------------------
1803 double FluxCreator::CalculateAcceptanceCorrection( TLorentzVector p4par, TLorentzVector p4HNL,
1804  double SMECM, double zm, double zp ) const
1805 {
1806  /*
1807  * This method calculates HNL acceptance by taking into account the collimation effect
1808  * HNL are massive so Lorentz boost from parent CM ==> lab is more effective
1809  * This means that, given a desired range of lab-frame emission angles, there are
1810  * more rest-frame emission angles that map into this range.
1811  * Find the measure of the rest-frame that maps onto the allowed lab-frame angles
1812  * and return the ratio over the relevant measure for a SM neutrino
1813  */
1814 
1815  assert( zm >= 0.0 && zp >= zm );
1816  if( zp == zm ) return 1.0;
1817 
1818  double M = p4HNL.M();
1819  if( M < 1.0e-3 ) return 1.0;
1820 
1821  TF1 * fHNL = ( TF1* ) gROOT->GetListOfFunctions()->FindObject( "fHNL" );
1822  if( !fHNL ){ fHNL = new TF1( "fHNL", labangle, 0.0, 180.0, 6 ); }
1823  fHNL->SetParameter( 0, p4par.E() );
1824  fHNL->SetParameter( 1, p4par.Px() );
1825  fHNL->SetParameter( 2, p4par.Py() );
1826  fHNL->SetParameter( 3, p4par.Pz() );
1827  fHNL->SetParameter( 4, p4HNL.P() );
1828  fHNL->SetParameter( 5, p4HNL.E() );
1829 
1830  double ymax = fHNL->GetMaximum(), xmax = fHNL->GetMaximumX();
1831  double range1 = 0.0;
1832 
1833  if( fHNL->GetMinimum() >= fHNL->GetMaximum() ) return 1.0; // bail on constant function
1834 
1835  if( zm < fHNL->GetMinimum() ){ // really good collimation, ignore checks on zm
1836  double z0 = fHNL->GetMinimum();
1837  if( ymax > zp && xmax < 180.0 ){ // >=2 pre-images, add them together
1838  int nPreim = 0;
1839 
1840  // RETHERE: Make this more sophisticated! Assumes 2 preimages, 1 before and 1 after max
1841  double xl1 = fHNL->GetX( z0, 0.0, xmax );
1842  double xh1 = fHNL->GetX( zp, 0.0, xmax );
1843  double xl2 = fHNL->GetX( z0, xmax, 180.0 );
1844  double xh2 = fHNL->GetX( zp, xmax, 180.0 );
1845 
1846  range1 += std::abs( xl1 - xh1 ) + std::abs( xh2 - xl2 ); nPreim = 2;
1847  } else if( ymax > zp && xmax == 180.0 ){ // 1 pre-image, SMv-like case
1848  double xl = fHNL->GetX( z0 ), xh = fHNL->GetX( zp );
1849  range1 = std::abs( xh - xl );
1850 
1851  } else if( ymax <= zp ){ // 1 pre-image but all emissions reach detector
1852  range1 = 180.0;
1853 
1854  }
1855  } else { // not so good collimation, enforce checks on zm
1856  if( ymax <= zm ){ // 0 pre-images
1857  return 0.0;
1858  } else if( ymax > zp && xmax < 180.0 ){ // >=2 pre-images, add them together
1859  int nPreim = 0;
1860 
1861  // RETHERE: Make this more sophisticated! Assumes 2 preimages, 1 before and 1 after max
1862  double xl1 = fHNL->GetX( zm, 0.0, xmax );
1863  double xh1 = fHNL->GetX( zp, 0.0, xmax );
1864  double xl2 = fHNL->GetX( zm, xmax, 180.0 );
1865  double xh2 = fHNL->GetX( zp, xmax, 180.0 );
1866 
1867  range1 += std::abs( xl1 - xh1 ) + std::abs( xh2 - xl2 ); nPreim = 2;
1868  } else if( ymax > zp && xmax == 180.0 ){ // 1 pre-image, SMv-like case
1869  double xl = fHNL->GetX( zm ), xh = fHNL->GetX( zp );
1870  range1 = std::abs( xh - xl );
1871 
1872  } else if( zm < ymax && ymax <= zp ){ // 1 pre-image
1873  double xl = fHNL->GetX( zm, 0., xmax ), xh = fHNL->GetX( zm, xmax, 180.0 );
1874  range1 = std::abs( xh - xl );
1875  }
1876  }
1877 
1878  TF1 * fSMv = ( TF1* ) gROOT->GetListOfFunctions()->FindObject( "fSMv" );
1879  if( !fSMv ){
1880  fSMv = new TF1( "fSMv", labangle, 0.0, 180.0, 6 );
1881  }
1882  fSMv->SetParameter( 0, p4par.E() );
1883  fSMv->SetParameter( 1, p4par.Px() );
1884  fSMv->SetParameter( 2, p4par.Py() );
1885  fSMv->SetParameter( 3, p4par.Pz() );
1886  fSMv->SetParameter( 4, SMECM );
1887  fSMv->SetParameter( 5, SMECM );
1888  double range2 = -1.0;
1889 
1890  if( fSMv->GetMaximum() == fSMv->GetMinimum() ) return 1.0; // bail
1891 
1892  // SMv deviates more from parent than HNL due to masslessness. This means a larger minimum of labangle
1893  // Sometimes the angle is so small, that this calculation fails as there is not SMv preimage to compare
1894  // with. Default to estimating dx/dz * dy/dz ratio in that case.
1895 
1896  if( fSMv->GetMinimum() < zp ){
1897  if( fSMv->GetMinimum() < zm ){
1898  range2 = std::abs( fSMv->GetX( zp ) - fSMv->GetX( zm ) );
1899  } else { // due to monotonicity all of [0.0, fSMv->GetX(zp)] is good
1900  range2 = fSMv->GetX( zp );
1901  }
1902  if( range2 < 1.0e-6 || range1 / range2 > 10.0 ) return 1.0; // sometimes this happens, gotta bail!
1903  } else { // can't decide based on SMv analytically.
1904  TVector3 bv = p4par.BoostVector();
1905 
1906  TLorentzVector vcx( p4HNL.P(), 0.0, 0.0, p4HNL.E() ),
1907  vcy( 0.0, p4HNL.P(), 0.0, p4HNL.E() ),
1908  vcz( 0.0, 0.0, p4HNL.P(), p4HNL.E() );
1909  vcx.Boost( bv ); vcy.Boost( bv ); vcz.Boost( bv );
1910 
1911  TLorentzVector vsx( SMECM, 0.0, 0.0, SMECM ),
1912  vsy( 0.0, SMECM, 0.0, SMECM ),
1913  vsz( 0.0, 0.0, SMECM, SMECM );
1914  vsx.Boost( bv ); vsy.Boost( bv ); vsz.Boost( bv );
1915 
1916  double xpart = std::abs( ( vcx.X() / vcz.Z() ) / ( vsx.X() / vsz.Z() ) );
1917  double ypart = std::abs( ( vcy.Y() / vcz.Z() ) / ( vsy.Y() / vsz.Z() ) );
1918 
1919  return 1.0 / ( xpart * ypart );
1920  }
1921 
1922  assert( range2 > 0.0 );
1923 
1924  return range1 / range2;
1925 
1926 }
1927 //----------------------------------------------------------------------------
1928 double FluxCreator::labangle( double * x, double * par )
1929 {
1930  double xrad = x[0] * TMath::DegToRad();
1931  double Ehad = par[0], pxhad = par[1], pyhad = par[2], pzhad = par[3];
1932  double phnl = par[4], Ehnl = par[5];
1933 
1934  TLorentzVector p4had( pxhad, pyhad, pzhad, Ehad );
1935  TVector3 boost_vec = p4had.BoostVector(); // beta of parent in lab frame
1936 
1937  // assume phi invariance so create HNL rest-frame momentum along y'z' plane
1938  TLorentzVector pncm( 0.0, phnl * TMath::Sin( xrad ), phnl * TMath::Cos( xrad ), Ehnl );
1939 
1940  // boost into lab frame
1941  pncm.Boost( boost_vec );
1942 
1943  // return lab frame theta wrt parent momentum in deg
1944  double num = pxhad * pncm.X() + pyhad * pncm.Y() + pzhad * pncm.Z();
1945  double den = p4had.P() * pncm.P();
1946  double theta = TMath::ACos( num / den ) * 180.0 / constants::kPi;
1947  return theta;
1948 }
1949 //----------------------------------------------------------------------------
1951 {
1952  if( fRadius < 0.0 ) fRadius = 1.0;
1953  LOG( "HNL", pFATAL )
1954  << "WARNING: This is a bounding box centred at config-given point and radius " << fRadius << " m";
1955 
1956  fLx = fRadius; fLy = fRadius; fLz = fRadius;
1957 }
1958 //----------------------------------------------------------------------------
1959 TVector3 FluxCreator::ApplyUserRotation( TVector3 vec, bool doBackwards ) const
1960 {
1961  double vx = vec.X(), vy = vec.Y(), vz = vec.Z();
1962 
1963  double Ax2 = ( doBackwards ) ? -fAx2 : fAx2;
1964  double Az = ( doBackwards ) ? -fAz : fAz;
1965  double Ax1 = ( doBackwards ) ? -fAx1 : fAx1;
1966 
1967  // Ax2 first
1968  double x = vx, y = vy, z = vz;
1969  vy = y * std::cos( Ax2 ) - z * std::sin( Ax2 );
1970  vz = y * std::sin( Ax2 ) + z * std::cos( Ax2 );
1971  y = vy; z = vz;
1972  // then Az
1973  vx = x * std::cos( Az ) - y * std::sin( Az );
1974  vy = x * std::sin( Az ) + y * std::cos( Az );
1975  x = vx; y = vy;
1976  // Ax1 last
1977  vy = y * std::cos( Ax1 ) - z * std::sin( Ax1 );
1978  vz = y * std::sin( Ax1 ) + z * std::cos( Ax1 );
1979 
1980  TVector3 nvec( vx, vy, vz );
1981  return nvec;
1982 }
1983 //----------------------------------------------------------------------------
1984 TVector3 FluxCreator::ApplyUserRotation( TVector3 vec, TVector3 oriVec, std::vector<double> rotVec, bool doBackwards ) const
1985 {
1986  double vx = vec.X(), vy = vec.Y(), vz = vec.Z();
1987  double ox = oriVec.X(), oy = oriVec.Y(), oz = oriVec.Z();
1988 
1989  vx -= ox; vy -= oy; vz -= oz; // make this rotation about detector origin
1990 
1991  assert( rotVec.size() == 3 ); // want 3 Euler angles, otherwise this is unphysical.
1992  double Ax2 = ( doBackwards ) ? -rotVec.at(2) : rotVec.at(2);
1993  double Az = ( doBackwards ) ? -rotVec.at(1) : rotVec.at(1);
1994  double Ax1 = ( doBackwards ) ? -rotVec.at(0) : rotVec.at(0);
1995 
1996  // Ax2 first
1997  double x = vx, y = vy, z = vz;
1998  vy = y * std::cos( Ax2 ) - z * std::sin( Ax2 );
1999  vz = y * std::sin( Ax2 ) + z * std::cos( Ax2 );
2000  y = vy; z = vz;
2001  // then Az
2002  vx = x * std::cos( Az ) - y * std::sin( Az );
2003  vy = x * std::sin( Az ) + y * std::cos( Az );
2004  x = vx; y = vy;
2005  // Ax1 last
2006  vy = y * std::cos( Ax1 ) - z * std::sin( Ax1 );
2007  vz = y * std::sin( Ax1 ) + z * std::cos( Ax1 );
2008 
2009  // back to beam frame
2010  vx += ox; vy += oy; vz += oz;
2011  TVector3 nvec( vx, vy, vz );
2012  return nvec;
2013 }
2014 //____________________________________________________________________________
2015 double FluxCreator::CalculateDetectorAcceptanceSAA( TVector3 detO ) const
2016 {
2017  // sang is solid-angle / 4pi
2018  double rad = std::sqrt( detO.X() * detO.X() + detO.Y() * detO.Y() + detO.Z() * detO.Z() );
2019  double sang = 1.0 - TMath::Cos( TMath::ATan( kRDET / rad ) ); sang *= 0.5;
2020  return sang;
2021 }
2022 //----------------------------------------------------------------------------
2024 {
2025  // for now this is just a square of length kRDET
2026  // returns 1 / area
2027  return 1.0 / ( kRDET * kRDET );
2028 }
2029 //----------------------------------------------------------------------------
2030 void FluxCreator::Configure(const Registry & config)
2031 {
2032  Algorithm::Configure(config);
2033  this->LoadConfig();
2034 }
2035 //----------------------------------------------------------------------------
2036 void FluxCreator::Configure(string config)
2037 {
2038  Algorithm::Configure(config);
2039  this->LoadConfig();
2040 }
2041 //----------------------------------------------------------------------------
2043 {
2044  if( fIsConfigLoaded ) return;
2045 
2046  LOG("HNL", pDEBUG)
2047  << "Loading flux-creation parameters from file...";
2048 
2049  this->GetParam( "HNL-Mass", fMass );
2050  this->GetParamVect( "HNL-LeptonMixing", fU4l2s );
2051  this->GetParam( "HNL-Majorana", fIsMajorana );
2052 
2053  this->GetParamVect( "Near2User_T", fB2UTranslation );
2054  this->GetParamVect( "Near2User_R", fDetRotation );
2055  this->GetParamVect( "Near2Beam_R", fB2URotation );
2056  this->GetParamVect( "DetCentre_User", fDetOffset );
2057 
2058  this->GetParamVect( "ParentPOTScalings", fScales );
2059  this->GetParam( "DoOldFluxCalculation", fDoingOldFluxCalc );
2060  this->GetParam( "RerollPoints", fRerollPoints );
2061  this->GetParam( "CollectionRadius", fRadius );
2062  this->GetParam( "InputFluxesInBEAM", fSupplyingBEAM );
2063  this->GetParam( "IncludePolarisation", doPol );
2064  this->GetParam( "FixPolarisationDirection", fixPol );
2065  this->GetParamVect( "HNL-PolDir", fFixedPolarisation );
2066 
2067  this->GetParam( "IsParentOnAxis", isParentOnAxis );
2068 
2069  fCx = fB2UTranslation.at(0);
2070  fCy = fB2UTranslation.at(1);
2071  fCz = fB2UTranslation.at(2);
2072 
2073  fAx1 = fB2URotation.at(0);
2074  fAz = fB2URotation.at(1);
2075  fAx2 = fB2URotation.at(2);
2076 
2077  fBx1 = fDetRotation.at(0);
2078  fBz = fDetRotation.at(1);
2079  fBx2 = fDetRotation.at(2);
2080 
2081  POTScaleWeight = 1.0;
2083  POTScaleWeight = fScales[0]; // all POT contribute
2086  POTScaleWeight = fScales[1]; // muons do not contribute
2089  POTScaleWeight = fScales[2]; // only kaons contribute
2094  POTScaleWeight = fScales[3]; // only charged kaons contribute
2095 
2096  /*
2097  LOG( "HNL", pDEBUG )
2098  << "Read the following parameters :"
2099  << "\n Mass = " << fMass
2100  << "\n couplings = " << fU4l2s.at(0) << " : " << fU4l2s.at(1) << " : " << fU4l2s.at(2)
2101  << "\n translation = " << fB2UTranslation.at(0) << ", " << fB2UTranslation.at(1) << ", " << fB2UTranslation.at(2)
2102  << "\n rotation = " << fB2URotation.at(0) << ", " << fB2URotation.at(1) << ", " << fB2URotation.at(2)
2103  << "\n isParentOnAxis = " << isParentOnAxis
2104  << "\n POTScaleWeight = " << POTScaleWeight;
2105  */
2106 
2107  fIsConfigLoaded = true;
2108 }
2109 //____________________________________________________________________________
2110 void FluxCreator::SetGeomFile( string geomfile ) const
2111 {
2112  LOG( "HNL", pDEBUG ) << "Setting geometry file to " << geomfile;
2113  fGeomFile = geomfile;
2114 }
2115 //____________________________________________________________________________
2116 void FluxCreator::SetFirstFluxEntry( int iFirst ) const
2117 {
2118  fFirstEntry = iFirst;
2119 }
2120 //____________________________________________________________________________
2121 void FluxCreator::ImportBoundingBox( TGeoBBox * box ) const
2122 {
2123  LOG( "HNL", pDEBUG ) << "Importing bounding box...";
2124  fLxR = 2.0 * box->GetDX() * units::cm / units::m;
2125  fLyR = 2.0 * box->GetDY() * units::cm / units::m;
2126  fLzR = 2.0 * box->GetDZ() * units::cm / units::m;
2127 
2128  double testRadius = fRadius; // m
2129  if( !fDoingOldFluxCalc ){
2130  fLx = std::min( testRadius, fLxR );
2131  fLy = std::min( testRadius, fLyR );
2132  fLz = std::min( testRadius, fLzR );
2133  }
2134 }
2135 //____________________________________________________________________________
2136 std::string FluxCreator::CheckGeomPoint( Double_t x, Double_t y, Double_t z ) const
2137 {
2138  Double_t point[3];
2139  Double_t local[3];
2140  point[0] = x;
2141  point[1] = y;
2142  point[2] = z;
2143  TGeoVolume *vol = gGeoManager->GetTopVolume();
2144  TGeoNode *node = gGeoManager->FindNode(point[0], point[1], point[2]);
2145  gGeoManager->MasterToLocal(point, local);
2146  return gGeoManager->GetPath();
2147 }
static constexpr double cm
Definition: Units.h:68
enum genie::hnl::t_HNLProd HNLProd_t
TLorentzVector parp4
parent momentum at HNL production in NEAR coords [GeV/c]
std::vector< double > fFixedPolarisation
virtual void SetXSec(double xsec)
Definition: GHepRecord.h:132
static constexpr double rad
Definition: Units.h:164
virtual GHepParticle * Particle(int position) const
Definition: GHepRecord.cxx:104
virtual void SetWeight(double wght)
Definition: GHepRecord.h:130
int decay_ptype
PDG code of parent.
TLorentzVector * GetX4(void) const
double Ecm
Parent rest-frame energy of HNL [GeV].
static const int maxArray
const int kPdgNuE
Definition: PDGCodes.h:28
double zetaMinus
minimum angular deviation from parent momentum to reach detector [deg]
std::map< genie::hnl::HNLProd_t, double > dynamicScores_neuk
double nuEcm
Parent rest-frame energy of equivalent SM neutrino [GeV].
double nuray_px[maxArray]
TVector3 PointToRandomPointInBBox() const
static double labangle(double *x, double *par)
#define pERROR
Definition: Messenger.h:59
Manages HNL BR (prod and decay)
double delay
delay HNL would have wrt SMv [ns]
double potnum
N POT for this SM-v.
double pots
how many pot in this job?
std::vector< double > fB2UTranslation
double ancestor_polz[maxArray]
static RandomGen * Instance()
Access instance.
Definition: RandomGen.cxx:71
void ProcessEventRecord(GHepRecord *event_rec) const
std::vector< double > fDetOffset
PDGCodeList ProductionProductList(genie::hnl::HNLProd_t hnldm)
void ImportBoundingBox(TGeoBBox *box) const
double decay_vz
coordinates of prod vtx [cm]
double GetAngDeviation(TLorentzVector p4par, TVector3 detO, bool seekingMax) const
double location_y[maxArray]
TVector3 startPoint
parent decay vertex in NEAR coords [m]
const double kRDET
double ancestor_startz[maxArray]
double traj_trkpx[maxArray]
int job
beamsim MC job number
#define pFATAL
Definition: Messenger.h:56
int lepPdg
PDG code of lepton produced with HNL on parent decay.
TVector3 polz
HNL polarisation vector, in HNL rest frame, in NEAR coords.
int parPdg
parent PDG code
const int kPdgNuMu
Definition: PDGCodes.h:30
int GetParamVect(const std::string &comm_name, std::vector< T > &v, bool is_top_call=true) const
Handle to load vectors of parameters.
char location_name[maxArray *maxC]
static constexpr double s
Definition: Units.h:95
double nuray_E[maxArray]
double CalculateDetectorAcceptanceSAA(TVector3 detO) const
A GENIE flux container specific for HNL containers. Based on the dk2nu flux paradigm and genie::flux:...
string P4AsString(const TLorentzVector *p)
Definition: PrintUtils.cxx:27
static const unsigned int kMaxUnweightDecayIterations
Definition: Controls.h:61
Algorithm abstract base class.
Definition: Algorithm.h:54
double zetaPlus
maximum angular deviation from parent momentum to reach detector [deg]
virtual double Weight(void) const
Definition: GHepRecord.h:124
static constexpr double ns
Definition: Units.h:98
const int kPdgElectron
Definition: PDGCodes.h:35
string dir
TVector3 startPointUser
parent decay vertex in USER coords [m]
genie::hnl::FluxContainer fGnmf
double decay_necm
SM v CM energy [GeV].
A singleton holding random number generator classes. All random number generation in GENIE should tak...
Definition: RandomGen.h:29
double ancestor_pprodpx[maxArray]
TLorentzVector parp4User
parent momentum at HNL production in USER coords [GeV/c]
double traj_trkz[maxArray]
double decay_nimpwt
Importance weight from beamsim.
A list of PDG codes.
Definition: PDGCodeList.h:32
std::vector< double > fB2URotation
double ancestor_startx[maxArray]
double traj_trkpy[maxArray]
double CalculateAcceptanceCorrection(TLorentzVector p4par, TLorentzVector p4HNL, double SMECM, double zm, double zp) const
void SetUsingRootGeom(bool IsUsingRootGeom) const
std::vector< double > fDetRotation
std::map< genie::hnl::HNLProd_t, double > dynamicScores
void SetPosition(const TLorentzVector &v4)
An exception thrown by EventRecordVisitorI when the normal processing sequence has to be disrupted (f...
double ancestor_stoppx[maxArray]
double KinematicScaling(genie::hnl::HNLProd_t hnlprod) const
const double e
#define LOG(stream, priority)
A macro that returns the requested log4cpp::Category appending a string (using the FILE...
Definition: Messenger.h:96
TLorentzVector p4
HNL momentum in NEAR coords [GeV/c].
const int kPdgTau
Definition: PDGCodes.h:39
double ancestor_starty[maxArray]
TLorentzVector p4User
HNL momentum in USER coords [GeV/c].
std::map< genie::hnl::HNLProd_t, double > dynamicScores_muon
double traj_trkx[maxArray]
std::string CheckGeomPoint(Double_t x, Double_t y, Double_t z) const
double location_z[maxArray]
virtual void Configure(const Registry &config)
Definition: Algorithm.cxx:62
int prodChan
Decay mode that produced HNL.
const Algorithm * GetAlgorithm(const AlgId &algid)
Definition: AlgFactory.cxx:75
const int kPdgKP
Definition: PDGCodes.h:172
const int kPdgHNL
Definition: PDGCodes.h:224
TVector3 ApplyUserRotation(TVector3 vec, bool doBackwards=false) const
void OpenFluxInput(std::string finpath) const
std::map< genie::hnl::HNLProd_t, double > GetProductionProbs(int parPDG) const
const int kPdgPiP
Definition: PDGCodes.h:158
static const double kASmallNum
Definition: Controls.h:40
std::map< genie::hnl::HNLProd_t, double > dynamicScores_pion
FluxContainer RetrieveFluxInfo() const
string ProdAsString(genie::hnl::HNLProd_t hnlprod)
#define pINFO
Definition: Messenger.h:62
const int kPdgK0L
Definition: PDGCodes.h:176
double acceptance
full acceptance == XYWgt * boostCorr^2 * accCorr
std::map< genie::hnl::HNLProd_t, double > dynamicScores_kaon
double ancestor_startpx[maxArray]
TVector3 targetPointUser
point in detector HNL is forced towards in USER coords [m]
double nuray_py[maxArray]
double decay_pdpz
final parent momentum [GeV]
#define pWARN
Definition: Messenger.h:60
TVector3 targetPoint
point in detector HNL is forced towards in NEAR coords [m]
double nimpwt
Weight of parent.
char ancestor_imat[maxArray *maxC]
Expands the EventRecordVisitorI interface to include public interfaces for the HNL FluxCreator module...
void FillNonsense(int iEntry, genie::hnl::FluxContainer &gnmf) const
char ancestor_proc[maxArray *maxC]
double accCorr
acceptance correction (collimation effect. SM v == 1)
double ancestor_startpz[maxArray]
FluxContainer MakeTupleFluxEntry(int iEntry, std::string finpath) const
double traj_trkpz[maxArray]
TRandom3 & RndHadro(void) const
rnd number generator used by hadronization models
Definition: RandomGen.h:53
double ancestor_pprodpz[maxArray]
static PDGLibrary * Instance(void)
Definition: PDGLibrary.cxx:68
bool IsProdKinematicallyAllowed(genie::hnl::HNLProd_t hnlprod)
static AlgFactory * Instance()
Definition: AlgFactory.cxx:64
std::vector< double > fU4l2s
int nuPdg
PDG code of SM neutrino that would have been produced.
double XYWgt
geometric acceptance (angular size of detector in parent rest frame)
A registry. Provides the container for algorithm configuration parameters.
Definition: Registry.h:65
double ancestor_stoppz[maxArray]
virtual void SetVertex(double x, double y, double z, double t)
Definition: GHepRecord.cxx:827
TRandom3 & RndGen(void) const
rnd number generator for generic usage
Definition: RandomGen.h:80
void SetFirstFluxEntry(int iFirst) const
double location_x[maxArray]
static const int maxC
std::vector< double > fScales
double nuray_wgt[maxArray]
double ancestor_stoppy[maxArray]
virtual double XSec(void) const
Definition: GHepRecord.h:126
int nuProdChan
Decay mode that would have produced SM neutrino.
virtual void AddParticle(const GHepParticle &p)
Definition: GHepRecord.cxx:499
double ancestor_startpy[maxArray]
double ancestor_pprodpy[maxArray]
TParticlePDG * Find(int pdgc, bool must_exist=true)
Definition: PDGLibrary.cxx:86
#define pNOTICE
Definition: Messenger.h:61
bool GetParam(const RgKey &name, T &p, bool is_top_call=true) const
double boostCorr
boost correction wrt parent rest-frame (ELAB = ECM * boostCorr)
void Configure(const Registry &config)
int ancestor_nucleus[maxArray]
double nuray_pz[maxArray]
const int kPdgMuon
Definition: PDGCodes.h:37
static constexpr double kSpeedOfLight
Definition: Units.h:32
char ancestor_ivol[maxArray *maxC]
TLorentzVector HNLEnergy(genie::hnl::HNLProd_t hnldm, TLorentzVector p4par) const
GENIE&#39;s GHEP MC event record.
Definition: GHepRecord.h:45
double ancestor_poly[maxArray]
static constexpr double m
Definition: Units.h:71
STDHEP-like event record entry that can fit a particle or a nucleus.
Definition: GHepParticle.h:39
void SetCurrentEntry(int iCurr) const
int ancestor_pdg[maxArray]
string Vec3AsString(const TVector3 *vec)
Definition: PrintUtils.cxx:80
double traj_trky[maxArray]
void SetInputFluxPath(std::string finpath) const
void push_back(int pdg_code)
Definition: PDGCodeList.cxx:58
double ancestor_polx[maxArray]
void SetGeomFile(string geomfile) const
#define pDEBUG
Definition: Messenger.h:63