// // ******************************************************************** // * 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: G4VRangeToEnergyConverter.cc,v 1.9 2008/03/02 10:52:56 kurasige Exp $ // GEANT4 tag $Name: geant4-09-02-ref-02 $ // // // -------------------------------------------------------------- // GEANT 4 class implementation file/ History: // 5 Oct. 2002, H.Kuirashige : Structure created based on object model // -------------------------------------------------------------- #include "G4VRangeToEnergyConverter.hh" #include "G4ParticleTable.hh" #include "G4Material.hh" #include "G4PhysicsLogVector.hh" #include "G4ios.hh" // energy range G4double G4VRangeToEnergyConverter::LowestEnergy = 0.99e-3*MeV; G4double G4VRangeToEnergyConverter::HighestEnergy = 100.0e6*MeV; G4VRangeToEnergyConverter::G4VRangeToEnergyConverter(): theParticle(0), theLossTable(0), NumberOfElements(0), TotBin(200), verboseLevel(1) { } G4VRangeToEnergyConverter::G4VRangeToEnergyConverter(const G4VRangeToEnergyConverter& right) { *this = right; } G4VRangeToEnergyConverter & G4VRangeToEnergyConverter::operator=(const G4VRangeToEnergyConverter &right) { if (this == &right) return *this; if (theLossTable) { theLossTable->clearAndDestroy(); delete theLossTable; theLossTable=0; } NumberOfElements = right.NumberOfElements; TotBin = right.TotBin; theParticle = right.theParticle; verboseLevel = right.verboseLevel; // create the loss table theLossTable = new G4LossTable(); theLossTable->reserve(G4Element::GetNumberOfElements()); // fill the loss table for (size_t j=0; jPutValue(i,Value); } theLossTable->insert(aVector); } return *this; } G4VRangeToEnergyConverter::~G4VRangeToEnergyConverter() { if (theLossTable) { theLossTable->clearAndDestroy(); delete theLossTable; } theLossTable=0; } G4int G4VRangeToEnergyConverter::operator==(const G4VRangeToEnergyConverter &right) const { return this == &right; } G4int G4VRangeToEnergyConverter::operator!=(const G4VRangeToEnergyConverter &right) const { return this != &right; } // ********************************************************************** // ************************* Convert *********************************** // ********************************************************************** G4double G4VRangeToEnergyConverter::Convert(G4double rangeCut, const G4Material* material) { G4double Mass = theParticle->GetPDGMass(); G4double theKineticEnergyCuts = 0.; // Build the energy loss table BuildLossTable(); // Build range vector for every material, convert cut into energy-cut, // fill theKineticEnergyCuts and delete the range vector G4double tune = 0.025*mm*g/cm3 ,lowen = 30.*keV ; G4int idx = material->GetIndex(); G4double density = material->GetDensity() ; if(density > 0.) { G4RangeVector* rangeVector = new G4RangeVector(LowestEnergy, HighestEnergy, TotBin); BuildRangeVector(material, HighestEnergy, Mass, rangeVector); theKineticEnergyCuts = ConvertCutToKineticEnergy(rangeVector, rangeCut, idx); if( ((theParticle->GetParticleName()=="e-")||(theParticle->GetParticleName()=="e+")) && (theKineticEnergyCuts < lowen) ) // corr. should be switched on smoothly { theKineticEnergyCuts /= (1.+(1.-theKineticEnergyCuts/lowen)* tune/(rangeCut*density)); } if(theKineticEnergyCuts < LowestEnergy) { theKineticEnergyCuts = LowestEnergy ; } delete rangeVector; } return theKineticEnergyCuts; } // ********************************************************************** // ************************ SetEnergyRange ***************************** // ********************************************************************** void G4VRangeToEnergyConverter::SetEnergyRange(G4double lowedge, G4double highedge) { // check LowestEnergy/ HighestEnergy if ( (lowedge<0.0)||(highedge<=lowedge) ){ G4cerr << "Error in G4VRangeToEnergyConverter::SetEnergyRange"; G4cerr << " : illegal energy range" << "(" << lowedge/GeV; G4cerr << "," << highedge/GeV << ") [GeV]" << G4endl; } else { LowestEnergy = lowedge; HighestEnergy = highedge; } } G4double G4VRangeToEnergyConverter::GetLowEdgeEnergy() { return LowestEnergy; } G4double G4VRangeToEnergyConverter::GetHighEdgeEnergy() { return HighestEnergy; } // ********************************************************************** // ************************ RangeLinSimpson ***************************** // ********************************************************************** G4double G4VRangeToEnergyConverter::RangeLinSimpson( G4int numberOfElement, const G4ElementVector* elementVector, const G4double* atomicNumDensityVector, G4double aMass, G4double taulow, G4double tauhigh, G4int nbin) { // Simpson numerical integration, linear binning G4double dtau = (tauhigh-taulow)/nbin; G4double Value=0.; for (size_t i=0; i<=size_t(nbin); i++){ G4double taui=taulow+dtau*i; G4double ti=aMass*taui; G4double lossi=0.; size_t nEl = (size_t)(numberOfElement); for (size_t j=0; jGetIndex(); lossi += atomicNumDensityVector[j]* (*theLossTable)[IndEl]->GetValue(ti,isOut); } if ( i==0 ) { Value += 0.5/lossi; } else { if ( iGetIndex(); lossi += atomicNumDensityVector[j]* (*theLossTable)[IndEl]->GetValue(ti,isOut); } if ( i==0 ) { Value += 0.5*taui/lossi; } else { if ( iclearAndDestroy(); delete theLossTable; } theLossTable =0; NumberOfElements = 0; } if (NumberOfElements ==0) { NumberOfElements = G4Element::GetNumberOfElements(); theLossTable = new G4LossTable(); theLossTable->reserve(G4Element::GetNumberOfElements()); #ifdef G4VERBOSE if (GetVerboseLevel()>3) { G4cout << "G4VRangeToEnergyConverter::BuildLossTable() "; G4cout << "Create theLossTable[" << theLossTable << "]"; G4cout << " NumberOfElements=" << NumberOfElements <GetZ(), aVector->GetLowEdgeEnergy(i) ); aVector->PutValue(i,Value); } theLossTable->insert(aVector); } } } // ********************************************************************** // ************************** ComputeLoss ******************************* // ********************************************************************** G4double G4VRangeToEnergyConverter::ComputeLoss(G4double AtomicNumber, G4double KineticEnergy) const { // calculate dE/dx static G4double Z; static G4double ionpot, tau0, taum, taul, ca, cba, cc; G4double z2Particle = theParticle->GetPDGCharge()/eplus; z2Particle *= z2Particle; if (z2Particle < 0.1) return 0.0; if( std::abs(AtomicNumber-Z)>0.1 ){ // recalculate constants Z = AtomicNumber; G4double Z13 = std::exp(std::log(Z)/3.); tau0 = 0.1*Z13*MeV/proton_mass_c2; taum = 0.035*Z13*MeV/proton_mass_c2; taul = 2.*MeV/proton_mass_c2; ionpot = 1.6e-5*MeV*std::exp(0.9*std::log(Z)); cc = (taul+1.)*(taul+1.)*std::log(2.*electron_mass_c2*taul*(taul+2.)/ionpot)/(taul*(taul+2.))-1.; cc = 2.*twopi_mc2_rcl2*Z*cc*std::sqrt(taul); ca = cc/((1.-0.5*std::sqrt(tau0/taum))*tau0); cba = -0.5/std::sqrt(taum); } G4double tau = KineticEnergy/theParticle->GetPDGMass(); G4double dEdx; if ( tau <= tau0 ) { dEdx = ca*(std::sqrt(tau)+cba*tau); } else { if( tau <= taul ) { dEdx = cc/std::sqrt(tau); } else { dEdx = (tau+1.)*(tau+1.)* std::log(2.*electron_mass_c2*tau*(tau+2.)/ionpot)/(tau*(tau+2.))-1.; dEdx = 2.*twopi_mc2_rcl2*Z*dEdx; } } return dEdx*z2Particle ; } // ********************************************************************** // ************************ BuildRangeVector **************************** // ********************************************************************** void G4VRangeToEnergyConverter::BuildRangeVector( const G4Material* aMaterial, G4double maxEnergy, G4double aMass, G4RangeVector* rangeVector) { // create range vector for a material const G4double tlim=2.*MeV, t1=0.1*MeV, t2=0.025*MeV; const G4int maxnbint=100; const G4ElementVector* elementVector = aMaterial->GetElementVector(); const G4double* atomicNumDensityVector = aMaterial->GetAtomicNumDensityVector(); G4int NumEl = aMaterial->GetNumberOfElements(); // calculate parameters of the low energy part first G4double loss1=0.; G4double loss2=0.; size_t i; for (i=0; iGetIndex(); loss1 += atomicNumDensityVector[i]* (*theLossTable)[IndEl]->GetValue(t1,isOut); loss2 += atomicNumDensityVector[i]* (*theLossTable)[IndEl]->GetValue(t2,isOut); } G4double tau1 = t1/proton_mass_c2; G4double sqtau1 = std::sqrt(tau1); G4double ca = (4.*loss2-loss1)/sqtau1; G4double cb = (2.*loss1-4.*loss2)/tau1; G4double cba = cb/ca; G4double taulim = tlim/proton_mass_c2; G4double taumax = maxEnergy/aMass; G4double ltaumax = std::log(taumax); // now we can fill the range vector.... G4double rmax = 0.0; for (i=0; iGetLowEdgeEnergy(i); G4double tau = LowEdgeEnergy/aMass; G4double Value; if ( tau <= tau1 ){ Value =2.*aMass*std::log(1.+cba*std::sqrt(tau))/cb; } else { Value = 2.*aMass*std::log(1.+cba*sqtau1)/cb; if ( tau <= taulim ) { G4int nbin = (G4int)(maxnbint*(tau-tau1)/(taulim-tau1)); if ( nbin<1 ) nbin = 1; Value += RangeLinSimpson( NumEl, elementVector, atomicNumDensityVector, aMass, tau1, tau, nbin); } else { Value += RangeLinSimpson( NumEl, elementVector, atomicNumDensityVector, aMass, tau1, taulim, maxnbint); G4double ltaulow = std::log(taulim); G4double ltauhigh = std::log(tau); G4int nbin = (G4int)(maxnbint*(ltauhigh-ltaulow)/(ltaumax-ltaulow)); if ( nbin<1 ) nbin = 1; Value += RangeLogSimpson(NumEl, elementVector, atomicNumDensityVector, aMass, ltaulow, ltauhigh, nbin); } } rangeVector->PutValue(i,Value); if (rmax < Value) rmax = Value; } } // ********************************************************************** // ****************** ConvertCutToKineticEnergy ************************* // ********************************************************************** G4double G4VRangeToEnergyConverter::ConvertCutToKineticEnergy( G4RangeVector* rangeVector, G4double theCutInLength, size_t materialIndex ) const { const G4double epsilon=0.01; // find max. range and the corresponding energy (rmax,Tmax) G4double rmax= -1.e10*mm; G4double Tmax= HighestEnergy; G4double fac = std::exp( std::log(HighestEnergy/LowestEnergy)/TotBin ); G4double T=LowestEnergy/fac; G4bool isOut; for (size_t ibin=0; ibinGetValue(T,isOut); if ( r>rmax ) { Tmax=T; rmax=r; } } // check cut in length is smaller than range max if ( theCutInLength >= rmax ) { #ifdef G4VERBOSE if (GetVerboseLevel()>2) { G4cout << "G4VRangeToEnergyConverter::ConvertCutToKineticEnergy "; G4cout << " for " << theParticle->GetParticleName() << G4endl; G4cout << "The cut in range [" << theCutInLength/mm << " (mm)] "; G4cout << " is too big " ; G4cout << " for material idx=" << materialIndex <GetValue(T1,isOut); if ( theCutInLength <= r1 ) { return T1; } G4double T2 = Tmax ; G4double T3 = std::sqrt(T1*T2); G4double r3 = rangeVector->GetValue(T3,isOut); while ( std::abs(1.-r3/theCutInLength)>epsilon ) { if ( theCutInLength <= r3 ) { T2 = T3; } else { T1 = T3; } T3 = std::sqrt(T1*T2); r3 = rangeVector->GetValue(T3,isOut); } return T3; }