source.cc
1 #include <map>
2 #include <string>
3 #include <iostream>
4 
5 #include "TMath.h"
6 #include "TObjString.h"
7 #include "TTimeStamp.h"
8 #include "TH1.h"
9 #include "TFile.h"
10 #include "TTree.h"
11 #include "EnvironmentVariable.h"
12 #include "Math/WrappedTF1.h"
13 #include "Math/GaussIntegrator.h"
14 
15 #include "blazars/fava.h"
16 #include "source.hh"
17 #include "icemc_random.h"
18 
19 // SourceModel
20 
21 
22 SourceModel::SourceModel(const char * n)
23  : name(n)
24 {
25  weight_Emin = 0;
26  weight_Emax = 0;
27  average_flux = -1;
28  average_nonzero_flux= -1;
29 }
30 
31 SourceModel::~SourceModel()
32 {
33  for (unsigned i = 0; i < sources.size(); i++) delete sources[i];
34 }
35 
36 
37 // convert redshift to MPC
38 static double luminosity_distance(double z)
39 {
40  static TF1 * f_comoving = 0;
41  if (!f_comoving)
42  {
43  f_comoving = new TF1("comoving_distance", "[0] / sqrt([1]*(1+x)^4 + [2] * (1+x)^3 + [3] * (1+x)^2 + [4])", 0,10);
44  f_comoving->SetParameters(67.74, 5e-5, 0.3089, 0, 0.6911);
45  }
46 
47  double d_M = f_comoving->Integral(0,z);
48  return (1+z) * d_M;
49 }
50 
51 
52 // TXS blazar (from icecube)
53 const double txs_index = 2;
54 const double txs_norm = 2.2e-8;
55 const double txs_E0 = 100e3;
56 const double txs_flux = 3.8;
57 const double txs_z = 0.3365;
58 const double txs_D = luminosity_distance(txs_z);
59 
60 
61 // M77
62 const double m77_index = 3.16;
63 
64 
65 
66 // SNe
67 const double gamma_index = 2;
68 
69 double SourceModel::Restriction::fromString(const char * the_time)
70 {
71  printf("PARSING \"%s\"\n",the_time);
72  // Start times
73  if(strcasestr(the_time, "A4_MIN"))
74  {
75  return 1480707643;
76  }
77  else if(strcasestr(the_time, "A3_MIN"))
78  {
79  return 1418938406;
80  }
81  else if(strcasestr(the_time,"A4_MAX"))
82  {
83  return 1483004563;
84  }
85  else if(strcasestr(the_time, "A3_MAX"))
86  {
87  return 1420777814;
88  }
89  else if(strcasestr(the_time,"MAX"))
90  {
91  return 2147483646;
92  }
93 
94  return atof(the_time);
95 }
96 
97 
98 
100 {
101 
102  if (!key || !strcasecmp(key,"NONE")) return NULL;
103 
104  SourceModel * m = new SourceModel(key);
105 
106  TString str(key);
107  TObjArray * tokens = str.Tokenize("+");
108 
109 
110  for (int i =0; i < tokens->GetEntries(); i++)
111  {
112  TObjString * tok = (TObjString*) tokens->At(i);
113 
114  TString stripped( tok->String().Strip( TString::kBoth));
115 
116  // The flux from TXS
117  if (stripped == "TXS")
118  {
119 
120  double txs_ra = 77.3582; // decimal degrees
121  double txs_dec = 5.69314; // decimal degrees
122 
123  m->addSource(new Source("TXS 0506+056", txs_ra/15., txs_dec,
124  new ConstantExponentialSourceFlux(txs_index,txs_flux,txs_E0) //from IceCube paper, assume it's constant over flight for now
125  )) ;
126  }
127 
128  else if (stripped.BeginsWith("M77"))
129  {
130 
131  double index = m77_index;
132  const char * index_string = strcasestr(stripped.Data(),"INDEX=");
133  if (index_string)
134  {
135  sscanf(index_string,"HOURS=%lf%*s", &index);
136 
137 
138  }
139  m->addSource(new Source("M77", 2 + 42./60 + 40.771/3600, -47.84/3600, new ConstantExponentialSourceFlux(index, txs_flux, txs_E0))); //flux and E0 don't matter
140  }
141 
142  //A3 SN
143  else if(stripped=="A3SN")
144  {
145  TFile f("./supernovae/data/supernovaA3.root");
146  TTree *tree = (TTree*) f.Get("sn");
147  std::string *objName = new std::string;
148  std::string *objType = new std::string;
149  int tdiscovery;
150  int texplode;
151  double ra, dec;
152 
153  tree->SetBranchAddress("ra",&ra);
154  tree->SetBranchAddress("dec",&dec);
155  tree->SetBranchAddress("tdiscovery",&tdiscovery);
156  tree->SetBranchAddress("texplode",&texplode);
157  tree->SetBranchAddress("name",&objName);
158  tree->SetBranchAddress("type",&objType);
159  int two_weeks = 24*3600*14;
160 
161  for (int i = 0; i < tree->GetEntries(); i++)
162  {
163  tree->GetEntry(i);
164  if( abs(dec)>r.maxDec){continue;}
165  m->addSource(new Source(objName->c_str(), ra, dec,
166  new TimeWindowedExponentialSourceFlux(texplode, texplode+two_weeks, 2,txs_flux,txs_E0) // Just use these params as the first step
167  )) ;
168  }
169 
170  delete objName;
171  delete objType;
172  }
173  // Supernovae
174  else if(stripped=="SN")
175  {
176  TFile *file = new TFile("./supernovae/data/supernovaeTNS.root");
177  TTree *tree = (TTree*) file->Get("tnsTree");
178 
179  // Tree vars
180  float ra, dec;
181  std::string *objName = new std::string;
182  std::string *fullObjType = new std::string;
183  std::string *objSubtype = new std::string;
184  int discoveryUnixTime = 0;
185 
186  tree->SetBranchAddress("ra",&ra);
187  tree->SetBranchAddress("dec",&dec);
188  tree->SetBranchAddress("name",&objName);
189  tree->SetBranchAddress("fullObjType",&fullObjType);
190  tree->SetBranchAddress("objSubtype",&objSubtype);
191  tree->SetBranchAddress("discoveryUnixTime",&discoveryUnixTime);
192 
193  int two_weeks = 24*3600*14;
194  //int two_days = 24*3600*2;
195  for (unsigned int i = 0; i < tree->GetEntries(); i++)
196  {
197  tree->GetEntry(i);
198 
200  // Time cut for finding sources within a certain time period
201  // NOTE we are working with _neutrinos_ instead of photons. Neutrinos reach Earth before photons because they escape the star before the shockwaves of the supernova explosion.
202  // assume photons arrive, at max, 2 weeks after the neutrinos
203  // discoveryUnixTime is the discovery time of the supernova using photons, not neutrinos, thus:
204  if( (discoveryUnixTime < r.whichStartTime) || (discoveryUnixTime > r.whichEndTime + two_weeks) ){continue;}
205  // Declination cut (ANITA won't see neutrinos from these sources)
206  if( abs(dec)>r.maxDec){continue;}
207  // Search for specific subtype
208  if(strcasecmp(r.whichSources,"All"))
209  {
210  // Account for the chosen one
211  if(strcasestr(r.whichSources, objName->c_str()))
212  {
213  std::cout << "Using the specified source: " << objName << std::endl;
214  }
215  else{continue;}
216  }
217  // Only look at a single subtype (REMEMBER this searches for a complete string
218  // but SNII can take the formats of: SN IIa ,SN IIb, SN IIa-91bg-like, etc)
219  if(strcasecmp(r.whichSubtype,"All"))
220  {
221  if(strcasestr(r.whichSubtype,objSubtype->c_str()))
222  {
223  //std::cout << "Using the specified subtype: " << whichSubtype << std::endl;
224  }
225  else{continue;}
226  }
227 
228  m->addSource(new Source(objName->c_str(), ra/=15., dec,
229  new TimeWindowedExponentialSourceFlux(discoveryUnixTime - two_weeks, discoveryUnixTime, 2,txs_flux,txs_E0) // Just use these params as the first step
230  )) ;
231  }
232 
233  }
234 
235  else if (stripped.BeginsWith("GRB"))
236  {
237  TFile *file = new TFile("./GRBs/data/GRBIceCube.root");
238  TTree *tree = (TTree*) file->Get("iceCubeTree");
239 
240  // Tree vars
241  float RA, dec;
242  std::string *objName = new std::string;
243  int unixTriggerTime = 0;
244  float t90, fluence, redshift;
245 
246  tree->SetBranchAddress("RA",&RA);
247  tree->SetBranchAddress("dec",&dec);
248  tree->SetBranchAddress("name",&objName);
249  tree->SetBranchAddress("unixTriggerTime",&unixTriggerTime);
250  tree->SetBranchAddress("T90",&t90);
251  tree->SetBranchAddress("fluence",&fluence);
252  tree->SetBranchAddress("redshift",&redshift);
253 
254  for (unsigned int i = 0; i < tree->GetEntries(); i++)
255  {
256  tree->GetEntry(i);
257 
258  if (fluence < 0) fluence = 1e-6;
259 
261  // Time cut for finding sources within a certain time period
262  if( (unixTriggerTime < r.whichStartTime) || (unixTriggerTime > r.whichEndTime) ){continue;}
263  // Declination cut (ANITA won't see neutrinos from these sources)
264  if( abs(dec)>r.maxDec){continue;}
265 
266  printf("%d\n", unixTriggerTime);
267 
268  // Search for specific subtype
269  if(strcasecmp(r.whichSources,"All"))
270  {
271  // Account for the chosen one
272  if(strcasestr(r.whichSources,objName->c_str()))
273  {
274  std::cout << "Using the specified source: " << objName << std::endl;
275  }
276  else{continue;}
277  }
278 
279  // Calculate the explicit maximum neutrino energy via fireball model
280  bool modelCalculation = true;
281  double energyCutoff = 1e10;
282  if(modelCalculation==true)
283  {
284 
285  // Cosmology
286  double omegaRad = 9.236e-5;
287  double omegaM = 0.315;
288  double omegaLambda = 0.679;
289  double H0 = 67.4 * pow(10,3); // We want this in ms^-1/Mpc, not the standard kms^-1/Mpc
290  double c = 299792458; // ms^-1, as we we calculate c/H0
291  double EGammaIso = 0.;
292  double dL = 0.; // we will calculate in Mpc
293  double MpcToCm = 3.08e+24;
294 
295  // GRB model vars
296  double r = 0.;
297 
298  // Model assumptions
299  double lorentzFactor = 300.;
300  double eB = 0.1;
301  double eE = 0.1;
303  double nEx = 1.; // cm^-3
304  double EKinIso = 0.;
305  double shockLorentzFactor = 0.;
306  double energyCGamma = 0.;
307  double fPiAG = 0.;
308  double energyBreakNeutrinoAG = 0.;
309  double shockRadius = 0.;
310  double massProton = 0.0015; // erg/c^2
311  double magFieldShock = 0.;
312  double maxShockNuE = 0.;
313 
314  // GRB spectra calculation starts
315  // Some T90s are not recorded, set to 20s.
316  if(t90==-999){t90 = 20;}
317  // Some redshifts are not calculated, set to 2.
318  if(redshift==-999){redshift = 2;}
319  // Just set the fluence to this for now
320  if(fluence==-999){fluence=1e-6;}
321 
322  // Cosmology stuff
323  // Assume flat Universe
324  // Curvature is k = 0, omegaK = 0
325  TF1 Ez("E(z)", "1/(pow([0] * pow((1+x),3) + [1] + [2] * pow((1+x),4),0.5))", 0, redshift);
326  //TF1 Ez("E(z)", "1/(pow([0] * pow((1+x),3) + [1],0.5))", 0, redshift);
327  Ez.SetParameter(0,omegaM); Ez.SetParName(0,"omegaM");
328  Ez.SetParameter(1,omegaLambda); Ez.SetParName(1,"omegaLambda");
329  Ez.SetParameter(2,omegaRad); Ez.SetParName(2,"omegaRad");
330  ROOT::Math::WrappedTF1 wEz(Ez);
331  ROOT::Math::GaussIntegrator ig;
332  ig.SetFunction(wEz);
333  ig.SetRelTolerance(0.001);
334  double integral = ig.Integral(0, redshift);
335 
336  // Now calc lum dist
337  dL = (1+redshift) * c/H0 * integral; // Mpc
338 
339  // Calc gamma-ray bolometric energy
340  EGammaIso = 4 * TMath::Pi() * dL*dL * fluence * 1/(1+redshift); // Mpc^2 * erg cm^-2
341  EGammaIso*=pow(MpcToCm,2); // erg
342 
344  // Afterglow specific calcs
346 
347  // Calculate total jet kinetic energy and bulk Lorentz factor of GRB afterglow
348  EKinIso = EGammaIso/eE; //erg
349 
350  // Radii, depths
351  shockRadius = pow(((3*EKinIso)/(4*TMath::Pi() * nEx * massProton * c*c * lorentzFactor*lorentzFactor)),(1./3.)); // in cm
352  shockRadius*=100; // in meters, to compare with internal rad
353  if(shockRadius < r)
354  {
355  std::cout << "Afterglow shock radius < burst radius. Something might be wrong." << std::endl;
356  }
357 
358  shockRadius/=100;
359  magFieldShock = pow(8 * TMath::Pi() * eB * nEx * massProton,0.5);
360 
361  shockLorentzFactor = 195 * pow(((EKinIso)/(pow(10,54))),0.125) * pow(t90/10,-0.375) * pow(nEx/1.,-0.125);
362 
363  // Peak sync gamma energy radiated by electrons in B field:
364  energyCGamma = 0.4 * pow(((EKinIso)/(pow(10,54))),(-25./24.)) * pow(((lorentzFactor)/(300)),(4./3.)) * pow(((t90)/(10)),(9./8.)) * pow(((nEx)/(1)),(-11./24.));
365 
366  // Calculate proton-to-pion conversion factor
367  fPiAG = 0.2;
368  //fPiAG = 0.2 * pow(((EKinIso)/(pow(10,54))),(33./24.)) * pow(t90/10,(-9./8.)) * pow(nEx/1,(9./8.));
369 
370  // convert to GeV
371  energyCGamma/=1e+9;
372  // break energies
373  energyBreakNeutrinoAG = 0.015 * shockLorentzFactor * 1/(1+redshift) * pow(energyCGamma,-1); // GeV
374  // max shock neutrino energy
375  maxShockNuE = fPiAG/(4*(1+redshift)) * eE * shockLorentzFactor * magFieldShock * shockRadius;
376 
377  // normal case
378  if(maxShockNuE > energyBreakNeutrinoAG)
379  {
380  // All good
381  }
382  else if(maxShockNuE < 1e9) // account for minimum possible energy
383  {
384  maxShockNuE = 1e9;
385  }
386  else
387  {
388  maxShockNuE = energyBreakNeutrinoAG+energyBreakNeutrinoAG*0.01;
389  }
390 
391  energyCutoff = maxShockNuE;
392 
393  }
394 
395  double afterglow_hrs = 1;
396 
397  const char * hours_string = strcasestr(stripped.Data(),"HOURS=");
398  if (hours_string)
399  {
400  sscanf(hours_string, "HOURS=%lf%*s", &afterglow_hrs);
401  printf("AFTERGLOW HOURS is %f\n", afterglow_hrs);
402  }
403 
404 
405  if (strcasestr(stripped.Data(),"AFTERGLOW") )
406  {
407  m->addSource(new Source(objName->c_str(), RA/=15., dec,
408  new TimeWindowedExponentialSourceFlux(unixTriggerTime,
409  unixTriggerTime + 3600*afterglow_hrs,1.5,
410  fluence,1e9, energyCutoff)
411  ));
412 
413  }
414  else if (strcasestr(stripped.Data(),"PROMPT") )
415  {
416  m->addSource(new Source(objName->c_str(), RA/=15., dec,
417  //fireball model for E > 1e18 eV, I think
418  new TimeWindowedExponentialSourceFlux(unixTriggerTime,
419  unixTriggerTime + t90,4,
420  fluence,1e9)
421  )) ;
422 
423  }
424 
425  // conservative case... 5 minutes before, one hour after, use afterglow flux up to 1e10 GeV.
426  else
427  {
428  m->addSource(new Source(objName->c_str(), RA/=15., dec,
429  //fireball model for E > 1e18 eV, I think
430  new TimeWindowedExponentialSourceFlux(unixTriggerTime-300,
431  unixTriggerTime + 3600*afterglow_hrs,1.5,
432  fluence,1e9, 1e10)
433  )) ;
434 
435  }
436 
437  }
438  }
439 
440  else if (stripped.BeginsWith("FAVA"))
441  {
442  // Let's load all the blazars from FAVA that occurred during a flight
443 
444  bool flux_weighted = strcasestr(stripped.Data(),"FLUX");
445  bool z_weighted = strcasestr(stripped.Data(),"REDSHIFT");
446 
447  const char * dir = EnvironmentVariable::ICEMC_SRC_DIR() ?: ".";
448 
449  TFile ffava(Form("%s/blazars/fava.root", dir));
450  TTree * fava_tree = (TTree*) ffava.Get("fava");
451  FAVAEntry *fava = 0;
452 
453  fava_tree->SetBranchAddress("fava",&fava);
454 
455  for (int i = 0; i < fava_tree->GetEntries(); i++)
456  {
457  fava_tree->GetEntry(i);
458 
459  // time cut
460  if( (fava->unix_tmax < r.whichStartTime) || (fava->unix_tmin > r.whichEndTime) ){continue;}
461 
462  const char * fava_name = fava->association.GetString().Data();
463 
464  // Skip flares that are not associated with a source
465  if(strcasestr(fava_name,"None"))
466  {
467  continue;
468  }
469 
470  // If we didn't specific all sources
471 
472  if(strcasecmp(r.whichSources,"All"))
473  {
474  // Account for the chosen one
475  if(strcasestr(r.whichSources,fava_name))
476  {
477  std::cout << "Using the specified source: " << fava_name << std::endl;
478  }
479  else{continue;}
480  }
481 
482 
483  //say no to |dec| >30
484  //if (fabs(fava->dec) > r.maxDec) continue;
485 
486  //say no to low HE flux
487 
488  //if (fava->he_sigma < 4 && fava->he_flux < 0) continue;
489  //printf("passed flux cut \n");
490 
491  //only blazars
492  if (fava->source_class.GetString() == "bcu" || fava->source_class.GetString() == "fsrq" || fava->source_class.GetString() == "bll")
493  {
494 
495  m->addSource(new Source(fava_name, (fava->ra)/15., fava->dec,
496  new TimeWindowedExponentialSourceFlux( fava->unix_tmin, fava->unix_tmax, txs_index,
497  txs_norm * (flux_weighted ? fava->he_flux / txs_flux : z_weighted ? txs_flux * pow(txs_D/luminosity_distance(fava->z),2): 1), txs_E0))
498  );
499 
500  }
501  }
502  }
503  }
504 
505  delete tokens;
506 
507  std::cout << "----------------------" << std::endl;
508 
509  if (!m->getNSources())
510  {
511  fprintf(stderr,"WARNING: no sources added for key %s\n", key);
512  delete m;
513  m = NULL;
514  }
515  else
516  {
517  std::cout << "Sources added: " << m->getNSources() << std::endl;
518  }
519 
520  return m;
521 }
522 
523 TH1 * SourceModel::estimateFlux(double tmin, double tmax, double Emin, double Emax, int nbins, int N)
524 {
525  double from = log10(Emin);
526  double to = log10(Emax);
527 
528  TH1D * spectrum = new TH1D("spec", name, nbins, from, to);
529  spectrum->SetDirectory(0);
530  spectrum->GetXaxis()->SetTitle("log10 (E GeV)");
531  TRandom * rng = getRNG(RNG_SOURCE);
532  for (int i = 0; i < N; i++)
533  {
534  double t = rng->Uniform(tmin,tmax);
535  for (unsigned j = 0; j < sources.size(); j++)
536  {
537  //figure out the flux between each energy bin
538  for (int E = 1; E<= nbins; E++)
539  {
540  double l = TMath::Power(10,spectrum->GetBinLowEdge(E));
541  double h = TMath::Power(10,spectrum->GetBinLowEdge(E) + spectrum->GetBinWidth(E));
542  spectrum->Fill(spectrum->GetBinCenter(E), sources[j]->getFlux()->getFluxBetween(l,h,t));
543  }
544  }
545  }
546 
547  spectrum->Scale(1./N);
548  return spectrum;
549 }
550 
551 
552 void SourceModel::computeFluxTimeChanges(std::vector<double> * changes) const
553 {
554 
555  changes->clear();
556 
557  for (unsigned i = 0; i < sources.size(); i++)
558  {
559  sources[i]->getFlux()->getFluxTimeChanges(changes);
560  }
561 
562  //sort and remove dupes
563  std::sort(changes->begin(), changes->end());
564  std::vector<double>::iterator it = std::unique(changes->begin(),changes->end());
565  changes->resize(std::distance(changes->begin(),it));
566 }
567 
568 int SourceModel::getDirectionAndEnergy( Vector * nudir, double t, double & nuE, double minE, double maxE)
569 {
570  if (!sources.size()) return -1;
571 
572  bool fixedEnergy = minE==maxE;
573  if (fixedEnergy) nuE = minE;
574 
575  //pick a random source, weighted by the flux
576  double total_flux = 0 ;
577  std::vector<double> fluxes(sources.size());
578 
579  for (unsigned i = 0; i < sources.size(); i++)
580  {
581  double f = fixedEnergy ? sources[i]->getFlux()->getFlux(nuE,t) : sources[i]->getFlux()->getFluxBetween(minE,maxE,t);
582  total_flux += f;
583  fluxes[i] = total_flux;
584  }
585 
586  TRandom * rng = getRNG(RNG_SOURCE);
587  double random = rng->Uniform(0, total_flux);
588  unsigned index = std::upper_bound(fluxes.begin(), fluxes.end(), random) - fluxes.begin();
589 
590 
591  // printf("random: %g total_flux%g, index:%u \n",random, total_flux, index);
592  if (total_flux == 0)
593  {
594  nuE = minE; // do something...
595  return -1;
596  }
597  // This is the chosen source, so we should retain some info about it
598  // Then we can easily access info about *each* simulated neutrino's origin
599  const Source * which = sources[index];
600  //std::cout << objName << std::endl;
601  //std::cout << RA << std::endl;
602  //std::cout << dec << std::endl;
603 
604  if (nudir) *nudir = which->getDirection(t);
605 
606  if (!fixedEnergy) nuE = which->getFlux()->pickEnergy(minE,maxE,t,rng);
607 
608  return index;
609 }
610 
611 
612 Source::Source(const char * nm, double ra, double dc, SourceFlux * f)
613 : name(nm), flux(f) , RA(ra), dec(dc*TMath::DegToRad())
614 {
615 }
616 
617 Vector Source::getDirection(double t) const
618 {
619 
620 #if ROOT_VERSION_CODE < ROOT_VERSION(6,0,0)
621  std::cerr << "ROOT 5 doesn't have AsLMST. Returning nonsense." << std::endl;
622  return Vector(1,0,0);
623 #else
624 
625  time_t secs = t;
626  int nsecs = 1e9 *(t-secs);
627  TTimeStamp ts(secs, nsecs);
628  double lst = ts.AsLMST(-90);
629  double h = lst - RA;
630  h *= (TMath::Pi()/12);
631  return Vector(cos(h) * cos(dec), sin(h) * cos(dec), sin(dec));
632 #endif
633 }
634 
635 
636 ConstantExponentialSourceFlux::ConstantExponentialSourceFlux(double e, double norm, double normE)
637  : gamma(e) , f("f", "[0] * x^-[1]",1e9,1e12)
638 {
639  //figure out what A is
640  A = norm * TMath::Power(normE,gamma);
641  f.SetParameters(A,gamma);
642 }
643 
644 double ConstantExponentialSourceFlux::pickEnergy(double Emin, double Emax, double t, TRandom * rng) const
645 {
646  (void) t;
647  TRandom * old = gRandom;
648  gRandom = rng;
649  if (Emin == Emax) return Emin;
650 
651  //I'm too bad at math to figure out the proper quantile function here. It's probably trivial, but this works and isn't THAT slow.
652  if (f.GetXmin() != Emin || f.GetXmax() !=Emax) f.SetRange(Emin,Emax);
653 
654  double E = f.GetRandom();
655  gRandom = old;
656  return E;
657 }
658 
659 
660 TimeWindowedExponentialSourceFlux::TimeWindowedExponentialSourceFlux(double t0, double t1, double e, double norm, double normE, double cutoff)
661  : gamma(e) , f("f", "[0] * x^-[1]",1e9,cutoff ? cutoff : 1e12) , t0(t0), t1(t1) , cutoff(cutoff)
662 {
663  //figure out what A is
664  A = norm * TMath::Power(normE,gamma);
665  f.SetParameters(A,gamma);
666 }
667 
668 double TimeWindowedExponentialSourceFlux::pickEnergy(double Emin, double Emax, double t, TRandom * rng) const
669 {
670  if (t < t0 || t > t1) return 0;
671  TRandom * old = gRandom;
672  gRandom = rng;
673  if (cutoff && Emax > cutoff) Emax = cutoff;
674 
675  //I'm too bad at math to figure out the proper quantile function here. It's probably trivial, but this works and isn't THAT slow.
676  if (f.GetXmin() != Emin || f.GetXmax() !=Emax) f.SetRange(Emin,Emax);
677 
678  double E = f.GetRandom();
679  gRandom = old;
680  return E;
681 }
682 
683 
684 void SourceModel::setUpWeights(double t0, double t1, double minE, double maxE, int N)
685 {
686 
687  average_flux = 0;
688  average_nonzero_flux = 0;
689 
690  per_source_average_flux.clear();
691  per_source_average_nonzero_flux.clear();
692  per_source_average_flux.resize(sources.size());
693  per_source_average_nonzero_flux.resize(sources.size());
694  std::vector<int> Nnonzero_per_source(sources.size());
695  int Nnonzero = 0;
696 
697  weight_Emin = minE;
698  weight_Emax = maxE;
699 
700  TRandom * rng = getRNG(RNG_SOURCE);
701  for (int i =0; i < N; i++)
702  {
703  double t = rng->Uniform(t0,t1);
704  double sumFlux = 0;
705  for (unsigned j = 0; j < sources.size(); j++)
706  {
707  double dFlux = minE == maxE ? sources[j]->getFlux()->getFlux(minE, t) : sources[j]->getFlux()->getFluxBetween(weight_Emin,weight_Emax,t);
708  sumFlux += dFlux;
709 
710  average_flux += dFlux/N;
711  per_source_average_flux[j]+=dFlux/N;
712  average_nonzero_flux += dFlux;
713 
714  if (dFlux!=0)
715  {
716  Nnonzero_per_source[j]++;
717  per_source_average_nonzero_flux[j]+= dFlux;
718  }
719  }
720  if (sumFlux != 0)
721  {
722  Nnonzero++;
723  }
724  }
725 
726  for (unsigned j = 0; j < sources.size(); j++)
727  {
728  if (per_source_average_nonzero_flux[j]) per_source_average_nonzero_flux[j] /= Nnonzero_per_source[j];
729  }
730 
731  if (average_nonzero_flux) average_nonzero_flux /= Nnonzero;
732  printf("Set up weights: average flux is %g (average non-zero flux: %g)\n", average_flux, average_nonzero_flux);
733 }
734 
735 double SourceModel::getPerSourceTimeWeight(double t, int i, bool use_average_nonzero_flux) const
736 {
737  double weight = weight_Emin == weight_Emax ? sources[i]->getFlux()->getFlux(weight_Emin,t)
738  : sources[i]->getFlux()->getFluxBetween(weight_Emin,weight_Emax,t);
739 
740  return weight / (use_average_nonzero_flux ? per_source_average_nonzero_flux[i] : per_source_average_flux[i]);
741 }
742 
743 double SourceModel::getTimeWeight(double t, bool use_average_nonzero_flux) const
744 {
745  static int nnag = 0;
746  if (average_flux < 0)
747  {
748  if (nnag++ < 100) fprintf(stderr,"WARNING: setUpWeights() hasn't been called yet. Will return crazy value!\n");
749  }
750  double weight = 0;
751  for (unsigned i = 0; i < sources.size(); i++)
752  {
753  weight += weight_Emin == weight_Emax ? sources[i]->getFlux()->getFlux(weight_Emin,t) : sources[i]->getFlux()->getFluxBetween(weight_Emin,weight_Emax,t);
754  }
755  return weight / (use_average_nonzero_flux ? average_nonzero_flux : average_flux);
756 }
757 
void computeFluxTimeChanges(std::vector< double > *changes) const
Definition: source.cc:552
int getDirectionAndEnergy(Vector *nudir, double t, double &nuE, double minE=1e9, double maxE=1e12)
Definition: source.cc:568
const char * ICEMC_SRC_DIR()
static SourceModel * getSourceModel(const char *key, Restriction r=Restriction())
Definition: source.cc:99
void addSource(Source *source)
Definition: source.hh:65
This class represents a three-vector. Operators are overloaded to provide for the familiar operations...
Definition: vector.hh:27
void setUpWeights(double t0, double t1, double minE=1e9, double maxE=1e12, int N=1e6)
Definition: source.cc:684