GReWeightINukeParams.cxx
Go to the documentation of this file.
1 //____________________________________________________________________________
2 /*
3  Copyright (c) 2003-2017, GENIE Neutrino MC Generator Collaboration
4  For the full text of the license visit http://copyright.genie-mc.org
5  or see $GENIE/LICENSE
6 
7  Author: Jim Dobson <J.Dobson07 \at imperial.ac.uk>
8  Imperial College London
9 
10  Costas Andreopoulos <costas.andreopoulos \at stfc.ac.uk>
11  University of Liverpool & STFC Rutherford Appleton Lab
12 
13  For the class documentation see the corresponding header file.
14 
15  Important revisions after version 2.0.0 :
16  @ Sep 10, 2009 - CA
17  Was adapted from Jim's and Costas' T2K-specific GENIE reweighting code.
18  First included in v2.5.1.
19  @ Mar 09, 2010 - JD
20  Protect against negative hadron fate cross section
21  @ Feb 09, 2011 - JD
22  Remove condition that require a non-zero tweak dial when calling SetCurTwkDial
23  as this can lead to old tweak dial value being used inadvertently.
24  @ Feb 08, 2013 - CA
25  Almost complete re-write of GReWeightINukeParams::Fates to improve readability
26  and make sure it handles all special cases correctly.
27 
28 */
29 //____________________________________________________________________________
30 
31 #include <cassert>
32 #include <cstdlib>
33 
34 #include <TMath.h>
35 #include <TLorentzVector.h>
36 
37 #include "Conventions/Controls.h"
40 #include "Messenger/Messenger.h"
41 #include "Conventions/Units.h"
42 #include "Messenger/Messenger.h"
43 #include "Utils/NuclearUtils.h"
44 #include "Numerical/Spline.h"
47 #include "PDG/PDGCodes.h"
49 
50 using namespace genie;
51 using namespace genie::rew;
52 
53 //___________________________________________________________________________
55 {
56  this->Init();
57 }
58 //___________________________________________________________________________
60 {
61 
62 }
63 //___________________________________________________________________________
65 {
70 }
71 //___________________________________________________________________________
74 {
75  if(pdg::IsPion (pdgc)) return fParmPionFates;
76  if(pdg::IsNucleon(pdgc)) return fParmNuclFates;
77  return 0;
78 }
79 //___________________________________________________________________________
82 {
83  if(pdg::IsPion (pdgc)) return fParmPionMFP;
84  if(pdg::IsNucleon(pdgc)) return fParmNuclMFP;
85  return 0;
86 }
87 //___________________________________________________________________________
89 {
90  fParmPionFates -> Reset();
91  fParmNuclFates -> Reset();
92  fParmPionMFP -> Reset();
93  fParmNuclMFP -> Reset();
94 }
95 //___________________________________________________________________________
97 {
98  LOG("ReW", pINFO)
99  << "Reconfiguring weight calculator for pion intranuke fate systematics";
101 
102  LOG("ReW", pINFO)
103  << "Reconfiguring weight calculator for nucleon intranuke fate systematics";
105 }
106 //___________________________________________________________________________
108 {
109  double chisq = 0.;
110 
111  chisq += (fParmPionFates -> ChisqPenalty());
112  chisq += (fParmNuclFates -> ChisqPenalty());
113  chisq += (fParmPionMFP -> ChisqPenalty());
114  chisq += (fParmNuclMFP -> ChisqPenalty());
115 
116  return chisq;
117 }
118 //___________________________________________________________________________
120 {
122  {
123  fParmPionFates->SetTwkDial(syst,val);
124  }
125  else
127  {
128  fParmNuclFates->SetTwkDial(syst,val);
129  }
130  else
132  {
133  fParmPionMFP->SetTwkDial(val);
134  }
135  else
137  {
138  fParmNuclMFP->SetTwkDial(val);
139  }
140 }
141 //___________________________________________________________________________
142 //
143 // Fates nested class
144 //
145 //___________________________________________________________________________
147 {
148  if (ht != kRwINukePion && ht != kRwINukeNucl) {
149  LOG("ReW", pFATAL)
150  << "Mean-free path reweighting code handles only pions or nucleons";
151  exit(1);
152  }
153 
154  fHadType = ht;
155 
156  this->Reset();
157 }
158 //___________________________________________________________________________
160 {
161  this->Reset();
162 }
163 //___________________________________________________________________________
165 {
166  fSystValuesUser.clear();
167  fSystValuesActual.clear();
168  fIsCushion.clear();
169 }
170 //___________________________________________________________________________
172 {
173  this->AddCushionTerms();
174 }
175 //___________________________________________________________________________
177 {
178  // check type of systematic
179  if(!this->IsHandled(syst)) return;
180 
181  // can not explicitly set params designated as cushion terms
182  if(this->IsIncluded(syst)) {
183  if(this->IsCushionTerm(syst)) {
184  LOG("ReW", pWARN)
185  << "You may not set the value of cushion term " << GSyst::AsString(syst)
186  << ". It is set automatically to maintain unitarity";
187  return;
188  }
189  }
190 
191  // update tweaking dial
192  fSystValuesUser[syst] = val;
193  fIsCushion[syst] = false;
194 }
195 //___________________________________________________________________________
197  GSyst_t syst, const TLorentzVector & p4) const
198 {
199  double KE = p4.Energy() - p4.M(); // kinetic energy
200  return this->ScaleFactor(syst, KE);
201 }
202 //___________________________________________________________________________
204  GSyst_t syst, double KE) const
205 {
207 
208  double fractional_error = uncert->OneSigmaErr(syst);
209  double twk_dial = this->ActualTwkDial(syst, KE);
210 
211  double fate_fraction_scale = 1. + twk_dial * fractional_error;
212 
213  LOG("ReW", pNOTICE)
214  << "Systematic " << GSyst::AsString(syst)
215  << " (1 sigma frac. err = " << 100*fractional_error
216  << "%, tweak dial = " << twk_dial
217  << "): Weight = " << fate_fraction_scale;
218 
219  return fate_fraction_scale;
220 }
221 //___________________________________________________________________________
223 {
224  if(! this->IsIncluded(syst)) {
225  LOG("ReW", pWARN)
226  << "Systematic " << GSyst::AsString(syst) << " not included";
227  return 0.;
228  }
229  if(KE < 0.) {
230  LOG("ReW", pERROR) << "Negative kinetic energy! (KE = " << KE << ")";
231  return 0.;
232  }
233 
236 
237  //
238  // initialize actual systematic parameter values to the ones set by the user
239  //
240  fSystValuesActual.clear();
241  fSystValuesActual.insert(fSystValuesUser.begin(), fSystValuesUser.end());
242 
243  //
244  // Check the tweaking dials set by the user.
245  // If a systematic variation is too extreme so that the scale factor for
246  // the corresponding fate fraction becomes negative, automatically re-adjust
247  // the systematic variation so that the scale factor is exactly 0
248  //
249 
250  for(iter = fSystValuesActual.begin(); iter != fSystValuesActual.end(); ++iter)
251  {
252  GSyst_t curr_syst = iter->first;
253  double curr_twk_dial = iter->second;
254 
255  bool curr_is_cushion = this->IsCushionTerm(curr_syst);
256  if(curr_is_cushion) continue;
257 
258  double fractional_frac_err = uncert->OneSigmaErr(curr_syst); // fractional 1 sigma error
259  double frac_scale = 1. + curr_twk_dial * fractional_frac_err;
260 
261  if(frac_scale < 0) {
262  double curr_twk_dial_min = -1/fractional_frac_err;
263  fSystValuesActual[curr_syst] = curr_twk_dial_min;
264  LOG("ReW", pNOTICE)
265  << "Too large systematic variation for non-cushion systematic " << GSyst::AsString(curr_syst)
266  << " makes the corresponding fate scaling factor negative! Taking corrective action...";
267  LOG("ReW", pINFO)
268  << "Tweak dial automatically re-adjusted from " << curr_twk_dial
269  << " to " << curr_twk_dial_min;
270  }
271  }
272 
273  //
274  // Loop over all non-cushion terms and calculate the change in their total fraction
275  //
276 
277  double sum_nocushion_fate_fraction_nom = 0.;
278  double sum_nocushion_fate_fraction_twk = 0.;
279 
280  for(iter = fSystValuesActual.begin(); iter != fSystValuesActual.end(); ++iter)
281  {
282  GSyst_t curr_syst = iter->first;
283  double curr_twk_dial = iter->second;
284 
285  bool curr_is_cushion = this->IsCushionTerm(curr_syst);
286  if(curr_is_cushion) continue;
287 
288  double fractional_frac_err = uncert->OneSigmaErr(curr_syst); // fractional 1 sigma error
289 
290  double frac_scale = 1. + curr_twk_dial * fractional_frac_err;
291  double curr_frac = genie::utils::rew::FateFraction(curr_syst, KE, frac_scale);
292  double nom_frac = genie::utils::rew::FateFraction(curr_syst, KE, 1.);
293 
294  sum_nocushion_fate_fraction_nom += nom_frac;
295  sum_nocushion_fate_fraction_twk += curr_frac;
296 
297  LOG("ReW", pDEBUG)
298  << "Non-cushion systematic " << GSyst::AsString(curr_syst)
299  << " (1 sigma frac. err = " << 100*fractional_frac_err
300  << "%, tweak dial = " << curr_twk_dial
301  << "): fate fraction (KE = " << KE << " GeV) = "
302  << nom_frac << " (nominal), "
303  << curr_frac << " (tweaked)";
304  }
305 
306  LOG("ReW", pDEBUG)
307  << "Sum of non-cushion fate fractions = "
308  << sum_nocushion_fate_fraction_nom << " (nominal), "
309  << sum_nocushion_fate_fraction_twk << " (tweaked)";
310 
311  //
312  // If the systematic variation specified by the user was so extreme that the
313  // tweaked total fate fraction of non-cushion terms is above 1, automatically re-scale the
314  // tweak dials so that the sum of their fate fraction is 1.
315  // In that case, all cushion terms will be set to 0.
316  //
317 
318  if(sum_nocushion_fate_fraction_twk > 1.) {
319 
320  LOG("ReW", pNOTICE)
321  << "Current sum of non-cushion fate fractions = "
322  << sum_nocushion_fate_fraction_twk << " ( > 1 ). "
323  << "Can not maintain unitarity with current set of systematic parameter values. "
324  << "Taking corrective action...";
325 
326  for(iter = fSystValuesActual.begin(); iter != fSystValuesActual.end(); ++iter)
327  {
328  GSyst_t curr_syst = iter->first;
329  double curr_twk_dial = iter->second;
330 
331  bool curr_is_cushion = this->IsCushionTerm(curr_syst);
332 
333  double fractional_frac_err = uncert->OneSigmaErr(curr_syst); // fractional 1 sigma error
334 
335  double frac_scale = 1. + curr_twk_dial * fractional_frac_err;
336  double curr_frac = genie::utils::rew::FateFraction(curr_syst, KE, frac_scale);
337 
338  double curr_frac_new = (curr_is_cushion) ? 0 : curr_frac * (1./sum_nocushion_fate_fraction_twk);
339  double frac_scale_new = genie::utils::rew::WhichFateFractionScaleFactor(curr_syst, KE, curr_frac_new);
340  double curr_twk_dial_new = (frac_scale_new != 0) ? (frac_scale_new - 1.)/fractional_frac_err : 0;
341 
342  fSystValuesActual[curr_syst] = curr_twk_dial_new;
343 
344  LOG("ReW", pINFO)
345  << ((curr_is_cushion) ? " Cushion " : " Non-cushion ")
346  << "systematic " << GSyst::AsString(curr_syst)
347  << ": fate fraction re-adjusted from " << curr_frac << " to " << curr_frac_new
348  << " (tweak dial re-adjusted from " << curr_twk_dial
349  << " to " << curr_twk_dial_new << ")";
350  }
351 
352  // update sum of fate fraction (set at limit)
353  sum_nocushion_fate_fraction_twk = 1.;
354  }
355 
356  //
357  // In the normal case where the sum of all non-cushion terms was less than 1,
358  // leave them as they are. Adjust all all non-cushion terms accordingly so as to respect unitarity.
359  // There are many ways to adjust the cushion terms, if more than one such term exists.
360  // Chose to tweak them so that they all change the same amount, in units of the corresponding 1 sigma error.
361  //
362 
363  else {
364 
365  double delta_fate_fraction =
366  sum_nocushion_fate_fraction_twk - sum_nocushion_fate_fraction_nom;
367 
368  LOG("ReW", pDEBUG)
369  << "Sum of non-cushion fate fractions changed by " << delta_fate_fraction
370  << " - Change to be absorbed by the selected cushion terms...";
371 
372  double sum = 0;
373  for(iter = fSystValuesActual.begin(); iter != fSystValuesActual.end(); ++iter)
374  {
375  GSyst_t curr_syst = iter->first;
376 
377  bool curr_is_cushion = this->IsCushionTerm(curr_syst);
378  if(!curr_is_cushion) continue;
379 
380  double fractional_frac_err = uncert->OneSigmaErr(curr_syst); // fractional 1 sigma error
381  double nom_frac = genie::utils::rew::FateFraction(curr_syst, KE, 1.);
382  sum += (nom_frac * fractional_frac_err);
383  }
384 
385  double twk_dial = -1.*delta_fate_fraction / sum;
386 
387  for(iter = fSystValuesActual.begin(); iter != fSystValuesActual.end(); ++iter)
388  {
389  GSyst_t curr_syst = iter->first;
390  bool curr_is_cushion = this->IsCushionTerm(curr_syst);
391  if(!curr_is_cushion) continue;
392 
393  fSystValuesActual[curr_syst] = twk_dial;
394  }
395 
396  LOG("ReW", pDEBUG)
397  << "To absorb change in the sum of non-cushion fate fractions, "
398  << "the tweaking dial for all cushion terms needs to be set to "
399  << twk_dial;
400 
401  } //check sum_nocushion_fate_fraction_twk < 1.
402 
403  //
404  // Confirm unitarity
405  //
406 
407  LOG("ReW", pINFO) << "Confirming unitarity for current fate fractions";
408 
409  double sum_fate_fraction_all = 0;
410  for(iter = fSystValuesActual.begin(); iter != fSystValuesActual.end(); ++iter)
411  {
412  GSyst_t curr_syst = iter->first;
413  double curr_twk_dial = iter->second;
414 
415  bool curr_is_cushion = this->IsCushionTerm(curr_syst);
416 
417  double fractional_frac_err = uncert->OneSigmaErr(curr_syst); // fractional 1 sigma error
418 
419  double frac_scale = 1. + curr_twk_dial * fractional_frac_err;
420  double curr_frac = genie::utils::rew::FateFraction(curr_syst, KE, frac_scale);
421 
422  LOG("ReW", pINFO)
423  << "Systematic " << GSyst::AsString(curr_syst)
424  << " (1 sigma frac. err = " << 100*fractional_frac_err
425  << "%, tweak dial = " << curr_twk_dial
426  << "): Current fate fraction (KE = " << KE << " GeV) = " << curr_frac
427  << ((curr_is_cushion) ? " ** cushion term ** " : "");
428 
429  sum_fate_fraction_all += curr_frac;
430  }
431 
432  LOG("ReW", pINFO) << "Current sum of all fate fractions = " << sum_fate_fraction_all;
433 
434  if(TMath::Abs(sum_fate_fraction_all-1) > 0.01) {
435  LOG("ReW", pWARN) << "Unitarity violation level exceeded 1 part in 100.";
436  LOG("ReW", pWARN) << "Current sum of all fate fractions = " << sum_fate_fraction_all;
437  }
438 
439  map<GSyst_t, double>::const_iterator dial_iter = fSystValuesActual.find(syst);
440  return dial_iter->second;
441 }
442 //___________________________________________________________________________
444 {
445 // calculate an average chisq over the energy range
446 
447  double chisq = 0.0;
448 
449  const int kN = 200;
450  const double kKEmin = 0;
451  const double kKEmax = 10;
452  const double kKEstep = (kKEmax-kKEmin)/(kN-1);
453 
454  GSyst_t syst = kNullSystematic;
455  int i=0;
456  while( (syst = (fHadType == kRwINukePion) ?
459  ) != kNullSystematic
460  )
461  {
462  for(int j=0; j < kN; j++) {
463  double KE = kKEmin + j * kKEstep;
464  double twkdial = this->ActualTwkDial(syst,KE);
465  chisq += (TMath::Power(twkdial, 2.));
466  }//j
467  }//i
468 
469  chisq /= kN;
470 
471  return chisq;
472 }
473 //___________________________________________________________________________
475 {
476  map<GSyst_t, double>::const_iterator iter = fSystValuesUser.find(syst);
477  if(iter != fSystValuesUser.end()) return true;
478  return false;
479 }
480 //___________________________________________________________________________
482 {
483  map<GSyst_t, bool>::const_iterator iter = fIsCushion.find(syst);
484  if(iter != fIsCushion.end()) return iter->second;
485 
486  LOG("ReW", pERROR)
487  << "Cannot query 'is cushion' flag for systematic "
488  << GSyst::AsString(syst);
489 
490  return false;
491 }
492 //___________________________________________________________________________
494 {
495  if(!this->IsHandled (syst)) return false;
496  if(!this->IsIncluded(syst)) return false;
497 
498  map<GSyst_t, double>::const_iterator dial_iter = fSystValuesUser.find(syst);
499  double dial = dial_iter->second;
500 
501  bool tweaked = (TMath::Abs(dial) >= controls::kASmallNum);
502 
503  LOG("ReW", pDEBUG)
504  << "Systematic " << GSyst::AsString(syst)
505  << ((tweaked) ? " was " : " was not ")
506  << "tweaked (tweaking dial = " << dial << ")";
507 
508  return tweaked;
509 }
510 //___________________________________________________________________________
512 {
513  GSyst_t syst = kNullSystematic;
514  int i=0;
515  while( (syst = (fHadType == kRwINukePion) ?
518  ) != kNullSystematic
519  )
520  {
521  bool tweaked = this->IsTweaked(syst);
522  if(tweaked) return true;
523  }
524  return false;
525 }
526 //___________________________________________________________________________
528 {
529 //
530 //
531  if(fHadType == kRwINukePion) {
532  if(GSyst::IsINukePionFateSystematic(syst)) return true;
533  }
534  else
535  if(fHadType == kRwINukeNucl) {
536  if(GSyst::IsINukeNuclFateSystematic(syst)) return true;
537  }
538 
539  LOG("ReW", pWARN)
540  << "Can not handle systematic " << GSyst::AsString(syst);
541 
542  return false;
543 }
544 //___________________________________________________________________________
546 {
547 // When this method is called for the first time, all terms not already
548 // included are included as cushion terms.
549 // On every call, the values of the cushion terms are reset.
550 
551  LOG("ReW", pINFO) << "Adding cushion terms...";
552 
553  int ncushions = 0;
554 
555  GSyst_t syst = kNullSystematic;
556  int i=0;
557  while( (syst = (fHadType == kRwINukePion) ?
560  ) != kNullSystematic
561  )
562  {
563  if(this->IsIncluded(syst)) {
564  bool is_cushion = this->IsCushionTerm(syst);
565  LOG("ReW", pINFO)
566  << "Systematic " << GSyst::AsString(syst)
567  << " was already specified as a"
568  << ((is_cushion) ? " cuhsion " : " non-cushion ")
569  << "term";
570  if(is_cushion) {
571  fSystValuesUser[syst] = 0.;
572  ncushions++;
573  }
574  }
575  else {
576  LOG("ReW", pINFO)
577  << "Adding systematic " << GSyst::AsString(syst)
578  << " as a cushion term.";
579 
580  fSystValuesUser[syst] = 0.;
581  fIsCushion[syst] = true;
582  ncushions++;
583  }
584  } // end loop over relevant fate systematics
585 
586  LOG("ReW", pINFO) << "Number of cushion terms = " << ncushions;
587 
588  if(ncushions <= 0) {
589  LOG("ReW", pFATAL)
590  << "There must be at least one cushion term ("
591  << ncushions << " were set)";
592  exit(1);
593  }
594 }
595 //___________________________________________________________________________
596 //
597 // MFP nested class
598 //
599 //___________________________________________________________________________
601 {
602  assert(ht != kRwINukeUndefined);
603 
604  fHadType = ht;
605 
606  // get corresponding GSyst_t param
607  if (ht == kRwINukePion) fSyst = kINukeTwkDial_MFP_pi;
608  else if (ht == kRwINukeNucl) fSyst = kINukeTwkDial_MFP_N;
609  else {
610  LOG("ReW", pFATAL)
611  << "Mean-free path reweighting code handles only pions or nucleons";
612  exit(1);
613  }
614 
615  this->Reset();
616 }
617 //___________________________________________________________________________
619 {
620 
621 }
622 //___________________________________________________________________________
624 {
625  if(! this->IsTweaked()) return 1.;
626 
628 
629  double mfp_sigma = uncert->OneSigmaErr(fSyst);
630  double mfp_twkdial = this->TwkDial();
631  double scale_factor = 1.0 + mfp_sigma * mfp_twkdial;
632 
633  return scale_factor;
634 }
635 //___________________________________________________________________________
637 {
638  return fTwkDial;
639 }
640 //___________________________________________________________________________
642 {
643  return fIsIncluded;
644 }
645 //___________________________________________________________________________
647 {
648  return (TMath::Abs(fTwkDial) >= controls::kASmallNum);
649 }
650 //___________________________________________________________________________
652 {
653  return TMath::Power(fTwkDial, 2.0);
654 }
655 //___________________________________________________________________________
657 {
658  fTwkDial = 0.0;
659  fIsIncluded = false;
660 }
661 //___________________________________________________________________________
663 {
664  fTwkDial = val;
665  fIsIncluded = true;
666 }
667 //___________________________________________________________________________
668 
bool IsPion(int pdgc)
Definition: PDGUtils.cxx:296
bool IsTweaked(void) const
is any param tweaked
double WhichFateFractionScaleFactor(genie::rew::GSyst_t syst, double kinE, double fate_frac)
#include "Numerical/GSFunc.h"
Definition: AlgCmp.h:26
#define pERROR
Definition: Messenger.h:50
MFP * MeanFreePathParams(int pdgc) const
bool IsNucleon(int pdgc)
Definition: PDGUtils.cxx:316
#define pFATAL
Definition: Messenger.h:47
bool IsCushionTerm(GSyst_t s) const
is it a cushion term?
double OneSigmaErr(GSyst_t syst, int sign=0) const
static bool IsINukeNuclMeanFreePathSystematic(GSyst_t syst)
Definition: GSyst.h:418
MFP(HadronType_t hadtype=kRwINukeUndefined)
#define LOG(stream, priority)
A macro that returns the requested log4cpp::Category appending a string (using the FILE...
Definition: Messenger.h:87
intermediate_table::const_iterator const_iterator
void SetTwkDial(GSyst_t s, double val)
bool IsIncluded(GSyst_t s) const
is included?
static const double kASmallNum
Definition: Controls.h:40
An enumeration of systematic parameters.
#define pINFO
Definition: Messenger.h:53
#define pWARN
Definition: Messenger.h:51
tweak mean free path for nucleons
Definition: GSyst.h:125
tweak mean free path for pions
Definition: GSyst.h:124
static GSyst_t NextPionFateSystematic(int i)
Definition: GSyst.h:447
double ScaleFactor(void) const
mean free path scale factor = 1 + twk_dial * fractional_err
double TwkDial(void) const
current value of mfp tweak dial
static string AsString(GSyst_t syst)
Definition: GSyst.h:175
static bool IsINukePionFateSystematic(GSyst_t syst)
Definition: GSyst.h:347
static bool IsINukePionMeanFreePathSystematic(GSyst_t syst)
Definition: GSyst.h:404
Fates(HadronType_t hadtype=kRwINukeUndefined)
double ScaleFactor(GSyst_t s, const TLorentzVector &p4) const
see next
double FateFraction(genie::rew::GSyst_t syst, double kinE, double frac_scale_factor=1.)
static GSystUncertainty * Instance(void)
#define pNOTICE
Definition: Messenger.h:52
enum genie::rew::GReWeightINukeParams::EHadronType HadronType_t
static bool IsINukeNuclFateSystematic(GSyst_t syst)
Definition: GSyst.h:364
double ActualTwkDial(GSyst_t s, double KE=-1.) const
actual tweaking dial for input systematic at input kinetic energy
Most commonly used PDG codes. A set of utility functions to handle PDG codes is provided in PDGUtils...
static GSyst_t NextNuclFateSystematic(int i)
Definition: GSyst.h:458
#define pDEBUG
Definition: Messenger.h:54