// // ******************************************************************** // * 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. * // ******************************************************************** // // // $Id: G4VXTRenergyLoss.cc,v 1.45 2010-06-16 15:34:15 gcosmo Exp $ // GEANT4 tag $Name: not supported by cvs2svn $ // // History: // 2001-2002 R&D by V.Grichine // 19.06.03 V. Grichine, modifications in BuildTable for the integration // in respect of angle: range is increased, accuracy is // improved // 28.07.05, P.Gumplinger add G4ProcessType to constructor // 28.09.07, V.Ivanchenko general cleanup without change of algorithms // #include "G4Timer.hh" #include "G4VXTRenergyLoss.hh" #include "G4Poisson.hh" #include "G4MaterialTable.hh" #include "G4VDiscreteProcess.hh" #include "G4VParticleChange.hh" #include "G4VSolid.hh" #include "G4RotationMatrix.hh" #include "G4ThreeVector.hh" #include "G4AffineTransform.hh" #include "G4SandiaTable.hh" #include "G4PhysicsVector.hh" #include "G4PhysicsFreeVector.hh" #include "G4PhysicsLinearVector.hh" //////////////////////////////////////////////////////////////////////////// // // Constructor, destructor G4VXTRenergyLoss::G4VXTRenergyLoss(G4LogicalVolume *anEnvelope, G4Material* foilMat,G4Material* gasMat, G4double a, G4double b, G4int n,const G4String& processName, G4ProcessType type) : G4VDiscreteProcess(processName, type), fGammaCutInKineticEnergy(0), fGammaTkinCut(0), fAngleDistrTable(0), fEnergyDistrTable(0), fPlatePhotoAbsCof(0), fGasPhotoAbsCof(0), fAngleForEnergyTable(0) { verboseLevel = 1; fPtrGamma = 0; fMinEnergyTR = fMaxEnergyTR = fMaxThetaTR = fGamma = fEnergy = fVarAngle = fLambda = fTotalDist = fPlateThick = fGasThick = fAlphaPlate = fAlphaGas = 0.0; // Initialization of local constants fTheMinEnergyTR = 1.0*keV; fTheMaxEnergyTR = 100.0*keV; fTheMaxAngle = 1.0e-3; fTheMinAngle = 5.0e-6; fBinTR = 50; fMinProtonTkin = 100.0*GeV; fMaxProtonTkin = 100.0*TeV; fTotBin = 50; // Proton energy vector initialization fProtonEnergyVector = new G4PhysicsLogVector(fMinProtonTkin, fMaxProtonTkin, fTotBin ); fXTREnergyVector = new G4PhysicsLogVector(fTheMinEnergyTR, fTheMaxEnergyTR, fBinTR ); fPlasmaCof = 4.0*pi*fine_structure_const*hbarc*hbarc*hbarc/electron_mass_c2; fCofTR = fine_structure_const/pi; fEnvelope = anEnvelope; fPlateNumber = n; if(verboseLevel > 0) G4cout<<"### G4VXTRenergyLoss: the number of TR radiator plates = " < 0) G4cout<<"total radiator thickness = "<GetIndex(); if(verboseLevel > 0) G4cout<<"plate material = "<GetName()<GetIndex(); if(verboseLevel > 0) G4cout<<"gas material = "<GetName()<GetElectronDensity(); // fSigma1 = (20.9*eV)*(20.9*eV); if(verboseLevel > 0) G4cout<<"plate plasma energy = "<GetElectronDensity(); if(verboseLevel > 0) G4cout<<"gas plasma energy = "<GetLogicalVolume() != fEnvelope ) lambda = DBL_MAX; else { const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle(); kinEnergy = aParticle->GetKineticEnergy(); mass = aParticle->GetDefinition()->GetPDGMass(); gamma = 1.0 + kinEnergy/mass; if(verboseLevel > 1) { G4cout<<" gamma = "< 0) G4cout<<"Build angle distribution according the transparent regular radiator" < fTheMinEnergyTR) fMinEnergyTR = fGammaTkinCut; else fMinEnergyTR = fTheMinEnergyTR; if(fGammaTkinCut > fTheMaxEnergyTR) fMaxEnergyTR = 2.0*fGammaTkinCut; else fMaxEnergyTR = fTheMaxEnergyTR; G4cout.precision(4); G4Timer timer; timer.Start(); if(verboseLevel > 0) { G4cout< GetLowEdgeEnergy(iTkin)/proton_mass_c2); fMaxThetaTR = 25.0/(fGamma*fGamma); // theta^2 fTheMinAngle = 1.0e-3; // was 5.e-6, e-6 !!!, e-5, e-4 if( fMaxThetaTR > fTheMaxAngle ) fMaxThetaTR = fTheMaxAngle; else { if( fMaxThetaTR < fTheMinAngle ) fMaxThetaTR = fTheMinAngle; } G4PhysicsLinearVector* angleVector = new G4PhysicsLinearVector(0.0, fMaxThetaTR, fBinTR ); G4double energySum = 0.0; G4double angleSum = 0.0; G4Integrator integral; energyVector->PutValue(fBinTR-1,energySum); angleVector->PutValue(fBinTR-1,angleSum); for( iTR = fBinTR - 2; iTR >= 0; iTR-- ) { // Legendre96 or Legendre20 energySum += radiatorCof*fCofTR*integral.Legendre10( this,&G4VXTRenergyLoss::SpectralXTRdEdx, energyVector->GetLowEdgeEnergy(iTR), energyVector->GetLowEdgeEnergy(iTR+1) ); if(fAngleRadDistr) { angleSum += fCofTR*integral.Legendre10( this,&G4VXTRenergyLoss::AngleXTRdEdx, angleVector->GetLowEdgeEnergy(iTR), angleVector->GetLowEdgeEnergy(iTR+1) ); angleVector ->PutValue(iTR,angleSum); } energyVector->PutValue(iTR,energySum/fTotalDist); } if(verboseLevel > 0) { G4cout // < fTheMinEnergyTR) fMinEnergyTR = fGammaTkinCut; else fMinEnergyTR = fTheMinEnergyTR; if(fGammaTkinCut > fTheMaxEnergyTR) fMaxEnergyTR = 2.0*fGammaTkinCut; else fMaxEnergyTR = fTheMaxEnergyTR; G4cout.precision(4); G4Timer timer; timer.Start(); if(verboseLevel > 0) { G4cout< GetLowEdgeEnergy(iTkin)/proton_mass_c2); fMaxThetaTR = 25.0/(fGamma*fGamma); // theta^2 fTheMinAngle = 1.0e-3; // was 5.e-6, e-6 !!!, e-5, e-4 if( fMaxThetaTR > fTheMaxAngle ) fMaxThetaTR = fTheMaxAngle; else { if( fMaxThetaTR < fTheMinAngle ) fMaxThetaTR = fTheMinAngle; } fAngleForEnergyTable = new G4PhysicsTable(fBinTR); for( iTR = 0; iTR < fBinTR; iTR++ ) { // energy = fMinEnergyTR*(iTR+1); energy = fXTREnergyVector->GetLowEdgeEnergy(iTR); G4PhysicsFreeVector* angleVector = GetAngleVector(energy,fBinTR); // G4cout<insertAt(iTR,angleVector); } fAngleBank.push_back(fAngleForEnergyTable); } timer.Stop(); G4cout.precision(6); if(verboseLevel > 0) { G4cout< kMin) kMin++; //kMax = kMin + fBinTR -1; if(verboseLevel > 2) { G4cout<<"n-1 = "< GetLowEdgeEnergy(iTkin)/proton_mass_c2); fMaxThetaTR = 25.0/(fGamma*fGamma); // theta^2 fTheMinAngle = 1.0e-3; // was 5.e-6, e-6 !!!, e-5, e-4 if( fMaxThetaTR > fTheMaxAngle ) fMaxThetaTR = fTheMaxAngle; else { if( fMaxThetaTR < fTheMinAngle ) fMaxThetaTR = fTheMinAngle; } G4PhysicsLinearVector* angleVector = new G4PhysicsLinearVector(0.0, fMaxThetaTR, fBinTR ); angleSum = 0.0; G4Integrator integral; angleVector->PutValue(fBinTR-1,angleSum); for( iTR = fBinTR - 2; iTR >= 0; iTR-- ) { angleSum += radiatorCof*fCofTR*integral.Legendre96( this,&G4VXTRenergyLoss::AngleXTRdEdx, angleVector->GetLowEdgeEnergy(iTR), angleVector->GetLowEdgeEnergy(iTR+1) ); angleVector ->PutValue(iTR,angleSum); } if(verboseLevel > 1) { G4cout // < 1) { G4cout<<"Start of G4VXTRenergyLoss::PostStepDoIt "<GetKineticEnergy(); G4double mass = aParticle->GetDefinition()->GetPDGMass(); G4double gamma = 1.0 + kinEnergy/mass; if(verboseLevel > 1 ) { G4cout<<"gamma = "<GetPosition(); G4ParticleMomentum direction = aParticle->GetMomentumDirection(); G4double startTime = pPostStepPoint->GetGlobalTime(); for( iTkin = 0; iTkin < fTotBin; iTkin++ ) { if(TkinScaled < fProtonEnergyVector->GetLowEdgeEnergy(iTkin)) break; } //iPlace = iTkin - 1; if(iTkin == 0) // Tkin is too small, neglect of TR photon generation { if( verboseLevel > 0) { G4cout<<"Go out from G4VXTRenergyLoss::PostStepDoIt:iTkin = "< 1) { G4cout<<"energyTR = "< 0.) theta = std::sqrt(theta2); else theta = theta2; } else theta = std::fabs(G4RandGauss::shoot(0.0,pi/gamma)); // theta = 0.; // check no spread if( theta >= 0.1 ) theta = 0.1; // G4cout<<" : theta = "<GetTouchable()->GetRotation(); G4ThreeVector transl = pPostStepPoint->GetTouchable()->GetTranslation(); G4AffineTransform transform = G4AffineTransform(rotM,transl); transform.Invert(); G4ThreeVector localP = transform.TransformPoint(position); G4ThreeVector localV = transform.TransformAxis(directionTR); G4double distance = fEnvelope->GetSolid()->DistanceToOut(localP, localV); if(verboseLevel > 1) { G4cout<<"distance to exit = "<SetTouchableHandle( aStep.GetPostStepPoint()->GetTouchableHandle()); aSecondaryTrack->SetParentID( aTrack.GetTrackID() ); fParticleChange.AddSecondary(aSecondaryTrack); fParticleChange.ProposeEnergy(kinEnergy); } } return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep); } /////////////////////////////////////////////////////////////////////// // // This function returns the spectral and angle density of TR quanta // in X-ray energy region generated forward when a relativistic // charged particle crosses interface between two materials. // The high energy small theta approximation is applied. // (matter1 -> matter2, or 2->1) // varAngle =2* (1 - std::cos(theta)) or approximately = theta*theta // G4complex G4VXTRenergyLoss::OneInterfaceXTRdEdx( G4double energy, G4double gamma, G4double varAngle ) { G4complex Z1 = GetPlateComplexFZ(energy,gamma,varAngle); G4complex Z2 = GetGasComplexFZ(energy,gamma,varAngle); G4complex zOut = (Z1 - Z2)*(Z1 - Z2) * (varAngle*energy/hbarc/hbarc); return zOut; } ////////////////////////////////////////////////////////////////////////////// // // For photon energy distribution tables. Integrate first over angle // G4double G4VXTRenergyLoss::SpectralAngleXTRdEdx(G4double varAngle) { G4double result = GetStackFactor(fEnergy,fGamma,varAngle); if(result < 0.0) result = 0.0; return result; } ///////////////////////////////////////////////////////////////////////// // // For second integration over energy G4double G4VXTRenergyLoss::SpectralXTRdEdx(G4double energy) { G4int i, iMax = 8; G4double result = 0.0; G4double lim[8] = { 0.0, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0 }; for( i = 0; i < iMax; i++ ) lim[i] *= fMaxThetaTR; G4Integrator integral; fEnergy = energy; for( i = 0; i < iMax-1; i++ ) { result += integral.Legendre96(this,&G4VXTRenergyLoss::SpectralAngleXTRdEdx, lim[i],lim[i+1]); // result += integral.Legendre10(this,&G4VXTRenergyLoss::SpectralAngleXTRdEdx, // lim[i],lim[i+1]); } return result; } ////////////////////////////////////////////////////////////////////////// // // for photon angle distribution tables // G4double G4VXTRenergyLoss::AngleSpectralXTRdEdx(G4double energy) { G4double result = GetStackFactor(energy,fGamma,fVarAngle); if(result < 0) result = 0.0; return result; } /////////////////////////////////////////////////////////////////////////// // // The XTR angular distribution based on transparent regular radiator G4double G4VXTRenergyLoss::AngleXTRdEdx(G4double varAngle) { // G4cout<<"angle2 = "< 0) G4cout<<"energy, keV"<<"\t"<<"Zmu for plate"< 1) G4cout< 0) outPlate< 0) G4cout<<"energy, keV"<<"\t"<<"Zmu for gas"< 1) G4cout< 0) outGas<GetNumberOfElements(); for( i = 0; i < numberOfElements; i++ ) { nowZ = (*theMaterialTable)[fMatIndex1]->GetElement(i)->GetZ(); sumZ += nowZ; xSection += GetComptonPerAtom(omega,nowZ); // *nowZ; } xSection /= sumZ; xSection *= (*theMaterialTable)[fMatIndex1]->GetElectronDensity(); return xSection; } //////////////////////////////////////////////////////////////////////// // // Computes Compton cross section for gas material in 1/mm G4double G4VXTRenergyLoss::GetGasCompton(G4double omega) { G4int i, numberOfElements; G4double xSection = 0., nowZ, sumZ = 0.; const G4MaterialTable* theMaterialTable = G4Material::GetMaterialTable(); numberOfElements = (*theMaterialTable)[fMatIndex2]->GetNumberOfElements(); for( i = 0; i < numberOfElements; i++ ) { nowZ = (*theMaterialTable)[fMatIndex2]->GetElement(i)->GetZ(); sumZ += nowZ; xSection += GetComptonPerAtom(omega,nowZ); // *nowZ; } xSection /= sumZ; xSection *= (*theMaterialTable)[fMatIndex2]->GetElectronDensity(); return xSection; } //////////////////////////////////////////////////////////////////////// // // Computes Compton cross section per atom with Z electrons for gamma with // the energy GammaEnergy G4double G4VXTRenergyLoss::GetComptonPerAtom(G4double GammaEnergy, G4double Z) { G4double CrossSection = 0.0; if ( Z < 0.9999 ) return CrossSection; if ( GammaEnergy < 0.1*keV ) return CrossSection; if ( GammaEnergy > (100.*GeV/Z) ) return CrossSection; static const G4double a = 20.0 , b = 230.0 , c = 440.0; static const G4double d1= 2.7965e-1*barn, d2=-1.8300e-1*barn, d3= 6.7527 *barn, d4=-1.9798e+1*barn, e1= 1.9756e-5*barn, e2=-1.0205e-2*barn, e3=-7.3913e-2*barn, e4= 2.7079e-2*barn, f1=-3.9178e-7*barn, f2= 6.8241e-5*barn, f3= 6.0480e-5*barn, f4= 3.0274e-4*barn; G4double p1Z = Z*(d1 + e1*Z + f1*Z*Z), p2Z = Z*(d2 + e2*Z + f2*Z*Z), p3Z = Z*(d3 + e3*Z + f3*Z*Z), p4Z = Z*(d4 + e4*Z + f4*Z*Z); G4double T0 = 15.0*keV; if (Z < 1.5) T0 = 40.0*keV; G4double X = std::max(GammaEnergy, T0) / electron_mass_c2; CrossSection = p1Z*std::log(1.+2.*X)/X + (p2Z + p3Z*X + p4Z*X*X)/(1. + a*X + b*X*X + c*X*X*X); // modification for low energy. (special case for Hydrogen) if (GammaEnergy < T0) { G4double dT0 = 1.*keV; X = (T0+dT0) / electron_mass_c2; G4double sigma = p1Z*std::log(1.+2*X)/X + (p2Z + p3Z*X + p4Z*X*X)/(1. + a*X + b*X*X + c*X*X*X); G4double c1 = -T0*(sigma-CrossSection)/(CrossSection*dT0); G4double c2 = 0.150; if (Z > 1.5) c2 = 0.375-0.0556*std::log(Z); G4double y = std::log(GammaEnergy/T0); CrossSection *= std::exp(-y*(c1+c2*y)); } // G4cout << "e= " << GammaEnergy << " Z= " << Z << " cross= " << CrossSection << G4endl; return CrossSection; } /////////////////////////////////////////////////////////////////////// // // This function returns the spectral and angle density of TR quanta // in X-ray energy region generated forward when a relativistic // charged particle crosses interface between two materials. // The high energy small theta approximation is applied. // (matter1 -> matter2, or 2->1) // varAngle =2* (1 - std::cos(theta)) or approximately = theta*theta // G4double G4VXTRenergyLoss::OneBoundaryXTRNdensity( G4double energy,G4double gamma, G4double varAngle ) const { G4double formationLength1, formationLength2; formationLength1 = 1.0/ (1.0/(gamma*gamma) + fSigma1/(energy*energy) + varAngle); formationLength2 = 1.0/ (1.0/(gamma*gamma) + fSigma2/(energy*energy) + varAngle); return (varAngle/energy)*(formationLength1 - formationLength2) *(formationLength1 - formationLength2); } G4double G4VXTRenergyLoss::GetStackFactor( G4double energy, G4double gamma, G4double varAngle ) { // return stack factor corresponding to one interface return std::real( OneInterfaceXTRdEdx(energy,gamma,varAngle) ); } ////////////////////////////////////////////////////////////////////////////// // // For photon energy distribution tables. Integrate first over angle // G4double G4VXTRenergyLoss::XTRNSpectralAngleDensity(G4double varAngle) { return OneBoundaryXTRNdensity(fEnergy,fGamma,varAngle)* GetStackFactor(fEnergy,fGamma,varAngle); } ///////////////////////////////////////////////////////////////////////// // // For second integration over energy G4double G4VXTRenergyLoss::XTRNSpectralDensity(G4double energy) { fEnergy = energy; G4Integrator integral; return integral.Legendre96(this,&G4VXTRenergyLoss::XTRNSpectralAngleDensity, 0.0,0.2*fMaxThetaTR) + integral.Legendre10(this,&G4VXTRenergyLoss::XTRNSpectralAngleDensity, 0.2*fMaxThetaTR,fMaxThetaTR); } ////////////////////////////////////////////////////////////////////////// // // for photon angle distribution tables // G4double G4VXTRenergyLoss::XTRNAngleSpectralDensity(G4double energy) { return OneBoundaryXTRNdensity(energy,fGamma,fVarAngle)* GetStackFactor(energy,fGamma,fVarAngle); } /////////////////////////////////////////////////////////////////////////// // // G4double G4VXTRenergyLoss::XTRNAngleDensity(G4double varAngle) { fVarAngle = varAngle; G4Integrator integral; return integral.Legendre96(this,&G4VXTRenergyLoss::XTRNAngleSpectralDensity, fMinEnergyTR,fMaxEnergyTR); } ////////////////////////////////////////////////////////////////////////////// // // Check number of photons for a range of Lorentz factors from both energy // and angular tables void G4VXTRenergyLoss::GetNumberOfPhotons() { G4int iTkin; G4double gamma, numberE; std::ofstream outEn("numberE.dat", std::ios::out ); outEn.setf( std::ios::scientific, std::ios::floatfield ); std::ofstream outAng("numberAng.dat", std::ios::out ); outAng.setf( std::ios::scientific, std::ios::floatfield ); for(iTkin=0;iTkin GetLowEdgeEnergy(iTkin)/proton_mass_c2); numberE = (*(*fEnergyDistrTable)(iTkin))(0); // numberA = (*(*fAngleDistrTable)(iTkin))(0); if(verboseLevel > 1) G4cout< 0) outEn<= ( (*(*fEnergyDistrTable)(iPlace))(iTransfer)*W1 + (*(*fEnergyDistrTable)(iPlace+1))(iTransfer)*W2) ) break; } transfer = GetXTRenergy(iPlace,position,iTransfer); } // G4cout<<"XTR transfer = "<GetLowEdgeEnergy(iTransfer); } else { y1 = (*(*fEnergyDistrTable)(iPlace))(iTransfer-1); y2 = (*(*fEnergyDistrTable)(iPlace))(iTransfer); x1 = (*fEnergyDistrTable)(iPlace)->GetLowEdgeEnergy(iTransfer-1); x2 = (*fEnergyDistrTable)(iPlace)->GetLowEdgeEnergy(iTransfer); if ( x1 == x2 ) result = x2; else { if ( y1 == y2 ) result = x1 + (x2 - x1)*G4UniformRand(); else { // result = x1 + (position - y1)*(x2 - x1)/(y2 - y1); result = x1 + (x2 - x1)*G4UniformRand(); } } } return result; } ///////////////////////////////////////////////////////////////////////// // // Get XTR photon angle at given energy and Tkin G4double G4VXTRenergyLoss::GetRandomAngle( G4double energyXTR, G4int iTkin ) { G4int iTR, iAngle; G4double position, angle; if (iTkin == fTotBin) iTkin--; fAngleForEnergyTable = fAngleBank[iTkin]; for( iTR = 0; iTR < fBinTR; iTR++ ) { if( energyXTR < fXTREnergyVector->GetLowEdgeEnergy(iTR) ) break; } if (iTR == fBinTR) iTR--; position = (*(*fAngleForEnergyTable)(iTR))(0)*G4UniformRand(); for(iAngle = 0;;iAngle++) { if(position >= (*(*fAngleForEnergyTable)(iTR))(iAngle)) break; } angle = GetAngleXTR(iTR,position,iAngle); return angle; } //////////////////////////////////////////////////////////////////////// // // Returns approximate position of X-ray photon angle at given energy during random sampling // over integral energy distribution G4double G4VXTRenergyLoss::GetAngleXTR( G4int iPlace, G4double position, G4int iTransfer ) { G4double x1, x2, y1, y2, result; if(iTransfer == 0) { result = (*fAngleForEnergyTable)(iPlace)->GetLowEdgeEnergy(iTransfer); } else { y1 = (*(*fAngleForEnergyTable)(iPlace))(iTransfer-1); y2 = (*(*fAngleForEnergyTable)(iPlace))(iTransfer); x1 = (*fAngleForEnergyTable)(iPlace)->GetLowEdgeEnergy(iTransfer-1); x2 = (*fAngleForEnergyTable)(iPlace)->GetLowEdgeEnergy(iTransfer); if ( x1 == x2 ) result = x2; else { if ( y1 == y2 ) result = x1 + (x2 - x1)*G4UniformRand(); else { result = x1 + (position - y1)*(x2 - x1)/(y2 - y1); } } } return result; } // // ///////////////////////////////////////////////////////////////////////