HighLAND
SIPionSystematics.cxx
1 #include "SIPionSystematics.hxx"
2 #include "ND280AnalysisUtils.hxx"
3 #include "MultiThread.hxx"
4 #include "SystId.hxx"
5 
6 //#define DEBUG
7 
8 //********************************************************************
9 SIPionSystematics::SIPionSystematics():EventWeightBase(1),BinnedParams("SIPion",k1D_SYMMETRIC){
10  //********************************************************************
11 
12  char inputFileName[256];
13 #define JMDEBUGFIXMATERIAL 1
14 #if JMDEBUGFIXMATERIAL == 0
15  sprintf(inputFileName,"%s/data/SIPionXsec.dat",getenv("PSYCHESYSTEMATICSROOT"));
16 #else
17  sprintf(inputFileName,"%s/data/SIPionXsecFixG10.dat",getenv("PSYCHESYSTEMATICSROOT"));
18 #endif
19  anaUtils::LoadPionCrossSections(inputFileName, _xsec_data, _err_data);
21 
22  // Interaction types
23  _interactionID[0]=0; // Abs
24  _interactionID[1]=1; // CEX
25  _interactionID[2]=4; // QE
26 
27  _pionWeightInfo = NULL;
28  _pionSIManager = NULL;
29 
30  _initialized = false;
31 
32 }
33 
34 //********************************************************************
36  //********************************************************************
37 
38  if (_initialized) return;
39 
40  if (!_pionSIManager)
41  _pionSIManager = new PionSIManager();
42 
43  _initialized = true;
44 
45 }
46 
47 //********************************************************************
48 SIPionSystematics::~SIPionSystematics(){
49  //********************************************************************
50 
51  std::map<Int_t, std::pair<TGraphErrors*,TGraph*> > xsecs = PiXSec::AllXSecs;
52  std::map<Int_t, std::pair<TGraphErrors*,TGraph*> >::iterator it;
53 
54  for (it = xsecs.begin(); it != xsecs.end(); it++) {
55  delete it->second.first;
56  delete it->second.second;
57  }
58 
59  if (_pionSIManager)
60  delete _pionSIManager;
61 
62  _pionSIManager = NULL;
63 
64 }
65 
66 //********************************************************************
67 Weight_h SIPionSystematics::ComputeWeight(const ToyExperiment& toy, const AnaEventC& event, const ToyBoxB& box, const SelectionBase& sel){
68  //********************************************************************
69 
70  (void)box;
71 
72  Weight_h eventWeight;
73  Int_t uniqueID = 0;
74 
75 #ifdef MULTITHREAD
76  uniqueID = event.UniqueID;
77 #else
78  (void)event;
79 #endif
80 
81  //retrieve the pion secondary interaction information for the event
82  PionInteractionSystematic& pionSI = *_pionWeightInfo[uniqueID];
83 
84 #ifdef DEBUG
85  std::cout << "nPions = " << pionSI.nPions << std::endl;
86 #endif
87 
88  // Only bother doing anything if there is at least one pion in the event.
89  if(pionSI.nPions == 0) return eventWeight;
90 
91  // Correction part, available from the beginning: scales MC cross-sections to data
92  eventWeight.Correction = pionSI.weightMCToData ;
93 
94  if (!TMath::Finite(eventWeight.Correction) || eventWeight.Correction != eventWeight.Correction){
95  return Weight_h(1);
96  }
97 
98  //Start by defining the number density of carbon in FGD Scintillator.
99  Float_t nC = 4.6487939E22;
100 
101  //Loop over all the saved steps.
102  //For adding up what is computed in each step, to feed to the final exponential.
103  Float_t weightExp[3] = {0,0,0};
104 
105  int stepsConsidered = 0;
106 
107  //The weight we will be calculating for the event.
108  Float_t siUncWeight[3] = {1,1,1};
109 
110  //Loop over the pions.
111  for(int ns = 0; ns < pionSI.nPions; ns++){
112 
113  // For example, for inclusive selection only when the lepton candidate is a true pion should be considered. Other tracks contribute at second order
114  if (!sel.IsRelevantTrueObjectForSystematicInToy(event,box,pionSI.TrueParticles[ns],SystId::kSIPion,box.SuccessfulBranch)) continue;
115 
116 #ifdef DEBUG
117  std::cout << "nSteps = " << pionSI.nSteps[ns] << std::endl;
118 #endif
119  //Loop over only the steps belonging to that pion in the stepLengths array.
120  //Note: totalSteps is necessarily the sum over nSteps[nPions]
121  for(unsigned int ts = stepsConsidered; ts < (unsigned int)(stepsConsidered + pionSI.nSteps[ns]); ts++){
122 
123 #ifdef DEBUG
124  std::cout << "ts = " << ts << std::endl;
125 #endif
126  //baseAnalysis puts a limit of 128 on ts. So, enforce that limit here.
127  if (ts >= 128) break;
128 
129  //Steps are taken at 10 MeV/c momentum intervals. Use this to compute the kinetic
130  //energy of a step using the initial momentum of the pion and the number of steps
131  Float_t pStep = pionSI.initMom[ns] - 10.0*((Float_t)(ts - stepsConsidered));
132 #ifdef DEBUG
133  std::cout << "pStep = " << pStep << std::endl;
134 #endif
135  //Now, the pStep needs to be converted to the correct bin number to take the cross
136  //section from. We're probably better off rounding properly, so divide pStep by 10.0, add
137  //0.5, then cast to int.
138  int unsigned momBin = (int)((pStep*0.1) + 0.5);
139 #ifdef DEBUG
140  std::cout << "momBin = " << momBin << std::endl;
141 #endif
142  //If the bin number is larger than the maximum (i.e. the momentum is larger
143  //than 2000 MeV/c) use the information in the last bin.
144  if (momBin > NMAXMOMBINS-1) momBin = NMAXMOMBINS-1;
145 
146  //Now compute the nominal and varied cross section multiplied by the step length.
147  //Remember to convert the cross section to cm^2 (multiply by 1E-27)
148  //and step length to cm (multiply by 0.1) to match the
149  //number density of Carbon (particles/cm^3).
150  //If the pion was travelling through a material other than FGD Scintillator,
151  //the contribution to the step length was pre-scaled to an FGD scintillator equivalent
152  //length so could just use the properties of FGD scintillator , and the
153  //cross section on Carbon while stepping.
154 
155  //Note: Stepping is ostensibly for computing survival probability, but we're really
156  //interested in P_(data + unc)/P_(data). Exp(data+unc)/Exp(data) = Exp(unc),
157  //so we would only need to use the uncertainties while stepping. However, since some
158  //of the extrapolated uncertainties are so large that the data+unc*sigmafraction would be
159  //less than zero (non-physical), so have to add the cross section to the fraction
160  //sigma (which could be positive or negative), use zero if it is negative, then subtract the
161  //data cross section.
162 
163  //Use the pi+ data for pi+ (offset=0)
164  //If it's not a pi+, it's a pi-, for which we use the pi- data (offset=100)
165  int pim_offset = 0;
166  if(pionSI.pionType[ns] != 1) pim_offset = 100;
167 
168  for (Int_t mech=0;mech<3;mech++){
169  // No CEX systematic for CCOther when the pion is negative. Charge exchange
170  // if (mech==1 && box.SuccessfulBranch==2 && pionSI.pionType[ns] == 0) continue;
171  // if (mech==0 && box.SuccessfulBranch==0 && toy.Variations[mech]>0) continue;
172  // if (mech==0 && box.SuccessfulBranch==2 && toy.Variations[mech]<0) continue;
173  weightExp[mech] += (std::max((Float_t)(_xsec_data[_interactionID[mech]+pim_offset][momBin] + _err_data[_interactionID[mech]+pim_offset][momBin] *
174  toy.GetToyVariations(_index)->Variations[mech]),(Float_t)0.0)
175  - ( _xsec_data[_interactionID[mech]+pim_offset][momBin]))*(1E-27)*(0.1)*pionSI.stepLengths[ts];
176  }
177 
178 #ifdef DEBUG
179  std::cout << _xsec_data[4+pim_offset][momBin] << " " << _err_data[4+pim_offset][momBin] << " " << pionSI.stepLengths[ts] << std::endl;
180  std::cout << "weightExp = " << weightExp[0] << " " << weightExp[1] << " " << weightExp[2] << std::endl;
181 #endif
182  }//Close loop over this pion's steps.
183 
184  //Having considered this pion's steps,
185  //add the number of steps taken by this pion to the stepsConsidered counter.
186  stepsConsidered += pionSI.nSteps[ns];
187 
188  }//Close loop over pions.
189 
190  //Multiply weightExp by the number density of Carbon, and compute the portion of the weight
191  //that is due to the survival probability.
192  for (int mech=0;mech<3;mech++)
193  siUncWeight[mech] = exp(-nC*weightExp[mech]);
194 
195  //With the survival probability portion of the weight calculated, apply the correct interaction cross section factors.
196  //Loop over the interactions
197  for(int ii = 0; ii < pionSI.nInteractions; ii++){
198 
199  // only consider the interactions corresponding to relevant pions
200  if (!sel.IsRelevantTrueObjectForSystematicInToy(event,box,pionSI.InteractionTrueParticles[ii],SystId::kSIPion,box.SuccessfulBranch)) continue;
201 
202  //Convert the momentum of the interaction to a bin number.
203  unsigned int momBin = (int)((pionSI.pInteraction[ii]*0.1) + 0.5);
204 
205  //If the bin number is larger than the maximum (i.e. the momentum is larger
206  //than 2000 MeV/c) use the information in the last bin.
207  // if (momBin > NMAXMOMBINS-1) momBin = NMAXMOMBINS-1;
208  if (momBin > NMAXMOMBINS) momBin = NMAXMOMBINS;
209 
210  int intType = pionSI.typeInteraction[ii];
211 
212  //Interactions: 4, 0, 1.
213  //Materials: 0, 20, 60, 70, 80, 10, 30, 40, 50
214  //Due to pi- being non-insignificant, go back and forth
215  //between pi+ and pi- (add 100 for pi-)
216  //Also, only apply the weight if the data cross section is
217  //non-zero. (The data value is rarely 0.0. This only happens when the extrapolation
218  //encounters a MC cross section of 0.0. There may be a more correct way of handling this,
219  //but since it affects a small number of points, this should suffice.)
220  //Additionally, do not apply a negative weight.
221 
222  if(_xsec_data[intType][momBin] != 0.0){
223  int mech;
224  if (intType%10 == _interactionID[0]) mech = 0;
225  else if (intType%10 == _interactionID[1]) mech = 1;
226  else if (intType%10 == _interactionID[2]) mech = 2;
227  else break;
228 
229  // if (mech==0 && box.SuccessfulBranch==0 && toy.Variations[mech]>0) continue;
230  // if (mech==0 && box.SuccessfulBranch==2 && toy.Variations[mech]<0) continue;
231  siUncWeight[mech] *= (std::max((Float_t)(_xsec_data[intType][momBin]
232  + _err_data[intType][momBin] * toy.GetToyVariations(_index)->Variations[mech]),(Float_t)0.0)
233  /_xsec_data[intType][momBin]);
234  }
235 
236  }//Close loop over the interactions.
237 
238  //With all the interactions looped over, all additional weights should be applied.
239  //Apply this weight and the MC to data weight.
240  Float_t siUncWeightTotal=1.;
241  for (int mech=0;mech<3;mech++){
242  siUncWeightTotal*=siUncWeight[mech];
243  }
244 
245 
246  eventWeight.Systematic = eventWeight.Correction * siUncWeightTotal;
247 
248  if (!TMath::Finite(eventWeight.Systematic) || eventWeight.Systematic != eventWeight.Systematic){
249 #ifdef DEBUG
250 
251  std::cout << event.EventInfo.Event << " " << event.EventInfo.SubRun << " " << event.EventInfo.Run << " " << pionSI.nPions << " " << pionSI.nInteractions << " " << eventWeight << std::endl;
252 
253  for (int mech=0;mech<3;mech++)
254  std::cout << " " << mech << " " << siUncWeight[mech] << " " << toy.GetToyVariations(_index)->Variations[mech] << " " << pionSI.weightMCToData << std::endl;
255 #endif
256  // Fall back to correction only
257  eventWeight.Systematic = eventWeight.Correction;
258  }
259 
260  return eventWeight;
261 }
262 
263 //********************************************************************
264 void SIPionSystematics::FillSystBox(const AnaEventC& eventC, const SelectionBase& sel, Int_t ibranch){
265  //********************************************************************
266 
267  (void)sel;
268  (void)ibranch;
269 
270  Int_t uniqueID = 0;
271 #ifdef MULTITHREAD
272  uniqueID = eventC.UniqueID;
273 #endif
274 
275  const AnaEventB& event = *static_cast<const AnaEventB*>(&eventC);
276 
277  // Compute Pion weight info needed by PionSISystematics (TODO, only when this systematic is enabled)
278  _pionWeightInfo[uniqueID] = _pionSIManager->ComputePionWeightInfo(event, sel, ibranch);
279 }
280 
281 //********************************************************************
282 void SIPionSystematics::Initialize(Int_t nsel, Int_t isel, Int_t nbranches, Int_t nevents){
283  //********************************************************************
284 
285  Initialize();
286 
287  // TODO: This is a temporary solution to make this work with multithreading. Instead we would need a derived SystBox
288 
289  // Although the SystBox is actually not used we need to create it because SystematicBase::InitializeEvent uses it
290  EventWeightBase::Initialize(nsel,isel,nbranches,nevents);
291 
292  // Since this is called for several selections use for the moment a single one for all selections
293  if (_pionWeightInfo) return;
294 
295 #ifndef MULTITHREAD
296  nevents=1;
297 #endif
298 
299  _pionWeightInfo = new PionInteractionSystematic*[nevents];
300  for (Int_t k= 0;k<nevents;k++)
301  _pionWeightInfo[k]=NULL;
302 }
303 
304 
305 //********************************************************************
307  //********************************************************************
308 
309  // TODO: This is a temporary solution to make this work with multithreading. Instead we would need a derived SystBox
310 
311  // Although the SystBox is actually not used we need to create it because SystematicBase::FinalizeEvent uses it
313 
314  Int_t uniqueID = 0;
315 #ifdef MULTITHREAD
316  uniqueID = event.UniqueID;
317 #endif
318 
319  if (_pionWeightInfo[uniqueID])
320  delete _pionWeightInfo[uniqueID];
321  _pionWeightInfo[uniqueID]=NULL;
322 
323 }
Int_t _index
The index of this systematic (needed by SystematicsManager);.
Float_t * Variations
the vector of Variations, one for each of the systematic parameters
void SetNParameters(int N)
Set the number of systematic parameters associated to this systematic.
virtual void Initialize(Int_t nsel, Int_t isel, Int_t nbranch, Int_t nevents)
Create the array of SystBox.
virtual void FinalizeEvent(const AnaEventC &event)
Delete the SystBox for this event.
Weight_h ComputeWeight(const ToyExperiment &, const AnaEventC &, const ToyBoxB &)
Apply the systematic.
Int_t UniqueID
The event unique ID.
Int_t SuccessfulBranch
The branch that is successful for this toy in the selection this ToyBox belongs to.
Definition: ToyBoxB.hxx:46
ToyVariations * GetToyVariations(UInt_t index) const
returns the variations for a given systematic (index)
void FinalizeEvent(const AnaEventC &event)
Delete the PionInteractionSystematic for this event.
virtual void Initialize()
Initilaize the systematics itself, basically the manager.
virtual bool IsRelevantTrueObjectForSystematicInToy(const AnaEventC &, const ToyBoxB &, AnaTrueObjectC *, SystId_h syst_index, Int_t branch=0) const
Is this true track relevant for a given systematic (after selection, called for each toy) ...
void LoadPionCrossSections(char *inputFileName, Float_t(&xsec_array)[nIntBins][nMomBins], Float_t(&err_array)[nIntBins][nMomBins])
Method to load external ipon cross section data into arrays.
void FillSystBox(const AnaEventC &event, const SelectionBase &sel, Int_t ibranch)
Fill the SystBox for this event, selection and branch.
Int_t GetNBins()
Get the number of bins.