Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/event/src/G4SPSEneDistribution.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 // G4SPSEneDistribution class implementation
 27 //
 28 // Author: Fan Lei, QinetiQ ltd - 05/02/2004
 29 // Customer: ESA/ESTEC
 30 // Revisions: Andrew Green, Andrea Dotti
 31 // --------------------------------------------------------------------
 32 #include "G4SPSEneDistribution.hh"
 33 
 34 #include "G4Exp.hh"
 35 #include "G4SystemOfUnits.hh"
 36 #include "G4UnitsTable.hh"
 37 #include "Randomize.hh"
 38 #include "G4AutoLock.hh"
 39 #include "G4Threading.hh"
 40 
 41 G4SPSEneDistribution::G4SPSEneDistribution()
 42 {
 43   G4MUTEXINIT(mutex);
 44 
 45   // Initialise all variables
 46 
 47   particle_energy = 1.0 * MeV;
 48   EnergyDisType = "Mono";
 49   weight=1.;
 50   MonoEnergy = 1 * MeV;
 51   Emin = 0.;
 52   Emax = 1.e30;
 53   alpha = 0.;
 54   biasalpha = 0.;
 55   prob_norm = 1.0;
 56   Ezero = 0.;
 57   SE = 0.;
 58   Temp = 0.;
 59   grad = 0.;
 60   cept = 0.;
 61   IntType = "NULL"; // Interpolation type
 62 
 63   ArbEmin = 0.;
 64   ArbEmax = 1.e30;
 65 
 66   verbosityLevel = 0;
 67     
 68   threadLocal_t& data = threadLocalData.Get();
 69   data.Emax = Emax;
 70   data.Emin = Emin;
 71   data.alpha =alpha;
 72   data.cept = cept;
 73   data.Ezero = Ezero;
 74   data.grad = grad;
 75   data.particle_energy = 0.;
 76   data.particle_definition = nullptr;
 77   data.weight = weight;
 78 }
 79 
 80 G4SPSEneDistribution::~G4SPSEneDistribution()
 81 {
 82   G4MUTEXDESTROY(mutex);
 83   if(Arb_grad_cept_flag)
 84   {
 85     delete [] Arb_grad;
 86     delete [] Arb_cept;
 87   }
 88     
 89   if(Arb_alpha_Const_flag)
 90   {
 91     delete [] Arb_alpha;
 92     delete [] Arb_Const;
 93   }
 94     
 95   if(Arb_ezero_flag)
 96   {
 97     delete [] Arb_ezero;
 98   }
 99   delete Bbody_x;
100   delete BBHist;
101   delete CP_x;
102   delete CPHist;
103   for (auto & it : SplineInt)
104   {
105     delete it;
106     it = nullptr;
107   }
108   SplineInt.clear();
109 }
110 
111 void G4SPSEneDistribution::SetEnergyDisType(const G4String& DisType)
112 {
113   G4AutoLock l(&mutex);
114   EnergyDisType = DisType;
115   if (EnergyDisType == "User")
116   {
117     UDefEnergyH = IPDFEnergyH = ZeroPhysVector;
118     IPDFEnergyExist = false;
119   }
120   else if (EnergyDisType == "Arb")
121   {
122     ArbEnergyH = IPDFArbEnergyH = ZeroPhysVector;
123     IPDFArbExist = false;
124   }
125   else if (EnergyDisType == "Epn")
126   {
127     UDefEnergyH = IPDFEnergyH = ZeroPhysVector;
128     IPDFEnergyExist = false;
129     EpnEnergyH = ZeroPhysVector;
130   }
131 }
132 
133 const G4String& G4SPSEneDistribution::GetEnergyDisType()
134 {
135   G4AutoLock l(&mutex);
136   return EnergyDisType;
137 }
138 
139 void G4SPSEneDistribution::SetEmin(G4double emi)
140 {
141   G4AutoLock l(&mutex);
142   Emin = emi;
143   threadLocalData.Get().Emin = Emin;
144 }
145 
146 G4double G4SPSEneDistribution::GetEmin() const
147 {
148   return threadLocalData.Get().Emin;
149 }
150 
151 G4double G4SPSEneDistribution::GetArbEmin()
152 {
153   G4AutoLock l(&mutex);
154   return ArbEmin;
155 }
156 
157 G4double G4SPSEneDistribution::GetArbEmax()
158 {
159   G4AutoLock l(&mutex);
160   return ArbEmax;
161 }
162 
163 void G4SPSEneDistribution::SetEmax(G4double ema)
164 {
165   G4AutoLock l(&mutex);
166   Emax = ema;
167   threadLocalData.Get().Emax = Emax;
168 }
169 
170 G4double G4SPSEneDistribution::GetEmax() const
171 {
172   return threadLocalData.Get().Emax;
173 }
174 
175 void G4SPSEneDistribution::SetMonoEnergy(G4double menergy)
176 {
177   G4AutoLock l(&mutex);
178   MonoEnergy = menergy;
179 }
180 
181 void G4SPSEneDistribution::SetBeamSigmaInE(G4double e)
182 {
183   G4AutoLock l(&mutex);
184   SE = e;
185 }
186 void G4SPSEneDistribution::SetAlpha(G4double alp)
187 {
188   G4AutoLock l(&mutex);
189   alpha = alp;
190   threadLocalData.Get().alpha = alpha;
191 }
192 
193 void G4SPSEneDistribution::SetBiasAlpha(G4double alp)
194 {
195   G4AutoLock l(&mutex);
196   biasalpha = alp;
197   Biased = true;
198 }
199 
200 void G4SPSEneDistribution::SetTemp(G4double tem)
201 {
202   G4AutoLock l(&mutex);
203   Temp = tem;
204 }
205 
206 void G4SPSEneDistribution::SetEzero(G4double eze)
207 {
208   G4AutoLock l(&mutex);
209   Ezero = eze;
210   threadLocalData.Get().Ezero = Ezero;
211 }
212 
213 void G4SPSEneDistribution::SetGradient(G4double gr)
214 {
215   G4AutoLock l(&mutex);
216   grad = gr;
217   threadLocalData.Get().grad = grad;
218 }
219 
220 void G4SPSEneDistribution::SetInterCept(G4double c)
221 {
222   G4AutoLock l(&mutex);
223   cept = c;
224   threadLocalData.Get().cept = cept;
225 }
226 
227 const G4String& G4SPSEneDistribution::GetIntType()
228 {
229   G4AutoLock l(&mutex);
230   return IntType;
231 }
232 
233 void G4SPSEneDistribution::SetBiasRndm(G4SPSRandomGenerator* a)
234 {
235   G4AutoLock l(&mutex);
236   eneRndm = a;
237 }
238 
239 void G4SPSEneDistribution::SetVerbosity(G4int a)
240 {
241   G4AutoLock l(&mutex);
242   verbosityLevel = a;
243 }
244 
245 G4double G4SPSEneDistribution::GetWeight() const
246 {
247   return threadLocalData.Get().weight;
248 }
249 
250 G4double G4SPSEneDistribution::GetMonoEnergy()
251 {
252   G4AutoLock l(&mutex);
253   return MonoEnergy;
254 }
255 
256 G4double G4SPSEneDistribution::GetSE()
257 {
258   G4AutoLock l(&mutex);
259   return SE;
260 }
261 
262 G4double G4SPSEneDistribution::Getalpha() const
263 {
264   return threadLocalData.Get().alpha;
265 }
266 
267 G4double G4SPSEneDistribution::GetEzero() const
268 {
269   return threadLocalData.Get().Ezero;
270 }
271 
272 G4double G4SPSEneDistribution::GetTemp()
273 {
274   G4AutoLock l(&mutex);
275   return Temp;
276 }
277 
278 G4double G4SPSEneDistribution::Getgrad() const
279 {
280   return threadLocalData.Get().grad;
281 }
282 
283 G4double G4SPSEneDistribution::Getcept() const
284 {
285   return threadLocalData.Get().cept;
286 }
287 
288 G4PhysicsFreeVector G4SPSEneDistribution::GetUserDefinedEnergyHisto()
289 {
290   G4AutoLock l(&mutex);
291   return UDefEnergyH;
292 }
293 
294 G4PhysicsFreeVector G4SPSEneDistribution::GetArbEnergyHisto()
295 {
296   G4AutoLock l(&mutex);
297   return ArbEnergyH;
298 }
299 
300 void G4SPSEneDistribution::UserEnergyHisto(const G4ThreeVector& input)
301 {
302   G4AutoLock l(&mutex);
303   G4double ehi = input.x(),
304            val = input.y();
305   if (verbosityLevel > 1)
306   {
307     G4cout << "In UserEnergyHisto" << G4endl;
308     G4cout << " " << ehi << " " << val << G4endl;
309   }
310   UDefEnergyH.InsertValues(ehi, val);
311   Emax = ehi;
312   threadLocalData.Get().Emax = Emax;
313 }
314 
315 void G4SPSEneDistribution::ArbEnergyHisto(const G4ThreeVector& input)
316 {
317   G4AutoLock l(&mutex);
318   G4double ehi = input.x(),
319            val = input.y();
320   if (verbosityLevel > 1)
321   {
322     G4cout << "In ArbEnergyHisto" << G4endl;
323     G4cout << " " << ehi << " " << val << G4endl;
324   }
325   ArbEnergyH.InsertValues(ehi, val);
326 }
327 
328 void G4SPSEneDistribution::ArbEnergyHistoFile(const G4String& filename)
329 {
330   G4AutoLock l(&mutex);
331   std::ifstream infile(filename, std::ios::in);
332   if (!infile)
333   {
334     G4Exception("G4SPSEneDistribution::ArbEnergyHistoFile", "Event0301",
335                 FatalException, "Unable to open the histo ASCII file");
336   }
337   G4double ehi, val;
338   while (infile >> ehi >> val)
339   {
340     ArbEnergyH.InsertValues(ehi, val);
341   }
342 }
343 
344 void G4SPSEneDistribution::EpnEnergyHisto(const G4ThreeVector& input)
345 {
346   G4AutoLock l(&mutex);
347   G4double ehi = input.x(),
348            val = input.y();
349   if (verbosityLevel > 1)
350   {
351     G4cout << "In EpnEnergyHisto" << G4endl;
352     G4cout << " " << ehi << " " << val << G4endl;
353   }
354   EpnEnergyH.InsertValues(ehi, val);
355   Emax = ehi;
356   threadLocalData.Get().Emax = Emax;
357   Epnflag = true;
358 }
359 
360 void G4SPSEneDistribution::Calculate()
361 {
362   G4AutoLock l(&mutex);
363   if (EnergyDisType == "Cdg")
364   {
365     CalculateCdgSpectrum();
366   }
367   else if (EnergyDisType == "Bbody")
368   {
369     if(!BBhistInit)
370     {
371       BBInitHists();
372     }
373     CalculateBbodySpectrum();
374   }
375   else if (EnergyDisType == "CPow")
376   {
377     if(!CPhistInit)
378     {
379       CPInitHists();
380     }
381     CalculateCPowSpectrum();
382   }
383 }
384 
385 void G4SPSEneDistribution::BBInitHists()  // MT: Lock in caller
386 {
387   BBHist = new std::vector<G4double>(10001, 0.0);
388   Bbody_x = new std::vector<G4double>(10001, 0.0);
389   BBhistInit = true;
390 }
391 
392 void G4SPSEneDistribution::CPInitHists()  // MT: Lock in caller
393 {
394   CPHist = new std::vector<G4double>(10001, 0.0);
395   CP_x = new std::vector<G4double>(10001, 0.0);
396   CPhistInit = true;
397 }
398 
399 void G4SPSEneDistribution::CalculateCdgSpectrum()  // MT: Lock in caller
400 {
401   // This uses the spectrum from the INTEGRAL Mass Model (TIMM)
402   // to generate a Cosmic Diffuse X/gamma ray spectrum.
403 
404   G4double pfact[2] = { 8.5, 112 };
405   G4double spind[2] = { 1.4, 2.3 };
406   G4double ene_line[3] = { 1. * keV, 18. * keV, 1E6 * keV };
407   G4int n_par;
408 
409   ene_line[0] = threadLocalData.Get().Emin;
410   if (threadLocalData.Get().Emin < 18 * keV)
411   {
412     n_par = 2;
413     ene_line[2] = threadLocalData.Get().Emax;
414     if (threadLocalData.Get().Emax < 18 * keV)
415     {
416       n_par = 1;
417       ene_line[1] = threadLocalData.Get().Emax;
418     }
419   }
420   else
421   {
422     n_par = 1;
423     pfact[0] = 112.;
424     spind[0] = 2.3;
425     ene_line[1] = threadLocalData.Get().Emax;
426   }
427 
428   // Create a cumulative histogram
429   //
430   CDGhist[0] = 0.;
431   G4double omalpha;
432   G4int i = 0;
433   while (i < n_par)
434   {
435     omalpha = 1. - spind[i];
436     CDGhist[i + 1] = CDGhist[i] + (pfact[i] / omalpha)
437                                 * (std::pow(ene_line[i + 1] / keV, omalpha)
438                                 - std::pow(ene_line[i] / keV,omalpha));
439     ++i;
440   }
441 
442   // Normalise histo and divide by 1000 to make MeV
443   //
444   i = 0;
445   while (i < n_par)
446   {
447     CDGhist[i + 1] = CDGhist[i + 1] / CDGhist[n_par];
448     ++i;
449   }
450 }
451 
452 void G4SPSEneDistribution::CalculateBbodySpectrum()  // MT: Lock in caller
453 {
454   // Create bbody spectrum
455   // Proved very hard to integrate indefinitely, so different method.
456   // User inputs emin, emax and T. These are used to create a 10,000
457   // bin histogram.
458   // Use photon density spectrum = 2 nu**2/c**2 * (std::exp(h nu/kT)-1)
459   // = 2 E**2/h**2c**2 times the exponential
460     
461   G4double erange = threadLocalData.Get().Emax - threadLocalData.Get().Emin;
462   G4double steps = erange / 10000.;
463     
464   const G4double k = 8.6181e-11; //Boltzmann const in MeV/K
465   const G4double h = 4.1362e-21; // Plancks const in MeV s
466   const G4double c = 3e8; // Speed of light
467   const G4double h2 = h * h;
468   const G4double c2 = c * c;
469   G4int count = 0;
470   G4double sum = 0.;
471   BBHist->at(0) = 0.;
472     
473   while (count < 10000)
474   {
475     Bbody_x->at(count) = threadLocalData.Get().Emin + G4double(count * steps);
476     G4double Bbody_y = (2. * std::pow(Bbody_x->at(count), 2.))
477                      / (h2*c2*(std::exp(Bbody_x->at(count) / (k*Temp)) - 1.));
478     sum = sum + Bbody_y;
479     BBHist->at(count + 1) = BBHist->at(count) + Bbody_y;
480     ++count;
481   }
482 
483   Bbody_x->at(10000) = threadLocalData.Get().Emax;
484 
485   // Normalise cumulative histo
486   //
487   count = 0;
488   while (count < 10001)
489   {
490     BBHist->at(count) = BBHist->at(count) / sum;
491     ++count;
492   }
493 }
494 
495 void G4SPSEneDistribution::CalculateCPowSpectrum()  // MT: Lock in caller
496 {
497   // Create cutoff power-law spectrum, x^a exp(-x/b)
498   // The integral of this function is an incomplete gamma function, which
499   // is only available in the Boost library.
500   //
501   // User inputs are emin, emax and alpha and Ezero. These are used to
502   // create a 10,000 bin histogram.
503     
504   G4double erange = threadLocalData.Get().Emax - threadLocalData.Get().Emin;
505   G4double steps = erange / 10000.;
506   alpha = threadLocalData.Get().alpha ;
507   Ezero = threadLocalData.Get().Ezero ;
508     
509   G4int count = 0;
510   G4double sum = 0.;
511   CPHist->at(0) = 0.;
512     
513   while (count < 10000)
514   {
515     CP_x->at(count) = threadLocalData.Get().Emin + G4double(count * steps);
516     G4double CP_y = std::pow(CP_x->at(count), alpha)
517                   * std::exp(-CP_x->at(count) / Ezero);
518     sum = sum + CP_y;
519     CPHist->at(count + 1) = CPHist->at(count) + CP_y;
520     ++count;
521   }
522 
523   CP_x->at(10000) = threadLocalData.Get().Emax;
524 
525   // Normalise cumulative histo
526   //
527   count = 0;
528   while (count < 10001)
529   {
530     CPHist->at(count) = CPHist->at(count) / sum;
531     ++count;
532   }
533 }
534 
535 void G4SPSEneDistribution::InputEnergySpectra(G4bool value)
536 {
537   G4AutoLock l(&mutex);
538 
539   // Allows user to specify spectrum is momentum
540   //
541   EnergySpec = value; // false if momentum
542   if (verbosityLevel > 1)
543   {
544     G4cout << "EnergySpec has value " << EnergySpec << G4endl;
545   }
546 }
547 
548 void G4SPSEneDistribution::InputDifferentialSpectra(G4bool value)
549 {
550   G4AutoLock l(&mutex);
551 
552   // Allows user to specify integral or differential spectra
553   //
554   DiffSpec = value; // true = differential, false = integral
555   if (verbosityLevel > 1)
556   {
557     G4cout << "Diffspec has value " << DiffSpec << G4endl;
558   }
559 }
560 
561 void G4SPSEneDistribution::ArbInterpolate(const G4String& IType)
562 {
563   G4AutoLock l(&mutex);
564 
565   IntType = IType;
566   ArbEmax = ArbEnergyH.GetMaxEnergy();
567   ArbEmin = ArbEnergyH.Energy(0);
568 
569   // Now interpolate points
570 
571   if (IntType == "Lin") LinearInterpolation();
572   if (IntType == "Log") LogInterpolation();
573   if (IntType == "Exp") ExpInterpolation();
574   if (IntType == "Spline") SplineInterpolation();
575 }
576 
577 void G4SPSEneDistribution::LinearInterpolation()  // MT: Lock in caller
578 {
579   // Method to do linear interpolation on the Arb points
580   // Calculate equation of each line segment, max 1024.
581   // Calculate Area under each segment
582   // Create a cumulative array which is then normalised Arb_Cum_Area
583 
584   G4double Area_seg[1024]; // Stores area under each segment
585   G4double sum = 0., Arb_x[1024]={0.}, Arb_y[1024]={0.}, Arb_Cum_Area[1024]={0.};
586   std::size_t i, count;
587   std::size_t maxi = ArbEnergyH.GetVectorLength();
588   for (i = 0; i < maxi; ++i)
589   {
590     Arb_x[i] = ArbEnergyH.GetLowEdgeEnergy(i);
591     Arb_y[i] = ArbEnergyH(i);
592   }
593 
594   // Points are now in x,y arrays. If the spectrum is integral it has to be
595   // made differential and if momentum it has to be made energy
596 
597   if (!DiffSpec)
598   {
599     // Converts integral point-wise spectra to Differential
600     //
601     for (count = 0; count < maxi-1; ++count)
602     {
603       Arb_y[count] = (Arb_y[count] - Arb_y[count + 1])
604                    / (Arb_x[count + 1] - Arb_x[count]);
605     }
606     --maxi;
607   }
608 
609   if (!EnergySpec)
610   {
611     // change currently stored values (emin etc) which are actually momenta
612     // to energies
613     //
614     G4ParticleDefinition* pdef = threadLocalData.Get().particle_definition;
615     if (pdef == nullptr)
616     {
617       G4Exception("G4SPSEneDistribution::LinearInterpolation",
618                   "Event0302", FatalException,
619                   "Error: particle not defined");
620     }
621     else
622     {
623       // Apply Energy**2 = p**2c**2 + m0**2c**4
624       // p should be entered as E/c i.e. without the division by c
625       // being done - energy equivalent
626 
627       G4double mass = pdef->GetPDGMass();
628 
629       // Convert point to energy unit and its value to per energy unit
630       //
631       G4double total_energy;
632       for (count = 0; count < maxi; ++count)
633       {
634         total_energy = std::sqrt((Arb_x[count] * Arb_x[count])
635                      + (mass * mass)); // total energy
636         Arb_y[count] = Arb_y[count] * Arb_x[count] / total_energy;
637         Arb_x[count] = total_energy - mass; // kinetic energy
638       }
639     }
640   }
641 
642   i = 1;
643 
644   Arb_grad = new G4double [1024];
645   Arb_cept = new G4double [1024];
646   Arb_grad_cept_flag = true;
647     
648   Arb_grad[0] = 0.;
649   Arb_cept[0] = 0.;
650   Area_seg[0] = 0.;
651   Arb_Cum_Area[0] = 0.;
652   while (i < maxi)
653   {
654     // calculate gradient and intercept for each segment
655     //
656     Arb_grad[i] = (Arb_y[i] - Arb_y[i - 1]) / (Arb_x[i] - Arb_x[i - 1]);
657     if (verbosityLevel == 2)
658     {
659       G4cout << Arb_grad[i] << G4endl;
660     }
661     if (Arb_grad[i] > 0.)
662     {
663       if (verbosityLevel == 2)
664       {
665          G4cout << "Arb_grad is positive" << G4endl;
666       }
667       Arb_cept[i] = Arb_y[i] - (Arb_grad[i] * Arb_x[i]);
668     }
669     else if (Arb_grad[i] < 0.)
670     {
671       if (verbosityLevel == 2)
672       {
673         G4cout << "Arb_grad is negative" << G4endl;
674       }
675       Arb_cept[i] = Arb_y[i] + (-Arb_grad[i] * Arb_x[i]);
676     }
677     else
678     {
679       if (verbosityLevel == 2)
680       {
681         G4cout << "Arb_grad is 0." << G4endl;
682       }
683       Arb_cept[i] = Arb_y[i];
684     }
685 
686     Area_seg[i] = ((Arb_grad[i] / 2)
687                 * (Arb_x[i] * Arb_x[i] - Arb_x[i - 1] * Arb_x[i - 1])
688                 + Arb_cept[i] * (Arb_x[i] - Arb_x[i - 1]));
689     Arb_Cum_Area[i] = Arb_Cum_Area[i - 1] + Area_seg[i];
690     sum = sum + Area_seg[i];
691     if (verbosityLevel == 2)
692     {
693       G4cout << Arb_x[i] << Arb_y[i] << Area_seg[i] << sum
694              << Arb_grad[i] << G4endl;
695     }
696     ++i;
697   }
698 
699   i = 0;
700   while (i < maxi)
701   {
702     Arb_Cum_Area[i] = Arb_Cum_Area[i] / sum; // normalisation
703     IPDFArbEnergyH.InsertValues(Arb_x[i], Arb_Cum_Area[i]);
704     ++i;
705   }
706 
707   // now scale the ArbEnergyH, needed by Probability()
708   //
709   ArbEnergyH.ScaleVector(1., 1./sum);
710 
711   if (verbosityLevel >= 1)
712   {
713     G4cout << "Leaving LinearInterpolation" << G4endl;
714     ArbEnergyH.DumpValues();
715     IPDFArbEnergyH.DumpValues();
716   }
717 }
718 
719 void G4SPSEneDistribution::LogInterpolation()  // MT: Lock in caller
720 {
721   // Interpolation based on Logarithmic equations
722   // Generate equations of line segments
723   // y = Ax**alpha => log y = alpha*logx + logA
724   // Find area under line segments
725   // Create normalised, cumulative array Arb_Cum_Area
726 
727   G4double Area_seg[1024]; // Stores area under each segment
728   G4double sum = 0., Arb_x[1024]={0.}, Arb_y[1024]={0.}, Arb_Cum_Area[1024]={0.};
729   std::size_t i, count;
730   std::size_t maxi = ArbEnergyH.GetVectorLength();
731   for (i = 0; i < maxi; ++i)
732   {
733     Arb_x[i] = ArbEnergyH.GetLowEdgeEnergy(i);
734     Arb_y[i] = ArbEnergyH(i);
735   }
736 
737   // Points are now in x,y arrays. If the spectrum is integral it has to be
738   // made differential and if momentum it has to be made energy
739 
740   if (!DiffSpec)
741   {
742     // Converts integral point-wise spectra to Differential
743     //
744     for (count = 0; count < maxi-1; ++count)
745     {
746       Arb_y[count] = (Arb_y[count] - Arb_y[count + 1])
747                    / (Arb_x[count + 1] - Arb_x[count]);
748     }
749     --maxi;
750   }
751 
752   if (!EnergySpec)
753   {
754     // Change currently stored values (emin etc) which are actually momenta
755     // to energies
756 
757     G4ParticleDefinition* pdef = threadLocalData.Get().particle_definition;
758     if (pdef == nullptr)
759     {
760       G4Exception("G4SPSEneDistribution::LogInterpolation",
761                   "Event0302", FatalException,
762                   "Error: particle not defined");
763     }
764     else
765     {
766       // Apply Energy**2 = p**2c**2 + m0**2c**4
767       // p should be entered as E/c i.e. without the division by c
768       // being done - energy equivalent
769 
770       G4double mass = pdef->GetPDGMass();
771 
772       // Convert point to energy unit and its value to per energy unit
773       //
774       G4double total_energy;
775       for (count = 0; count < maxi; ++count)
776       {
777         total_energy = std::sqrt((Arb_x[count] * Arb_x[count]) + (mass * mass));
778         Arb_y[count] = Arb_y[count] * Arb_x[count] / total_energy;
779         Arb_x[count] = total_energy - mass; // kinetic energy
780       }
781     }
782   }
783 
784   i = 1;
785 
786   if ( Arb_ezero != nullptr ) { delete [] Arb_ezero; Arb_ezero = nullptr; }
787   if ( Arb_Const != nullptr ) { delete [] Arb_Const; Arb_Const = nullptr; }
788   Arb_alpha = new G4double [1024];
789   Arb_Const = new G4double [1024];
790   Arb_alpha_Const_flag = true;
791 
792   Arb_alpha[0] = 0.;
793   Arb_Const[0] = 0.;
794   Area_seg[0] = 0.;
795   Arb_Cum_Area[0] = 0.;
796   if (Arb_x[0] <= 0. || Arb_y[0] <= 0.)
797   {
798     G4cout << "You should not use log interpolation with points <= 0."
799            << G4endl;
800     G4cout << "These will be changed to 1e-20, which may cause problems"
801            << G4endl;
802     if (Arb_x[0] <= 0.)
803     {
804       Arb_x[0] = 1e-20;
805     }
806     if (Arb_y[0] <= 0.)
807     {
808       Arb_y[0] = 1e-20;
809     }
810   }
811 
812   G4double alp;
813   while (i < maxi)
814   {
815     // In case points are negative or zero
816     //
817     if (Arb_x[i] <= 0. || Arb_y[i] <= 0.)
818     {
819       G4cout << "You should not use log interpolation with points <= 0."
820              << G4endl;
821       G4cout << "These will be changed to 1e-20, which may cause problems"
822              << G4endl;
823       if (Arb_x[i] <= 0.)
824       {
825         Arb_x[i] = 1e-20;
826       }
827       if (Arb_y[i] <= 0.)
828       {
829         Arb_y[i] = 1e-20;
830       }
831     }
832 
833     Arb_alpha[i] = (std::log10(Arb_y[i]) - std::log10(Arb_y[i - 1]))
834                  / (std::log10(Arb_x[i]) - std::log10(Arb_x[i - 1]));
835     Arb_Const[i] = Arb_y[i] / (std::pow(Arb_x[i], Arb_alpha[i]));
836     alp = Arb_alpha[i] + 1;
837     if (alp == 0.)
838     {
839       Area_seg[i] = Arb_Const[i]
840                   * (std::log(Arb_x[i]) - std::log(Arb_x[i - 1])); 
841     }
842     else
843     {
844       Area_seg[i] = (Arb_Const[i] / alp)
845                   * (std::pow(Arb_x[i], alp) - std::pow(Arb_x[i - 1], alp));
846     }
847     sum = sum + Area_seg[i];
848     Arb_Cum_Area[i] = Arb_Cum_Area[i - 1] + Area_seg[i];
849     if (verbosityLevel == 2)
850     {
851       G4cout << Arb_alpha[i] << Arb_Const[i] << Area_seg[i] << G4endl;
852     }
853     ++i;
854   }
855 
856   i = 0;
857   while (i < maxi)
858   {
859     Arb_Cum_Area[i] = Arb_Cum_Area[i] / sum;
860     IPDFArbEnergyH.InsertValues(Arb_x[i], Arb_Cum_Area[i]);
861     ++i;
862   }
863 
864   // Now scale the ArbEnergyH, needed by Probability()
865   //
866   ArbEnergyH.ScaleVector(1., 1./sum);
867 
868   if (verbosityLevel >= 1)
869   {
870     G4cout << "Leaving LogInterpolation " << G4endl;
871   }
872 }
873 
874 void G4SPSEneDistribution::ExpInterpolation()  // MT: Lock in caller
875 {
876   // Interpolation based on Exponential equations
877   // Generate equations of line segments
878   // y = Ae**-(x/e0) => ln y = -x/e0 + lnA
879   // Find area under line segments
880   // Create normalised, cumulative array Arb_Cum_Area
881 
882   G4double Area_seg[1024]; // Stores area under each segment
883   G4double sum = 0., Arb_x[1024]={0.}, Arb_y[1024]={0.}, Arb_Cum_Area[1024]={0.};
884   std::size_t i, count;
885   std::size_t maxi = ArbEnergyH.GetVectorLength();
886   for (i = 0; i < maxi; ++i)
887   {
888     Arb_x[i] = ArbEnergyH.GetLowEdgeEnergy(i);
889     Arb_y[i] = ArbEnergyH(i);
890   }
891 
892   // Points are now in x,y arrays. If the spectrum is integral it has to be
893   // made differential and if momentum it has to be made energy
894 
895   if (!DiffSpec)
896   {
897     // Converts integral point-wise spectra to Differential
898     //
899     for (count = 0; count < maxi - 1; ++count)
900     {
901       Arb_y[count] = (Arb_y[count] - Arb_y[count + 1])
902                    / (Arb_x[count + 1] - Arb_x[count]);
903     }
904     --maxi;
905   }
906 
907   if (!EnergySpec)
908   {
909     // Change currently stored values (emin etc) which are actually momenta
910     // to energies
911     //
912     G4ParticleDefinition* pdef = threadLocalData.Get().particle_definition;
913     if (pdef == nullptr)
914     {
915       G4Exception("G4SPSEneDistribution::ExpInterpolation",
916                   "Event0302", FatalException,
917                   "Error: particle not defined");
918     }
919     else
920     {
921       // Apply Energy**2 = p**2c**2 + m0**2c**4
922       // p should be entered as E/c i.e. without the division by c
923       // being done - energy equivalent
924 
925       G4double mass = pdef->GetPDGMass();
926 
927       // Convert point to energy unit and its value to per energy unit
928       //
929       G4double total_energy;
930       for (count = 0; count < maxi; ++count)
931       {
932         total_energy = std::sqrt((Arb_x[count] * Arb_x[count]) + (mass * mass));
933         Arb_y[count] = Arb_y[count] * Arb_x[count] / total_energy;
934         Arb_x[count] = total_energy - mass; // kinetic energy
935       }
936     }
937   }
938 
939   i = 1;
940 
941   if ( Arb_ezero != nullptr ) { delete[] Arb_ezero; Arb_ezero = nullptr; }
942   if ( Arb_Const != nullptr ) { delete[] Arb_Const; Arb_Const = nullptr; }
943   Arb_ezero = new G4double [1024];
944   Arb_Const = new G4double [1024];
945   Arb_ezero_flag = true;
946     
947   Arb_ezero[0] = 0.;
948   Arb_Const[0] = 0.;
949   Area_seg[0] = 0.;
950   Arb_Cum_Area[0] = 0.;
951   while (i < maxi)
952   {
953     G4double test = std::log(Arb_y[i]) - std::log(Arb_y[i - 1]);
954     if (test > 0. || test < 0.)
955     {
956       Arb_ezero[i] = -(Arb_x[i] - Arb_x[i - 1])
957                    / (std::log(Arb_y[i]) - std::log(Arb_y[i - 1]));
958       Arb_Const[i] = Arb_y[i] / (std::exp(-Arb_x[i] / Arb_ezero[i]));
959       Area_seg[i] = -(Arb_Const[i] * Arb_ezero[i])
960                   * (std::exp(-Arb_x[i] / Arb_ezero[i])
961                    - std::exp(-Arb_x[i - 1] / Arb_ezero[i]));
962     }
963     else
964     {
965       G4Exception("G4SPSEneDistribution::ExpInterpolation",
966                   "Event0302", JustWarning,
967                   "Flat line segment: problem, setting to zero parameters.");
968       G4cout << "Flat line segment: problem" << G4endl;
969       Arb_ezero[i] = 0.;
970       Arb_Const[i] = 0.;
971       Area_seg[i] = 0.;
972     }
973     sum = sum + Area_seg[i];
974     Arb_Cum_Area[i] = Arb_Cum_Area[i - 1] + Area_seg[i];
975     if (verbosityLevel == 2)
976     {
977       G4cout << Arb_ezero[i] << Arb_Const[i] << Area_seg[i] << G4endl;
978     }
979     ++i;
980   }
981 
982   i = 0;
983   while (i < maxi)
984   {
985     Arb_Cum_Area[i] = Arb_Cum_Area[i] / sum;
986     IPDFArbEnergyH.InsertValues(Arb_x[i], Arb_Cum_Area[i]);
987     ++i;
988   }
989 
990   // Now scale the ArbEnergyH, needed by Probability()
991   //
992   ArbEnergyH.ScaleVector(1., 1./sum);
993 
994   if (verbosityLevel >= 1)
995   {
996     G4cout << "Leaving ExpInterpolation " << G4endl;
997   }
998 }
999 
1000 void G4SPSEneDistribution::SplineInterpolation()  // MT: Lock in caller
1001 {
1002   // Interpolation using Splines.
1003   // Create Normalised arrays, make x 0->1 and y hold the function (Energy)
1004   // 
1005   // Current method based on the above will not work in all cases. 
1006   // New method is implemented below.
1007   
1008   G4double sum, Arb_x[1024]={0.}, Arb_y[1024]={0.}, Arb_Cum_Area[1024]={0.};
1009   std::size_t i, count;
1010   std::size_t maxi = ArbEnergyH.GetVectorLength();
1011 
1012   for (i = 0; i < maxi; ++i)
1013   {
1014     Arb_x[i] = ArbEnergyH.GetLowEdgeEnergy(i);
1015     Arb_y[i] = ArbEnergyH(i);
1016   }
1017 
1018   // Points are now in x,y arrays. If the spectrum is integral it has to be
1019   // made differential and if momentum it has to be made energy
1020 
1021   if (!DiffSpec)
1022   {
1023     // Converts integral point-wise spectra to Differential
1024     //
1025     for (count = 0; count < maxi - 1; ++count)
1026     {
1027       Arb_y[count] = (Arb_y[count] - Arb_y[count + 1])
1028                    / (Arb_x[count + 1] - Arb_x[count]);
1029     }
1030     --maxi;
1031   }
1032 
1033   if (!EnergySpec)
1034   {
1035     // Change currently stored values (emin etc) which are actually momenta
1036     // to energies
1037     //
1038     G4ParticleDefinition* pdef = threadLocalData.Get().particle_definition;
1039     if (pdef == nullptr)
1040     {
1041       G4Exception("G4SPSEneDistribution::SplineInterpolation",
1042                   "Event0302", FatalException,
1043                   "Error: particle not defined");
1044     }
1045     else
1046     {
1047       // Apply Energy**2 = p**2c**2 + m0**2c**4
1048       // p should be entered as E/c i.e. without the division by c
1049       // being done - energy equivalent
1050 
1051       G4double mass = pdef->GetPDGMass();
1052 
1053       // Convert point to energy unit and its value to per energy unit
1054       //
1055       G4double total_energy;
1056       for (count = 0; count < maxi; ++count)
1057       {
1058         total_energy = std::sqrt((Arb_x[count] * Arb_x[count]) + (mass * mass));
1059         Arb_y[count] = Arb_y[count] * Arb_x[count] / total_energy;
1060         Arb_x[count] = total_energy - mass; // kinetic energy
1061       }
1062     }
1063   }
1064 
1065   i = 1;
1066   Arb_Cum_Area[0] = 0.;
1067   sum = 0.;
1068   Splinetemp = new G4DataInterpolation(Arb_x, Arb_y, (G4int)maxi, 0., 0.);
1069   G4double ei[101], prob[101];
1070   for (auto & it : SplineInt)
1071   {
1072     delete it;
1073     it = nullptr;
1074   }
1075   SplineInt.clear();
1076   SplineInt.resize(1024,nullptr);
1077   while (i < maxi)
1078   {
1079     // 100 step per segment for the integration of area
1080 
1081     G4double de = (Arb_x[i] - Arb_x[i - 1])/100.;
1082     G4double area = 0.;
1083 
1084     for (count = 0; count < 101; ++count)
1085     {
1086       ei[count] = Arb_x[i - 1] + de*count ;
1087       prob[count] =  Splinetemp->CubicSplineInterpolation(ei[count]);
1088       if (prob[count] < 0.)
1089       { 
1090         G4ExceptionDescription ED;
1091         ED << "Warning: G4DataInterpolation returns value < 0  " << prob[count]
1092            << " " << ei[count] << G4endl;
1093         G4Exception("G4SPSEneDistribution::SplineInterpolation", "Event0303",
1094                     FatalException, ED);
1095       }
1096       area += prob[count]*de;
1097     }
1098     Arb_Cum_Area[i] = Arb_Cum_Area[i - 1] + area;
1099     sum += area; 
1100 
1101     prob[0] = prob[0]/(area/de);
1102     for (count = 1; count < 100; ++count)
1103     {
1104       prob[count] = prob[count-1] + prob[count]/(area/de);
1105     }
1106 
1107     SplineInt[i] = new G4DataInterpolation(prob, ei, 101, 0., 0.);
1108 
1109     // NOTE: i starts from 1!
1110     //
1111     ++i;
1112   }
1113 
1114   i = 0;
1115   while (i < maxi)
1116   {
1117     Arb_Cum_Area[i] = Arb_Cum_Area[i] / sum; // normalisation
1118     IPDFArbEnergyH.InsertValues(Arb_x[i], Arb_Cum_Area[i]);
1119     ++i;
1120   }
1121 
1122   // Now scale the ArbEnergyH, needed by Probability()
1123   //
1124   ArbEnergyH.ScaleVector(1., 1./sum);
1125 
1126   if (verbosityLevel > 0)
1127   {
1128     G4cout << "Leaving SplineInterpolation " << G4endl;
1129   }
1130 }
1131 
1132 void G4SPSEneDistribution::GenerateMonoEnergetic()
1133 {
1134   // Method to generate MonoEnergetic particles
1135 
1136   threadLocalData.Get().particle_energy = MonoEnergy;
1137 }
1138 
1139 void G4SPSEneDistribution::GenerateGaussEnergies()
1140 {
1141   // Method to generate Gaussian particles
1142 
1143   G4double ene = G4RandGauss::shoot(MonoEnergy,SE);
1144   if (ene < 0) ene = 0.;
1145   threadLocalData.Get().particle_energy = ene;
1146 }
1147 
1148 void G4SPSEneDistribution::GenerateLinearEnergies(G4bool bArb = false)
1149 {
1150   G4double rndm;
1151   threadLocal_t& params = threadLocalData.Get();
1152   G4double emaxsq = std::pow(params.Emax, 2.); // Emax squared
1153   G4double eminsq = std::pow(params.Emin, 2.); // Emin squared
1154   G4double intersq = std::pow(params.cept, 2.); // cept squared
1155 
1156   if (bArb) rndm = G4UniformRand();
1157   else      rndm = eneRndm->GenRandEnergy();
1158 
1159   G4double bracket = ((params.grad / 2.)
1160                    * (emaxsq - eminsq)
1161                    + params.cept * (params.Emax - params.Emin));
1162   bracket = bracket * rndm;
1163   bracket = bracket + (params.grad / 2.) * eminsq + params.cept * params.Emin;
1164 
1165   // Now have a quad of form m/2 E**2 + cE - bracket = 0
1166   //
1167   bracket = -bracket;
1168 
1169   if (params.grad != 0.)
1170   {
1171     G4double sqbrack = (intersq - 4 * (params.grad / 2.) * (bracket));
1172     sqbrack = std::sqrt(sqbrack);
1173     G4double root1 = -params.cept + sqbrack;
1174     root1 = root1 / (2. * (params.grad / 2.));
1175 
1176     G4double root2 = -params.cept - sqbrack;
1177     root2 = root2 / (2. * (params.grad / 2.));
1178 
1179     if (root1 > params.Emin && root1 < params.Emax)
1180     {
1181       params.particle_energy = root1;
1182     }
1183     if (root2 > params.Emin && root2 < params.Emax)
1184     {
1185       params.particle_energy = root2;
1186     }
1187   }
1188   else if (params.grad == 0.)
1189   {
1190     // have equation of form cE - bracket =0
1191     //
1192     params.particle_energy = bracket / params.cept;
1193   }
1194 
1195   if (params.particle_energy < 0.)
1196   {
1197     params.particle_energy = -params.particle_energy;
1198   }
1199 
1200   if (verbosityLevel >= 1)
1201   {
1202     G4cout << "Energy is " << params.particle_energy << G4endl;
1203   }
1204 }
1205 
1206 void G4SPSEneDistribution::GeneratePowEnergies(G4bool bArb = false)
1207 {
1208   // Method to generate particle energies distributed as a power-law
1209 
1210   G4double rndm;
1211   G4double emina, emaxa;
1212     
1213   threadLocal_t& params = threadLocalData.Get();
1214     
1215   emina = std::pow(params.Emin, params.alpha + 1);
1216   emaxa = std::pow(params.Emax, params.alpha + 1);
1217 
1218   if (bArb) rndm = G4UniformRand();
1219   else      rndm = eneRndm->GenRandEnergy();
1220 
1221   if (params.alpha != -1.)
1222   {
1223     G4double ene = ((rndm * (emaxa - emina)) + emina);
1224     ene = std::pow(ene, (1. / (params.alpha + 1.)));
1225     params.particle_energy = ene;
1226   }
1227   else
1228   {
1229     G4double ene = (std::log(params.Emin) 
1230                  + rndm * (std::log(params.Emax) - std::log(params.Emin)));
1231     params.particle_energy = std::exp(ene);
1232   }
1233   if (verbosityLevel >= 1)
1234   {
1235     G4cout << "Energy is " << params.particle_energy << G4endl;
1236   }
1237 }
1238 
1239 void G4SPSEneDistribution::GenerateCPowEnergies()
1240 {
1241   // Method to generate particle energies distributed in
1242   // cutoff power-law distribution
1243   //
1244   // CP_x holds Energies, and CPHist holds the cumulative histo.
1245   // binary search to find correct bin then lin interpolation.
1246   // Use the earlier defined histogram + RandGeneral method to generate
1247   // random numbers following the histos distribution
1248 
1249   G4double rndm = eneRndm->GenRandEnergy();
1250   G4int nabove = 10001, nbelow = 0, middle;
1251 
1252   G4AutoLock l(&mutex);
1253   G4bool done = CPhistCalcd;
1254   l.unlock();
1255 
1256   if(!done)
1257   {
1258     Calculate(); //This is has a lock inside, risk is to do it twice
1259     l.lock();
1260     CPhistCalcd = true;
1261     l.unlock();
1262   }
1263 
1264   // Binary search to find bin that rndm is in
1265   //
1266   while (nabove - nbelow > 1)
1267   {
1268     middle = (nabove + nbelow) / 2;
1269     if (rndm == CPHist->at(middle))
1270     {
1271       break;
1272     }
1273     if (rndm < CPHist->at(middle))
1274     {
1275       nabove = middle;
1276     }
1277     else
1278     {
1279       nbelow = middle;
1280     }
1281   }
1282 
1283   // Now interpolate in that bin to find the correct output value
1284   //
1285   G4double x1, x2, y1, y2, t, q;
1286   x1 = CP_x->at(nbelow);
1287   if(nbelow+1 == static_cast<G4int>(CP_x->size()))
1288   {
1289     x2 = CP_x->back();
1290   }
1291   else
1292   {
1293     x2 = CP_x->at(nbelow + 1);
1294   }
1295   y1 = CPHist->at(nbelow);
1296   if(nbelow+1 == static_cast<G4int>(CPHist->size()))
1297   {
1298     G4cout << CPHist->back() << G4endl;
1299     y2 = CPHist->back();
1300   }
1301   else
1302   {
1303     y2 = CPHist->at(nbelow + 1);
1304   }
1305   t = (y2 - y1) / (x2 - x1);
1306   q = y1 - t * x1;
1307 
1308   threadLocalData.Get().particle_energy = (rndm - q) / t;
1309 
1310   if (verbosityLevel >= 1)
1311   {
1312     G4cout << "Energy is " << threadLocalData.Get().particle_energy << G4endl;
1313   }
1314 }
1315 
1316 void G4SPSEneDistribution::GenerateBiasPowEnergies()
1317 {
1318   // Method to generate particle energies distributed as
1319   // in biased power-law and calculate its weight
1320     
1321   threadLocal_t& params = threadLocalData.Get();
1322     
1323   G4double rndm;
1324   G4double emina, emaxa, emin, emax;
1325 
1326   G4double normal = 1.;
1327 
1328   emin = params.Emin;
1329   emax = params.Emax;
1330 
1331   rndm = eneRndm->GenRandEnergy();
1332 
1333   if (biasalpha != -1.)
1334   {
1335     emina = std::pow(emin, biasalpha + 1);
1336     emaxa = std::pow(emax, biasalpha + 1);
1337     G4double ee = ((rndm * (emaxa - emina)) + emina);
1338     params.particle_energy = std::pow(ee, (1. / (biasalpha + 1.)));
1339     normal = 1./(1+biasalpha) * (emaxa - emina);
1340   }
1341   else
1342   {
1343     G4double ee = (std::log(emin) + rndm * (std::log(emax) - std::log(emin)));
1344     params.particle_energy = std::exp(ee);
1345     normal = std::log(emax) - std::log(emin);
1346   }
1347   params.weight = GetProbability(params.particle_energy)
1348                 / (std::pow(params.particle_energy,biasalpha)/normal);
1349 
1350   if (verbosityLevel >= 1)
1351   {
1352     G4cout << "Energy is " << params.particle_energy << G4endl;
1353   }
1354 }
1355 
1356 void G4SPSEneDistribution::GenerateExpEnergies(G4bool bArb = false)
1357 {
1358   // Method to generate particle energies distributed according
1359   // to an exponential curve
1360 
1361   G4double rndm;
1362 
1363   if (bArb) rndm = G4UniformRand();
1364   else      rndm = eneRndm->GenRandEnergy();
1365 
1366   threadLocal_t& params = threadLocalData.Get();
1367   params.particle_energy = -params.Ezero
1368                          * (std::log(rndm * (std::exp(-params.Emax
1369                                                      / params.Ezero)
1370                                            - std::exp(-params.Emin
1371                                                      / params.Ezero))
1372                                    + std::exp(-params.Emin / params.Ezero)));
1373   if (verbosityLevel >= 1)
1374   {
1375     G4cout << "Energy is " << params.particle_energy  << G4endl;
1376   }
1377 }
1378 
1379 void G4SPSEneDistribution::GenerateBremEnergies()
1380 {
1381   // Method to generate particle energies distributed according
1382   // to a Bremstrahlung equation of the form
1383   // I = const*((kT)**1/2)*E*(e**(-E/kT))
1384 
1385   G4double rndm = eneRndm->GenRandEnergy();
1386   G4double expmax, expmin, k;
1387     
1388   k = 8.6181e-11; // Boltzmann's const in MeV/K
1389   G4double ksq = std::pow(k, 2.); // k squared
1390   G4double Tsq = std::pow(Temp, 2.); // Temp squared
1391 
1392   threadLocal_t& params = threadLocalData.Get();
1393 
1394   expmax = std::exp(-params.Emax / (k * Temp));
1395   expmin = std::exp(-params.Emin / (k * Temp));
1396 
1397   // If either expmax or expmin are zero then this will cause problems
1398   // Most probably this will be because T is too low or E is too high
1399 
1400   if (expmax == 0.)
1401   {
1402     G4Exception("G4SPSEneDistribution::GenerateBremEnergies",
1403                 "Event0302", FatalException,
1404                 "*****EXPMAX=0. Choose different E's or Temp");
1405   }
1406   if (expmin == 0.)
1407   {
1408     G4Exception("G4SPSEneDistribution::GenerateBremEnergies",
1409                 "Event0302", FatalException,
1410                 "*****EXPMIN=0. Choose different E's or Temp");
1411   }
1412 
1413   G4double tempvar = rndm * ((-k) * Temp * (params.Emax * expmax
1414                                           - params.Emin * expmin)
1415                           - (ksq * Tsq * (expmax - expmin)));
1416 
1417   G4double bigc = (tempvar - k * Temp * params.Emin * expmin
1418                  - ksq * Tsq * expmin) / (-k * Temp);
1419 
1420   // This gives an equation of form: Ee(-E/kT) + kTe(-E/kT) - C =0
1421   // Solve this iteratively, step from Emin to Emax in 1000 steps
1422   // and take the best solution.
1423 
1424   G4double erange = params.Emax - params.Emin;
1425   G4double steps = erange / 1000.;
1426   G4int i;
1427   G4double etest, diff, err = 100000.;
1428 
1429   for (i = 1; i < 1000; ++i)
1430   {
1431     etest = params.Emin + (i - 1) * steps;
1432     diff = etest * (std::exp(-etest / (k * Temp)))
1433          + k * Temp * (std::exp(-etest / (k * Temp))) - bigc;
1434 
1435     if (diff < 0.)
1436     {
1437       diff = -diff;
1438     }
1439 
1440     if (diff < err)
1441     {
1442       err = diff;
1443       params.particle_energy = etest;
1444     }
1445   }
1446   if (verbosityLevel >= 1)
1447   {
1448     G4cout << "Energy is " << params.particle_energy << G4endl;
1449   }
1450 }
1451 
1452 void G4SPSEneDistribution::GenerateBbodyEnergies()
1453 {
1454   // BBody_x holds Energies, and BBHist holds the cumulative histo.
1455   // Binary search to find correct bin then lin interpolation.
1456   // Use the earlier defined histogram + RandGeneral method to generate
1457   // random numbers following the histos distribution
1458 
1459   G4double rndm = eneRndm->GenRandEnergy();
1460   G4int nabove = 10001, nbelow = 0, middle;
1461 
1462   G4AutoLock l(&mutex);
1463   G4bool done = BBhistCalcd;
1464   l.unlock();
1465 
1466   if(!done)
1467   {
1468     Calculate(); //This is has a lock inside, risk is to do it twice
1469     l.lock();
1470     BBhistCalcd = true;
1471     l.unlock();
1472   }
1473 
1474   // Binary search to find bin that rndm is in
1475   //
1476   while (nabove - nbelow > 1)
1477   {
1478     middle = (nabove + nbelow) / 2;
1479     if (rndm == BBHist->at(middle))
1480     {
1481       break;
1482     }
1483     if (rndm < BBHist->at(middle))
1484     {
1485       nabove = middle;
1486     }
1487     else
1488     {
1489       nbelow = middle;
1490     }
1491   }
1492 
1493   // Now interpolate in that bin to find the correct output value
1494   //
1495   G4double x1, x2, y1, y2, t, q;
1496   x1 = Bbody_x->at(nbelow);
1497 
1498   if(nbelow+1 == static_cast<G4int>(Bbody_x->size()))
1499   {
1500     x2 = Bbody_x->back();
1501   }
1502   else
1503   {
1504     x2 = Bbody_x->at(nbelow + 1);
1505   }
1506   y1 = BBHist->at(nbelow);
1507   if(nbelow+1 == static_cast<G4int>(BBHist->size()))
1508   {
1509     G4cout << BBHist->back() << G4endl;
1510     y2 = BBHist->back();
1511   }
1512   else
1513   {
1514     y2 = BBHist->at(nbelow + 1);
1515   }
1516   t = (y2 - y1) / (x2 - x1);
1517   q = y1 - t * x1;
1518 
1519   threadLocalData.Get().particle_energy = (rndm - q) / t;
1520 
1521   if (verbosityLevel >= 1)
1522   {
1523     G4cout << "Energy is " << threadLocalData.Get().particle_energy << G4endl;
1524   }
1525 }
1526 
1527 void G4SPSEneDistribution::GenerateCdgEnergies()
1528 {
1529   // Generate random numbers, compare with values in cumhist
1530   // to find appropriate part of spectrum and then
1531   // generate energy in the usual inversion way
1532 
1533   G4double rndm, rndm2;
1534   G4double ene_line[3]={0,0,0};
1535   G4double omalpha[2]={0,0};
1536   threadLocal_t& params = threadLocalData.Get();
1537   if (params.Emin < 18 * keV && params.Emax < 18 * keV)
1538   {
1539     omalpha[0] = 1. - 1.4;
1540     ene_line[0] = params.Emin;
1541     ene_line[1] = params.Emax;
1542   }
1543   if (params.Emin < 18 * keV && params.Emax > 18 * keV)
1544   {
1545     omalpha[0] = 1. - 1.4;
1546     omalpha[1] = 1. - 2.3;
1547     ene_line[0] = params.Emin;
1548     ene_line[1] = 18. * keV;
1549     ene_line[2] = params.Emax;
1550   }
1551   if (params.Emin > 18 * keV)
1552   {
1553     omalpha[0] = 1. - 2.3;
1554     ene_line[0] = params.Emin;
1555     ene_line[1] = params.Emax;
1556   }
1557   rndm = eneRndm->GenRandEnergy();
1558   rndm2 = eneRndm->GenRandEnergy();
1559 
1560   G4int i = 0;
1561   while (rndm >= CDGhist[i])
1562   {
1563     ++i;
1564   }
1565 
1566   // Generate final energy
1567   //
1568   G4double ene = (std::pow(ene_line[i - 1], omalpha[i - 1])
1569                + (std::pow(ene_line[i], omalpha[i - 1])
1570                 - std::pow(ene_line[i - 1], omalpha[i- 1])) * rndm2);
1571   params.particle_energy = std::pow(ene, (1. / omalpha[i - 1]));
1572 
1573   if (verbosityLevel >= 1)
1574   {
1575     G4cout << "Energy is " << params.particle_energy << G4endl;
1576   }
1577 }
1578 
1579 void G4SPSEneDistribution::GenUserHistEnergies()
1580 {
1581   // Histograms are DIFFERENTIAL
1582 
1583   G4AutoLock l(&mutex);
1584 
1585   if (!IPDFEnergyExist)
1586   {
1587     std::size_t ii;
1588     std::size_t maxbin = UDefEnergyH.GetVectorLength();
1589     G4double bins[1024], vals[1024], sum;
1590     for ( ii = 0 ; ii<1024 ; ++ii ) { bins[ii]=0; vals[ii]=0; }
1591     sum = 0.;
1592 
1593     if ( (!EnergySpec)
1594       && (threadLocalData.Get().particle_definition == nullptr))
1595     {
1596       G4Exception("G4SPSEneDistribution::GenUserHistEnergies",
1597                   "Event0302", FatalException,
1598                   "Error: particle definition is NULL");
1599     }
1600 
1601     if (maxbin > 1024)
1602     {
1603       G4Exception("G4SPSEneDistribution::GenUserHistEnergies",
1604                   "Event0302", JustWarning,
1605                  "Maxbin>1024\n Setting maxbin to 1024, other bins are lost");
1606       maxbin = 1024;
1607     }
1608 
1609     if (!DiffSpec)
1610     {
1611       G4cout << "Histograms are Differential!!! " << G4endl;
1612     }
1613     else
1614     {
1615       bins[0] = UDefEnergyH.GetLowEdgeEnergy(0);
1616       vals[0] = UDefEnergyH(0);
1617       sum = vals[0];
1618       for (ii = 1; ii < maxbin; ++ii)
1619       {
1620         bins[ii] = UDefEnergyH.GetLowEdgeEnergy(ii);
1621         vals[ii] = UDefEnergyH(ii) + vals[ii - 1];
1622         sum = sum + UDefEnergyH(ii);
1623       }
1624     }
1625 
1626     if (!EnergySpec)
1627     {
1628       G4double mass = threadLocalData.Get().particle_definition->GetPDGMass();
1629 
1630       // Multiply the function (vals) up by the bin width
1631       // to make the function counts/s (i.e. get rid of momentum dependence)
1632 
1633       for (ii = 1; ii < maxbin; ++ii)
1634       {
1635         vals[ii] = vals[ii] * (bins[ii] - bins[ii - 1]);
1636       }
1637 
1638       // Put energy bins into new histo, plus divide by energy bin width
1639       // to make evals counts/s/energy
1640       //
1641       for (ii = 0; ii < maxbin; ++ii)
1642       {
1643         // kinetic energy
1644         //
1645         bins[ii] = std::sqrt((bins[ii]*bins[ii])+(mass*mass))-mass;
1646       }
1647       for (ii = 1; ii < maxbin; ++ii)
1648       {
1649         vals[ii] = vals[ii] / (bins[ii] - bins[ii - 1]);
1650       }
1651       sum = vals[maxbin - 1];
1652       vals[0] = 0.;
1653     }
1654     for (ii = 0; ii < maxbin; ++ii)
1655     {
1656       vals[ii] = vals[ii] / sum;
1657       IPDFEnergyH.InsertValues(bins[ii], vals[ii]);
1658     }
1659 
1660     IPDFEnergyExist = true;
1661     if (verbosityLevel > 1)
1662     {
1663       IPDFEnergyH.DumpValues();
1664     }
1665   }
1666   l.unlock();
1667     
1668   // IPDF has been create so carry on
1669   //
1670   G4double rndm = eneRndm->GenRandEnergy();
1671   threadLocalData.Get().particle_energy= IPDFEnergyH.GetEnergy(rndm);
1672 
1673   if (verbosityLevel >= 1)
1674   {
1675     G4cout << "Energy is " << particle_energy << G4endl;
1676   }
1677 }
1678 
1679 G4double G4SPSEneDistribution::GetArbEneWeight(G4double ene)
1680 {
1681   auto nbelow = IPDFArbEnergyH.FindBin(ene,(IPDFArbEnergyH.GetVectorLength())/2);
1682   G4double wei = 0.;
1683   if(IntType=="Lin")
1684   {
1685     // note: grad[i] and cept[i] are calculated with x[i-1] and x[i]
1686     auto gr = Arb_grad[nbelow + 1];
1687     auto ce = Arb_cept[nbelow + 1];
1688     wei = ene*gr + ce;
1689   }
1690   else if(IntType=="Log")
1691   {
1692     auto alp = Arb_alpha[nbelow + 1];
1693     auto cns = Arb_Const[nbelow + 1];
1694     wei = cns * std::pow(ene,alp);
1695   }
1696   else if(IntType=="Exp")
1697   {
1698     auto e0 = Arb_ezero[nbelow + 1];
1699     auto cns = Arb_Const[nbelow + 1];
1700     wei = cns * std::exp(-ene/e0);
1701   }
1702   else if(IntType=="Spline")
1703   {
1704     wei = SplineInt[nbelow+1]->CubicSplineInterpolation(ene);
1705   }
1706   return wei;
1707 }
1708 
1709 void G4SPSEneDistribution::GenArbPointEnergies()
1710 {
1711   if (verbosityLevel > 0)
1712   {
1713     G4cout << "In GenArbPointEnergies" << G4endl;
1714   }
1715 
1716   G4double rndm = eneRndm->GenRandEnergy();
1717 
1718   // Find the Bin, have x, y, no of points, and cumulative area distribution
1719   //
1720   std::size_t nabove = IPDFArbEnergyH.GetVectorLength(), nbelow = 0, middle;
1721 
1722   // Binary search to find bin that rndm is in
1723   //
1724   while (nabove - nbelow > 1)
1725   {
1726     middle = (nabove + nbelow) / 2;
1727     if (rndm == IPDFArbEnergyH(middle))
1728     {
1729       break;
1730     }
1731     if (rndm < IPDFArbEnergyH(middle))
1732     {
1733       nabove = middle;
1734     }
1735     else
1736     {
1737       nbelow = middle;
1738     }
1739   }
1740   threadLocal_t& params = threadLocalData.Get();
1741   if (IntType == "Lin")
1742   {
1743     // Update thread-local copy of parameters
1744     //
1745     params.Emax = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow + 1);
1746     params.Emin = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow);
1747     params.grad = Arb_grad[nbelow + 1];
1748     params.cept = Arb_cept[nbelow + 1];
1749     GenerateLinearEnergies(true);
1750   }
1751   else if (IntType == "Log")
1752   {
1753     params.Emax = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow + 1);
1754     params.Emin = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow);
1755     params.alpha = Arb_alpha[nbelow + 1];
1756     GeneratePowEnergies(true);
1757   }
1758   else if (IntType == "Exp")
1759   {
1760     params.Emax = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow + 1);
1761     params.Emin = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow);
1762     params.Ezero = Arb_ezero[nbelow + 1];
1763     GenerateExpEnergies(true);
1764   }
1765   else if (IntType == "Spline")
1766   {
1767     params.Emax = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow + 1);
1768     params.Emin = IPDFArbEnergyH.GetLowEdgeEnergy(nbelow);
1769     params.particle_energy = -1e100;
1770     rndm = eneRndm->GenRandEnergy();
1771     while (params.particle_energy < params.Emin
1772         || params.particle_energy > params.Emax)
1773     {
1774       params.particle_energy =
1775         SplineInt[nbelow+1]->CubicSplineInterpolation(rndm);
1776       rndm = eneRndm->GenRandEnergy();
1777     }
1778     if (verbosityLevel >= 1)
1779     {
1780       G4cout << "Energy is " << params.particle_energy << G4endl;
1781     }
1782   }
1783   else
1784   {
1785     G4Exception("G4SPSEneDistribution::GenArbPointEnergies", "Event0302",
1786                 FatalException, "Error: IntType unknown type");
1787   }
1788 }
1789 
1790 void G4SPSEneDistribution::GenEpnHistEnergies()
1791 {
1792   // Firstly convert to energy if not already done
1793 
1794   G4AutoLock l(&mutex);
1795 
1796   if (Epnflag)  // true means spectrum is epn, false means e
1797   {
1798     // Convert to energy by multiplying by A number
1799     //
1800     ConvertEPNToEnergy();
1801   }
1802   if (!IPDFEnergyExist)
1803   {
1804     // IPDF has not been created, so create it
1805     //
1806     G4double bins[1024], vals[1024], sum;
1807     std::size_t ii;
1808     std::size_t maxbin = UDefEnergyH.GetVectorLength();
1809     bins[0] = UDefEnergyH.GetLowEdgeEnergy(0);
1810     vals[0] = UDefEnergyH(0);
1811     sum = vals[0];
1812     for (ii = 1; ii < maxbin; ++ii)
1813     {
1814       bins[ii] = UDefEnergyH.GetLowEdgeEnergy(ii);
1815       vals[ii] = UDefEnergyH(ii) + vals[ii - 1];
1816       sum = sum + UDefEnergyH(ii);
1817     }
1818 
1819     l.lock();
1820     for (ii = 0; ii < maxbin; ++ii)
1821     {
1822       vals[ii] = vals[ii] / sum;
1823       IPDFEnergyH.InsertValues(bins[ii], vals[ii]);
1824     }
1825     IPDFEnergyExist = true;
1826        
1827   }
1828   l.unlock();
1829 
1830   // IPDF has been create so carry on
1831   //
1832   G4double rndm = eneRndm->GenRandEnergy();
1833   threadLocalData.Get().particle_energy = IPDFEnergyH.GetEnergy(rndm);
1834 
1835   if (verbosityLevel >= 1)
1836   {
1837     G4cout << "Energy is " << threadLocalData.Get().particle_energy << G4endl;
1838   }
1839 }
1840 
1841 void G4SPSEneDistribution::ConvertEPNToEnergy()  // MT: lock in caller
1842 {
1843   // Use this before particle generation to convert the
1844   // currently stored histogram from energy/nucleon to energy.
1845 
1846   threadLocal_t& params = threadLocalData.Get();
1847   if (params.particle_definition == nullptr)
1848   {
1849     G4cout << "Error: particle not defined" << G4endl;
1850   }
1851   else
1852   {
1853     // Need to multiply histogram by the number of nucleons.
1854     // Baryon Number looks to hold the no. of nucleons
1855     //
1856     G4int Bary = params.particle_definition->GetBaryonNumber();
1857 
1858     // Change values in histogram, Read it out, delete it, re-create it
1859     //
1860     std::size_t count, maxcount;
1861     maxcount = EpnEnergyH.GetVectorLength();
1862     G4double ebins[1024], evals[1024];
1863     if (maxcount > 1024)
1864     {
1865       G4Exception("G4SPSEneDistribution::ConvertEPNToEnergy()",
1866                   "gps001", JustWarning,
1867                   "Histogram contains more than 1024 bins!\n\
1868                    Those above 1024 will be ignored");
1869       maxcount = 1024;
1870     }
1871     if (maxcount < 1)
1872     {
1873       G4Exception("G4SPSEneDistribution::ConvertEPNToEnergy()",
1874                  "gps001", FatalException,
1875                  "Histogram contains less than 1 bin!\nRedefine the histogram");
1876       return;
1877     }
1878     for (count = 0; count < maxcount; ++count)
1879     {
1880       // Read out
1881       ebins[count] = EpnEnergyH.GetLowEdgeEnergy(count);
1882       evals[count] = EpnEnergyH(count);
1883     }
1884 
1885     // Multiply the channels by the nucleon number to give energies
1886     //
1887     for (count = 0; count < maxcount; ++count)
1888     {
1889       ebins[count] = ebins[count] * Bary;
1890     }
1891 
1892     // Set Emin and Emax
1893     //
1894     params.Emin = ebins[0];
1895     if (maxcount > 1)
1896     {
1897       params.Emax = ebins[maxcount - 1];
1898     }
1899     else
1900     {
1901       params.Emax = ebins[0];
1902     }
1903 
1904     // Put energy bins into new histogram - UDefEnergyH
1905     //
1906     for (count = 0; count < maxcount; ++count)
1907     {
1908       UDefEnergyH.InsertValues(ebins[count], evals[count]);
1909     }
1910     Epnflag = false; // so that you dont repeat this method
1911   }
1912 }
1913 
1914 void G4SPSEneDistribution::ReSetHist(const G4String& atype)
1915 {
1916   G4AutoLock l(&mutex);
1917   if (atype == "energy")
1918   {
1919     UDefEnergyH = IPDFEnergyH = ZeroPhysVector;
1920     IPDFEnergyExist = false;
1921     Emin = 0.;
1922     Emax = 1e30;
1923   }
1924   else if (atype == "arb")
1925   {
1926     ArbEnergyH = IPDFArbEnergyH = ZeroPhysVector;
1927     IPDFArbExist = false;
1928   }
1929   else if (atype == "epn")
1930   {
1931     UDefEnergyH = IPDFEnergyH = ZeroPhysVector;
1932     IPDFEnergyExist = false;
1933     EpnEnergyH = ZeroPhysVector;
1934   }
1935   else
1936   {
1937     G4cout << "Error, histtype not accepted " << G4endl;
1938   }
1939 }
1940 
1941 G4double G4SPSEneDistribution::GenerateOne(G4ParticleDefinition* a)
1942 {
1943   // Copy global shared status to thread-local one
1944   //
1945   threadLocal_t& params = threadLocalData.Get();
1946   params.particle_definition=a;
1947   params.particle_energy=-1;
1948   if(applyEvergyWeight)
1949   {
1950     params.Emax = ArbEmax;
1951     params.Emin = ArbEmin;
1952   }
1953   else
1954   {
1955     params.Emax = Emax;
1956     params.Emin = Emin;
1957   }
1958   params.alpha = alpha;
1959   params.Ezero = Ezero;
1960   params.grad = grad;
1961   params.cept = cept;
1962   params.weight = weight;
1963   // particle_energy = -1.;
1964 
1965   if((EnergyDisType == "Mono") && ((MonoEnergy>Emax)||(MonoEnergy<Emin)))
1966   {
1967     G4ExceptionDescription ed;
1968     ed << "MonoEnergy " << G4BestUnit(MonoEnergy,"Energy")
1969        << " is outside of [Emin,Emax] = ["
1970        << G4BestUnit(Emin,"Energy") << ", "
1971        << G4BestUnit(Emax,"Energy") << ". MonoEnergy is used anyway.";
1972     G4Exception("G4SPSEneDistribution::GenerateOne()",
1973                 "GPS0001", JustWarning, ed);
1974     params.particle_energy=MonoEnergy;
1975     return params.particle_energy;
1976   }
1977   while ( (EnergyDisType == "Arb")
1978         ?   (params.particle_energy < ArbEmin
1979           || params.particle_energy > ArbEmax)
1980         :   (params.particle_energy < params.Emin
1981           || params.particle_energy > params.Emax) )
1982   {
1983     if (Biased)
1984     {
1985       GenerateBiasPowEnergies();
1986     }
1987     else
1988     {
1989       if (EnergyDisType == "Mono")
1990       {
1991         GenerateMonoEnergetic();
1992       }
1993       else if (EnergyDisType == "Lin")
1994       {
1995         GenerateLinearEnergies(false);
1996       }
1997       else if (EnergyDisType == "Pow")
1998       {
1999         GeneratePowEnergies(false);
2000       }
2001       else if (EnergyDisType == "CPow")
2002       {
2003         GenerateCPowEnergies();
2004       }
2005       else if (EnergyDisType == "Exp")
2006       {
2007         GenerateExpEnergies(false);
2008       }
2009       else if (EnergyDisType == "Gauss")
2010       {
2011         GenerateGaussEnergies();
2012       }
2013       else if (EnergyDisType == "Brem")
2014       {
2015         GenerateBremEnergies();
2016       }
2017       else if (EnergyDisType == "Bbody")
2018       {
2019         GenerateBbodyEnergies();
2020       }
2021       else if (EnergyDisType == "Cdg")
2022       {
2023         GenerateCdgEnergies();
2024       }
2025       else if (EnergyDisType == "User")
2026       {
2027         GenUserHistEnergies();
2028       }
2029       else if (EnergyDisType == "Arb")
2030       {
2031         GenArbPointEnergies();
2032       }
2033       else if (EnergyDisType == "Epn")
2034       {
2035         GenEpnHistEnergies();
2036       }
2037       else
2038       {
2039         G4cout << "Error: EnergyDisType has unusual value" << G4endl;
2040       }
2041     }
2042   }
2043    return params.particle_energy;
2044 }
2045 
2046 G4double G4SPSEneDistribution::GetProbability(G4double ene)
2047 {
2048   G4double prob = 1.;
2049 
2050   threadLocal_t& params = threadLocalData.Get();
2051   if (EnergyDisType == "Lin")
2052   {
2053     if (prob_norm == 1.)
2054     {
2055       prob_norm = 0.5*params.grad*params.Emax*params.Emax
2056                 + params.cept*params.Emax
2057                 - 0.5*params.grad*params.Emin*params.Emin
2058                 - params.cept*params.Emin;
2059     }
2060     prob = params.cept + params.grad * ene;
2061     prob /= prob_norm;
2062   }
2063   else if (EnergyDisType == "Pow")
2064   {
2065     if (prob_norm == 1.)
2066     {
2067       if (alpha != -1.)
2068       {
2069         G4double emina = std::pow(params.Emin, params.alpha + 1);
2070         G4double emaxa = std::pow(params.Emax, params.alpha + 1);
2071         prob_norm = 1./(1.+alpha) * (emaxa - emina);
2072       }
2073       else
2074       {
2075         prob_norm = std::log(params.Emax) - std::log(params.Emin) ;
2076       }
2077     }
2078     prob = std::pow(ene, params.alpha)/prob_norm;
2079   }
2080   else if (EnergyDisType == "Exp")
2081   {
2082     if (prob_norm == 1.)
2083     {
2084       prob_norm = -params.Ezero*(std::exp(-params.Emax/params.Ezero)
2085                                - std::exp(params.Emin/params.Ezero));
2086     }  
2087     prob = std::exp(-ene / params.Ezero);
2088     prob /= prob_norm;
2089   }
2090   else if (EnergyDisType == "Arb")
2091   {
2092     prob = ArbEnergyH.Value(ene);
2093 
2094     if (prob <= 0.)
2095     {
2096       G4cout << " Warning:G4SPSEneDistribution::GetProbability: prob<= 0. "
2097              << prob << " " << ene << G4endl;
2098       prob = 1e-30;
2099     }
2100   }
2101   else
2102   {
2103     G4cout << "Error: EnergyDisType not supported" << G4endl;
2104   }
2105 
2106   return prob;
2107 }
2108