Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/processes/electromagnetic/standard/src/G4eDPWAElasticDCS.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 //
 27 // -------------------------------------------------------------------
 28 //
 29 // GEANT4 Class header file
 30 //
 31 //
 32 // File name:     G4eDPWAElasticDCS
 33 //
 34 // Author:        Mihaly Novak
 35 //
 36 // Creation date: 02.07.2020
 37 //
 38 // Modifications:
 39 //
 40 //
 41 // -------------------------------------------------------------------
 42 
 43 #include "G4eDPWAElasticDCS.hh"
 44 #include "G4EmParameters.hh"
 45 #include "G4Physics2DVector.hh"
 46 
 47 #include "zlib.h"
 48 
 49 //
 50 // Global variables:
 51 //
 52 G4bool                G4eDPWAElasticDCS::gIsGridLoaded  = false;
 53 G4String              G4eDPWAElasticDCS::gDataDirectory = "";
 54 // final values of these variables will be set in LoadGrid() called by master
 55 std::size_t           G4eDPWAElasticDCS::gNumEnergies   = 106;
 56 std::size_t           G4eDPWAElasticDCS::gIndxEnergyLim =  35;
 57 std::size_t           G4eDPWAElasticDCS::gNumThetas1    = 247;
 58 std::size_t           G4eDPWAElasticDCS::gNumThetas2    = 128;
 59 G4double              G4eDPWAElasticDCS::gLogMinEkin    = 1.0;
 60 G4double              G4eDPWAElasticDCS::gInvDelLogEkin = 1.0;
 61 // containers for grids: Ekin, mu(t)=0.5[1-cos(t)] and u(mu,A)=(A+1)mu/(mu+A)
 62 std::vector<G4double> G4eDPWAElasticDCS::gTheEnergies(G4eDPWAElasticDCS::gNumEnergies);
 63 std::vector<G4double> G4eDPWAElasticDCS::gTheMus1(G4eDPWAElasticDCS::gNumThetas1);
 64 std::vector<G4double> G4eDPWAElasticDCS::gTheMus2(G4eDPWAElasticDCS::gNumThetas2);
 65 std::vector<G4double> G4eDPWAElasticDCS::gTheU1(G4eDPWAElasticDCS::gNumThetas1);
 66 std::vector<G4double> G4eDPWAElasticDCS::gTheU2(G4eDPWAElasticDCS::gNumThetas2);
 67 // abscissas and weights of an 8 point Gauss-Legendre quadrature
 68 // for numerical integration on [0,1]
 69 const G4double        G4eDPWAElasticDCS::gXGL[] = {
 70   1.98550718E-02, 1.01666761E-01, 2.37233795E-01, 4.08282679E-01,
 71   5.91717321E-01, 7.62766205E-01, 8.98333239E-01, 9.80144928E-01
 72 };
 73 const G4double        G4eDPWAElasticDCS::gWGL[] = {
 74   5.06142681E-02, 1.11190517E-01, 1.56853323E-01, 1.81341892E-01,
 75   1.81341892E-01, 1.56853323E-01, 1.11190517E-01, 5.06142681E-02
 76 };
 77 
 78 
 79 // - iselectron   : data for e- (for e+ otherwise)
 80 // - isrestricted : sampling of angular deflection on restricted interavl is
 81 //                  required (i.e. in case of mixed-simulation models)
 82 G4eDPWAElasticDCS::G4eDPWAElasticDCS(G4bool iselectron, G4bool isrestricted)
 83 : fIsRestrictedSamplingRequired(isrestricted), fIsElectron(iselectron) {
 84   fDCS.resize(gMaxZ+1, nullptr);
 85   fDCSLow.resize(gMaxZ+1, nullptr);
 86   fSamplingTables.resize(gMaxZ+1, nullptr);
 87 }
 88 
 89 
 90  // DTR
 91 G4eDPWAElasticDCS::~G4eDPWAElasticDCS() {
 92   for (std::size_t i=0; i<fDCS.size(); ++i) {
 93     if (fDCS[i]) delete fDCS[i];
 94   }
 95   for (std::size_t i=0; i<fDCSLow.size(); ++i) {
 96     if (fDCSLow[i]) delete fDCSLow[i];
 97   }
 98   for (std::size_t i=0; i<fSamplingTables.size(); ++i) {
 99     if (fSamplingTables[i]) delete fSamplingTables[i];
100   }
101   // clear scp correction data
102   for (std::size_t imc=0; imc<fSCPCPerMatCuts.size(); ++imc) {
103     if (fSCPCPerMatCuts[imc]) {
104       fSCPCPerMatCuts[imc]->fVSCPC.clear();
105       delete fSCPCPerMatCuts[imc];
106     }
107   }
108   fSCPCPerMatCuts.clear();
109 }
110 
111 
112 // initialise for a given 'iz' atomic number:
113 //  - nothing happens if it has already been initialised for that Z.
114 void G4eDPWAElasticDCS::InitialiseForZ(std::size_t iz) {
115   if (!gIsGridLoaded) {
116     LoadGrid();
117   }
118   LoadDCSForZ((G4int)iz);
119   BuildSmplingTableForZ((G4int)iz);
120 }
121 
122 
123 // loads the kinetic energy and theta grids for the DCS data (first init step)
124 // should be called only by the master
125 void G4eDPWAElasticDCS::LoadGrid() {
126   G4String fname = FindDirectoryPath() + "grid.dat";
127   std::ifstream infile(fname.c_str());
128   if (!infile.is_open()) {
129     G4String msg  =
130          "    Problem while trying to read " + fname + " file.\n"+
131          "    G4LEDATA version should be G4EMLOW7.12 or later.\n";
132     G4Exception("G4eDPWAElasticDCS::ReadCompressedFile","em0006",
133                 FatalException,msg.c_str());
134     return;
135   }
136   // read size
137   infile >> gNumEnergies;
138   infile >> gNumThetas1;
139   infile >> gNumThetas2;
140   // read the grids
141   // - energy in [MeV]
142   G4double dum = 0.0;
143   gTheEnergies.resize(gNumEnergies);
144   for (std::size_t ie=0; ie<gNumEnergies; ++ie) {
145     infile >> dum;
146     gTheEnergies[ie] = G4Log(dum*CLHEP::MeV);
147     if (gTheEnergies[ie]<G4Log(2.0*CLHEP::keV)) gIndxEnergyLim = ie;  // only for e-
148   }
149   ++gIndxEnergyLim;
150   // store/set usefull logarithms of the kinetic energy grid
151   gLogMinEkin    = gTheEnergies[0];
152   gInvDelLogEkin = (gNumEnergies-1)/(gTheEnergies[gNumEnergies-1]-gTheEnergies[0]);
153   // - theta1 in [deg.] (247): we store mu(theta) = 0.5[1-cos(theta)]
154   gTheMus1.resize(gNumThetas1);
155   gTheU1.resize(gNumThetas1);
156   const double theA = 0.01;
157   for (std::size_t it=0; it<gNumThetas1; ++it) {
158     infile >> dum;
159     gTheMus1[it] = 0.5*(1.0-std::cos(dum*CLHEP::degree));
160     gTheU1[it]   = (theA+1.0)*gTheMus1[it]/(theA+gTheMus1[it]);
161   }
162   // - theta2 in [deg.] (128): we store mu(theta) = 0.5[1-cos(theta)]
163   gTheMus2.resize(gNumThetas2);
164   gTheU2.resize(gNumThetas2);
165   for (std::size_t it=0; it<gNumThetas2; ++it) {
166     infile >> dum;
167     gTheMus2[it] = 0.5*(1.0-std::cos(dum*CLHEP::degree));
168     gTheU2[it]   = (theA+1.0)*gTheMus2[it]/(theA+gTheMus2[it]);
169 
170   }
171   infile.close();
172   gIsGridLoaded = true;
173 }
174 
175 
176 // load DCS data for a given Z
177 void G4eDPWAElasticDCS::LoadDCSForZ(G4int iz) {
178   // Check if it has already been done:
179   if (fDCS[iz]) return;
180   // Do it otherwise
181   if (fIsElectron) {
182     // e-
183     // load the high energy part firt:
184     // - with gNumThetas2 theta and gNumEnergies-gIndxEnergyLim energy values
185     const std::size_t hNumEnergries = gNumEnergies-gIndxEnergyLim;
186     auto v2DHigh = new G4Physics2DVector(gNumThetas2, hNumEnergries);
187     v2DHigh->SetBicubicInterpolation(true);
188     for (std::size_t it=0; it<gNumThetas2; ++it) {
189       v2DHigh->PutX(it, gTheMus2[it]);
190     }
191     for (std::size_t ie=0; ie<hNumEnergries; ++ie) {
192       v2DHigh->PutY(ie, gTheEnergies[gIndxEnergyLim+ie]);
193     }
194     std::ostringstream ossh;
195     ossh << FindDirectoryPath() << "dcss/el/dcs_"<< iz<<"_h";
196     std::istringstream finh(std::ios::in);
197     ReadCompressedFile(ossh.str(), finh);
198     G4double dum = 0.0;
199     for (std::size_t it=0; it<gNumThetas2; ++it) {
200       finh >> dum;
201       for (std::size_t ie=0; ie<hNumEnergries; ++ie) {
202         finh >> dum;
203         v2DHigh->PutValue(it, ie, G4Log(dum*CLHEP::cm2/CLHEP::sr));
204       }
205     }
206     // load the low energy part:
207     // - with gNumThetas1 theta and gIndxEnergyLim+1 energy values (the +1 is
208     //   for including the firts DCS from the higher part above for being
209     //   able to perform interpolation between the high and low energy DCS set)
210     auto v2DLow = new G4Physics2DVector(gNumThetas1, gIndxEnergyLim+1);
211     v2DLow->SetBicubicInterpolation(true);
212     for (std::size_t it=0; it<gNumThetas1; ++it) {
213       v2DLow->PutX(it, gTheMus1[it]);
214     }
215     for (std::size_t ie=0; ie<gIndxEnergyLim+1; ++ie) {
216       v2DLow->PutY(ie, gTheEnergies[ie]);
217     }
218     std::ostringstream ossl;
219     ossl << FindDirectoryPath() << "dcss/el/dcs_"<< iz<<"_l";
220     std::istringstream finl(std::ios::in);
221     ReadCompressedFile(ossl.str(), finl);
222     for (std::size_t it=0; it<gNumThetas1; ++it) {
223       finl >> dum;
224       for (std::size_t ie=0; ie<gIndxEnergyLim; ++ie) {
225         finl >> dum;
226         v2DLow->PutValue(it, ie, G4Log(dum*CLHEP::cm2/CLHEP::sr));
227       }
228     }
229     // add the +1 part: interpolate the firts DCS from the high energy
230     std::size_t ix = 0;
231     std::size_t iy = 0;
232     for (std::size_t it=0; it<gNumThetas1; ++it) {
233       const G4double val = v2DHigh->Value(gTheMus1[it], gTheEnergies[gIndxEnergyLim], ix, iy);
234       v2DLow->PutValue(it, gIndxEnergyLim, val);
235     }
236     // store
237     fDCSLow[iz] = v2DLow;
238     fDCS[iz]    = v2DHigh;
239   } else {
240     // e+
241     auto v2D= new G4Physics2DVector(gNumThetas2, gNumEnergies);
242     v2D->SetBicubicInterpolation(true);
243     for (std::size_t it=0; it<gNumThetas2; ++it) {
244       v2D->PutX(it, gTheMus2[it]);
245     }
246     for (std::size_t ie=0; ie<gNumEnergies; ++ie) {
247       v2D->PutY(ie, gTheEnergies[ie]);
248     }
249     std::ostringstream oss;
250     oss << FindDirectoryPath() << "dcss/pos/dcs_"<< iz;
251     std::istringstream fin(std::ios::in);
252     ReadCompressedFile(oss.str(), fin);
253     G4double dum = 0.0;
254     for (std::size_t it=0; it<gNumThetas2; ++it) {
255       fin >> dum;
256       for (std::size_t ie=0; ie<gNumEnergies; ++ie) {
257         fin >> dum;
258         v2D->PutValue(it, ie, G4Log(dum*CLHEP::cm2/CLHEP::sr));
259       }
260     }
261     fDCS[iz]= v2D;
262   }
263 }
264 
265 
266 
267 // Computes the elastic, first and second cross sections for the given kinetic
268 // energy and target atom.
269 // Cross sections are zero ff ekin is below/above the kinetic energy grid
270 void G4eDPWAElasticDCS::ComputeCSPerAtom(G4int iz, G4double ekin, G4double& elcs,
271                                          G4double& tr1cs, G4double& tr2cs,
272                                          G4double mumin, G4double mumax) {
273   // init all cross section values to zero;
274   elcs  = 0.0;
275   tr1cs = 0.0;
276   tr2cs = 0.0;
277   // make sure that mu(theta) = 0.5[1-cos(theta)] limits have appropriate vals
278   mumin = std::max(0.0, std::min(1.0, mumin));
279   mumax = std::max(0.0, std::min(1.0, mumax));
280   if (mumin>=mumax) return;
281   // make sure that kin. energy is within the available range (10 eV-100MeV)  
282   const G4double lekin = std::max(gTheEnergies[0], std::min(gTheEnergies[gNumEnergies-1], G4Log(ekin)));
283   // if the lower, denser in theta, DCS set should be used
284   const G4bool isLowerGrid = (fIsElectron && lekin<gTheEnergies[gIndxEnergyLim]);
285   const std::vector<G4double>& theMuVector = (isLowerGrid) ? gTheMus1    : gTheMus2;
286   const G4Physics2DVector*     the2DDCS    = (isLowerGrid) ? fDCSLow[iz] : fDCS[iz];
287   // find lower/upper mu bin of integration:
288   // 0.0 <= mumin < 1.0 for sure here
289   const std::size_t iMuStart = (mumin == 0.0) ? 0 : std::distance( theMuVector.begin(), std::upper_bound(theMuVector.begin(), theMuVector.end(), mumin) )-1 ;
290   // 0.0 < mumax <= 1.0 for sure here
291   const std::size_t iMuEnd   = (mumax == 1.0) ? theMuVector.size()-2 : std::distance( theMuVector.begin(), std::upper_bound(theMuVector.begin(), theMuVector.end(), mumax) )-1 ;
292   // perform numerical integration of the DCS over the given [mumin, mumax]
293   // interval (where mu(theta) = 0.5[1-cos(theta)]) to get the elastic, first
294   std::size_t ix = 0;
295   std::size_t iy = 0;
296   for (std::size_t imu=iMuStart; imu<=iMuEnd; ++imu) {
297     G4double elcsPar   = 0.0;
298     G4double tr1csPar  = 0.0;
299     G4double tr2csPar  = 0.0;
300     const G4double low = (imu==iMuStart) ? mumin     : theMuVector[imu];
301     const G4double del = (imu==iMuEnd)   ? mumax-low : theMuVector[imu+1]-low;
302     ix = imu;
303     for (std::size_t igl=0; igl<8; ++igl) {
304       const double mu  = low + del*gXGL[igl];
305       const double dcs = G4Exp(the2DDCS->Value(mu, lekin, ix, iy));
306       elcsPar  += gWGL[igl]*dcs;             // elastic
307       tr1csPar += gWGL[igl]*dcs*mu;          // first transport
308       tr2csPar += gWGL[igl]*dcs*mu*(1.0-mu); // second transport
309     }
310     elcs  += del*elcsPar;
311     tr1cs += del*tr1csPar;
312     tr2cs += del*tr2csPar;
313   }
314   elcs  *=  2.0*CLHEP::twopi;
315   tr1cs *=  4.0*CLHEP::twopi;
316   tr2cs *= 12.0*CLHEP::twopi;
317 }
318 
319 
320 // data structure to store one sampling table: combined Alias + RatIn
321 // NOTE: when Alias is used, sampling on a resctricted interval is not possible
322 //       However, Alias makes possible faster sampling. Alias is used in case
323 //       of single scattering model while it's not used in case of mixed-model
324 //       when restricted interval sampling is needed. This is controlled by
325 //       the fIsRestrictedSamplingRequired flag (false by default).
326 struct OneSamplingTable {
327   OneSamplingTable () = default;
328   void SetSize(std::size_t nx, G4bool useAlias)  {
329     fN = nx;
330     // Alias
331     if (useAlias) {
332       fW.resize(nx);
333       fI.resize(nx);
334     }
335     // Ratin
336     fCum.resize(nx);
337     fA.resize(nx);
338     fB.resize(nx);
339   }
340 
341   // members
342   std::size_t           fN;            // # data points
343   G4double              fScreenParA;   // the screening parameter
344   std::vector<G4double> fW;
345   std::vector<G4double> fCum;
346   std::vector<G4double> fA;
347   std::vector<G4double> fB;
348   std::vector<G4int>    fI;
349 };
350 
351 
352 // loads sampling table for the given Z over the enrgy grid
353 void G4eDPWAElasticDCS::BuildSmplingTableForZ(G4int iz) {
354   // Check if it has already been done:
355   if (fSamplingTables[iz]) return;
356   // Do it otherwise:
357   // allocate space
358   auto sTables = new std::vector<OneSamplingTable>(gNumEnergies);
359   // read compressed sampling table data
360   std::ostringstream oss;
361   const G4String fname = fIsElectron ? "stables/el/" : "stables/pos/";
362   oss << FindDirectoryPath() << fname << "stable_" << iz;
363   std::istringstream fin(std::ios::in);
364   ReadCompressedFile(oss.str(), fin);
365   std::size_t ndata = 0;
366   for (std::size_t ie=0; ie<gNumEnergies; ++ie) {
367     OneSamplingTable& aTable = (*sTables)[ie];
368     // #data in this table
369     fin >> ndata;
370     aTable.SetSize(ndata, !fIsRestrictedSamplingRequired);
371     // the A screening parameter value used for transformation of mu to u
372     fin >> aTable.fScreenParA;
373     // load data: Alias(W,I) + RatIn(Cum, A, B)
374     if (!fIsRestrictedSamplingRequired)  {
375       for (std::size_t id=0; id<ndata; ++id) {
376         fin >> aTable.fW[id];
377       }
378       for (std::size_t id=0; id<ndata; ++id) {
379         fin >> aTable.fI[id];
380       }
381     }
382     for (std::size_t id=0; id<ndata; ++id) {
383       fin >> aTable.fCum[id];
384     }
385     for (std::size_t id=0; id<ndata; ++id) {
386       fin >> aTable.fA[id];
387     }
388     for (std::size_t id=0; id<ndata; ++id) {
389       fin >> aTable.fB[id];
390     }
391   }
392   fSamplingTables[iz] = sTables;
393 }
394 
395 
396 
397 
398 
399 // samples cos(theta) i.e. cosine of the polar angle of scattering in elastic
400 // interaction (Coulomb scattering) of the projectile (e- or e+ depending on
401 // fIsElectron) with kinetic energy of exp('lekin'), target atom with atomic
402 // muber of 'iz'. See the 'SampleCosineThetaRestricted' for obtain samples on
403 // a restricted inteval.
404 G4double
405 G4eDPWAElasticDCS::SampleCosineTheta(std::size_t iz, G4double lekin, G4double r1,
406                                      G4double r2, G4double r3) {
407   lekin = std::max(gTheEnergies[0], std::min(gTheEnergies[gNumEnergies-1], lekin));
408   // determine the discrete ekin sampling table to be used:
409   //  - statistical interpolation (i.e. linear) on log energy scale
410   const G4double      rem = (lekin-gLogMinEkin)*gInvDelLogEkin;
411   const auto            k = (std::size_t)rem;
412   const std::size_t iekin = (r1 < rem-k) ? k+1 : k;
413   // sample the mu(t)=0.5(1-cos(t))
414   const double mu    = SampleMu(iz, iekin, r2, r3);
415   return std::max(-1.0, std::min(1.0, 1.0-2.0*mu));
416 }
417 
418 
419 // samples cos(theta) i.e. cosine of the polar angle of scattering in elastic
420 // interaction (Coulomb scattering) of the projectile (e- or e+ depending on
421 // fIsElectron) with kinetic energy of exp('lekin'), target atom with atomic
422 // muber of 'iz'.
423 // The cosine theta will be in the [costMin, costMax] interval where costMin
424 // corresponds to a maximum allowed polar scattering angle thetaMax while
425 // costMin corresponds to minimum allowed polar scatterin angle thetaMin.
426 // See the 'SampleCosineTheta' for obtain samples on the entire [-1,1] range.
427 G4double
428 G4eDPWAElasticDCS::SampleCosineThetaRestricted(std::size_t iz, G4double lekin,
429                                                G4double r1, G4double r2,
430                                                G4double costMax, G4double costMin) {
431   // costMin corresponds to mu-max while costMax to mu-min: mu(t)=0.5[1-cos(t)]
432   lekin = std::max(gTheEnergies[0], std::min(gTheEnergies[gNumEnergies-1], lekin));
433   // determine the discrete ekin sampling table to be used:
434   //  - statistical interpolation (i.e. linear) on log energy scale
435   const G4double      rem = (lekin-gLogMinEkin)*gInvDelLogEkin;
436   const auto            k = (size_t)rem;
437   const std::size_t iekin = (r1 < rem-k) ? k : k+1;
438   // sample the mu(t)=0.5(1-cos(t))
439   const G4double mu  = SampleMu(iz, iekin, r2, 0.5*(1.0-costMax), 0.5*(1.0-costMin));
440   return std::max(-1.0, std::min(1.0, 1.0-2.0*mu));
441 }
442 
443 
444 
445 G4double
446 G4eDPWAElasticDCS::SampleMu(std::size_t izet, std::size_t ie, G4double r1, G4double r2) {
447   OneSamplingTable& rtn = (*fSamplingTables[izet])[ie];
448   // get the lower index of the bin by using the alias part
449   const G4double rest = r1 * (rtn.fN - 1);
450   auto indxl          = (std::size_t)rest;
451   const G4double dum0 = rest - indxl;
452   if (rtn.fW[indxl] < dum0) indxl = rtn.fI[indxl];
453   // sample value within the selected bin by using ratin based numerical inversion
454   const G4double delta = rtn.fCum[indxl + 1] - rtn.fCum[indxl];
455   const G4double aval  = r2 * delta;
456 
457   const G4double dum1  = (1.0 + rtn.fA[indxl] + rtn.fB[indxl]) * delta * aval;
458   const G4double dum2  = delta * delta + rtn.fA[indxl] * delta * aval + rtn.fB[indxl] * aval * aval;
459   const std::vector<G4double>& theUVect = (fIsElectron && ie<gIndxEnergyLim) ? gTheU1 : gTheU2;
460   const G4double    u  = theUVect[indxl] + dum1 / dum2 * (theUVect[indxl + 1] - theUVect[indxl]);
461   // transform back u to mu
462   return rtn.fScreenParA*u/(rtn.fScreenParA+1.0-u);
463 }
464 
465 
466 
467 G4double
468 G4eDPWAElasticDCS::FindCumValue(G4double u, const OneSamplingTable& stable,
469                                 const std::vector<G4double>& uvect) {
470   const std::size_t iLow = std::distance( uvect.begin(), std::upper_bound(uvect.begin(), uvect.end(), u) )-1;
471   const G4double    tau  = (u-uvect[iLow])/(uvect[iLow+1]-uvect[iLow]); // Note: I could store 1/(fX[iLow+1]-fX[iLow])
472   const G4double    dum0 = (1.0+stable.fA[iLow]*(1.0-tau)+stable.fB[iLow]);
473   const G4double    dum1 = 2.0*stable.fB[iLow]*tau;
474   const G4double    dum2 = 1.0 - std::sqrt(std::max(0.0, 1.0-2.0*dum1*tau/(dum0*dum0)));
475   return std::min(stable.fCum[iLow+1], std::max(stable.fCum[iLow], stable.fCum[iLow]+dum0*dum2*(stable.fCum[iLow+1]-stable.fCum[iLow])/dum1 ));
476 }
477 
478 // muMin and muMax : no checks on these
479 G4double G4eDPWAElasticDCS::SampleMu(std::size_t izet, std::size_t ie, G4double r1,
480                                      G4double muMin, G4double muMax) {
481   const OneSamplingTable& rtn = (*fSamplingTables[izet])[ie];
482   const G4double theA    = rtn.fScreenParA;
483   //
484   const std::vector<G4double>& theUVect = (fIsElectron && ie<gIndxEnergyLim) ? gTheU1 : gTheU2;
485   const G4double xiMin   = (muMin > 0.0) ? FindCumValue((theA+1.0)*muMin/(theA+muMin), rtn, theUVect) : 0.0;
486   const G4double xiMax   = (muMax < 1.0) ? FindCumValue((theA+1.0)*muMax/(theA+muMax), rtn, theUVect) : 1.0;
487   //
488   const G4double    xi   = xiMin+r1*(xiMax-xiMin); // a smaple within the range
489   const std::size_t iLow = std::distance( rtn.fCum.begin(), std::upper_bound(rtn.fCum.begin(), rtn.fCum.end(), xi) )-1;
490   const G4double   delta = rtn.fCum[iLow + 1] - rtn.fCum[iLow];
491   const G4double   aval  = xi - rtn.fCum[iLow];
492 
493   const G4double dum1    = (1.0 + rtn.fA[iLow] + rtn.fB[iLow]) * delta * aval;
494   const G4double dum2    = delta * delta + rtn.fA[iLow] * delta * aval + rtn.fB[iLow] * aval * aval;
495   const G4double    u    = theUVect[iLow] + dum1 / dum2 * (theUVect[iLow + 1] - theUVect[iLow]);
496   return theA*u/(theA+1.0-u);
497 }
498 
499 
500 
501 // set the DCS data directory path
502 const G4String& G4eDPWAElasticDCS::FindDirectoryPath() {
503   // check environment variable
504   if (gDataDirectory.empty()) {
505     std::ostringstream ost;
506     ost << G4EmParameters::Instance()->GetDirLEDATA() << "/dpwa/";
507     gDataDirectory = ost.str();
508   }
509   return gDataDirectory;
510 }
511 
512 
513 
514 // uncompress one data file into the input string stream
515 void
516 G4eDPWAElasticDCS::ReadCompressedFile(G4String fname, std::istringstream &iss) {
517   G4String *dataString = nullptr;
518   G4String compfilename(fname+".z");
519   // create input stream with binary mode operation and positioning at the end of the file
520   std::ifstream in(compfilename, std::ios::binary | std::ios::ate);
521   if (in.good()) {
522      // get current position in the stream (was set to the end)
523      std::size_t fileSize = in.tellg();
524      // set current position being the beginning of the stream
525      in.seekg(0,std::ios::beg);
526      // create (zlib) byte buffer for the data
527      Bytef *compdata = new Bytef[fileSize];
528      while(in) {
529         in.read((char*)compdata, fileSize);
530      }
531      // create (zlib) byte buffer for the uncompressed data
532      uLongf complen    = (uLongf)(fileSize*4);
533      Bytef *uncompdata = new Bytef[complen];
534      while (Z_OK!=uncompress(uncompdata, &complen, compdata, fileSize)) {
535         // increase uncompressed byte buffer
536         delete[] uncompdata;
537         complen   *= 2;
538         uncompdata = new Bytef[complen];
539      }
540      // delete the compressed data buffer
541      delete [] compdata;
542      // create a string from the uncompressed data (will be deallocated by the caller)
543      dataString = new G4String((char*)uncompdata, (long)complen);
544      // delete the uncompressed data buffer
545      delete [] uncompdata;
546   } else {
547     G4String msg  =
548          "    Problem while trying to read " + fname + " data file.\n"+
549          "    G4LEDATA version should be G4EMLOW7.12 or later.\n";
550     G4Exception("G4eDPWAElasticDCS::ReadCompressedFile","em0006",
551                 FatalException,msg.c_str());
552     return;
553   }
554   // create the input string stream from the data string
555   if (dataString) {
556     iss.str(*dataString);
557     in.close();
558     delete dataString;
559   }
560 }
561 
562 
563 
564 
565 G4double
566 G4eDPWAElasticDCS::ComputeScatteringPowerCorrection(const G4MaterialCutsCouple *matcut,
567                                                     G4double ekin) {
568   const G4int imc  = matcut->GetIndex();
569   G4double corFactor = 1.0;
570   if (!(fSCPCPerMatCuts[imc]->fIsUse) || ekin<=fSCPCPerMatCuts[imc]->fPrCut) {
571     return corFactor;
572   }
573   // get the scattering power correction factor
574   const G4double lekin = G4Log(ekin);
575   G4double remaining   = (lekin-fSCPCPerMatCuts[imc]->fLEmin)*fSCPCPerMatCuts[imc]->fILDel;
576   std::size_t lindx    = (G4int)remaining;
577   remaining           -= lindx;
578   std::size_t imax     = fSCPCPerMatCuts[imc]->fVSCPC.size()-1;
579   if (lindx>=imax) {
580     corFactor = fSCPCPerMatCuts[imc]->fVSCPC[imax];
581   } else {
582     corFactor = fSCPCPerMatCuts[imc]->fVSCPC[lindx] + remaining*(fSCPCPerMatCuts[imc]->fVSCPC[lindx+1]-fSCPCPerMatCuts[imc]->fVSCPC[lindx]);
583   }
584   return corFactor;
585 }
586 
587 
588 void G4eDPWAElasticDCS::InitSCPCorrection(G4double lowEnergyLimit,
589                                           G4double highEnergyLimit) {
590   // get the material-cuts table
591   G4ProductionCutsTable *thePCTable = G4ProductionCutsTable::GetProductionCutsTable();
592   std::size_t numMatCuts            = thePCTable->GetTableSize();
593   // clear container if any
594   for (std::size_t imc=0; imc<fSCPCPerMatCuts.size(); ++imc) {
595     if (fSCPCPerMatCuts[imc]) {
596       fSCPCPerMatCuts[imc]->fVSCPC.clear();
597       delete fSCPCPerMatCuts[imc];
598       fSCPCPerMatCuts[imc] = nullptr;
599     }
600   }
601   //
602   // set size of the container and create the corresponding data structures
603   fSCPCPerMatCuts.resize(numMatCuts,nullptr);
604   // loop over the material-cuts and create scattering power correction data structure for each
605   for (G4int imc=0; imc<(G4int)numMatCuts; ++imc) {
606     const G4MaterialCutsCouple *matCut =  thePCTable->GetMaterialCutsCouple(imc);
607     const G4Material* mat  = matCut->GetMaterial();
608     // get e- production cut in the current material-cuts in energy
609     const G4double ecut    = (*(thePCTable->GetEnergyCutsVector(idxG4ElectronCut)))[matCut->GetIndex()];
610     const G4double limit   = fIsElectron ? 2.0*ecut : ecut;
611     const G4double min     = std::max(limit,lowEnergyLimit);
612     const G4double max     = highEnergyLimit;
613     if (min>=max) {
614       fSCPCPerMatCuts[imc] = new SCPCorrection();
615       fSCPCPerMatCuts[imc]->fIsUse = false;
616       fSCPCPerMatCuts[imc]->fPrCut = min;
617       continue;
618     }
619     G4int numEbins         = fNumSPCEbinPerDec*G4lrint(std::log10(max/min));
620     numEbins               = std::max(numEbins,3);
621     const G4double lmin    = G4Log(min);
622     const G4double ldel    = G4Log(max/min)/(numEbins-1.0);
623     fSCPCPerMatCuts[imc]   = new SCPCorrection();
624     fSCPCPerMatCuts[imc]->fVSCPC.resize(numEbins,1.0);
625     fSCPCPerMatCuts[imc]->fIsUse = true;
626     fSCPCPerMatCuts[imc]->fPrCut = min;
627     fSCPCPerMatCuts[imc]->fLEmin = lmin;
628     fSCPCPerMatCuts[imc]->fILDel = 1./ldel;
629     // compute Moliere material dependet parameetrs
630     G4double moliereBc     = 0.0;
631     G4double moliereXc2    = 0.0;
632     ComputeMParams(mat, moliereBc, moliereXc2);
633     // compute scattering power correction over the enrgy grid
634     for (G4int ie=0; ie<numEbins; ++ie) {
635       const G4double ekin = G4Exp(lmin+ie*ldel);
636       G4double scpCorr    = 1.0;
637       // compute correction factor: I.Kawrakow, Med.Phys.24,505-517(1997)(Eqs(32-37)
638       if (ie>0) {
639          const G4double tau     = ekin/CLHEP::electron_mass_c2;
640          const G4double tauCut  = ecut/CLHEP::electron_mass_c2;
641          // Moliere's screening parameter
642          const G4double A       = moliereXc2/(4.0*tau*(tau+2.)*moliereBc);
643          const G4double gr      = (1.+2.*A)*G4Log(1.+1./A)-2.;
644          const G4double dum0    = (tau+2.)/(tau+1.);
645          const G4double dum1    = tau+1.;
646          G4double gm  = G4Log(0.5*tau/tauCut) + (1.+dum0*dum0)*G4Log(2.*(tau-tauCut+2.)/(tau+4.))
647                         - 0.25*(tau+2.)*( tau+2.+2.*(2.*tau+1.)/(dum1*dum1))*
648                           G4Log((tau+4.)*(tau-tauCut)/tau/(tau-tauCut+2.))
649                         + 0.5*(tau-2*tauCut)*(tau+2.)*(1./(tau-tauCut)-1./(dum1*dum1));
650          if (gm<gr) {
651            gm = gm/gr;
652          } else {
653            gm = 1.;
654          }
655          const G4double z0 = matCut->GetMaterial()->GetIonisation()->GetZeffective();
656          scpCorr = 1.-gm*z0/(z0*(z0+1.));
657       }
658       fSCPCPerMatCuts[imc]->fVSCPC[ie] = scpCorr;
659     }
660   }
661 }
662 
663 
664 // compute material dependent Moliere MSC parameters at initialisation
665 void G4eDPWAElasticDCS::ComputeMParams(const G4Material* mat, G4double& theBc,
666                                        G4double& theXc2) {
667    const G4double const1   = 7821.6;      // [cm2/g]
668    const G4double const2   = 0.1569;      // [cm2 MeV2 / g]
669    const G4double finstrc2 = 5.325135453E-5; // fine-structure const. square
670    //   G4double xi   = 1.0;
671    const G4ElementVector* theElemVect     = mat->GetElementVector();
672    const std::size_t      numelems        = mat->GetNumberOfElements();
673    //
674    const G4double*  theNbAtomsPerVolVect  = mat->GetVecNbOfAtomsPerVolume();
675    G4double         theTotNbAtomsPerVol   = mat->GetTotNbOfAtomsPerVolume();
676    //
677    G4double zs = 0.0;
678    G4double zx = 0.0;
679    G4double ze = 0.0;
680    G4double sa = 0.0;
681    //
682    for(std::size_t ielem = 0; ielem < numelems; ++ielem) {
683      const G4double zet  = (*theElemVect)[ielem]->GetZ();
684      const G4double iwa  = (*theElemVect)[ielem]->GetN();
685      const G4double ipz  = theNbAtomsPerVolVect[ielem]/theTotNbAtomsPerVol;
686      const G4double dum  = ipz*zet*(zet+1.0);
687      zs           += dum;
688      ze           += dum*(-2.0/3.0)*G4Log(zet);
689      zx           += dum*G4Log(1.0+3.34*finstrc2*zet*zet);
690      sa           += ipz*iwa;
691    }
692    const G4double density = mat->GetDensity()*CLHEP::cm3/CLHEP::g; // [g/cm3]
693    //
694    G4double z0 = (0.0 == sa) ? 0.0 : zs/sa;
695    G4double z1 = (0.0 == zs) ? 0.0 : (ze - zx)/zs;
696    
697    theBc  = const1*density*z0*G4Exp(z1);  //[1/cm]
698    theXc2 = const2*density*z0;  // [MeV2/cm]
699    // change to Geant4 internal units of 1/length and energ2/length
700    theBc  *= 1.0/CLHEP::cm;
701    theXc2 *= CLHEP::MeV*CLHEP::MeV/CLHEP::cm;
702 }
703 
704