EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GFMaterialEffects.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file GFMaterialEffects.cxx
1 /* Copyright 2008-2009, Technische Universitaet Muenchen,
2  Authors: Christian Hoeppner & Sebastian Neubert
3 
4  This file is part of GENFIT.
5 
6  GENFIT is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  GENFIT is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with GENFIT. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "GFMaterialEffects.h"
21 #include "GFException.h"
22 #include <iostream>
23 #include <string>
24 #include "stdlib.h"
25 
26 #include "TDatabasePDG.h"
27 #include "TGeoMedium.h"
28 #include "TGeoMaterial.h"
29 #include "TGeoManager.h"
30 #include "TMath.h"
31 
32 #include "math.h"
33 #include "assert.h"
34 
35 
36 //#define DEBUG
37 
38 
40 
41 float MeanExcEnergy_get(int Z);
42 float MeanExcEnergy_get(TGeoMaterial*);
43 
44 
46  fNoEffects(false),
47  fEnergyLossBetheBloch(true), fNoiseBetheBloch(true),
48  fNoiseCoulomb(true),
49  fEnergyLossBrems(true), fNoiseBrems(true),
50  me(0.510998910E-3),
51  fstep(0),
52  fbeta(0),
53  fdedx(0),
54  fgamma(0),
55  fgammaSquare(0),
56  fmatDensity(0),
57  fmatZ(0),
58  fmatA(0),
59  fradiationLength(0),
60  fmEE(0),
61  fpdg(0),
62  fcharge(0),
63  fmass(0),
64  fMscModelCode(0)
65 {
66 }
67 
69 {
70 }
71 
73 {
74  if (finstance == NULL) finstance = new GFMaterialEffects();
75  return finstance;
76 }
77 
79 {
80  if (finstance != NULL) {
81  delete finstance;
82  finstance = NULL;
83  }
84 }
85 
86 void GFMaterialEffects::setMscModel(const std::string& modelName)
87 {
88  if (modelName == std::string("GEANE")) {
89  fMscModelCode = 0;
90  } else if (modelName == std::string("Highland")) {
91  fMscModelCode = 1;
92  } else {// throw exception
93  std::string errorMsg = std::string("There is no MSC model called \"") + modelName + "\". Maybe it is not implemented or you misspelled the model name";
94  GFException exc(errorMsg, __LINE__, __FILE__);
95  exc.setFatal();
96  throw exc;
97  }
98 }
99 
100 
101 double GFMaterialEffects::effects(const std::vector<GFPointPath>& points,
102  const double& mom,
103  const int& pdg,
104  double& xx0,
105  const bool& doNoise,
106  double* noise,
107  const double* jacobian,
108  const TVector3* directionBefore,
109  const TVector3* directionAfter)
110 {
111 
112  if (fNoEffects) return 0.;
113 
114  fpdg = pdg;
116 
117  double momLoss = 0.;
118  unsigned int nPoints(points.size());
119 
120  for (unsigned int i = 1; i < nPoints; ++i) { // loop over points
121 
122  TVector3 dir(points.at(i).getPos() - points.at(i-1).getPos()); // straight line from one point to the next
123  double dist = dir.Mag(); // straight line distance
124 
125  if (dist > 1.E-8) { // do material effects only if distance is not too small
126 
127  dir.SetMag(1.);
128  double X(0.); // path already gone through material (straight line)
129  double step(0); // straight line step
130  double realPath = points.at(i-1).getPath(); // real (curved) distance, signed
131 
132  gGeoManager->InitTrack(points.at(i-1).X(), points.at(i-1).Y(), points.at(i-1).Z(),
133  dir.X(), dir.Y(), dir.Z());
134 
135  while (X < dist) {
136 
137  getMaterialParameters(gGeoManager->GetCurrentVolume()->GetMedium()->GetMaterial());
138 
139  gGeoManager->FindNextBoundaryAndStep(dist - X);
140  step = gGeoManager->GetStep();
141  fstep = fabs(step * realPath / dist); // the actual path is curved, not straight!
142  if (fstep <= 0.) continue;
143 
144  double stepSign(1.);
145  if (realPath < 0) stepSign = -1.;
146 
147  if (fmatZ > 1.E-3) { // don't calculate energy loss for vacuum
148 
150  momLoss += stepSign * this->energyLossBetheBloch(mom);
151  if (doNoise && fEnergyLossBetheBloch && fNoiseBetheBloch)
152  this->noiseBetheBloch(mom, noise);
153 
154  if (doNoise && fNoiseCoulomb)
155  this->noiseCoulomb(mom, noise, jacobian, directionBefore, directionAfter);
156 
157  if (fEnergyLossBrems)
158  momLoss += stepSign * this->energyLossBrems(mom);
159  if (doNoise && fEnergyLossBrems && fNoiseBrems)
160  this->noiseBrems(mom, noise);
161 
162  xx0 += fstep / fradiationLength;
163  }
164  X += step;
165  }
166  }
167  } // end loop over points
168 
169  return momLoss;
170 }
171 
172 
173 double GFMaterialEffects::stepper(const double& maxStep, // unsigned!
174  const double& maxAngleStep,
175  const double& posx,
176  const double& posy,
177  const double& posz,
178  const double& dirx,
179  const double& diry,
180  const double& dirz,
181  const double& mom,
182  double& relMomLoss,
183  const int& pdg)
184 {
185 
186  static const double maxRelMomLoss = .005; // maximum relative momentum loss allowed
187  static const double minStep = 1.E-4; // 1 µm
188 
189  if (fNoEffects) return maxStep;
190  if (relMomLoss > maxRelMomLoss) return 0;
191  if (maxStep < minStep) return minStep;
192 
193  fpdg = pdg;
194  double X(minStep);
195  double relMomLossStep(0);
196  TGeoMaterial* mat(NULL);
198 
199  gGeoManager->InitTrack(posx+minStep*dirx,
200  posy+minStep*diry,
201  posz+minStep*dirz,
202  dirx, diry, dirz);
203 
204  #ifdef DEBUG
205  //gGeoManager->SetVerboseLevel(5);
206  #endif
207 
208  while (X < maxStep){
209  relMomLossStep = 0;
210  TGeoMedium* medium = gGeoManager->GetCurrentVolume()->GetMedium();
211  assert(medium != NULL);
212  mat = medium->GetMaterial();
213  gGeoManager->FindNextBoundaryAndStep(maxStep-X);
214  fstep = gGeoManager->GetStep();
215 
216  #ifdef DEBUG
217  std::cout<<" gGeoManager->GetStep() = " << gGeoManager->GetStep() << " fstep = " << fstep << "\n";
218  #endif
219 
220  if (fstep <= 0.) continue;
221 
223 
224  if (fmatZ > 1.E-3) { // don't calculate energy loss for vacuum
225 
226  if (fEnergyLossBetheBloch) relMomLossStep += this->energyLossBetheBloch(mom) / mom;
227  if (fEnergyLossBrems) relMomLossStep += this->energyLossBrems(mom) / mom;
228  }
229 
230  if (relMomLoss + relMomLossStep > maxRelMomLoss) {
231  double fraction = (maxRelMomLoss - relMomLoss) / relMomLossStep;
232  X += fraction * fstep;
233  #ifdef DEBUG
234  std::cout<<" momLoss exceeded \n";
235  #endif
236  break;
237  }
238 
239  relMomLoss += relMomLossStep;
240  X += fstep;
241  }
242 
243  return X;
244 }
245 
246 
248 {
249  fmatDensity = mat->GetDensity();
250  fmatZ = mat->GetZ();
251  fmatA = mat->GetA();
252  fradiationLength = mat->GetRadLen();
253  fmEE = MeanExcEnergy_get(mat);
254 }
255 
256 
258 {
259  TParticlePDG* part = TDatabasePDG::Instance()->GetParticle(fpdg);
260  fcharge = part->Charge() / (3.);
261  fmass = part->Mass();
262 
263  fbeta = mom / sqrt(fmass * fmass + mom * mom);
264 
265  //for numerical stability
266  fgammaSquare = 1. - fbeta * fbeta;
267  if (fgammaSquare > 1.E-10) fgammaSquare = 1. / fgammaSquare;
268  else fgammaSquare = 1.E10;
269  fgamma = sqrt(fgammaSquare);
270 }
271 
272 
273 
274 //---- Energy-loss and Noise calculations -----------------------------------------
275 
277 {
278 
279  // calc fdedx, also needed in noiseBetheBloch!
280  fdedx = 0.307075 * fmatZ / fmatA * fmatDensity / (fbeta * fbeta) * fcharge * fcharge;
281  double massRatio = me / fmass;
282  double argument = fgammaSquare * fbeta * fbeta * me * 1.E3 * 2. / ((1.E-6 * fmEE) * sqrt(1 + 2 * sqrt(fgammaSquare) * massRatio + massRatio * massRatio));
283  if (argument <= exp(fbeta * fbeta))
284  fdedx = 0.;
285  else {
286  fdedx *= (log(argument) - fbeta * fbeta); // Bethe-Bloch [MeV/cm]
287  fdedx *= 1.E-3; // in GeV/cm, hence 1.e-3
288  if (fdedx < 0.) fdedx = 0;
289  }
290 
291  double DE = fabs(fstep) * fdedx; //always positive
292  double momLoss = sqrt(mom * mom + 2.*sqrt(mom * mom + fmass * fmass) * DE + DE * DE) - mom; //always positive
293 
294  //in vacuum it can numerically happen that momLoss becomes a small negative number.
295  if (momLoss < 0.) return 0.;
296  return momLoss;
297 }
298 
299 
301  double* noise) const
302 {
303 
304 
305  // ENERGY LOSS FLUCTUATIONS; calculate sigma^2(E);
306  double sigma2E = 0.;
307  double zeta = 153.4E3 * fcharge * fcharge / (fbeta * fbeta) * fmatZ / fmatA * fmatDensity * fabs(fstep); // eV
308  double Emax = 2.E9 * me * fbeta * fbeta * fgammaSquare / (1. + 2.*fgamma * me / fmass + (me / fmass) * (me / fmass)); // eV
309  double kappa = zeta / Emax;
310 
311  if (kappa > 0.01) { // Vavilov-Gaussian regime
312  sigma2E += zeta * Emax * (1. - fbeta * fbeta / 2.); // eV^2
313  }
314  else { // Urban/Landau approximation
315  double alpha = 0.996;
316  double sigmaalpha = 15.76;
317  // calculate number of collisions Nc
318  double I = 16. * pow(fmatZ, 0.9); // eV
319  double f2 = 0.;
320  if (fmatZ > 2.) f2 = 2. / fmatZ;
321  double f1 = 1. - f2;
322  double e2 = 10.*fmatZ * fmatZ; // eV
323  double e1 = pow((I / pow(e2, f2)), 1. / f1); // eV
324 
325  double mbbgg2 = 2.E9 * fmass * fbeta * fbeta * fgammaSquare; // eV
326  double Sigma1 = fdedx * 1.0E9 * f1 / e1 * (log(mbbgg2 / e1) - fbeta * fbeta) / (log(mbbgg2 / I) - fbeta * fbeta) * 0.6; // 1/cm
327  double Sigma2 = fdedx * 1.0E9 * f2 / e2 * (log(mbbgg2 / e2) - fbeta * fbeta) / (log(mbbgg2 / I) - fbeta * fbeta) * 0.6; // 1/cm
328  double Sigma3 = fdedx * 1.0E9 * Emax / (I * (Emax + I) * log((Emax + I) / I)) * 0.4; // 1/cm
329 
330  double Nc = (Sigma1 + Sigma2 + Sigma3) * fabs(fstep);
331 
332  if (Nc > 50.) { // truncated Landau distribution
333  // calculate sigmaalpha (see GEANT3 manual W5013)
334  double RLAMED = -0.422784 - fbeta * fbeta - log(zeta / Emax);
335  double RLAMAX = 0.60715 + 1.1934 * RLAMED + (0.67794 + 0.052382 * RLAMED) * exp(0.94753 + 0.74442 * RLAMED);
336  // from lambda max to sigmaalpha=sigma (empirical polynomial)
337  if (RLAMAX <= 1010.) {
338  sigmaalpha = 1.975560
339  + 9.898841e-02 * RLAMAX
340  - 2.828670e-04 * RLAMAX * RLAMAX
341  + 5.345406e-07 * pow(RLAMAX, 3.)
342  - 4.942035e-10 * pow(RLAMAX, 4.)
343  + 1.729807e-13 * pow(RLAMAX, 5.);
344  } else { sigmaalpha = 1.871887E+01 + 1.296254E-02 * RLAMAX; }
345  // alpha=54.6 corresponds to a 0.9996 maximum cut
346  if (sigmaalpha > 54.6) sigmaalpha = 54.6;
347  sigma2E += sigmaalpha * sigmaalpha * zeta * zeta; // eV^2
348  } else { // Urban model
349  double Ealpha = I / (1. - (alpha * Emax / (Emax + I))); // eV
350  double meanE32 = I * (Emax + I) / Emax * (Ealpha - I); // eV^2
351  sigma2E += fabs(fstep) * (Sigma1 * e1 * e1 + Sigma2 * e2 * e2 + Sigma3 * meanE32); // eV^2
352  }
353  }
354 
355  sigma2E *= 1.E-18; // eV -> GeV
356 
357  // update noise matrix
358  noise[6*7+6] += (mom * mom + fmass * fmass) / pow(mom, 6.) * sigma2E;
359 }
360 
361 
363  double* noise,
364  const double* jacobian,
365  const TVector3* directionBefore,
366  const TVector3* directionAfter) const
367 {
368 
369  // MULTIPLE SCATTERING; calculate sigma^2
370  double sigma2 = 0;
371  assert(fMscModelCode == 0 || fMscModelCode == 1);
372  if (fMscModelCode == 0) {// PANDA report PV/01-07 eq(43); linear in step length
373  sigma2 = 225.E-6*fcharge*fcharge / (fbeta * fbeta * mom * mom) * fabs(fstep) / fradiationLength * fmatZ / (fmatZ + 1) * log(159.*pow(fmatZ, -1. / 3.)) / log(287.*pow(fmatZ, -0.5)); // sigma^2 = 225E-6*z^2/mom^2 * XX0/fbeta^2 * Z/(Z+1) * ln(159*Z^(-1/3))/ln(287*Z^(-1/2)
374 
375  } else if (fMscModelCode == 1) { //Highland not linear in step length formula taken from PDG book 2011 edition
376  double stepOverRadLength = fabs(fstep) / fradiationLength;
377  double logCor = (1 + 0.038 * log(stepOverRadLength));
378  sigma2 = 0.0136 * 0.0136 *fcharge*fcharge / (fbeta * fbeta * mom * mom) * stepOverRadLength * logCor * logCor;
379  }
380  assert(sigma2 > 0.0);
381 
382 
383 
384  // noiseBefore
385  double noiseBefore[7*7];
386  memset(noiseBefore,0x00,7*7*sizeof(double));
387 
388  // calculate euler angles theta, psi (so that directionBefore' points in z' direction)
389  double psi = 0;
390  if (fabs((*directionBefore)[1]) < 1E-14) { // numerical case: arctan(+-inf)=+-PI/2
391  if ((*directionBefore)[0] >= 0.) psi = M_PI / 2.;
392  else psi = 3.*M_PI / 2.;
393  } else {
394  if ((*directionBefore)[1] > 0.) psi = M_PI - atan((*directionBefore)[0] / (*directionBefore)[1]);
395  else psi = -atan((*directionBefore)[0] / (*directionBefore)[1]);
396  }
397  // cache sin and cos
398  double sintheta = sqrt(1 - (*directionBefore)[2] * (*directionBefore)[2]); // theta = arccos(directionBefore[2])
399  double costheta = (*directionBefore)[2];
400  double sinpsi = sin(psi);
401  double cospsi = cos(psi);
402 
403  // calculate NoiseBefore Matrix R M R^T
404  const double noiseBefore33 = sigma2 * (cospsi * cospsi + costheta * costheta - costheta * costheta * cospsi * cospsi);
405  const double noiseBefore43 = sigma2 * cospsi * sinpsi * sintheta * sintheta;
406  const double noiseBefore53 = -sigma2 * costheta * sinpsi * sintheta;
407  const double noiseBefore44 = sigma2 * (sinpsi * sinpsi + costheta * costheta * cospsi * cospsi);
408  const double noiseBefore54 = sigma2 * costheta * cospsi * sintheta;
409  const double noiseBefore55 = sigma2 * sintheta * sintheta;
410 
411  // propagate
412  // last column of jac is [0,0,0,0,0,0,1]
413  double JTM0 = jacobian[21+0] * noiseBefore33 + jacobian[28+0] * noiseBefore43 + jacobian[35+0] * noiseBefore53;
414  double JTM1 = jacobian[21+0] * noiseBefore43 + jacobian[28+0] * noiseBefore44 + jacobian[35+0] * noiseBefore54;
415  double JTM2 = jacobian[21+0] * noiseBefore53 + jacobian[28+0] * noiseBefore54 + jacobian[35+0] * noiseBefore55;
416  double JTM3 = jacobian[21+1] * noiseBefore33 + jacobian[28+1] * noiseBefore43 + jacobian[35+1] * noiseBefore53;
417  double JTM4 = jacobian[21+1] * noiseBefore43 + jacobian[28+1] * noiseBefore44 + jacobian[35+1] * noiseBefore54;
418  double JTM5 = jacobian[21+1] * noiseBefore53 + jacobian[28+1] * noiseBefore54 + jacobian[35+1] * noiseBefore55;
419  double JTM6 = jacobian[21+2] * noiseBefore33 + jacobian[28+2] * noiseBefore43 + jacobian[35+2] * noiseBefore53;
420  double JTM7 = jacobian[21+2] * noiseBefore43 + jacobian[28+2] * noiseBefore44 + jacobian[35+2] * noiseBefore54;
421  double JTM8 = jacobian[21+2] * noiseBefore53 + jacobian[28+2] * noiseBefore54 + jacobian[35+2] * noiseBefore55;
422  double JTM9 = jacobian[21+3] * noiseBefore33 + jacobian[28+3] * noiseBefore43 + jacobian[35+3] * noiseBefore53;
423  double JTM10 = jacobian[21+3] * noiseBefore43 + jacobian[28+3] * noiseBefore44 + jacobian[35+3] * noiseBefore54;
424  double JTM11 = jacobian[21+3] * noiseBefore53 + jacobian[28+3] * noiseBefore54 + jacobian[35+3] * noiseBefore55;
425  double JTM12 = jacobian[21+4] * noiseBefore33 + jacobian[28+4] * noiseBefore43 + jacobian[35+4] * noiseBefore53;
426  double JTM13 = jacobian[21+4] * noiseBefore43 + jacobian[28+4] * noiseBefore44 + jacobian[35+4] * noiseBefore54;
427  double JTM14 = jacobian[21+4] * noiseBefore53 + jacobian[28+4] * noiseBefore54 + jacobian[35+4] * noiseBefore55;
428 
429  // loops are vectorizable by the compiler!
430  noiseBefore[35+5] = (jacobian[21+5] * noiseBefore33 + jacobian[28+5] * noiseBefore43 + jacobian[35+5] * noiseBefore53) * jacobian[21+5] + (jacobian[21+5] * noiseBefore43 + jacobian[28+5] * noiseBefore44 + jacobian[35+5] * noiseBefore54) * jacobian[28+5] + (jacobian[21+5] * noiseBefore53 + jacobian[28+5] * noiseBefore54 + jacobian[35+5] * noiseBefore55) * jacobian[35+5];
431  for (int i=0; i<6; ++i) noiseBefore[i] = JTM0 * jacobian[21+i] + JTM1 * jacobian[28+i] + JTM2 * jacobian[35+i];
432  for (int i=1; i<6; ++i) noiseBefore[7+i] = JTM3 * jacobian[21+i] + JTM4 * jacobian[28+i] + JTM5 * jacobian[35+i];
433  for (int i=2; i<6; ++i) noiseBefore[14+i] = JTM6 * jacobian[21+i] + JTM7 * jacobian[28+i] + JTM8 * jacobian[35+i];
434  for (int i=3; i<6; ++i) noiseBefore[21+i] = JTM9 * jacobian[21+i] + JTM10 * jacobian[28+i] + JTM11 * jacobian[35+i];
435  for (int i=4; i<6; ++i) noiseBefore[28+i] = JTM12 * jacobian[21+i] + JTM13 * jacobian[28+i] + JTM14 * jacobian[35+i];
436 
437  // symmetric part
438  noiseBefore[7+0] = noiseBefore[1];
439  noiseBefore[14+0] = noiseBefore[2]; noiseBefore[14+1] = noiseBefore[7+2];
440  noiseBefore[21+0] = noiseBefore[3]; noiseBefore[21+1] = noiseBefore[7+3]; noiseBefore[21+2] = noiseBefore[14+3];
441  noiseBefore[28+0] = noiseBefore[4]; noiseBefore[28+1] = noiseBefore[7+4]; noiseBefore[28+2] = noiseBefore[14+4]; noiseBefore[28+3] = noiseBefore[21+4];
442  noiseBefore[35+0] = noiseBefore[5]; noiseBefore[35+1] = noiseBefore[7+5]; noiseBefore[35+2] = noiseBefore[14+5]; noiseBefore[35+3] = noiseBefore[21+5]; noiseBefore[35+4] = noiseBefore[28+5];
443 
444 
445  // noiseAfter
446  double noiseAfter[7*7];
447  memset(noiseAfter,0x00,7*7*sizeof(double));
448 
449  // calculate euler angles theta, psi (so that A' points in z' direction)
450  psi = 0;
451  if (fabs((*directionAfter)[1]) < 1E-14) { // numerical case: arctan(+-inf)=+-PI/2
452  if ((*directionAfter)[0] >= 0.) psi = M_PI / 2.;
453  else psi = 3.*M_PI / 2.;
454  } else {
455  if ((*directionAfter)[1] > 0.) psi = M_PI - atan((*directionAfter)[0] / (*directionAfter)[1]);
456  else psi = -atan((*directionAfter)[0] / (*directionAfter)[1]);
457  }
458  // cache sin and cos
459  sintheta = sqrt(1 - (*directionAfter)[2] * (*directionAfter)[2]); // theta = arccos(directionAfter[2])
460  costheta = (*directionAfter)[2];
461  sinpsi = sin(psi);
462  cospsi = cos(psi);
463 
464  // calculate NoiseAfter Matrix R M R^T
465  noiseAfter[3*7+3] = sigma2 * (cospsi * cospsi + costheta * costheta - costheta * costheta * cospsi * cospsi);
466  noiseAfter[3*7+4] = sigma2 * cospsi * sinpsi * sintheta * sintheta; // noiseAfter_ij = noiseAfter_ji
467  noiseAfter[3*7+5] = -sigma2 * costheta * sinpsi * sintheta;
468 
469  noiseAfter[4*7+3] = noiseAfter[3*7+4];
470  noiseAfter[4*7+4] = sigma2 * (sinpsi * sinpsi + costheta * costheta * cospsi * cospsi);
471  noiseAfter[4*7+5] = sigma2 * costheta * cospsi * sintheta;
472 
473  noiseAfter[5*7+3] = noiseAfter[3*7+5];
474  noiseAfter[5*7+4] = noiseAfter[4*7+5];
475  noiseAfter[5*7+5] = sigma2 * sintheta * sintheta;
476 
477  //calculate mean of noiseBefore and noiseAfter and update noise
478  for (unsigned int i=0; i<7*7; ++i){
479  noise[i] += 0.5 * (noiseBefore[i]+noiseAfter[i]);
480  }
481 
482 }
483 
484 
485 double GFMaterialEffects::energyLossBrems(const double& mom) const
486 {
487 
488  if (fabs(fpdg) != 11) return 0; // only for electrons and positrons
489 
490 #if !defined(BETHE)
491  static const double C[101] = { 0.0, -0.960613E-01, 0.631029E-01, -0.142819E-01, 0.150437E-02, -0.733286E-04, 0.131404E-05, 0.859343E-01, -0.529023E-01, 0.131899E-01, -0.159201E-02, 0.926958E-04, -0.208439E-05, -0.684096E+01, 0.370364E+01, -0.786752E+00, 0.822670E-01, -0.424710E-02, 0.867980E-04, -0.200856E+01, 0.129573E+01, -0.306533E+00, 0.343682E-01, -0.185931E-02, 0.392432E-04, 0.127538E+01, -0.515705E+00, 0.820644E-01, -0.641997E-02, 0.245913E-03, -0.365789E-05, 0.115792E+00, -0.463143E-01, 0.725442E-02, -0.556266E-03, 0.208049E-04, -0.300895E-06, -0.271082E-01, 0.173949E-01, -0.452531E-02, 0.569405E-03, -0.344856E-04, 0.803964E-06, 0.419855E-02, -0.277188E-02, 0.737658E-03, -0.939463E-04, 0.569748E-05, -0.131737E-06, -0.318752E-03, 0.215144E-03, -0.579787E-04, 0.737972E-05, -0.441485E-06, 0.994726E-08, 0.938233E-05, -0.651642E-05, 0.177303E-05, -0.224680E-06, 0.132080E-07, -0.288593E-09, -0.245667E-03, 0.833406E-04, -0.129217E-04, 0.915099E-06, -0.247179E-07, 0.147696E-03, -0.498793E-04, 0.402375E-05, 0.989281E-07, -0.133378E-07, -0.737702E-02, 0.333057E-02, -0.553141E-03, 0.402464E-04, -0.107977E-05, -0.641533E-02, 0.290113E-02, -0.477641E-03, 0.342008E-04, -0.900582E-06, 0.574303E-05, 0.908521E-04, -0.256900E-04, 0.239921E-05, -0.741271E-07, -0.341260E-04, 0.971711E-05, -0.172031E-06, -0.119455E-06, 0.704166E-08, 0.341740E-05, -0.775867E-06, -0.653231E-07, 0.225605E-07, -0.114860E-08, -0.119391E-06, 0.194885E-07, 0.588959E-08, -0.127589E-08, 0.608247E-10};
492  static const double xi = 2.51, beta = 0.99, vl = 0.00004;
493 #endif
494 #if defined(BETHE) // no MIGDAL corrections
495  static const double C[101] = { 0.0, 0.834459E-02, 0.443979E-02, -0.101420E-02, 0.963240E-04, -0.409769E-05, 0.642589E-07, 0.464473E-02, -0.290378E-02, 0.547457E-03, -0.426949E-04, 0.137760E-05, -0.131050E-07, -0.547866E-02, 0.156218E-02, -0.167352E-03, 0.101026E-04, -0.427518E-06, 0.949555E-08, -0.406862E-02, 0.208317E-02, -0.374766E-03, 0.317610E-04, -0.130533E-05, 0.211051E-07, 0.158941E-02, -0.385362E-03, 0.315564E-04, -0.734968E-06, -0.230387E-07, 0.971174E-09, 0.467219E-03, -0.154047E-03, 0.202400E-04, -0.132438E-05, 0.431474E-07, -0.559750E-09, -0.220958E-02, 0.100698E-02, -0.596464E-04, -0.124653E-04, 0.142999E-05, -0.394378E-07, 0.477447E-03, -0.184952E-03, -0.152614E-04, 0.848418E-05, -0.736136E-06, 0.190192E-07, -0.552930E-04, 0.209858E-04, 0.290001E-05, -0.133254E-05, 0.116971E-06, -0.309716E-08, 0.212117E-05, -0.103884E-05, -0.110912E-06, 0.655143E-07, -0.613013E-08, 0.169207E-09, 0.301125E-04, -0.461920E-04, 0.871485E-05, -0.622331E-06, 0.151800E-07, -0.478023E-04, 0.247530E-04, -0.381763E-05, 0.232819E-06, -0.494487E-08, -0.336230E-04, 0.223822E-04, -0.384583E-05, 0.252867E-06, -0.572599E-08, 0.105335E-04, -0.567074E-06, -0.216564E-06, 0.237268E-07, -0.658131E-09, 0.282025E-05, -0.671965E-06, 0.565858E-07, -0.193843E-08, 0.211839E-10, 0.157544E-04, -0.304104E-05, -0.624410E-06, 0.120124E-06, -0.457445E-08, -0.188222E-05, -0.407118E-06, 0.375106E-06, -0.466881E-07, 0.158312E-08, 0.945037E-07, 0.564718E-07, -0.319231E-07, 0.371926E-08, -0.123111E-09};
496  static const double xi = 2.10, fbeta = 1.00, vl = 0.001;
497 #endif
498 
499  double BCUT = 10000.; // energy up to which soft bremsstrahlung energy loss is calculated
500 
501  double THIGH = 100., CHIGH = 50.;
502  double dedxBrems = 0.;
503 
504  if (BCUT > 0.) {
505  double T, kc;
506 
507  if (BCUT >= mom) BCUT = mom; // confine BCUT to mom
508 
509  // T=mom, confined to THIGH
510  // kc=BCUT, confined to CHIGH ??
511  if (mom >= THIGH) {
512  T = THIGH;
513  if (BCUT >= THIGH) kc = CHIGH;
514  else kc = BCUT;
515  } else {
516  T = mom;
517  kc = BCUT;
518  }
519 
520  double E = T + me; // total electron energy
521  if (BCUT > T) kc = T;
522 
523  double X = log(T / me);
524  double Y = log(kc / (E * vl));
525 
526  double XX;
527  int K;
528  double S = 0., YY = 1.;
529 
530  for (unsigned int I = 1; I <= 2; ++I) {
531  XX = 1.;
532  for (unsigned int J = 1; J <= 6; ++J) {
533  K = 6 * I + J - 6;
534  S = S + C[K] * XX * YY;
535  XX = XX * X;
536  }
537  YY = YY * Y;
538  }
539 
540  for (unsigned int I = 3; I <= 6; ++I) {
541  XX = 1.;
542  for (unsigned int J = 1; J <= 6; ++J) {
543  K = 6 * I + J - 6;
544  if (Y <= 0.) S = S + C[K] * XX * YY;
545  else S = S + C[K + 24] * XX * YY;
546  XX = XX * X;
547  }
548  YY = YY * Y;
549  }
550 
551  double SS = 0.;
552  YY = 1.;
553 
554  for (unsigned int I = 1; I <= 2; ++I) {
555  XX = 1.;
556  for (unsigned int J = 1; J <= 5; ++J) {
557  K = 5 * I + J + 55;
558  SS = SS + C[K] * XX * YY;
559  XX = XX * X;
560  }
561  YY = YY * Y;
562  }
563 
564  for (unsigned int I = 3; I <= 5; ++I) {
565  XX = 1.;
566  for (unsigned int J = 1; J <= 5; ++J) {
567  K = 5 * I + J + 55;
568  if (Y <= 0.) SS = SS + C[K] * XX * YY;
569  else SS = SS + C[K + 15] * XX * YY;
570  XX = XX * X;
571  }
572  YY = YY * Y;
573  }
574 
575  S = S + fmatZ * SS;
576 
577  if (S > 0.) {
578  double CORR = 1.;
579 #if !defined(BETHE)
580  CORR = 1. / (1. + 0.805485E-10 * fmatDensity * fmatZ * E * E / (fmatA * kc * kc)); // MIGDAL correction factor
581 #endif
582 
583  double FAC = fmatZ * (fmatZ + xi) * E * E * pow((kc * CORR / T), beta) / (E + me);
584  if (FAC <= 0.) return 0.;
585  dedxBrems = FAC * S;
586 
587  double RAT;
588 
589  if (mom > THIGH) {
590  if (BCUT < THIGH) {
591  RAT = BCUT / mom;
592  S = (1. - 0.5 * RAT + 2.*RAT * RAT / 9.);
593  RAT = BCUT / T;
594  S = S / (1. - 0.5 * RAT + 2.*RAT * RAT / 9.);
595  } else {
596  RAT = BCUT / mom;
597  S = BCUT * (1. - 0.5 * RAT + 2.*RAT * RAT / 9.);
598  RAT = kc / T;
599  S = S / (kc * (1. - 0.5 * RAT + 2.*RAT * RAT / 9.));
600  }
601  dedxBrems = dedxBrems * S; // GeV barn
602  }
603 
604  dedxBrems = 0.60221367 * fmatDensity * dedxBrems / fmatA; // energy loss dE/dx [GeV/cm]
605  }
606  }
607 
608  if (dedxBrems < 0.) dedxBrems = 0;
609 
610  double factor = 1.; // positron correction factor
611 
612  if (fpdg == -11) {
613  static const double AA = 7522100., A1 = 0.415, A3 = 0.0021, A5 = 0.00054;
614 
615  double ETA = 0.;
616  if (fmatZ > 0.) {
617  double X = log(AA * mom / fmatZ * fmatZ);
618  if (X > -8.) {
619  if (X >= +9.) ETA = 1.;
620  else {
621  double W = A1 * X + A3 * pow(X, 3.) + A5 * pow(X, 5.);
622  ETA = 0.5 + atan(W) / M_PI;
623  }
624  }
625  }
626 
627  double E0;
628 
629  if (ETA < 0.0001) factor = 1.E-10;
630  else if (ETA > 0.9999) factor = 1.;
631  else {
632  E0 = BCUT / mom;
633  if (E0 > 1.) E0 = 1.;
634  if (E0 < 1.E-8) factor = 1.;
635  else factor = ETA * (1. - pow(1. - E0, 1. / ETA)) / E0;
636  }
637  }
638 
639  double DE = fabs(fstep) * factor * dedxBrems; //always positive
640  double momLoss = sqrt(mom * mom + 2.*sqrt(mom * mom + fmass * fmass) * DE + DE * DE) - mom; //always positive
641 
642  return momLoss;
643 }
644 
645 
647  double* noise) const
648 {
649 
650  if (fabs(fpdg) != 11) return; // only for electrons and positrons
651 
652  double LX = 1.442695 * fabs(fstep) / fradiationLength;
653  double S2B = mom * mom * (1. / pow(3., LX) - 1. / pow(4., LX));
654  double DEDXB = pow(fabs(S2B), 0.5);
655  DEDXB = 1.2E9 * DEDXB; //eV
656  double sigma2E = DEDXB * DEDXB; //eV^2
657  sigma2E *= 1.E-18; // eV -> GeV
658 
659  noise[6*7+6] += (mom * mom + fmass * fmass) / pow(mom, 6.) * sigma2E;
660 }
661 
662 //---------------------------------------------------------------------------------
663 
665 
666 
667 /*
668 Reference for elemental mean excitation energies at:
669 http://physics.nist.gov/PhysRefData/XrayMassCoef/tab1.html
670 */
671 
672 const int MeanExcEnergy_NELEMENTS = 93; // 0 = vacuum, 1 = hydrogen, 92 = uranium
673 const float MeanExcEnergy_vals[] = {1.e15, 19.2, 41.8, 40.0, 63.7, 76.0, 78., 82.0, 95.0, 115.0, 137.0, 149.0, 156.0, 166.0, 173.0, 173.0, 180.0, 174.0, 188.0, 190.0, 191.0, 216.0, 233.0, 245.0, 257.0, 272.0, 286.0, 297.0, 311.0, 322.0, 330.0, 334.0, 350.0, 347.0, 348.0, 343.0, 352.0, 363.0, 366.0, 379.0, 393.0, 417.0, 424.0, 428.0, 441.0, 449.0, 470.0, 470.0, 469.0, 488.0, 488.0, 487.0, 485.0, 491.0, 482.0, 488.0, 491.0, 501.0, 523.0, 535.0, 546.0, 560.0, 574.0, 580.0, 591.0, 614.0, 628.0, 650.0, 658.0, 674.0, 684.0, 694.0, 705.0, 718.0, 727.0, 736.0, 746.0, 757.0, 790.0, 790.0, 800.0, 810.0, 823.0, 823.0, 830.0, 825.0, 794.0, 827.0, 826.0, 841.0, 847.0, 878.0, 890.0};
674 
675 float MeanExcEnergy_get(int Z)
676 {
677  assert(Z >= 0 && Z < MeanExcEnergy_NELEMENTS);
678  return MeanExcEnergy_vals[Z];
679 }
680 
681 float MeanExcEnergy_get(TGeoMaterial* mat)
682 {
683  if (mat->IsMixture()) {
684  double logMEE = 0.;
685  double denom = 0.;
686  TGeoMixture* mix = (TGeoMixture*)mat;
687  for (int i = 0; i < mix->GetNelements(); ++i) {
688  int index = int(floor((mix->GetZmixt())[i]));
689  logMEE += 1. / (mix->GetAmixt())[i] * (mix->GetWmixt())[i] * (mix->GetZmixt())[i] * log(MeanExcEnergy_get(index));
690  denom += (mix->GetWmixt())[i] * (mix->GetZmixt())[i] * 1. / (mix->GetAmixt())[i];
691  }
692  logMEE /= denom;
693  return exp(logMEE);
694  } else { // not a mixture
695  int index = int(floor(mat->GetZ()));
696  return MeanExcEnergy_get(index);
697  }
698 }