807 lines
27 KiB
C++
807 lines
27 KiB
C++
//
|
|
// ********************************************************************
|
|
// * License and Disclaimer *
|
|
// * *
|
|
// * The Geant4 software is copyright of the Copyright Holders of *
|
|
// * the Geant4 Collaboration. It is provided under the terms and *
|
|
// * conditions of the Geant4 Software License, included in the file *
|
|
// * LICENSE and available at http://cern.ch/geant4/license . These *
|
|
// * include a list of copyright holders. *
|
|
// * *
|
|
// * Neither the authors of this software system, nor their employing *
|
|
// * institutes,nor the agencies providing financial support for this *
|
|
// * work make any representation or warranty, express or implied, *
|
|
// * regarding this software system or assume any liability for its *
|
|
// * use. Please see the license in the file LICENSE and URL above *
|
|
// * for the full disclaimer and the limitation of liability. *
|
|
// * *
|
|
// * This code implementation is the result of the scientific and *
|
|
// * technical work of the GEANT4 collaboration. *
|
|
// * By using, copying, modifying or distributing the software (or *
|
|
// * any work based on the software) you agree to acknowledge its *
|
|
// * use in resulting scientific publications, and indicate your *
|
|
// * acceptance of all terms of the Geant4 Software license. *
|
|
// ********************************************************************
|
|
//
|
|
//
|
|
// Author: Luciano Pandola
|
|
//
|
|
// History:
|
|
// --------
|
|
// 23 Nov 2010 L Pandola First complete implementation
|
|
// 02 May 2011 L.Pandola Remove dependency on CLHEP::HepMatrix
|
|
// 24 May 2011 L.Pandola Renamed (make v2008 as default Penelope)
|
|
// 03 Oct 2013 L.Pandola Migration to MT
|
|
// 30 Oct 2013 L.Pandola Use G4Cache to avoid new/delete of the
|
|
// data vector on the fly in SampleGammaEnergy()
|
|
//
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
#include "G4PenelopeBremsstrahlungFS.hh"
|
|
#include "G4PhysicsFreeVector.hh"
|
|
#include "G4PhysicsTable.hh"
|
|
#include "G4Material.hh"
|
|
#include "Randomize.hh"
|
|
#include "G4AutoDelete.hh"
|
|
#include "G4Exp.hh"
|
|
#include "G4PhysicalConstants.hh"
|
|
#include "G4SystemOfUnits.hh"
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4PenelopeBremsstrahlungFS::G4PenelopeBremsstrahlungFS(G4int verbosity) :
|
|
fReducedXSTable(nullptr),fEffectiveZSq(nullptr),fSamplingTable(nullptr),
|
|
fPBcut(nullptr),fVerbosity(verbosity)
|
|
{
|
|
fCache.Put(0);
|
|
G4double tempvector[fNBinsX] =
|
|
{1.0e-12,0.025e0,0.05e0,0.075e0,0.1e0,0.15e0,0.2e0,0.25e0,
|
|
0.3e0,0.35e0,0.4e0,0.45e0,0.5e0,0.55e0,0.6e0,0.65e0,0.7e0,
|
|
0.75e0,0.8e0,0.85e0,0.9e0,0.925e0,0.95e0,0.97e0,0.99e0,
|
|
0.995e0,0.999e0,0.9995e0,0.9999e0,0.99995e0,0.99999e0,1.0e0};
|
|
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
theXGrid[ix] = tempvector[ix];
|
|
|
|
for (size_t i=0;i<fNBinsE;i++)
|
|
theEGrid[i] = 0.;
|
|
|
|
fElementData = new std::map<G4int,G4DataVector*>;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4PenelopeBremsstrahlungFS::~G4PenelopeBremsstrahlungFS()
|
|
{
|
|
ClearTables();
|
|
|
|
//The G4Physics*Vector pointers contained in the fCache are automatically deleted by
|
|
//the G4AutoDelete so there is no need to take care of them manually
|
|
|
|
//Clear manually fElementData
|
|
if (fElementData)
|
|
{
|
|
for (auto& item : (*fElementData))
|
|
delete item.second;
|
|
delete fElementData;
|
|
fElementData = nullptr;
|
|
}
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...
|
|
|
|
|
|
void G4PenelopeBremsstrahlungFS::ClearTables(G4bool isMaster)
|
|
{
|
|
//Just to check
|
|
if (!isMaster)
|
|
G4Exception("G4PenelopeBremsstrahlungFS::ClearTables()",
|
|
"em0100",FatalException,"Worker thread in this method");
|
|
|
|
if (fReducedXSTable)
|
|
{
|
|
for (auto& item : (*fReducedXSTable))
|
|
{
|
|
G4PhysicsTable* tab = item.second;
|
|
tab->clearAndDestroy();
|
|
delete tab;
|
|
}
|
|
fReducedXSTable->clear();
|
|
delete fReducedXSTable;
|
|
fReducedXSTable = nullptr;
|
|
}
|
|
|
|
if (fSamplingTable)
|
|
{
|
|
for (auto& item : (*fSamplingTable))
|
|
{
|
|
G4PhysicsTable* tab = item.second;
|
|
tab->clearAndDestroy();
|
|
delete tab;
|
|
}
|
|
fSamplingTable->clear();
|
|
delete fSamplingTable;
|
|
fSamplingTable = nullptr;
|
|
}
|
|
if (fPBcut)
|
|
{
|
|
/*
|
|
std::map< std::pair<const G4Material*,G4double> ,G4PhysicsFreeVector*>::iterator kk;
|
|
for (kk=fPBcut->begin(); kk != fPBcut->end(); kk++)
|
|
delete kk->second;
|
|
*/
|
|
delete fPBcut;
|
|
fPBcut = nullptr;
|
|
}
|
|
|
|
if (fEffectiveZSq)
|
|
{
|
|
delete fEffectiveZSq;
|
|
fEffectiveZSq = nullptr;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4double G4PenelopeBremsstrahlungFS::GetEffectiveZSquared(const G4Material* material) const
|
|
{
|
|
if (!fEffectiveZSq)
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "The container for the <Z^2> values is not initialized" << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::GetEffectiveZSquared()",
|
|
"em2007",FatalException,ed);
|
|
return 0;
|
|
}
|
|
//found in the table: return it
|
|
if (fEffectiveZSq->count(material))
|
|
return fEffectiveZSq->find(material)->second;
|
|
else
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "The value of <Z^2> is not properly set for material " <<
|
|
material->GetName() << G4endl;
|
|
//requires running of BuildScaledXSTable()
|
|
G4Exception("G4PenelopeBremsstrahlungFS::GetEffectiveZSquared()",
|
|
"em2008",FatalException,ed);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
void G4PenelopeBremsstrahlungFS::BuildScaledXSTable(const G4Material* material,
|
|
G4double cut,G4bool isMaster)
|
|
{
|
|
//Corresponds to subroutines EBRaW and EBRaR of PENELOPE
|
|
/*
|
|
This method generates the table of the scaled energy-loss cross section from
|
|
bremsstrahlung emission for the given material. Original data are read from
|
|
file. The table is normalized according to the Berger-Seltzer cross section.
|
|
*/
|
|
|
|
//Just to check
|
|
if (!isMaster)
|
|
G4Exception("G4PenelopeBremsstrahlungFS::BuildScaledXSTable()",
|
|
"em0100",FatalException,"Worker thread in this method");
|
|
|
|
if (fVerbosity > 2)
|
|
{
|
|
G4cout << "Entering in G4PenelopeBremsstrahlungFS::BuildScaledXSTable for " <<
|
|
material->GetName() << G4endl;
|
|
G4cout << "Threshold = " << cut/keV << " keV, isMaster= " << isMaster <<
|
|
G4endl;
|
|
}
|
|
|
|
//This method should be accessed by the master only
|
|
if (!fSamplingTable)
|
|
fSamplingTable =
|
|
new std::map< std::pair<const G4Material*,G4double> , G4PhysicsTable*>;
|
|
if (!fPBcut)
|
|
fPBcut =
|
|
new std::map< std::pair<const G4Material*,G4double> , G4PhysicsFreeVector* >;
|
|
|
|
//check if the container exists (if not, create it)
|
|
if (!fReducedXSTable)
|
|
fReducedXSTable = new std::map< std::pair<const G4Material*,G4double> ,
|
|
G4PhysicsTable*>;
|
|
if (!fEffectiveZSq)
|
|
fEffectiveZSq = new std::map<const G4Material*,G4double>;
|
|
|
|
//*********************************************************************
|
|
//Determine the equivalent atomic number <Z^2>
|
|
//*********************************************************************
|
|
std::vector<G4double> *StechiometricFactors = new std::vector<G4double>;
|
|
G4int nElements = material->GetNumberOfElements();
|
|
const G4ElementVector* elementVector = material->GetElementVector();
|
|
const G4double* fractionVector = material->GetFractionVector();
|
|
for (G4int i=0;i<nElements;i++)
|
|
{
|
|
G4double fraction = fractionVector[i];
|
|
G4double atomicWeigth = (*elementVector)[i]->GetA()/(g/mole);
|
|
StechiometricFactors->push_back(fraction/atomicWeigth);
|
|
}
|
|
//Find max
|
|
G4double MaxStechiometricFactor = 0.;
|
|
for (G4int i=0;i<nElements;i++)
|
|
{
|
|
if ((*StechiometricFactors)[i] > MaxStechiometricFactor)
|
|
MaxStechiometricFactor = (*StechiometricFactors)[i];
|
|
}
|
|
//Normalize
|
|
for (G4int i=0;i<nElements;i++)
|
|
(*StechiometricFactors)[i] /= MaxStechiometricFactor;
|
|
|
|
G4double sumz2 = 0;
|
|
G4double sums = 0;
|
|
for (G4int i=0;i<nElements;i++)
|
|
{
|
|
G4double Z = (*elementVector)[i]->GetZ();
|
|
sumz2 += (*StechiometricFactors)[i]*Z*Z;
|
|
sums += (*StechiometricFactors)[i];
|
|
}
|
|
G4double ZBR2 = sumz2/sums;
|
|
|
|
fEffectiveZSq->insert(std::make_pair(material,ZBR2));
|
|
|
|
//*********************************************************************
|
|
// loop on elements and read data files
|
|
//*********************************************************************
|
|
G4DataVector* tempData = new G4DataVector(fNBinsE);
|
|
G4DataVector* tempMatrix = new G4DataVector(fNBinsE*fNBinsX,0.);
|
|
|
|
for (G4int iel=0;iel<nElements;iel++)
|
|
{
|
|
G4double Z = (*elementVector)[iel]->GetZ();
|
|
G4int iZ = (G4int) Z;
|
|
G4double wgt = (*StechiometricFactors)[iel]*Z*Z/ZBR2;
|
|
|
|
//the element is not already loaded
|
|
if (!fElementData->count(iZ))
|
|
{
|
|
ReadDataFile(iZ);
|
|
if (!fElementData->count(iZ))
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "Error in G4PenelopeBremsstrahlungFS::BuildScaledXSTable" << G4endl;
|
|
ed << "Unable to retrieve data for element " << iZ << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::BuildScaledXSTable()",
|
|
"em2009",FatalException,ed);
|
|
}
|
|
}
|
|
|
|
G4DataVector* atomData = fElementData->find(iZ)->second;
|
|
|
|
for (size_t ie=0;ie<fNBinsE;ie++)
|
|
{
|
|
(*tempData)[ie] += wgt*(*atomData)[ie*(fNBinsX+1)+fNBinsX]; //last column contains total XS
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
(*tempMatrix)[ie*fNBinsX+ix] += wgt*(*atomData)[ie*(fNBinsX+1)+ix];
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
// the total energy loss spectrum is re-normalized to reproduce the total
|
|
// scaled cross section of Berger and Seltzer
|
|
//*********************************************************************
|
|
for (size_t ie=0;ie<fNBinsE;ie++)
|
|
{
|
|
//for each energy, calculate integral of dSigma/dx over dx
|
|
G4double* tempData2 = new G4double[fNBinsX];
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
tempData2[ix] = (*tempMatrix)[ie*fNBinsX+ix];
|
|
G4double rsum = GetMomentumIntegral(tempData2,1.0,0);
|
|
delete[] tempData2;
|
|
G4double fact = millibarn*(theEGrid[ie]+electron_mass_c2)*(1./fine_structure_const)/
|
|
(classic_electr_radius*classic_electr_radius*(theEGrid[ie]+2.0*electron_mass_c2));
|
|
G4double fnorm = (*tempData)[ie]/(rsum*fact);
|
|
G4double TST = 100.*std::fabs(fnorm-1.0);
|
|
if (TST > 1.0)
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "G4PenelopeBremsstrahlungFS. Corrupted data files?" << G4endl;
|
|
G4cout << "TST= " << TST << "; fnorm = " << fnorm << G4endl;
|
|
G4cout << "rsum = " << rsum << G4endl;
|
|
G4cout << "fact = " << fact << G4endl;
|
|
G4cout << ie << " " << theEGrid[ie]/keV << " " << (*tempData)[ie]/barn << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::BuildScaledXSTable()",
|
|
"em2010",FatalException,ed);
|
|
}
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
(*tempMatrix)[ie*fNBinsX+ix] *= fnorm;
|
|
}
|
|
|
|
//*********************************************************************
|
|
// create and fill the tables
|
|
//*********************************************************************
|
|
G4PhysicsTable* thePhysicsTable = new G4PhysicsTable();
|
|
// the table will contain 32 G4PhysicsFreeVectors with different
|
|
// values of x. Each of the G4PhysicsFreeVectors has a profile of
|
|
// log(XS) vs. log(E)
|
|
|
|
//reserve space of the vectors. Everything is log-log
|
|
//I add one extra "fake" point at low energy, since the Penelope
|
|
//table starts at 1 keV
|
|
for (size_t i=0;i<fNBinsX;i++)
|
|
thePhysicsTable->push_back(new G4PhysicsFreeVector(fNBinsE+1));
|
|
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
{
|
|
G4PhysicsFreeVector* theVec =
|
|
(G4PhysicsFreeVector*) ((*thePhysicsTable)[ix]);
|
|
for (size_t ie=0;ie<fNBinsE;ie++)
|
|
{
|
|
G4double logene = G4Log(theEGrid[ie]);
|
|
G4double aValue = (*tempMatrix)[ie*fNBinsX+ix];
|
|
if (aValue < 1e-20*millibarn) //protection against log(0)
|
|
aValue = 1e-20*millibarn;
|
|
theVec->PutValues(ie+1,logene,G4Log(aValue));
|
|
}
|
|
//Add fake point at 1 eV using an extrapolation with the derivative
|
|
//at the first valid point (Penelope approach)
|
|
G4double derivative = ((*theVec)[2]-(*theVec)[1])/(theVec->Energy(2) - theVec->Energy(1));
|
|
G4double log1eV = G4Log(1*eV);
|
|
G4double val1eV = (*theVec)[1]+derivative*(log1eV-theVec->Energy(1));
|
|
//fake point at very low energy
|
|
theVec->PutValues(0,log1eV,val1eV);
|
|
}
|
|
std::pair<const G4Material*,G4double> theKey = std::make_pair(material,cut);
|
|
fReducedXSTable->insert(std::make_pair(theKey,thePhysicsTable));
|
|
|
|
delete StechiometricFactors;
|
|
delete tempData;
|
|
delete tempMatrix;
|
|
|
|
//Do here also the initialization of the energy sampling
|
|
if (!(fSamplingTable->count(theKey)))
|
|
InitializeEnergySampling(material,cut);
|
|
|
|
return;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
void G4PenelopeBremsstrahlungFS::ReadDataFile(G4int Z)
|
|
{
|
|
const char* path = G4FindDataDir("G4LEDATA");
|
|
if (!path)
|
|
{
|
|
G4String excep = "G4PenelopeBremsstrahlungFS - G4LEDATA environment variable not set!";
|
|
G4Exception("G4PenelopeBremsstrahlungFS::ReadDataFile()",
|
|
"em0006",FatalException,excep);
|
|
return;
|
|
}
|
|
/*
|
|
Read the cross section file
|
|
*/
|
|
std::ostringstream ost;
|
|
if (Z>9)
|
|
ost << path << "/penelope/bremsstrahlung/pdebr" << Z << ".p08";
|
|
else
|
|
ost << path << "/penelope/bremsstrahlung/pdebr0" << Z << ".p08";
|
|
std::ifstream file(ost.str().c_str());
|
|
if (!file.is_open())
|
|
{
|
|
G4String excep = "G4PenelopeBremsstrahlungFS - data file " +
|
|
G4String(ost.str()) + " not found!";
|
|
G4Exception("G4PenelopeBremsstrahlungFS::ReadDataFile()",
|
|
"em0003",FatalException,excep);
|
|
return;
|
|
}
|
|
|
|
G4int readZ =0;
|
|
file >> readZ;
|
|
|
|
//check the right file is opened.
|
|
if (readZ != Z)
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "Corrupted data file for Z=" << Z << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::ReadDataFile()",
|
|
"em0005",FatalException,ed);
|
|
return;
|
|
}
|
|
|
|
G4DataVector* theMatrix = new G4DataVector(fNBinsE*(fNBinsX+1),0.); //initialized with zeros
|
|
|
|
for (size_t ie=0;ie<fNBinsE;ie++)
|
|
{
|
|
G4double myDouble = 0;
|
|
file >> myDouble; //energy (eV)
|
|
if (!theEGrid[ie]) //fill only the first time
|
|
theEGrid[ie] = myDouble*eV;
|
|
//
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
{
|
|
file >> myDouble;
|
|
(*theMatrix)[ie*(fNBinsX+1)+ix] = myDouble*millibarn;
|
|
}
|
|
file >> myDouble; //total cross section
|
|
(*theMatrix)[ie*(fNBinsX+1)+fNBinsX] = myDouble*millibarn;
|
|
}
|
|
|
|
if (fElementData)
|
|
fElementData->insert(std::make_pair(Z,theMatrix));
|
|
else
|
|
delete theMatrix;
|
|
file.close();
|
|
return;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4double G4PenelopeBremsstrahlungFS::GetMomentumIntegral(G4double* y,
|
|
G4double xup,G4int momOrder) const
|
|
//x is always the gridX
|
|
{
|
|
//Corresponds to the function RLMOM of Penelope
|
|
//This method performs the calculation of the integral of (x^momOrder)*y over the interval
|
|
//from x[0] to xup, obtained by linear interpolation on a table of y.
|
|
//The independent variable is assumed to take positive values only.
|
|
//
|
|
size_t size = fNBinsX;
|
|
const G4double eps = 1e-35;
|
|
|
|
//Check that the call is valid
|
|
if (momOrder<-1 || size<2 || theXGrid[0]<0)
|
|
{
|
|
G4Exception("G4PenelopeBremsstrahlungFS::GetMomentumIntegral()",
|
|
"em2011",FatalException,"Invalid call");
|
|
}
|
|
|
|
for (size_t i=1;i<size;i++)
|
|
{
|
|
if (theXGrid[i]<0 || theXGrid[i]<theXGrid[i-1])
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "Invalid call for bin " << i << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::GetMomentumIntegral()",
|
|
"em2012",FatalException,ed);
|
|
}
|
|
}
|
|
|
|
//Compute the integral
|
|
G4double result = 0;
|
|
if (xup < theXGrid[0])
|
|
return result;
|
|
G4bool loopAgain = true;
|
|
G4double xt = std::min(xup,theXGrid[size-1]);
|
|
G4double xtc = 0;
|
|
for (size_t i=0;i<size-1;i++)
|
|
{
|
|
G4double x1 = std::max(theXGrid[i],eps);
|
|
G4double y1 = y[i];
|
|
G4double x2 = std::max(theXGrid[i+1],eps);
|
|
G4double y2 = y[i+1];
|
|
if (xt < x2)
|
|
{
|
|
xtc = xt;
|
|
loopAgain = false;
|
|
}
|
|
else
|
|
xtc = x2;
|
|
G4double dx = x2-x1;
|
|
G4double dy = y2-y1;
|
|
G4double ds = 0;
|
|
if (std::fabs(dx)>1e-14*std::fabs(dy))
|
|
{
|
|
G4double b=dy/dx;
|
|
G4double a=y1-b*x1;
|
|
if (momOrder == -1)
|
|
ds = a*G4Log(xtc/x1)+b*(xtc-x1);
|
|
else if (momOrder == 0) //speed it up, not using pow()
|
|
ds = a*(xtc-x1) + 0.5*b*(xtc*xtc-x1*x1);
|
|
else
|
|
ds = a*(std::pow(xtc,momOrder+1)-std::pow(x1,momOrder+1))/((G4double) (momOrder + 1))
|
|
+ b*(std::pow(xtc,momOrder+2)-std::pow(x1,momOrder+2))/((G4double) (momOrder + 2));
|
|
}
|
|
else
|
|
ds = 0.5*(y1+y2)*(xtc-x1)*std::pow(xtc,momOrder);
|
|
result += ds;
|
|
if (!loopAgain)
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
const G4PhysicsTable* G4PenelopeBremsstrahlungFS::GetScaledXSTable(const G4Material* mat,
|
|
const G4double cut) const
|
|
{
|
|
//check if it already contains the entry
|
|
std::pair<const G4Material*,G4double> theKey = std::make_pair(mat,cut);
|
|
|
|
if (!(fReducedXSTable->count(theKey)))
|
|
{
|
|
G4Exception("G4PenelopeBremsstrahlungFS::GetScaledXSTable()",
|
|
"em2013",FatalException,"Unable to retrieve the cross section table");
|
|
}
|
|
|
|
return fReducedXSTable->find(theKey)->second;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
void G4PenelopeBremsstrahlungFS::InitializeEnergySampling(const G4Material* material,
|
|
G4double cut)
|
|
{
|
|
if (fVerbosity > 2)
|
|
G4cout << "Entering in G4PenelopeBremsstrahlungFS::InitializeEnergySampling() for " <<
|
|
material->GetName() << G4endl;
|
|
|
|
//This method should be accessed by the master only
|
|
std::pair<const G4Material*,G4double> theKey = std::make_pair(material,cut);
|
|
|
|
G4PhysicsTable* thePhysicsTable = new G4PhysicsTable();
|
|
// the table will contain 57 G4PhysicsFreeVectors with different
|
|
// values of E.
|
|
G4PhysicsFreeVector* thePBvec = new G4PhysicsFreeVector(fNBinsE);
|
|
|
|
//I reserve space of the vectors.
|
|
for (size_t i=0;i<fNBinsE;i++)
|
|
thePhysicsTable->push_back(new G4PhysicsFreeVector(fNBinsX));
|
|
|
|
//Retrieve the table. Must already exist at this point, because this
|
|
//method is invoked by GetScaledXSTable()
|
|
if (!(fReducedXSTable->count(theKey)))
|
|
G4Exception("G4PenelopeBremsstrahlungFS::InitializeEnergySampling()",
|
|
"em2013",FatalException,"Unable to retrieve the cross section table");
|
|
G4PhysicsTable* theTableReduced = fReducedXSTable->find(theKey)->second;
|
|
|
|
for (size_t ie=0;ie<fNBinsE;ie++)
|
|
{
|
|
G4PhysicsFreeVector* theVec =
|
|
(G4PhysicsFreeVector*) ((*thePhysicsTable)[ie]);
|
|
//Fill the table
|
|
G4double value = 0; //first value
|
|
theVec->PutValues(0,theXGrid[0],value);
|
|
for (size_t ix=1;ix<fNBinsX;ix++)
|
|
{
|
|
//Here calculate the cumulative distribution
|
|
// int_{0}^{x} dSigma(x',E)/dx' (1/x') dx'
|
|
G4PhysicsFreeVector* v1 = (G4PhysicsFreeVector*) (*theTableReduced)[ix-1];
|
|
G4PhysicsFreeVector* v2 = (G4PhysicsFreeVector*) (*theTableReduced)[ix];
|
|
|
|
G4double x1=std::max(theXGrid[ix-1],1.0e-35);
|
|
//Remember: the table fReducedXSTable has a fake first point in energy
|
|
//so, it contains one more bin than fNBinsE.
|
|
G4double y1=G4Exp((*v1)[ie+1]);
|
|
G4double x2=std::max(theXGrid[ix],1.0e-35);
|
|
G4double y2=G4Exp((*v2)[ie+1]);
|
|
G4double B = (y2-y1)/(x2-x1);
|
|
G4double A = y1-B*x1;
|
|
G4double dS = A*G4Log(x2/x1)+B*(x2-x1);
|
|
value += dS;
|
|
theVec->PutValues(ix,theXGrid[ix],value);
|
|
}
|
|
//fill the PB vector
|
|
G4double xc = cut/theEGrid[ie];
|
|
//Fill a temp data vector
|
|
G4double* tempData = new G4double[fNBinsX];
|
|
for (size_t ix=0;ix<fNBinsX;ix++)
|
|
{
|
|
G4PhysicsFreeVector* vv = (G4PhysicsFreeVector*) (*theTableReduced)[ix];
|
|
tempData[ix] = G4Exp((*vv)[ie+1]);
|
|
}
|
|
G4double pbval = (xc<=1) ?
|
|
GetMomentumIntegral(tempData,xc,-1) :
|
|
GetMomentumIntegral(tempData,1.0,-1);
|
|
thePBvec->PutValues(ie,theEGrid[ie],pbval);
|
|
delete[] tempData;
|
|
}
|
|
|
|
fSamplingTable->insert(std::make_pair(theKey,thePhysicsTable));
|
|
fPBcut->insert(std::make_pair(theKey,thePBvec));
|
|
return;
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4double G4PenelopeBremsstrahlungFS::SampleGammaEnergy(G4double energy,const G4Material* mat,
|
|
const G4double cut) const
|
|
{
|
|
std::pair<const G4Material*,G4double> theKey = std::make_pair(mat,cut);
|
|
if (!(fSamplingTable->count(theKey)) || !(fPBcut->count(theKey)))
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "Unable to retrieve the SamplingTable: " <<
|
|
fSamplingTable->count(theKey) << " " <<
|
|
fPBcut->count(theKey) << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::SampleGammaEnergy()",
|
|
"em2014",FatalException,ed);
|
|
}
|
|
const G4PhysicsTable* theTableInte = fSamplingTable->find(theKey)->second;
|
|
const G4PhysicsTable* theTableRed = fReducedXSTable->find(theKey)->second;
|
|
|
|
//Find the energy bin using bi-partition
|
|
size_t eBin = 0;
|
|
G4bool firstOrLastBin = false;
|
|
|
|
if (energy < theEGrid[0]) //below first bin
|
|
{
|
|
eBin = 0;
|
|
firstOrLastBin = true;
|
|
}
|
|
else if (energy > theEGrid[fNBinsE-1]) //after last bin
|
|
{
|
|
eBin = fNBinsE-1;
|
|
firstOrLastBin = true;
|
|
}
|
|
else
|
|
{
|
|
size_t i=0;
|
|
size_t j=fNBinsE-1;
|
|
while ((j-i)>1)
|
|
{
|
|
size_t k = (i+j)/2;
|
|
if (energy > theEGrid[k])
|
|
i = k;
|
|
else
|
|
j = k;
|
|
}
|
|
eBin = i;
|
|
}
|
|
|
|
//Get the appropriate physics vector
|
|
const G4PhysicsFreeVector* theVec1 = (G4PhysicsFreeVector*) (*theTableInte)[eBin];
|
|
|
|
//Use a "temporary" vector which contains the linear interpolation of the x spectra
|
|
//in energy. The temporary vector is thread-local, so that there is no conflict.
|
|
//This is achieved via G4Cache. The theTempVect is allocated only once per thread
|
|
//(member variable), but it is overwritten at every call of this method
|
|
//(because the interpolation factors change!)
|
|
G4PhysicsFreeVector* theTempVec = fCache.Get();
|
|
if (!theTempVec) //First time this thread gets the cache
|
|
{
|
|
theTempVec = new G4PhysicsFreeVector(fNBinsX);
|
|
fCache.Put(theTempVec);
|
|
// The G4AutoDelete takes care here to clean up the vectors
|
|
G4AutoDelete::Register(theTempVec);
|
|
if (fVerbosity > 4)
|
|
G4cout << "Creating new instance of G4PhysicsFreeVector() on the worker" << G4endl;
|
|
}
|
|
|
|
//theTempVect is allocated only once (member variable), but it is overwritten at
|
|
//every call of this method (because the interpolation factors change!)
|
|
if (!firstOrLastBin)
|
|
{
|
|
const G4PhysicsFreeVector* theVec2 = (G4PhysicsFreeVector*) (*theTableInte)[eBin+1];
|
|
for (size_t iloop=0;iloop<fNBinsX;iloop++)
|
|
{
|
|
G4double val = (*theVec1)[iloop]+(((*theVec2)[iloop]-(*theVec1)[iloop]))*
|
|
(energy-theEGrid[eBin])/(theEGrid[eBin+1]-theEGrid[eBin]);
|
|
theTempVec->PutValues(iloop,theXGrid[iloop],val);
|
|
}
|
|
}
|
|
else //first or last bin, no interpolation
|
|
{
|
|
for (size_t iloop=0;iloop<fNBinsX;iloop++)
|
|
theTempVec->PutValues(iloop,theXGrid[iloop],(*theVec1)[iloop]);
|
|
}
|
|
|
|
//Start the game
|
|
G4double pbcut = (*(fPBcut->find(theKey)->second))[eBin];
|
|
|
|
if (!firstOrLastBin) //linear interpolation on pbcut as well
|
|
{
|
|
pbcut = (*(fPBcut->find(theKey)->second))[eBin] +
|
|
((*(fPBcut->find(theKey)->second))[eBin+1]-(*(fPBcut->find(theKey)->second))[eBin])*
|
|
(energy-theEGrid[eBin])/(theEGrid[eBin+1]-theEGrid[eBin]);
|
|
}
|
|
|
|
G4double pCumulative = (*theTempVec)[fNBinsX-1]; //last value
|
|
|
|
G4double eGamma = 0;
|
|
G4int nIterations = 0;
|
|
do
|
|
{
|
|
G4double pt = pbcut + G4UniformRand()*(pCumulative - pbcut);
|
|
nIterations++;
|
|
|
|
//find where it is
|
|
size_t ibin = 0;
|
|
if (pt < (*theTempVec)[0])
|
|
ibin = 0;
|
|
else if (pt > (*theTempVec)[fNBinsX-1])
|
|
{
|
|
//We observed problems due to numerical rounding here (STT).
|
|
//delta here is a tiny positive number
|
|
G4double delta = pt-(*theTempVec)[fNBinsX-1];
|
|
if (delta < pt*1e-10) // very small! Numerical rounding only
|
|
{
|
|
ibin = fNBinsX-2;
|
|
G4ExceptionDescription ed;
|
|
ed << "Found that (pt > (*theTempVec)[fNBinsX-1]) with pt = " << pt <<
|
|
" , (*theTempVec)[fNBinsX-1] = " << (*theTempVec)[fNBinsX-1] << " and delta = " <<
|
|
(pt-(*theTempVec)[fNBinsX-1]) << G4endl;
|
|
ed << "Possible symptom of problem with numerical precision" << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::SampleGammaEnergy()",
|
|
"em2015",JustWarning,ed);
|
|
}
|
|
else //real problem
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "Crash at (pt > (*theTempVec)[fNBinsX-1]) with pt = " << pt <<
|
|
" , (*theTempVec)[fNBinsX-1]=" << (*theTempVec)[fNBinsX-1] << " and fNBinsX = " <<
|
|
fNBinsX << G4endl;
|
|
ed << "Material: " << mat->GetName() << ", energy = " << energy/keV << " keV" <<
|
|
G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::SampleGammaEnergy()",
|
|
"em2015",FatalException,ed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t i=0;
|
|
size_t j=fNBinsX-1;
|
|
while ((j-i)>1)
|
|
{
|
|
size_t k = (i+j)/2;
|
|
if (pt > (*theTempVec)[k])
|
|
i = k;
|
|
else
|
|
j = k;
|
|
}
|
|
ibin = i;
|
|
}
|
|
|
|
G4double w1 = theXGrid[ibin];
|
|
G4double w2 = theXGrid[ibin+1];
|
|
|
|
const G4PhysicsFreeVector* v1 = (G4PhysicsFreeVector*) (*theTableRed)[ibin];
|
|
const G4PhysicsFreeVector* v2 = (G4PhysicsFreeVector*) (*theTableRed)[ibin+1];
|
|
//Remember: the table fReducedXSTable has a fake first point in energy
|
|
//so, it contains one more bin than fNBinsE.
|
|
G4double pdf1 = G4Exp((*v1)[eBin+1]);
|
|
G4double pdf2 = G4Exp((*v2)[eBin+1]);
|
|
G4double deltaW = w2-w1;
|
|
G4double dpdfb = pdf2-pdf1;
|
|
G4double B = dpdfb/deltaW;
|
|
G4double A = pdf1-B*w1;
|
|
//I already made an interpolation in energy, so I can use the actual value for the
|
|
//calculation of the wbcut, instead of the grid values (except for the last bin)
|
|
G4double wbcut = (cut < energy) ? cut/energy : 1.0;
|
|
if (firstOrLastBin) //this is an particular case: no interpolation available
|
|
wbcut = (cut < theEGrid[eBin]) ? cut/theEGrid[eBin] : 1.0;
|
|
|
|
if (w1 < wbcut)
|
|
w1 = wbcut;
|
|
if (w2 < w1)
|
|
{
|
|
//This configuration can happen if initially wbcut > w2 > w1. Due to the previous
|
|
//statement, (w1 = wbcut), it becomes wbcut = w1 > w2. In this case, it is not a
|
|
//real problem. It becomes a problem if w2 < w1 before the w1 = wbcut statement. Issue
|
|
//a warning only in this specific case.
|
|
if (w2 > wbcut)
|
|
{
|
|
G4ExceptionDescription ed;
|
|
ed << "Warning in G4PenelopeBremsstrahlungFS::SampleX()" << G4endl;
|
|
ed << "Conflicting end-point values: w1=" << w1 << "; w2 = " << w2 << G4endl;
|
|
ed << "wbcut = " << wbcut << " energy= " << energy/keV << " keV" << G4endl;
|
|
ed << "cut = " << cut/keV << " keV" << G4endl;
|
|
G4Exception("G4PenelopeBremsstrahlungFS::SampleGammaEnergy()","em2015",
|
|
JustWarning,ed);
|
|
}
|
|
return w1*energy;
|
|
}
|
|
|
|
G4double pmax = std::max(A+B*w1,A+B*w2);
|
|
G4bool loopAgain = false;
|
|
do
|
|
{
|
|
loopAgain = false;
|
|
eGamma = w1* std::pow((w2/w1),G4UniformRand());
|
|
if (G4UniformRand()*pmax > (A+B*eGamma))
|
|
loopAgain = true;
|
|
}while(loopAgain);
|
|
eGamma *= energy;
|
|
if (nIterations > 100) //protection against infinite loops
|
|
return eGamma;
|
|
}while(eGamma < cut); //repeat if sampled sub-cut!
|
|
|
|
return eGamma;
|
|
}
|