Import Geant4 11.4.0 source tree

This commit is contained in:
Gabriele Cosmo
2025-12-05 08:54:02 +01:00
parent a499fb82e9
commit b4a16de652
6484 changed files with 232674 additions and 221097 deletions
@@ -77,19 +77,18 @@
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4Cerenkov::G4Cerenkov(const G4String& processName, G4ProcessType type)
: G4VProcess(processName, type)
: G4VDiscreteProcess(processName, type)
, fNumPhotons(0)
{
secID = G4PhysicsModelCatalog::GetModelID("model_Cerenkov");
SetProcessSubType(fCerenkov);
thePhysicsTable = nullptr;
if(verboseLevel > 0)
{
Initialise();
if (verboseLevel > 0) {
G4cout << GetProcessName() << " is created." << G4endl;
}
Initialise();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -118,17 +117,6 @@ void G4Cerenkov::ProcessDescription(std::ostream& out) const
out << "Verbose level: " << params->GetCerenkovVerboseLevel();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4Cerenkov::IsApplicable(const G4ParticleDefinition& aParticleType)
{
return (aParticleType.GetPDGCharge() != 0.0 &&
aParticleType.GetPDGMass() != 0.0 &&
aParticleType.GetParticleName() != "chargedgeantino" &&
!aParticleType.IsShortLived())
? true
: false;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4Cerenkov::Initialise()
{
@@ -140,6 +128,15 @@ void G4Cerenkov::Initialise()
SetVerboseLevel(params->GetCerenkovVerboseLevel());
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4Cerenkov::IsApplicable(const G4ParticleDefinition& aParticleType)
{
return ((aParticleType.GetPDGCharge() != 0.0 &&
aParticleType.GetPDGMass() != 0.0 &&
!aParticleType.IsShortLived()) ||
aParticleType.GetParticleName() == "unknown");
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4Cerenkov::BuildPhysicsTable(const G4ParticleDefinition&)
{
@@ -287,6 +284,10 @@ G4VParticleChange* G4Cerenkov::PostStepDoIt(const G4Track& aTrack,
return pParticleChange;
}
G4double deltaVelocity = pPostStepPoint->GetVelocity() -
pPreStepPoint->GetVelocity();
auto touchableHandle = aStep.GetPreStepPoint()->GetTouchableHandle();
////////////////////////////////////////////////////////////////
aParticleChange.SetNumberOfSecondaries(fNumPhotons);
@@ -300,18 +301,21 @@ G4VParticleChange* G4Cerenkov::PostStepDoIt(const G4Track& aTrack,
G4double Pmin = Rindex->Energy(0);
G4double Pmax = Rindex->GetMaxEnergy();
G4double dp = Pmax - Pmin;
G4double deltaNumberOfPhotons = MeanNumberOfPhotons1 - MeanNumberOfPhotons2;
G4double maxNumberOfPhotons =
std::max(MeanNumberOfPhotons1, MeanNumberOfPhotons2);
G4double nMax = Rindex->GetMaxValue();
G4double BetaInverse = 1. / beta;
G4double maxCos = BetaInverse / nMax;
G4double maxSin2 = (1.0 - maxCos) * (1.0 + maxCos);
G4double maxSin2 = 1.0 - maxCos * maxCos;
for(G4int i = 0; i < fNumPhotons; ++i)
{
// Determine photon energy
G4double rand;
G4double sampledEnergy, sampledRI;
G4double sampledEnergy;
G4double cosTheta, sin2Theta;
// sample an energy
@@ -319,10 +323,9 @@ G4VParticleChange* G4Cerenkov::PostStepDoIt(const G4Track& aTrack,
{
rand = G4UniformRand();
sampledEnergy = Pmin + rand * dp;
sampledRI = Rindex->Value(sampledEnergy);
cosTheta = BetaInverse / sampledRI;
cosTheta = BetaInverse / Rindex->Value(sampledEnergy);
sin2Theta = (1.0 - cosTheta) * (1.0 + cosTheta);
sin2Theta = 1.0 - cosTheta * cosTheta;
rand = G4UniformRand();
// Loop checking, 07-Aug-2015, Vladimir Ivanchenko
@@ -356,24 +359,18 @@ G4VParticleChange* G4Cerenkov::PostStepDoIt(const G4Track& aTrack,
aCerenkovPhoton->SetPolarization(photonPolarization);
aCerenkovPhoton->SetKineticEnergy(sampledEnergy);
G4double NumberOfPhotons, N;
G4double NumberOfPhotons;
do
{
rand = G4UniformRand();
NumberOfPhotons = MeanNumberOfPhotons1 -
rand * (MeanNumberOfPhotons1 - MeanNumberOfPhotons2);
N =
G4UniformRand() * std::max(MeanNumberOfPhotons1, MeanNumberOfPhotons2);
NumberOfPhotons = MeanNumberOfPhotons1 - rand * deltaNumberOfPhotons;
// Loop checking, 07-Aug-2015, Vladimir Ivanchenko
} while(N > NumberOfPhotons);
} while(G4UniformRand() * maxNumberOfPhotons > NumberOfPhotons);
G4double delta = rand * aStep.GetStepLength();
G4double deltaTime =
delta /
(pPreStepPoint->GetVelocity() +
rand * (pPostStepPoint->GetVelocity() - pPreStepPoint->GetVelocity()) *
0.5);
delta / (pPreStepPoint->GetVelocity() + rand * deltaVelocity * 0.5);
G4double aSecondaryTime = t0 + deltaTime;
G4ThreeVector aSecondaryPosition = x0 + rand * aStep.GetDeltaPosition();
@@ -382,8 +379,7 @@ G4VParticleChange* G4Cerenkov::PostStepDoIt(const G4Track& aTrack,
G4Track* aSecondaryTrack =
new G4Track(aCerenkovPhoton, aSecondaryTime, aSecondaryPosition);
aSecondaryTrack->SetTouchableHandle(
aStep.GetPreStepPoint()->GetTouchableHandle());
aSecondaryTrack->SetTouchableHandle(touchableHandle);
aSecondaryTrack->SetParentID(aTrack.GetTrackID());
aSecondaryTrack->SetCreatorModelID(secID);
aParticleChange.AddSecondary(aSecondaryTrack);
@@ -408,7 +404,7 @@ void G4Cerenkov::PreparePhysicsTable(const G4ParticleDefinition&)
G4double G4Cerenkov::GetMeanFreePath(const G4Track&, G4double,
G4ForceCondition*)
{
return 1.;
return DBL_MAX;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -417,6 +413,9 @@ G4double G4Cerenkov::PostStepGetPhysicalInteractionLength(
{
*condition = NotForced;
G4double StepLimit = DBL_MAX;
if (aTrack.GetDynamicParticle()->GetCharge() == 0.0) {
return StepLimit;
}
fNumPhotons = 0;
const G4Material* aMaterial = aTrack.GetMaterial();
@@ -424,11 +423,9 @@ G4double G4Cerenkov::PostStepGetPhysicalInteractionLength(
// If Physics Vector is not defined no Cerenkov photons
const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
G4MaterialPropertiesTable* MPT =
auto const MPT =
((*materialTable)[materialIndex])->GetMaterialPropertiesTable();
// if(!(*thePhysicsTable)[materialIndex])
if(!MPT)
{
if (nullptr == MPT) {
return StepLimit;
}
@@ -649,3 +646,11 @@ void G4Cerenkov::SetVerboseLevel(G4int verbose)
verboseLevel = verbose;
G4OpticalParameters::Instance()->SetCerenkovVerboseLevel(verboseLevel);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4Cerenkov::DumpInfo() const
{
ProcessDescription(G4cout);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -0,0 +1,59 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
#include "G4CerenkovQuasiTrackInfo.hh"
G4Allocator<G4CerenkovQuasiTrackInfo>*& aCerenkovATIAllocator()
{
G4ThreadLocalStatic G4Allocator<G4CerenkovQuasiTrackInfo>* _instance =
nullptr;
return _instance;
}
G4CerenkovQuasiTrackInfo::G4CerenkovQuasiTrackInfo(
const G4QuasiOpticalData& aOpticalData,
G4double aPreNumPhotons, G4double aPostNumPhotons)
: G4VAuxiliaryTrackInformation()
, fQuasiOpticalData(aOpticalData)
, fPreNumPhotons(aPreNumPhotons)
, fPostNumPhotons(aPostNumPhotons)
{}
void G4CerenkovQuasiTrackInfo::Print() const
{
G4cout << "Auxiliary track information for a Cerenkov step" << G4endl;
}
G4CerenkovQuasiTrackInfo* G4CerenkovQuasiTrackInfo::Cast(
const G4VAuxiliaryTrackInformation* const aATI)
{
G4CerenkovQuasiTrackInfo* CATI = nullptr;
if(aATI != nullptr)
{
// No change will be done to the pointer and to the pointed data
auto temp = const_cast<G4VAuxiliaryTrackInformation*>(aATI);
CATI = dynamic_cast<G4CerenkovQuasiTrackInfo*>(temp);
}
return CATI;
}
@@ -0,0 +1,350 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
//
// G4GeneralCerenkov
//
// Created 25.05.2025 V.Ivanchenko
//
// --------------------------------------------------------------------
#include "G4GeneralCerenkov.hh"
#include "G4StandardCerenkovModel.hh"
#include "G4Material.hh"
#include "G4MaterialPropertiesTable.hh"
#include "G4OpticalParameters.hh"
#include "G4PhysicsModelCatalog.hh"
#include "G4EmProcessSubType.hh"
#include "G4LogicalVolumeStore.hh"
std::vector<std::vector<const G4LogicalVolume*>* >* G4GeneralCerenkov::fLV = nullptr;
std::vector<G4VXRayModel*>* G4GeneralCerenkov::fSharedModels = nullptr;
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4GeneralCerenkov::G4GeneralCerenkov(const G4String& nam, G4ProcessType type)
: G4VDiscreteProcess(nam, type)
{
secID = G4PhysicsModelCatalog::GetModelID("model_Cerenkov");
SetProcessSubType(fCerenkov);
if (nullptr == fLV) {
// initialise static data
fSharedModels = new std::vector<G4VXRayModel*>;
fLV = new std::vector<std::vector<const G4LogicalVolume*>* >;
fLVNames = new std::vector<G4String>;
isInitializer = true;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4GeneralCerenkov::~G4GeneralCerenkov()
{
if (isInitializer) {
delete fSharedModels;
fSharedModels = nullptr;
delete fLVNames;
for (auto const & p : *fLV) {
delete p;
}
delete fLV;
fLV = nullptr;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4GeneralCerenkov::IsApplicable(const G4ParticleDefinition&)
{
return true;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::AddModelForVolume(G4VXRayModel* model,
const G4String& nameLogVolume)
{
if (isPrepared || !isInitializer || nullptr == model) {
G4ExceptionDescription ed;
G4String nam;
if (model != nullptr) { nam = model->GetName(); }
ed << " Attempt to add Cerenkov model <" << nam << "> for LogicalVolume "
<< nameLogVolume << " is failed!\n isPrepared:" << isPrepared
<< " isInitilizer:" << isInitializer;
G4Exception("G4GeneralCerenkov::AddModelForVolume", "em0304",
FatalException, ed, "");
return;
}
fSharedModels->push_back(model);
fLVNames->push_back(nameLogVolume);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::PreparePhysicsTable(const G4ParticleDefinition&)
{
// definition of models is done only once
if (isPrepared) { return; }
isPrepared = true;
const G4OpticalParameters* params = G4OpticalParameters::Instance();
fMaxBetaChange = params->GetCerenkovMaxBetaChange();
fMaxPhotons = params->GetCerenkovMaxPhotonsPerStep();
fStackingFlag = params->GetCerenkovStackPhotons();
fTrackSecondariesFirst = params->GetCerenkovTrackSecondariesFirst();
verboseLevel = params->GetCerenkovVerboseLevel();
auto nmod = fSharedModels->size();
if (0 == nmod) {
// the default model is added without association with a logical volume
G4VXRayModel* mod = new G4StandardCerenkovModel();
fSharedModels->push_back(mod);
nmod = 1;
}
fSecondaries.reserve(fMaxPhotons);
nModels = (G4int)nmod;
// fill static data structures
if (isInitializer) {
const G4LogicalVolumeStore* lvs = G4LogicalVolumeStore::GetInstance();
const auto & modAndVol = params->ActiveVolumes();
// preparation of logical volume vector per model
fLV->reserve(nmod);
for (G4int i=0; i<nModels; ++i) {
auto v = new std::vector<const G4LogicalVolume*>;
fLV->push_back(v);
}
for (auto const & lv : *lvs) {
// only volumes with material property defined are considered
auto const MPT = lv->GetMaterial()->GetMaterialPropertiesTable();
if (nullptr == MPT) { continue; }
const G4String& lvname = lv->GetName();
G4bool ok{false};
// search for the default model in the list
if (!modAndVol.empty()) {
for (auto const & it : modAndVol) {
if (it.second == lvname) {
if (kCerenkovDefault == it.first) {
(*fLV)[0]->push_back(lv);
fLVNames->push_back(lvname);
ok = true;
break;
}
}
}
}
if (ok) { continue; }
// search in external models
for (G4int i=0; i<nModels; ++i) {
if (lvname == (*fLVNames)[i]) {
(*fLV)[i]->push_back(lv);
ok = true;
break;
}
}
if (ok) { continue; }
// temporary for backward compatibility search for a RINDEX of the volume
if (nullptr != MPT->GetProperty(kRINDEX)) {
(*fLV)[0]->push_back(lv);
fLVNames->push_back(lvname);
}
}
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::BuildPhysicsTable(const G4ParticleDefinition&)
{
// Initialisation of models is done only once
if (isBuilt) { return; }
isBuilt = true;
if (nModels == 0) { return; }
// worker thread
if (!isInitializer) {
// worker initialisation - clone master models
fModels.reserve(nModels);
for (G4int i=0; i<nModels; ++i) {
auto newmod = new G4VXRayModel(*((*fSharedModels)[i]));
fModels.push_back(newmod);
G4double b = newmod->Initialise((*fLV)[i]);
fBetaMin = std::min(fBetaMin, b);
}
} else if (verboseLevel > 0) {
// needed for printout
std::size_t nn = 0;
for (G4int i=0; i<nModels; ++i) {
G4double b = (*fSharedModels)[i]->Initialise((*fLV)[i]);
fBetaMin = std::min(fBetaMin, b);
nn += ((*fLV)[i])->size();
}
G4long pres = G4cout.precision();
G4cout.precision(6);
G4cout << " " << GetProcessName() << std::setw(20) << " fBetaMin=" << fBetaMin
<< " fMaxBetaChange=" << fMaxBetaChange << G4endl;
G4cout << std::setw(20) << "fMaxNphot=" << fMaxPhotons
<< " Nlv=" << nn << " fStackingFlag:" << fStackingFlag
<< " fTrackSecondariesFirst:" << fTrackSecondariesFirst
<< G4endl;
for (G4int i=0; i<nModels; ++i) {
G4int n = (G4int)((*fLV)[i]->size());
G4cout << std::setw(10) << (*fSharedModels)[i]->GetName()
<< std::setw(30) << "Nvolumes=" << n << " Volumes:" << G4endl;
G4cout << std::setw(12);
for (G4int j=0; j<n; ++j) {
G4cout << (*((*fLV)[i]))[j]->GetName() << " ";
if (0 != j && (j/5)*5 == j) {
G4cout << G4endl << std::setw(12);
}
}
G4cout << G4endl;
}
G4cout.precision(pres);
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double
G4GeneralCerenkov::PostStepGetPhysicalInteractionLength(const G4Track& aTrack,
G4double,
G4ForceCondition* cond)
{
*cond = NotForced;
G4double limit = DBL_MAX;
auto const dp = aTrack.GetDynamicParticle();
if (dp->GetCharge() == 0.0) { return limit; }
fCurrentModel = nullptr;
fPreStepBeta = dp->GetBeta();
if (fPreStepBeta <= fBetaMin) { return limit; }
auto volume = aTrack.GetVolume();
if (nullptr == volume) { return limit; }
fCurrentLV = volume->GetLogicalVolume();
auto const MPT = fCurrentLV->GetMaterial()->GetMaterialPropertiesTable();
if (nullptr == MPT) { return limit; }
G4bool ok{false};
for (G4int i=0; i<nModels; ++i) {
auto const v = (*fLV)[i];
std::size_t nn = v->size();
for (std::size_t j = 0; j < nn; ++j) {
if ((*v)[j] == fCurrentLV) {
fCurrentModel = fModels[i];
if (fCurrentModel->StepLimit(j, aTrack, fPreStepBeta, limit)) {
*cond = StronglyForced;
}
ok = true;
break;
}
}
if (ok) { break; }
}
return limit;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4VParticleChange* G4GeneralCerenkov::PostStepDoIt(const G4Track& aTrack,
const G4Step& aStep)
{
aParticleChange.Initialize(aTrack);
if (fCurrentModel == nullptr) { return &aParticleChange; }
fCurrentModel->SampleXRays(fSecondaries, aStep);
if (!fSecondaries.empty()) {
// X-rays
auto touch = aStep.GetPreStepPoint()->GetTouchableHandle();
G4int parent = aTrack.GetTrackID();
for (auto & t : fSecondaries) {
t->SetTouchableHandle(touch);
t->SetParentID(parent);
t->SetCreatorModelID(secID);
aParticleChange.AddSecondary(t);
}
fSecondaries.clear();
// primary track suspended
if (fTrackSecondariesFirst && aTrack.GetTrackStatus() == fAlive) {
aParticleChange.ProposeTrackStatus(fSuspend);
}
}
return &aParticleChange;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::ProcessDescription(std::ostream& out) const
{
out << "The Cerenkov effect simulates optical photons created by the\n";
out << "passage of charged particles through matter. Materials need\n";
out << "to have the property RINDEX (refractive index) defined." << G4endl;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::SetTrackSecondariesFirst(const G4bool state)
{
fTrackSecondariesFirst = state;
G4OpticalParameters::Instance()->SetCerenkovTrackSecondariesFirst(
fTrackSecondariesFirst);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::SetMaxBetaChangePerStep(const G4double value)
{
fMaxBetaChange = value;
G4OpticalParameters::Instance()->SetCerenkovMaxBetaChange(value);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::SetMaxNumPhotonsPerStep(const G4int NumPhotons)
{
fMaxPhotons = NumPhotons;
G4OpticalParameters::Instance()->SetCerenkovMaxPhotonsPerStep(fMaxPhotons);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::SetStackPhotons(const G4bool stackingFlag)
{
fStackingFlag = stackingFlag;
G4OpticalParameters::Instance()->SetCerenkovStackPhotons(fStackingFlag);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4GeneralCerenkov::SetVerboseLevel(G4int verbose)
{
verboseLevel = verbose;
G4OpticalParameters::Instance()->SetCerenkovVerboseLevel(verboseLevel);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double
G4GeneralCerenkov::GetMeanFreePath(const G4Track&, G4double, G4ForceCondition*)
{
return DBL_MAX;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -0,0 +1,653 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
#include "G4QuasiCerenkov.hh"
#include "G4CerenkovQuasiTrackInfo.hh"
#include "G4ios.hh"
#include "G4LossTableManager.hh"
#include "G4Material.hh"
#include "G4MaterialCutsCouple.hh"
#include "G4MaterialPropertiesTable.hh"
#include "G4OpticalParameters.hh"
#include "G4OpticalPhoton.hh"
#include "G4ParticleDefinition.hh"
#include "G4ParticleMomentum.hh"
#include "G4PhysicalConstants.hh"
#include "G4PhysicsFreeVector.hh"
#include "G4Poisson.hh"
#include "G4QuasiOpticalData.hh"
#include "G4QuasiOpticalPhoton.hh"
#include "G4SystemOfUnits.hh"
#include "G4ThreeVector.hh"
#include "Randomize.hh"
#include "G4PhysicsModelCatalog.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4QuasiCerenkov::G4QuasiCerenkov(const G4String& processName, G4ProcessType type)
: G4VProcess(processName, type)
, fNumPhotons(0)
{
secID = G4PhysicsModelCatalog::GetModelID("model_QuasiCerenkov");
SetProcessSubType(fCerenkov);
thePhysicsTable = nullptr;
if(verboseLevel > 0)
{
G4cout << GetProcessName() << " is created." << G4endl;
}
Initialise();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4QuasiCerenkov::~G4QuasiCerenkov()
{
if(thePhysicsTable != nullptr)
{
thePhysicsTable->clearAndDestroy();
delete thePhysicsTable;
}
}
void G4QuasiCerenkov::ProcessDescription(std::ostream& out) const
{
out << "The Cerenkov effect simulates optical photons created by the\n";
out << "passage of charged particles through matter. Materials need\n";
out << "to have the property RINDEX (refractive index) defined.\n";
G4VProcess::DumpInfo();
G4OpticalParameters* params = G4OpticalParameters::Instance();
out << "Maximum beta change per step: " << params->GetCerenkovMaxBetaChange();
out << "Maximum photons per step: " << params->GetCerenkovMaxPhotonsPerStep();
out << "Track secondaries first: "
<< params->GetCerenkovTrackSecondariesFirst();
out << "Stack photons: " << params->GetCerenkovStackPhotons();
out << "Verbose level: " << params->GetCerenkovVerboseLevel();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4QuasiCerenkov::IsApplicable(const G4ParticleDefinition& aParticleType)
{
return (aParticleType.GetPDGCharge() != 0.0 &&
aParticleType.GetPDGMass() != 0.0 &&
aParticleType.GetParticleName() != "chargedgeantino" &&
!aParticleType.IsShortLived())
? true
: false;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::Initialise()
{
G4OpticalParameters* params = G4OpticalParameters::Instance();
SetMaxBetaChangePerStep(params->GetCerenkovMaxBetaChange());
SetMaxNumPhotonsPerStep(params->GetCerenkovMaxPhotonsPerStep());
SetTrackSecondariesFirst(params->GetCerenkovTrackSecondariesFirst());
SetStackPhotons(params->GetCerenkovStackPhotons());
SetOffloadPhotons(params->GetCerenkovOffloadPhotons());
SetVerboseLevel(params->GetCerenkovVerboseLevel());
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::BuildPhysicsTable(const G4ParticleDefinition&)
{
if(thePhysicsTable)
return;
const G4MaterialTable* theMaterialTable = G4Material::GetMaterialTable();
std::size_t numOfMaterials = G4Material::GetNumberOfMaterials();
// Find the number of materials that have non-empty material property tables
std::size_t numOfMaterialsWithMPT = 0;
for(std::size_t i = 0; i < numOfMaterials; ++i)
{
if(((*theMaterialTable)[i])->GetMaterialPropertiesTable())
{
++numOfMaterialsWithMPT;
}
}
thePhysicsTable = new G4PhysicsTable(numOfMaterialsWithMPT);
// loop over materials
std::size_t indexMPT = 0;
for(std::size_t i = 0; i < numOfMaterials; ++i)
{
G4PhysicsFreeVector* cerenkovIntegral = nullptr;
// Retrieve vector of refraction indices for the material
// from the material's optical properties table
G4Material* aMaterial = (*theMaterialTable)[i];
G4MaterialPropertiesTable* MPT = aMaterial->GetMaterialPropertiesTable();
if(MPT)
{
cerenkovIntegral = new G4PhysicsFreeVector();
G4MaterialPropertyVector* refractiveIndex = MPT->GetProperty(kRINDEX);
if(refractiveIndex)
{
// Retrieve the first refraction index in vector
// of (photon energy, refraction index) pairs
G4double currentRI = (*refractiveIndex)[0];
if(currentRI > 1.0)
{
// Create first (photon energy, Cerenkov Integral) pair
G4double currentPM = refractiveIndex->Energy(0);
G4double currentCAI = 0.0;
cerenkovIntegral->InsertValues(currentPM, currentCAI);
// Set previous values to current ones prior to loop
G4double prevPM = currentPM;
G4double prevCAI = currentCAI;
G4double prevRI = currentRI;
// loop over all (photon energy, refraction index)
// pairs stored for this material
for(std::size_t ii = 1; ii < refractiveIndex->GetVectorLength(); ++ii)
{
currentRI = (*refractiveIndex)[ii];
currentPM = refractiveIndex->Energy(ii);
currentCAI = prevCAI + (currentPM - prevPM) * 0.5 *
(1.0 / (prevRI * prevRI) +
1.0 / (currentRI * currentRI));
cerenkovIntegral->InsertValues(currentPM, currentCAI);
prevPM = currentPM;
prevCAI = currentCAI;
prevRI = currentRI;
}
}
}
// The Cerenkov integral for a given material will be inserted in
// thePhysicsTable according to the position of the material in
// the material table.
thePhysicsTable->insertAt(indexMPT, cerenkovIntegral);
fIndexMPT.insert(std::make_pair(i, indexMPT));
++indexMPT;
}
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4VParticleChange* G4QuasiCerenkov::PostStepDoIt(const G4Track& aTrack,
const G4Step& aStep)
// This routine is called for each tracking Step of a charged particle
// in a radiator. A Poisson-distributed number of photons is generated
// according to the Cerenkov formula, distributed evenly along the track
// segment and uniformly azimuth w.r.t. the particle direction. The
// parameters are then transformed into the Master Reference System, and
// they are added to the particle change.
{
aParticleChange.Initialize(aTrack);
const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
const G4Material* aMaterial = aTrack.GetMaterial();
G4StepPoint* pPreStepPoint = aStep.GetPreStepPoint();
G4StepPoint* pPostStepPoint = aStep.GetPostStepPoint();
G4ThreeVector x0 = pPreStepPoint->GetPosition();
G4ThreeVector p0 = aStep.GetDeltaPosition().unit();
G4double t0 = pPreStepPoint->GetGlobalTime();
G4MaterialPropertiesTable* MPT = aMaterial->GetMaterialPropertiesTable();
if(!MPT)
return pParticleChange;
G4MaterialPropertyVector* Rindex = MPT->GetProperty(kRINDEX);
if(!Rindex)
return pParticleChange;
G4double charge = aParticle->GetDefinition()->GetPDGCharge();
G4double beta1 = pPreStepPoint->GetBeta();
G4double beta2 = pPostStepPoint->GetBeta();
G4double beta = (beta1 + beta2) * 0.5;
G4double MeanNumberOfPhotons =
GetAverageNumberOfPhotons(charge, beta, aMaterial, Rindex);
G4double MeanNumberOfPhotons1 =
GetAverageNumberOfPhotons(charge, beta1, aMaterial, Rindex);
G4double MeanNumberOfPhotons2 =
GetAverageNumberOfPhotons(charge, beta2, aMaterial, Rindex);
if(MeanNumberOfPhotons <= 0.0)
{
// return unchanged particle and no secondaries
aParticleChange.SetNumberOfSecondaries(0);
return pParticleChange;
}
MeanNumberOfPhotons *= aStep.GetStepLength();
fNumPhotons = (G4int) G4Poisson(MeanNumberOfPhotons);
// third condition added to prevent infinite loop in do-while below,
// see bugzilla 2555
if(fNumPhotons <= 0 || !fStackingFlag ||
std::max(MeanNumberOfPhotons1, MeanNumberOfPhotons2) < 1e-15)
{
// return unchanged particle and no secondaries
aParticleChange.SetNumberOfSecondaries(0);
return pParticleChange;
}
G4double deltaVelocity = pPostStepPoint->GetVelocity() -
pPreStepPoint->GetVelocity();
auto touchableHandle = aStep.GetPreStepPoint()->GetTouchableHandle();
if(fOffloadingFlag)
{
// Create a G4DynamicParticle with G4QuasiOpticalPhoton
auto quasiPhoton = new G4DynamicParticle(
G4QuasiOpticalPhoton::QuasiOpticalPhotonDefinition(),
aParticle->GetMomentum());
// Create a new G4Track object with the quasi-optical photon
G4Track* aSecondaryTrack = new G4Track(quasiPhoton, t0, x0);
aSecondaryTrack->SetTouchableHandle(touchableHandle);
aSecondaryTrack->SetParentID(aTrack.GetTrackID());
aSecondaryTrack->SetCreatorModelID(secID);
// Attach auxiliary track information with associated metadata
G4QuasiOpticalData quasiTrackData{aMaterial->GetIndex(), fNumPhotons,
charge, aStep.GetStepLength(), pPreStepPoint->GetVelocity(),
deltaVelocity, aStep.GetDeltaPosition()};
aSecondaryTrack->SetAuxiliaryTrackInformation(secID,
new G4CerenkovQuasiTrackInfo(quasiTrackData,
MeanNumberOfPhotons1, MeanNumberOfPhotons2));
aParticleChange.AddSecondary(aSecondaryTrack);
// Return early when offloading is enabled
return pParticleChange;
}
////////////////////////////////////////////////////////////////
aParticleChange.SetNumberOfSecondaries(fNumPhotons);
if(fTrackSecondariesFirst)
{
if(aTrack.GetTrackStatus() == fAlive)
aParticleChange.ProposeTrackStatus(fSuspend);
}
////////////////////////////////////////////////////////////////
G4double Pmin = Rindex->Energy(0);
G4double Pmax = Rindex->GetMaxEnergy();
G4double dp = Pmax - Pmin;
G4double deltaNumberOfPhotons = MeanNumberOfPhotons1 - MeanNumberOfPhotons2;
G4double maxNumberOfPhotons =
std::max(MeanNumberOfPhotons1, MeanNumberOfPhotons2);
G4double nMax = Rindex->GetMaxValue();
G4double BetaInverse = 1. / beta;
G4double maxCos = BetaInverse / nMax;
G4double maxSin2 = 1.0 - maxCos * maxCos;
for(G4int i = 0; i < fNumPhotons; ++i)
{
// Determine photon energy
G4double rand;
G4double sampledEnergy;
G4double cosTheta, sin2Theta;
// sample an energy
do
{
rand = G4UniformRand();
sampledEnergy = Pmin + rand * dp;
cosTheta = BetaInverse / Rindex->Value(sampledEnergy);
sin2Theta = 1.0 - cosTheta * cosTheta;
rand = G4UniformRand();
// Loop checking, 07-Aug-2015, Vladimir Ivanchenko
} while(rand * maxSin2 > sin2Theta);
// Create photon momentum direction vector. The momentum direction is still
// with respect to the coordinate system where the primary particle
// direction is aligned with the z axis
rand = G4UniformRand();
G4double phi = twopi * rand;
G4double sinPhi = std::sin(phi);
G4double cosPhi = std::cos(phi);
G4double sinTheta = std::sqrt(sin2Theta);
G4ParticleMomentum photonMomentum(sinTheta * cosPhi, sinTheta * sinPhi,
cosTheta);
// Rotate momentum direction back to global reference system
photonMomentum.rotateUz(p0);
// Determine polarization of new photon
G4ThreeVector photonPolarization(cosTheta * cosPhi, cosTheta * sinPhi,
-sinTheta);
// Rotate back to original coord system
photonPolarization.rotateUz(p0);
// Generate a new photon:
auto aCerenkovPhoton =
new G4DynamicParticle(G4OpticalPhoton::OpticalPhoton(), photonMomentum);
aCerenkovPhoton->SetPolarization(photonPolarization);
aCerenkovPhoton->SetKineticEnergy(sampledEnergy);
G4double NumberOfPhotons;
do
{
rand = G4UniformRand();
NumberOfPhotons = MeanNumberOfPhotons1 - rand * deltaNumberOfPhotons;
// Loop checking, 07-Aug-2015, Vladimir Ivanchenko
} while(G4UniformRand() * maxNumberOfPhotons > NumberOfPhotons);
G4double delta = rand * aStep.GetStepLength();
G4double deltaTime =
delta / (pPreStepPoint->GetVelocity() + rand * deltaVelocity * 0.5);
G4double aSecondaryTime = t0 + deltaTime;
G4ThreeVector aSecondaryPosition = x0 + rand * aStep.GetDeltaPosition();
// Generate new G4Track object:
G4Track* aSecondaryTrack =
new G4Track(aCerenkovPhoton, aSecondaryTime, aSecondaryPosition);
aSecondaryTrack->SetTouchableHandle(touchableHandle);
aSecondaryTrack->SetParentID(aTrack.GetTrackID());
aSecondaryTrack->SetCreatorModelID(secID);
aParticleChange.AddSecondary(aSecondaryTrack);
}
if(verboseLevel > 1)
{
G4cout << "\n Exiting from G4QuasiCerenkov::DoIt -- NumberOfSecondaries = "
<< aParticleChange.GetNumberOfSecondaries() << G4endl;
}
return pParticleChange;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::PreparePhysicsTable(const G4ParticleDefinition&)
{
Initialise();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiCerenkov::GetMeanFreePath(const G4Track&, G4double,
G4ForceCondition*)
{
return 1.;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiCerenkov::PostStepGetPhysicalInteractionLength(
const G4Track& aTrack, G4double, G4ForceCondition* condition)
{
*condition = NotForced;
G4double StepLimit = DBL_MAX;
fNumPhotons = 0;
const G4Material* aMaterial = aTrack.GetMaterial();
std::size_t materialIndex = aMaterial->GetIndex();
// If Physics Vector is not defined no Cerenkov photons
const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
G4MaterialPropertiesTable* MPT =
((*materialTable)[materialIndex])->GetMaterialPropertiesTable();
// if(!(*thePhysicsTable)[materialIndex])
if(!MPT)
{
return StepLimit;
}
const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
const G4MaterialCutsCouple* couple = aTrack.GetMaterialCutsCouple();
G4double kineticEnergy = aParticle->GetKineticEnergy();
const G4ParticleDefinition* particleType = aParticle->GetDefinition();
G4double mass = particleType->GetPDGMass();
G4double beta = aParticle->GetTotalMomentum() / aParticle->GetTotalEnergy();
G4double gamma = aParticle->GetTotalEnergy() / mass;
G4MaterialPropertiesTable* aMaterialPropertiesTable =
aMaterial->GetMaterialPropertiesTable();
G4MaterialPropertyVector* Rindex = nullptr;
if(aMaterialPropertiesTable)
Rindex = aMaterialPropertiesTable->GetProperty(kRINDEX);
G4double nMax;
if(Rindex)
{
nMax = Rindex->GetMaxValue();
}
else
{
return StepLimit;
}
G4double BetaMin = 1. / nMax;
if(BetaMin >= 1.)
return StepLimit;
G4double GammaMin = 1. / std::sqrt(1. - BetaMin * BetaMin);
if(gamma < GammaMin)
return StepLimit;
G4double kinEmin = mass * (GammaMin - 1.);
G4double RangeMin =
G4LossTableManager::Instance()->GetRange(particleType, kinEmin, couple);
G4double Range = G4LossTableManager::Instance()->GetRange(
particleType, kineticEnergy, couple);
G4double Step = Range - RangeMin;
// If the step is smaller than G4ThreeVector::getTolerance(), it may happen
// that the particle does not move. See bug 1992.
static const G4double minAllowedStep = G4ThreeVector::getTolerance();
if(Step < minAllowedStep)
return StepLimit;
if(Step < StepLimit)
StepLimit = Step;
// If user has defined an average maximum number of photons to be generated in
// a Step, then calculate the Step length for that number of photons.
if(fMaxPhotons > 0)
{
const G4double charge = aParticle->GetDefinition()->GetPDGCharge();
G4double MeanNumberOfPhotons =
GetAverageNumberOfPhotons(charge, beta, aMaterial, Rindex);
Step = 0.;
if(MeanNumberOfPhotons > 0.0)
Step = fMaxPhotons / MeanNumberOfPhotons;
if(Step > 0. && Step < StepLimit)
StepLimit = Step;
}
// If user has defined an maximum allowed change in beta per step
if(fMaxBetaChange > 0.)
{
G4double dedx = G4LossTableManager::Instance()->GetDEDX(
particleType, kineticEnergy, couple);
G4double deltaGamma =
gamma - 1. / std::sqrt(1. - beta * beta * (1. - fMaxBetaChange) *
(1. - fMaxBetaChange));
Step = mass * deltaGamma / dedx;
if(Step > 0. && Step < StepLimit)
StepLimit = Step;
}
*condition = StronglyForced;
return StepLimit;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiCerenkov::GetAverageNumberOfPhotons(
const G4double charge, const G4double beta, const G4Material* aMaterial,
G4MaterialPropertyVector* Rindex) const
// This routine computes the number of Cerenkov photons produced per
// Geant4-unit (millimeter) in the current medium.
{
constexpr G4double Rfact = 369.81 / (eV * cm);
if(beta <= 0.0)
return 0.0;
G4double BetaInverse = 1. / beta;
// Vectors used in computation of Cerenkov Angle Integral:
// - Refraction Indices for the current material
// - new G4PhysicsFreeVector allocated to hold CAI's
std::size_t materialIndex = aMaterial->GetIndex();
// Retrieve the Cerenkov Angle Integrals for this material
auto it = fIndexMPT.find(materialIndex);
std::size_t indexMPT = 0;
if(it != fIndexMPT.end())
{
indexMPT = it->second;
}
else
{
G4ExceptionDescription ed;
ed << "G4MaterialPropertiesTable for " << aMaterial->GetName()
<< " is not found!" << G4endl;
G4Exception("G4QuasiCerenkov::GetAverageNumberOfPhotons",
"QuasiCerenkovCerenkov01", FatalException, ed);
}
G4PhysicsVector* CerenkovAngleIntegrals = ((*thePhysicsTable)(indexMPT));
std::size_t length = CerenkovAngleIntegrals->GetVectorLength();
if(0 == length)
return 0.0;
// Min and Max photon energies
G4double Pmin = Rindex->Energy(0);
G4double Pmax = Rindex->GetMaxEnergy();
// Min and Max Refraction Indices
G4double nMin = Rindex->GetMinValue();
G4double nMax = Rindex->GetMaxValue();
// Max Cerenkov Angle Integral
G4double CAImax = (*CerenkovAngleIntegrals)[length - 1];
G4double dp, ge;
// If n(Pmax) < 1/Beta -- no photons generated
if(nMax < BetaInverse)
{
dp = 0.0;
ge = 0.0;
}
// otherwise if n(Pmin) >= 1/Beta -- photons generated
else if(nMin > BetaInverse)
{
dp = Pmax - Pmin;
ge = CAImax;
}
// If n(Pmin) < 1/Beta, and n(Pmax) >= 1/Beta, then we need to find a P such
// that the value of n(P) == 1/Beta. Interpolation is performed by the
// GetEnergy() and Value() methods of the G4MaterialPropertiesTable and
// the Value() method of G4PhysicsVector.
else
{
Pmin = Rindex->GetEnergy(BetaInverse);
dp = Pmax - Pmin;
G4double CAImin = CerenkovAngleIntegrals->Value(Pmin);
ge = CAImax - CAImin;
if(verboseLevel > 1)
{
G4cout << "CAImin = " << CAImin << G4endl << "ge = " << ge << G4endl;
}
}
// Calculate number of photons
G4double NumPhotons = Rfact * charge / eplus * charge / eplus *
(dp - ge * BetaInverse * BetaInverse);
return NumPhotons;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::SetTrackSecondariesFirst(const G4bool state)
{
fTrackSecondariesFirst = state;
G4OpticalParameters::Instance()->SetCerenkovTrackSecondariesFirst(
fTrackSecondariesFirst);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::SetMaxBetaChangePerStep(const G4double value)
{
fMaxBetaChange = value * CLHEP::perCent;
G4OpticalParameters::Instance()->SetCerenkovMaxBetaChange(value);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::SetMaxNumPhotonsPerStep(const G4int NumPhotons)
{
fMaxPhotons = NumPhotons;
G4OpticalParameters::Instance()->SetCerenkovMaxPhotonsPerStep(fMaxPhotons);
}
void G4QuasiCerenkov::SetStackPhotons(const G4bool stackingFlag)
{
fStackingFlag = stackingFlag;
G4OpticalParameters::Instance()->SetCerenkovStackPhotons(fStackingFlag);
}
void G4QuasiCerenkov::SetOffloadPhotons(const G4bool offloadingFlag)
{
fOffloadingFlag = offloadingFlag;
G4OpticalParameters::Instance()->SetCerenkovOffloadPhotons(fOffloadingFlag);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::DumpPhysicsTable() const
{
G4cout << "Dump Physics Table!" << G4endl;
for(std::size_t i = 0; i < thePhysicsTable->entries(); ++i)
{
(*thePhysicsTable)[i]->DumpValues();
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiCerenkov::SetVerboseLevel(G4int verbose)
{
verboseLevel = verbose;
G4OpticalParameters::Instance()->SetCerenkovVerboseLevel(verboseLevel);
}
@@ -0,0 +1,947 @@
// ********************************************************************
// * 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. *
// ********************************************************************
//
#include "G4QuasiScintillation.hh"
#include "G4QuasiOpticalData.hh"
#include "G4QuasiOpticalPhoton.hh"
#include "G4ScintillationQuasiTrackInfo.hh"
#include "globals.hh"
#include "G4DynamicParticle.hh"
#include "G4EmProcessSubType.hh"
#include "G4Material.hh"
#include "G4MaterialPropertiesTable.hh"
#include "G4MaterialPropertyVector.hh"
#include "G4OpticalParameters.hh"
#include "G4ParticleMomentum.hh"
#include "G4ParticleTypes.hh"
#include "G4PhysicalConstants.hh"
#include "G4PhysicsFreeVector.hh"
#include "G4PhysicsTable.hh"
#include "G4Poisson.hh"
#include "G4ScintillationTrackInformation.hh"
#include "G4StepPoint.hh"
#include "G4SystemOfUnits.hh"
#include "G4ThreeVector.hh"
#include "Randomize.hh"
#include "G4PhysicsModelCatalog.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4QuasiScintillation::G4QuasiScintillation(const G4String& processName,
G4ProcessType type)
: G4VRestDiscreteProcess(processName, type)
, fIntegralTable1(nullptr)
, fIntegralTable2(nullptr)
, fIntegralTable3(nullptr)
, fEmSaturation(nullptr)
, fNumPhotons(0)
{
secID = G4PhysicsModelCatalog::GetModelID("model_QuasiScintillation");
SetProcessSubType(fScintillation);
#ifdef G4DEBUG_SCINTILLATION
ScintTrackEDep = 0.;
ScintTrackYield = 0.;
#endif
if(verboseLevel > 0)
{
G4cout << GetProcessName() << " is created " << G4endl;
}
Initialise();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4QuasiScintillation::~G4QuasiScintillation()
{
if(fIntegralTable1 != nullptr)
{
fIntegralTable1->clearAndDestroy();
delete fIntegralTable1;
}
if(fIntegralTable2 != nullptr)
{
fIntegralTable2->clearAndDestroy();
delete fIntegralTable2;
}
if(fIntegralTable3 != nullptr)
{
fIntegralTable3->clearAndDestroy();
delete fIntegralTable3;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::ProcessDescription(std::ostream& out) const
{
out << "Scintillation simulates production of optical photons produced\n"
"by a high energy particle traversing matter.\n"
"Various material properties need to be defined.\n";
G4VRestDiscreteProcess::DumpInfo();
G4OpticalParameters* params = G4OpticalParameters::Instance();
out << "Track secondaries first: " << params->GetScintTrackSecondariesFirst();
out << "Finite rise time: " << params->GetScintFiniteRiseTime();
out << "Scintillation by particle type: " << params->GetScintByParticleType();
out << "Save track information: " << params->GetScintTrackInfo();
out << "Stack photons: " << params->GetScintStackPhotons();
out << "Verbose level: " << params->GetScintVerboseLevel();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool
G4QuasiScintillation::IsApplicable(const G4ParticleDefinition& aParticleType)
{
if(aParticleType.GetParticleName() == "opticalphoton")
return false;
if(aParticleType.IsShortLived())
return false;
return true;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::PreparePhysicsTable(const G4ParticleDefinition&)
{
Initialise();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::Initialise()
{
G4OpticalParameters* params = G4OpticalParameters::Instance();
SetTrackSecondariesFirst(params->GetScintTrackSecondariesFirst());
SetOffloadPhotons(params->GetScintOffloadPhotons());
SetFiniteRiseTime(params->GetScintFiniteRiseTime());
SetScintillationByParticleType(params->GetScintByParticleType());
SetScintillationTrackInfo(params->GetScintTrackInfo());
SetStackPhotons(params->GetScintStackPhotons());
SetVerboseLevel(params->GetScintVerboseLevel());
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::BuildPhysicsTable(const G4ParticleDefinition&)
{
const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
std::size_t numOfMaterials = G4Material::GetNumberOfMaterials();
// Find the number of materials that have non-empty material property tables
std::size_t numOfMaterialsWithMPT = 0;
for(std::size_t i = 0; i < numOfMaterials; ++i)
{
if(((*materialTable)[i])->GetMaterialPropertiesTable())
{
++numOfMaterialsWithMPT;
}
}
// create new physics table
fIntegralTable1 = new G4PhysicsTable(numOfMaterialsWithMPT);
fIntegralTable2 = new G4PhysicsTable(numOfMaterialsWithMPT);
fIntegralTable3 = new G4PhysicsTable(numOfMaterialsWithMPT);
std::size_t indexMPT = 0;
for(std::size_t i = 0; i < numOfMaterials; ++i)
{
// Retrieve vector of scintillation wavelength intensity for
// the material from the material's optical properties table.
G4MaterialPropertiesTable* MPT =
((*materialTable)[i])->GetMaterialPropertiesTable();
if(MPT)
{
auto vector1 = new G4PhysicsFreeVector();
auto vector2 = new G4PhysicsFreeVector();
auto vector3 = new G4PhysicsFreeVector();
BuildInverseCdfTable(MPT->GetProperty(kSCINTILLATIONCOMPONENT1), vector1);
BuildInverseCdfTable(MPT->GetProperty(kSCINTILLATIONCOMPONENT2), vector2);
BuildInverseCdfTable(MPT->GetProperty(kSCINTILLATIONCOMPONENT3), vector3);
fIntegralTable1->insertAt(indexMPT, vector1);
fIntegralTable2->insertAt(indexMPT, vector2);
fIntegralTable3->insertAt(indexMPT, vector3);
fIndexMPT.insert(std::make_pair(i, indexMPT));
++indexMPT;
}
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void
G4QuasiScintillation::BuildInverseCdfTable(const G4MaterialPropertyVector* MPV,
G4PhysicsFreeVector* vec) const
// Build the inverse cumulative distribution function (C.D.F.) vector for the
// scintillation photon spectrum from a given G4MaterialPropertyVector.
// The resulting C.D.F. is stored in a G4PhysicsFreeVector, with values
// representing the inverse C.D.F. as a function of photon energy.
{
if(MPV && (*MPV)[0] >= 0.0)
{
std::vector<G4double> cdf(MPV->GetVectorLength());
cdf.front() = 0.0;
for (std::size_t ii = 1; ii < MPV->GetVectorLength() ; ++ii)
{
cdf[ii] = cdf[ii - 1] + 0.5 * (MPV->Energy(ii) - MPV->Energy(ii-1))
* ((*MPV)[ii] + (*MPV)[ii - 1]);
}
// Normalize for the inverse C.D.F. vector
for (std::size_t ii = 0; ii < MPV->GetVectorLength(); ++ii)
{
cdf[ii] = cdf[ii] / cdf.back();
vec->InsertValues(cdf[ii], MPV->Energy(ii));
}
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4VParticleChange* G4QuasiScintillation::AtRestDoIt(const G4Track& aTrack,
const G4Step& aStep)
// This routine simply calls the equivalent PostStepDoIt since all the
// necessary information resides in aStep.GetTotalEnergyDeposit()
{
return G4QuasiScintillation::PostStepDoIt(aTrack, aStep);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4VParticleChange* G4QuasiScintillation::PostStepDoIt(const G4Track& aTrack,
const G4Step& aStep)
// This routine is called for each tracking step of a charged particle
// in a scintillator. A Poisson/Gauss-distributed number of photons is
// generated according to the scintillation yield formula, distributed
// evenly along the track segment and uniformly into 4pi.
{
aParticleChange.Initialize(aTrack);
fNumPhotons = 0;
const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
const G4Material* aMaterial = aTrack.GetMaterial();
G4StepPoint* pPreStepPoint = aStep.GetPreStepPoint();
G4StepPoint* pPostStepPoint = aStep.GetPostStepPoint();
G4ThreeVector x0 = pPreStepPoint->GetPosition();
G4ThreeVector p0 = aStep.GetDeltaPosition().unit();
G4double t0 = pPreStepPoint->GetGlobalTime();
G4double TotalEnergyDeposit = aStep.GetTotalEnergyDeposit();
G4MaterialPropertiesTable* MPT = aMaterial->GetMaterialPropertiesTable();
if(!MPT)
return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
G4int N_timeconstants = 1;
if(MPT->GetProperty(kSCINTILLATIONCOMPONENT3))
N_timeconstants = 3;
else if(MPT->GetProperty(kSCINTILLATIONCOMPONENT2))
N_timeconstants = 2;
else if(!(MPT->GetProperty(kSCINTILLATIONCOMPONENT1)))
{
// no components were specified
return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
G4double ResolutionScale = MPT->GetConstProperty(kRESOLUTIONSCALE);
G4double MeanNumberOfPhotons;
G4double yield1 = 0.;
G4double yield2 = 0.;
G4double yield3 = 0.;
G4double timeconstant1 = 0.;
G4double timeconstant2 = 0.;
G4double timeconstant3 = 0.;
G4double sum_yields = 0.;
if(fScintillationByParticleType)
{
MeanNumberOfPhotons = GetScintillationYieldByParticleType(
aTrack, aStep, yield1, yield2, yield3, timeconstant1, timeconstant2,
timeconstant3);
}
else
{
yield1 = MPT->ConstPropertyExists(kSCINTILLATIONYIELD1)
? MPT->GetConstProperty(kSCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kSCINTILLATIONYIELD2)
? MPT->GetConstProperty(kSCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kSCINTILLATIONYIELD3)
? MPT->GetConstProperty(kSCINTILLATIONYIELD3)
: 0.;
// The default linear scintillation process
// Units: [# scintillation photons / MeV]
MeanNumberOfPhotons = MPT->GetConstProperty(kSCINTILLATIONYIELD);
// Birk's correction via fEmSaturation and specifying scintillation by
// by particle type are physically mutually exclusive
if(fEmSaturation)
MeanNumberOfPhotons *=
(fEmSaturation->VisibleEnergyDepositionAtAStep(&aStep));
else
MeanNumberOfPhotons *= TotalEnergyDeposit;
}
sum_yields = yield1 + yield2 + yield3;
if(MeanNumberOfPhotons > 10.)
{
G4double sigma = ResolutionScale * std::sqrt(MeanNumberOfPhotons);
fNumPhotons = G4int(G4RandGauss::shoot(MeanNumberOfPhotons, sigma) + 0.5);
}
else
{
fNumPhotons = G4int(G4Poisson(MeanNumberOfPhotons));
}
if(fNumPhotons <= 0 || !fStackingFlag)
{
// return unchanged particle and no secondaries
aParticleChange.SetNumberOfSecondaries(0);
return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
aParticleChange.SetNumberOfSecondaries(fNumPhotons);
if(fTrackSecondariesFirst)
{
if(aTrack.GetTrackStatus() == fAlive)
aParticleChange.ProposeTrackStatus(fSuspend);
}
std::size_t materialIndex = aMaterial->GetIndex();
auto it = fIndexMPT.find(materialIndex);
std::size_t indexMPT = 0;
if(it != fIndexMPT.end())
{
indexMPT = it->second;
}
else
{
G4ExceptionDescription ed;
ed << "G4MaterialPropertiesTable for " << aMaterial->GetName()
<< " is not found!" << G4endl;
G4Exception("G4QuasiScintillation::PostStepDoIt", "Scint04",
FatalException, ed);
}
// Retrieve the Scintillation Integral for this material
// new G4PhysicsFreeVector allocated to hold CII's
G4int numPhot = fNumPhotons;
G4double scintTime = 0.;
G4double riseTime = 0.;
G4PhysicsFreeVector* scintIntegral = nullptr;
G4ScintillationType scintType = Slow;
G4bool isNeutral = (aParticle->GetDefinition()->GetPDGCharge() == 0);
G4double deltaVelocity = pPostStepPoint->GetVelocity() -
pPreStepPoint->GetVelocity();
auto touchableHandle = aStep.GetPreStepPoint()->GetTouchableHandle();
for(G4int scnt = 0; scnt < N_timeconstants; ++scnt)
{
// if there is 1 time constant it is #1, etc.
if(scnt == 0)
{
if(N_timeconstants == 1)
{
numPhot = fNumPhotons;
}
else
{
numPhot = yield1 / sum_yields * fNumPhotons;
}
if(fScintillationByParticleType)
{
scintTime = timeconstant1;
}
else
{
scintTime = MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
}
if(fFiniteRiseTime)
{
riseTime = MPT->GetConstProperty(kSCINTILLATIONRISETIME1);
}
scintType = Fast;
scintIntegral = (G4PhysicsFreeVector*) ((*fIntegralTable1)(indexMPT));
}
else if(scnt == 1)
{
// to be consistent with old version (due to double->int conversion)
if(N_timeconstants == 2)
{
numPhot = fNumPhotons - numPhot;
}
else
{
numPhot = yield2 / sum_yields * fNumPhotons;
}
if(fScintillationByParticleType)
{
scintTime = timeconstant2;
}
else
{
scintTime = MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(fFiniteRiseTime)
{
riseTime = MPT->GetConstProperty(kSCINTILLATIONRISETIME2);
}
scintType = Medium;
scintIntegral = (G4PhysicsFreeVector*) ((*fIntegralTable2)(indexMPT));
}
else if(scnt == 2)
{
numPhot = yield3 / sum_yields * fNumPhotons;
if(fScintillationByParticleType)
{
scintTime = timeconstant3;
}
else
{
scintTime = MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
if(fFiniteRiseTime)
{
riseTime = MPT->GetConstProperty(kSCINTILLATIONRISETIME3);
}
scintType = Slow;
scintIntegral = (G4PhysicsFreeVector*) ((*fIntegralTable3)(indexMPT));
}
if(!scintIntegral)
continue;
if(fOffloadingFlag)
{
// Create a G4DynamicParticle with G4QuasiOpticalPhoton
auto quasiPhoton = new G4DynamicParticle(
G4QuasiOpticalPhoton::QuasiOpticalPhotonDefinition(),
aParticle->GetMomentum());
// Create a new G4Track object with the quasi-optical photon
G4Track* aSecondaryTrack = new G4Track(quasiPhoton, t0, x0);
aSecondaryTrack->SetTouchableHandle(touchableHandle);
aSecondaryTrack->SetParentID(aTrack.GetTrackID());
aSecondaryTrack->SetCreatorModelID(secID);
// Attach auxiliary track information with associated metadata
G4QuasiOpticalData quasiTrackData{aMaterial->GetIndex(), numPhot,
aParticle->GetDefinition()->GetPDGCharge(), aStep.GetStepLength(),
pPreStepPoint->GetVelocity(), deltaVelocity, aStep.GetDeltaPosition()};
aSecondaryTrack->SetAuxiliaryTrackInformation(secID,
new G4ScintillationQuasiTrackInfo(quasiTrackData, scintTime, riseTime));
aParticleChange.AddSecondary(aSecondaryTrack);
}
for(G4int i = 0; i < numPhot; ++i)
{
// Determine photon energy
G4double sampledEnergy = scintIntegral->Value(G4UniformRand());
if(verboseLevel > 1)
{
G4cout << "sampledEnergy = " << sampledEnergy << G4endl;
}
// Generate random photon direction
G4double cost = 1. - 2. * G4UniformRand();
G4double sint = std::sqrt((1. - cost) * (1. + cost));
G4double phi = twopi * G4UniformRand();
G4double sinp = std::sin(phi);
G4double cosp = std::cos(phi);
G4ParticleMomentum photonMomentum(sint * cosp, sint * sinp, cost);
// Determine polarization of new photon
G4ThreeVector photonPolarization(cost * cosp, cost * sinp, -sint);
G4ThreeVector perp = photonMomentum.cross(photonPolarization);
phi = twopi * G4UniformRand();
sinp = std::sin(phi);
cosp = std::cos(phi);
photonPolarization = (cosp * photonPolarization + sinp * perp).unit();
// Generate a new photon:
auto scintPhoton = new G4DynamicParticle(opticalphoton, photonMomentum);
scintPhoton->SetPolarization(photonPolarization);
scintPhoton->SetKineticEnergy(sampledEnergy);
// Generate new G4Track object:
G4double rand = (isNeutral) ? 1.0 : G4UniformRand();
// emission time distribution
G4double delta = rand * aStep.GetStepLength();
G4double deltaTime =
delta / (pPreStepPoint->GetVelocity() + 0.5 * rand * deltaVelocity);
if(riseTime == 0.0)
{
deltaTime -= scintTime * std::log(G4UniformRand());
}
else
{
deltaTime += sample_time(riseTime, scintTime);
}
G4double secTime = t0 + deltaTime;
G4ThreeVector secPosition = x0 + rand * aStep.GetDeltaPosition();
G4Track* secTrack = new G4Track(scintPhoton, secTime, secPosition);
secTrack->SetTouchableHandle(touchableHandle);
secTrack->SetParentID(aTrack.GetTrackID());
secTrack->SetCreatorModelID(secID);
if(fScintillationTrackInfo)
secTrack->SetUserInformation(
new G4ScintillationTrackInformation(scintType));
aParticleChange.AddSecondary(secTrack);
}
}
if(verboseLevel > 1)
{
G4cout << "\n Exiting from G4QuasiScintillation::DoIt -- "
<< " NumberOfSecondaries = "
<< aParticleChange.GetNumberOfSecondaries() << G4endl;
}
return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiScintillation::GetMeanFreePath(const G4Track&, G4double,
G4ForceCondition* condition)
{
*condition = StronglyForced;
return DBL_MAX;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiScintillation::GetMeanLifeTime(const G4Track&,
G4ForceCondition* condition)
{
*condition = Forced;
return DBL_MAX;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiScintillation::sample_time(G4double tau1, G4double tau2)
{
// tau1: rise time and tau2: decay time
// Loop checking, 07-Aug-2015, Vladimir Ivanchenko
G4double t;
do
{
// The exponential distribution as an envelope function: very efficient
t = -1.0 * tau2 * G4Log(1.0 - G4UniformRand());
}
while (G4UniformRand() > (1.0 - G4Exp(-t/tau1)));
return t;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4QuasiScintillation::GetScintillationYieldByParticleType(
const G4Track& aTrack, const G4Step& aStep, G4double& yield1,
G4double& yield2, G4double& yield3, G4double& timeconstant1,
G4double& timeconstant2, G4double& timeconstant3)
{
// new in 10.7, allow multiple time constants with ScintByParticleType
// Get the G4MaterialPropertyVector containing the scintillation
// yield as a function of the energy deposited and particle type
// In 11.2, allow different time constants for different particles
G4ParticleDefinition* pDef = aTrack.GetDynamicParticle()->GetDefinition();
G4MaterialPropertyVector* yieldVector = nullptr;
G4MaterialPropertiesTable* MPT =
aTrack.GetMaterial()->GetMaterialPropertiesTable();
// Protons
if(pDef == G4Proton::ProtonDefinition())
{
yieldVector = MPT->GetProperty(kPROTONSCINTILLATIONYIELD);
yield1 = MPT->ConstPropertyExists(kPROTONSCINTILLATIONYIELD1)
? MPT->GetConstProperty(kPROTONSCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kPROTONSCINTILLATIONYIELD2)
? MPT->GetConstProperty(kPROTONSCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kPROTONSCINTILLATIONYIELD3)
? MPT->GetConstProperty(kPROTONSCINTILLATIONYIELD3)
: 0.;
timeconstant1 = MPT->ConstPropertyExists(kPROTONSCINTILLATIONTIMECONSTANT1)
? MPT->GetConstProperty(kPROTONSCINTILLATIONTIMECONSTANT1)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
if(yield2 > 0.)
{
timeconstant2 = MPT->ConstPropertyExists(kPROTONSCINTILLATIONTIMECONSTANT2)
? MPT->GetConstProperty(kPROTONSCINTILLATIONTIMECONSTANT2)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(yield3 > 0.)
{
timeconstant3 = MPT->ConstPropertyExists(kPROTONSCINTILLATIONTIMECONSTANT3)
? MPT->GetConstProperty(kPROTONSCINTILLATIONTIMECONSTANT3)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
}
// Deuterons
else if(pDef == G4Deuteron::DeuteronDefinition())
{
yieldVector = MPT->GetProperty(kDEUTERONSCINTILLATIONYIELD);
yield1 = MPT->ConstPropertyExists(kDEUTERONSCINTILLATIONYIELD1)
? MPT->GetConstProperty(kDEUTERONSCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kDEUTERONSCINTILLATIONYIELD2)
? MPT->GetConstProperty(kDEUTERONSCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kDEUTERONSCINTILLATIONYIELD3)
? MPT->GetConstProperty(kDEUTERONSCINTILLATIONYIELD3)
: 0.;
timeconstant1 = MPT->ConstPropertyExists(kDEUTERONSCINTILLATIONTIMECONSTANT1)
? MPT->GetConstProperty(kDEUTERONSCINTILLATIONTIMECONSTANT1)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
if(yield2 > 0.)
{
timeconstant2 = MPT->ConstPropertyExists(kDEUTERONSCINTILLATIONTIMECONSTANT2)
? MPT->GetConstProperty(kDEUTERONSCINTILLATIONTIMECONSTANT2)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(yield3 > 0.)
{
timeconstant3 = MPT->ConstPropertyExists(kDEUTERONSCINTILLATIONTIMECONSTANT3)
? MPT->GetConstProperty(kDEUTERONSCINTILLATIONTIMECONSTANT3)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
}
// Tritons
else if(pDef == G4Triton::TritonDefinition())
{
yieldVector = MPT->GetProperty(kTRITONSCINTILLATIONYIELD);
yield1 = MPT->ConstPropertyExists(kTRITONSCINTILLATIONYIELD1)
? MPT->GetConstProperty(kTRITONSCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kTRITONSCINTILLATIONYIELD2)
? MPT->GetConstProperty(kTRITONSCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kTRITONSCINTILLATIONYIELD3)
? MPT->GetConstProperty(kTRITONSCINTILLATIONYIELD3)
: 0.;
timeconstant1 = MPT->ConstPropertyExists(kTRITONSCINTILLATIONTIMECONSTANT1)
? MPT->GetConstProperty(kTRITONSCINTILLATIONTIMECONSTANT1)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
if(yield2 > 0.)
{
timeconstant2 = MPT->ConstPropertyExists(kTRITONSCINTILLATIONTIMECONSTANT2)
? MPT->GetConstProperty(kTRITONSCINTILLATIONTIMECONSTANT2)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(yield3 > 0.)
{
timeconstant3 = MPT->ConstPropertyExists(kTRITONSCINTILLATIONTIMECONSTANT3)
? MPT->GetConstProperty(kTRITONSCINTILLATIONTIMECONSTANT3)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
}
// Alphas
else if(pDef == G4Alpha::AlphaDefinition())
{
yieldVector = MPT->GetProperty(kALPHASCINTILLATIONYIELD);
yield1 = MPT->ConstPropertyExists(kALPHASCINTILLATIONYIELD1)
? MPT->GetConstProperty(kALPHASCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kALPHASCINTILLATIONYIELD2)
? MPT->GetConstProperty(kALPHASCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kALPHASCINTILLATIONYIELD3)
? MPT->GetConstProperty(kALPHASCINTILLATIONYIELD3)
: 0.;
timeconstant1 = MPT->ConstPropertyExists(kALPHASCINTILLATIONTIMECONSTANT1)
? MPT->GetConstProperty(kALPHASCINTILLATIONTIMECONSTANT1)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
if(yield2 > 0.)
{
timeconstant2 = MPT->ConstPropertyExists(kALPHASCINTILLATIONTIMECONSTANT2)
? MPT->GetConstProperty(kALPHASCINTILLATIONTIMECONSTANT2)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(yield3 > 0.)
{
timeconstant3 = MPT->ConstPropertyExists(kALPHASCINTILLATIONTIMECONSTANT3)
? MPT->GetConstProperty(kALPHASCINTILLATIONTIMECONSTANT3)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
}
// Ions (particles derived from G4VIon and G4Ions) and recoil ions
// below the production cut from neutrons after hElastic
else if(pDef->GetParticleType() == "nucleus" ||
pDef == G4Neutron::NeutronDefinition())
{
yieldVector = MPT->GetProperty(kIONSCINTILLATIONYIELD);
yield1 = MPT->ConstPropertyExists(kIONSCINTILLATIONYIELD1)
? MPT->GetConstProperty(kIONSCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kIONSCINTILLATIONYIELD2)
? MPT->GetConstProperty(kIONSCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kIONSCINTILLATIONYIELD3)
? MPT->GetConstProperty(kIONSCINTILLATIONYIELD3)
: 0.;
timeconstant1 = MPT->ConstPropertyExists(kIONSCINTILLATIONTIMECONSTANT1)
? MPT->GetConstProperty(kIONSCINTILLATIONTIMECONSTANT1)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
if(yield2 > 0.)
{
timeconstant2 = MPT->ConstPropertyExists(kIONSCINTILLATIONTIMECONSTANT2)
? MPT->GetConstProperty(kIONSCINTILLATIONTIMECONSTANT2)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(yield3 > 0.)
{
timeconstant3 = MPT->ConstPropertyExists(kIONSCINTILLATIONTIMECONSTANT3)
? MPT->GetConstProperty(kIONSCINTILLATIONTIMECONSTANT3)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
}
// Electrons (must also account for shell-binding energy
// attributed to gamma from standard photoelectric effect)
// and, default for particles not enumerated/listed above
else
{
yieldVector = MPT->GetProperty(kELECTRONSCINTILLATIONYIELD);
yield1 = MPT->ConstPropertyExists(kELECTRONSCINTILLATIONYIELD1)
? MPT->GetConstProperty(kELECTRONSCINTILLATIONYIELD1)
: 1.;
yield2 = MPT->ConstPropertyExists(kELECTRONSCINTILLATIONYIELD2)
? MPT->GetConstProperty(kELECTRONSCINTILLATIONYIELD2)
: 0.;
yield3 = MPT->ConstPropertyExists(kELECTRONSCINTILLATIONYIELD3)
? MPT->GetConstProperty(kELECTRONSCINTILLATIONYIELD3)
: 0.;
timeconstant1 = MPT->ConstPropertyExists(kELECTRONSCINTILLATIONTIMECONSTANT1)
? MPT->GetConstProperty(kELECTRONSCINTILLATIONTIMECONSTANT1)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT1);
if(yield2 > 0.)
{
timeconstant2 = MPT->ConstPropertyExists(kELECTRONSCINTILLATIONTIMECONSTANT2)
? MPT->GetConstProperty(kELECTRONSCINTILLATIONTIMECONSTANT2)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT2);
}
if(yield3 > 0.)
{
timeconstant3 = MPT->ConstPropertyExists(kELECTRONSCINTILLATIONTIMECONSTANT3)
? MPT->GetConstProperty(kELECTRONSCINTILLATIONTIMECONSTANT3)
: MPT->GetConstProperty(kSCINTILLATIONTIMECONSTANT3);
}
}
// Throw an exception if no scintillation yield vector is found
if(yieldVector == nullptr)
{
G4ExceptionDescription ed;
ed << "\nG4QuasiScintillation::PostStepDoIt(): "
<< "Request for scintillation yield for energy deposit and particle\n"
<< "type without correct entry in MaterialPropertiesTable. A material\n"
<< "property (vector) with name like PARTICLESCINTILLATIONYIELD is\n"
<< "needed (hint: PARTICLE might not be the primary particle."
<< G4endl;
G4String comments = "Missing MaterialPropertiesTable entry - No correct "
"entry in MaterialPropertiesTable";
G4Exception("G4QuasiScintillation::PostStepDoIt", "Scint01", FatalException, ed,
comments);
return 0.; // NOLINT: required to help Coverity recognise this as exit point
}
///////////////////////////////////////
// Calculate the scintillation light //
///////////////////////////////////////
// To account for potential nonlinearity and scintillation photon
// density along the track, light (L) is produced according to:
// L_currentStep = L(PreStepKE) - L(PreStepKE - EDep)
G4double ScintillationYield = 0.;
G4double StepEnergyDeposit = aStep.GetTotalEnergyDeposit();
G4double PreStepKineticEnergy = aStep.GetPreStepPoint()->GetKineticEnergy();
if(PreStepKineticEnergy <= yieldVector->GetMaxEnergy())
{
// G4double Yield1 = yieldVector->Value(PreStepKineticEnergy);
// G4double Yield2 = yieldVector->Value(PreStepKineticEnergy -
// StepEnergyDeposit); ScintillationYield = Yield1 - Yield2;
ScintillationYield =
yieldVector->Value(PreStepKineticEnergy) -
yieldVector->Value(PreStepKineticEnergy - StepEnergyDeposit);
}
else
{
++fNumEnergyWarnings;
if(verboseLevel > 0 && fNumEnergyWarnings <= 10)
{
G4ExceptionDescription ed;
ed << "\nG4QuasiScintillation::GetScintillationYieldByParticleType(): "
<< "Request\n"
<< "for scintillation light yield above the available energy range\n"
<< "specified in G4MaterialPropertiesTable. A linear interpolation\n"
<< "will be performed to compute the scintillation light yield using\n"
<< "(L_max / E_max) as the photon yield per unit energy." << G4endl;
G4String cmt = "\nScintillation yield may be unphysical!\n";
if(fNumEnergyWarnings == 10)
{
ed << G4endl << "*** Scintillation energy warnings stopped.";
}
G4Exception("G4QuasiScintillation::GetScintillationYieldByParticleType()",
"Scint03", JustWarning, ed, cmt);
}
// Units: [# scintillation photons]
ScintillationYield = yieldVector->GetMaxValue() /
yieldVector->GetMaxEnergy() * StepEnergyDeposit;
}
#ifdef G4DEBUG_SCINTILLATION
// Increment track aggregators
ScintTrackYield += ScintillationYield;
ScintTrackEDep += StepEnergyDeposit;
G4cout << "\n-- "
<< "G4QuasiScintillation::GetScintillationYieldByParticleType() --\n"
<< "--\n"
<< "-- Name = "
<< aTrack.GetParticleDefinition()->GetParticleName() << "\n"
<< "-- TrackID = " << aTrack.GetTrackID() << "\n"
<< "-- ParentID = " << aTrack.GetParentID() << "\n"
<< "-- Current KE = " << aTrack.GetKineticEnergy() / MeV
<< " MeV\n"
<< "-- Step EDep = " << aStep.GetTotalEnergyDeposit() / MeV
<< " MeV\n"
<< "-- Track EDep = " << ScintTrackEDep / MeV << " MeV\n"
<< "-- Vertex KE = " << aTrack.GetVertexKineticEnergy() / MeV
<< " MeV\n"
<< "-- Step yield = " << ScintillationYield << " photons\n"
<< "-- Track yield = " << ScintTrackYield << " photons\n"
<< G4endl;
// The track has terminated within or has left the scintillator volume
if((aTrack.GetTrackStatus() == fStopButAlive) or
(aStep.GetPostStepPoint()->GetStepStatus() == fGeomBoundary))
{
// Reset aggregators for the next track
ScintTrackEDep = 0.;
ScintTrackYield = 0.;
}
#endif
return ScintillationYield;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::DumpPhysicsTable() const
{
if(fIntegralTable1)
{
for(std::size_t i = 0; i < fIntegralTable1->entries(); ++i)
{
((G4PhysicsFreeVector*) (*fIntegralTable1)[i])->DumpValues();
}
}
if(fIntegralTable2)
{
for(std::size_t i = 0; i < fIntegralTable2->entries(); ++i)
{
((G4PhysicsFreeVector*) (*fIntegralTable2)[i])->DumpValues();
}
}
if(fIntegralTable3)
{
for(std::size_t i = 0; i < fIntegralTable3->entries(); ++i)
{
((G4PhysicsFreeVector*) (*fIntegralTable3)[i])->DumpValues();
}
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetTrackSecondariesFirst(const G4bool state)
{
fTrackSecondariesFirst = state;
G4OpticalParameters::Instance()->SetScintTrackSecondariesFirst(
fTrackSecondariesFirst);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetFiniteRiseTime(const G4bool state)
{
fFiniteRiseTime = state;
G4OpticalParameters::Instance()->SetScintFiniteRiseTime(fFiniteRiseTime);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetScintillationByParticleType(const G4bool scintType)
{
if(fEmSaturation && scintType)
{
G4Exception("G4QuasiScintillation::SetScintillationByParticleType",
"Scint02", JustWarning,
"Redefinition: Birks Saturation is replaced by "
"ScintillationByParticleType!");
RemoveSaturation();
}
fScintillationByParticleType = scintType;
G4OpticalParameters::Instance()->SetScintByParticleType(
fScintillationByParticleType);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetScintillationTrackInfo(const G4bool trackType)
{
fScintillationTrackInfo = trackType;
G4OpticalParameters::Instance()->SetScintTrackInfo(fScintillationTrackInfo);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetStackPhotons(const G4bool stackingFlag)
{
fStackingFlag = stackingFlag;
G4OpticalParameters::Instance()->SetScintStackPhotons(fStackingFlag);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetOffloadPhotons(const G4bool offloadingFlag)
{
fOffloadingFlag = offloadingFlag;
G4OpticalParameters::Instance()->SetScintOffloadPhotons(fOffloadingFlag);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4QuasiScintillation::SetVerboseLevel(G4int verbose)
{
verboseLevel = verbose;
G4OpticalParameters::Instance()->SetScintVerboseLevel(verboseLevel);
}
@@ -149,11 +149,7 @@ void G4Scintillation::ProcessDescription(std::ostream& out) const
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4Scintillation::IsApplicable(const G4ParticleDefinition& aParticleType)
{
if(aParticleType.GetParticleName() == "opticalphoton")
return false;
if(aParticleType.IsShortLived())
return false;
return true;
return (!aParticleType.IsShortLived());
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -280,6 +276,9 @@ G4VParticleChange* G4Scintillation::PostStepDoIt(const G4Track& aTrack,
G4double t0 = pPreStepPoint->GetGlobalTime();
G4double TotalEnergyDeposit = aStep.GetTotalEnergyDeposit();
if (0.0 >= TotalEnergyDeposit) {
G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
G4MaterialPropertiesTable* MPT = aMaterial->GetMaterialPropertiesTable();
if(!MPT)
@@ -381,12 +380,17 @@ G4VParticleChange* G4Scintillation::PostStepDoIt(const G4Track& aTrack,
// Retrieve the Scintillation Integral for this material
// new G4PhysicsFreeVector allocated to hold CII's
std::size_t numPhot = fNumPhotons;
G4int numPhot = fNumPhotons;
G4double scintTime = 0.;
G4double riseTime = 0.;
G4PhysicsFreeVector* scintIntegral = nullptr;
G4ScintillationType scintType = Slow;
G4bool isNeutral = (aParticle->GetDefinition()->GetPDGCharge() == 0);
G4double deltaVelocity = pPostStepPoint->GetVelocity() -
pPreStepPoint->GetVelocity();
auto touchableHandle = aStep.GetPreStepPoint()->GetTouchableHandle();
for(G4int scnt = 0; scnt < N_timeconstants; ++scnt)
{
// if there is 1 time constant it is #1, etc.
@@ -463,7 +467,7 @@ G4VParticleChange* G4Scintillation::PostStepDoIt(const G4Track& aTrack,
if(!scintIntegral)
continue;
for(std::size_t i = 0; i < numPhot; ++i)
for(G4int i = 0; i < numPhot; ++i)
{
// Determine photon energy
G4double sampledEnergy = scintIntegral->Value(G4UniformRand());
@@ -495,19 +499,12 @@ G4VParticleChange* G4Scintillation::PostStepDoIt(const G4Track& aTrack,
scintPhoton->SetKineticEnergy(sampledEnergy);
// Generate new G4Track object:
G4double rand = G4UniformRand();
if(aParticle->GetDefinition()->GetPDGCharge() == 0)
{
rand = 1.0;
}
G4double rand = (isNeutral) ? 1.0 : G4UniformRand();
// emission time distribution
G4double delta = rand * aStep.GetStepLength();
G4double deltaTime =
delta /
(pPreStepPoint->GetVelocity() +
rand * (pPostStepPoint->GetVelocity() - pPreStepPoint->GetVelocity()) /
2.);
delta / (pPreStepPoint->GetVelocity() + 0.5 * rand * deltaVelocity);
if(riseTime == 0.0)
{
deltaTime -= scintTime * std::log(G4UniformRand());
@@ -521,8 +518,7 @@ G4VParticleChange* G4Scintillation::PostStepDoIt(const G4Track& aTrack,
G4ThreeVector secPosition = x0 + rand * aStep.GetDeltaPosition();
G4Track* secTrack = new G4Track(scintPhoton, secTime, secPosition);
secTrack->SetTouchableHandle(
aStep.GetPreStepPoint()->GetTouchableHandle());
secTrack->SetTouchableHandle(touchableHandle);
secTrack->SetParentID(aTrack.GetTrackID());
secTrack->SetCreatorModelID(secID);
if(fScintillationTrackInfo)
@@ -0,0 +1,58 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
#include "G4ScintillationQuasiTrackInfo.hh"
G4Allocator<G4ScintillationQuasiTrackInfo>*& aScintillationATIAllocator()
{
G4ThreadLocalStatic G4Allocator<G4ScintillationQuasiTrackInfo>* _instance =
nullptr;
return _instance;
}
G4ScintillationQuasiTrackInfo::G4ScintillationQuasiTrackInfo(
const G4QuasiOpticalData& aData, G4double aScintTime, G4double aRiseTime)
: G4VAuxiliaryTrackInformation()
, fQuasiOpticalData(aData)
, fScintTime(aScintTime)
, fRiseTime(aRiseTime)
{}
void G4ScintillationQuasiTrackInfo::Print() const
{
G4cout << "Auxiliary track information for a scintillation step" << G4endl;
}
G4ScintillationQuasiTrackInfo* G4ScintillationQuasiTrackInfo::Cast(
const G4VAuxiliaryTrackInformation* const aATI)
{
G4ScintillationQuasiTrackInfo* SATI = nullptr;
if(aATI != nullptr)
{
// No change will be done to the pointer and to the pointed data
auto temp = const_cast<G4VAuxiliaryTrackInformation*>(aATI);
SATI = dynamic_cast<G4ScintillationQuasiTrackInfo*>(temp);
}
return SATI;
}
@@ -0,0 +1,313 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
//
// G4StandardCerenkovModel
//
// Created 25.05.2025 V.Ivanchenko
//
// --------------------------------------------------------------------
#include "G4StandardCerenkovModel.hh"
#include "G4ios.hh"
#include "G4LossTableManager.hh"
#include "G4Material.hh"
#include "G4MaterialCutsCouple.hh"
#include "G4MaterialPropertiesTable.hh"
#include "G4OpticalParameters.hh"
#include "G4ParticleDefinition.hh"
#include "G4ParticleMomentum.hh"
#include "G4PhysicalConstants.hh"
#include "G4PhysicsFreeVector.hh"
#include "G4Poisson.hh"
#include "G4SystemOfUnits.hh"
#include "G4ThreeVector.hh"
#include "Randomize.hh"
#include "G4OpticalPhoton.hh"
namespace
{
constexpr G4double minAllowedStep = 0.001*CLHEP::mm;
constexpr G4int nvec = 10; // number of slices in beta of projectile
}
std::vector<G4double>* G4StandardCerenkovModel::fBetaLim = nullptr;
std::vector<G4MaterialPropertyVector*>* G4StandardCerenkovModel::fRindex = nullptr;
std::vector<std::vector<G4double>* >* G4StandardCerenkovModel::fMeanNumberOfPhotons = nullptr;
std::vector<std::vector<std::vector<G4double>* >* >* G4StandardCerenkovModel::fIntegral = nullptr;
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4StandardCerenkovModel::G4StandardCerenkovModel()
: G4VXRayModel("theCerenkov")
{
if (nullptr == fBetaLim) {
isInitializer = true;
fBetaLim = new std::vector<G4double>;
fMeanNumberOfPhotons = new std::vector<std::vector<G4double>* >;
fIntegral = new std::vector<std::vector<std::vector<G4double>* >* >;
fRindex = new std::vector<G4MaterialPropertyVector*>;
}
fPhoton = G4OpticalPhoton::OpticalPhoton();
fRfact = 369.81 / (CLHEP::eV * CLHEP::cm); // number of photons per mm
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4StandardCerenkovModel::~G4StandardCerenkovModel()
{
if (isInitializer && isInitialized) {
delete fBetaLim;
for (auto const & ptr : *fRindex) {
delete ptr;
}
delete fRindex;
for (auto const & ptr : *fMeanNumberOfPhotons) {
delete ptr;
}
delete fMeanNumberOfPhotons;
for (auto const & ptr : *fIntegral) {
for (auto const & p : *ptr) {
delete p;
}
delete ptr;
}
delete fIntegral;
fIntegral = nullptr;
fBetaLim = nullptr;
fRindex = nullptr;
fMeanNumberOfPhotons = nullptr;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4StandardCerenkovModel::InitialiseModel()
{
G4double beta = 1.0;
if (0 == nVolumes) { return; }
if (isInitializer && !isInitialized) {
isInitialized = true;
fBetaLim->resize(nVolumes, 1.0);
fRindex->resize(nVolumes, nullptr);
fMeanNumberOfPhotons->resize(nVolumes, new std::vector<G4double>(nvec, 0.0));
fIntegral->resize(nVolumes, nullptr);
for (std::size_t i = 0; i < nVolumes; ++i) {
auto mat = ((*pLogicalVolumes)[i])->GetMaterial();
auto MPT = mat->GetMaterialPropertiesTable();
if (nullptr == MPT) { continue; }
G4MaterialPropertyVector* rindex = MPT->GetProperty(kRINDEX);
if (nullptr == rindex) { continue; }
(*fRindex)[i] = rindex;
G4double nMax = rindex->GetMaxValue();
if (nMax <= 1.0) { continue; }
G4double b = 1.0/nMax;
(*fBetaLim)[i] = b;
beta = std::min(beta, b);
G4double dbeta = (1.0 - b)/(G4double)(nvec - 1);
G4double b0 = b;
std::size_t nn = rindex->GetVectorLength();
if (0 == nn) { continue; }
auto iptr = new std::vector<std::vector<G4double>* >((std::size_t)nvec, nullptr);
(*fIntegral)[i] = iptr;
for (auto & ptr : *iptr) {
ptr = new std::vector<G4double>(nn, 0.0);
}
// Initialisation for charge = 1.0
G4double y0 = AverageNumberOfPhotons(1.0, b0, (*rindex)[0]);
(*(*fMeanNumberOfPhotons)[i])[0] = y0;
if (1 == nn) { continue; }
// Initialisation for charge = 1.0
for (G4int k = 0; k < nvec; ++k) {
(*((*fIntegral)[i]))[k]->resize(nn, 0.0);
G4double sum = 0.0;
G4double e0 = rindex->GetMinEnergy();
G4double deltae = rindex->GetMaxEnergy() - e0;
for (std::size_t j = 1; j < nn; ++j) {
G4double e = rindex->Energy(j);
G4double y = AverageNumberOfPhotons(1.0, beta, (*rindex)[j]);
sum += 0.5*(y - y0)*(e - e0);
y0 = y;
e0 = e;
(*(*((*fIntegral)[i]))[k])[j] = sum;
}
if (deltae > 0.0) { (*(*fMeanNumberOfPhotons)[i])[k] = sum/deltae; }
if (sum > 0.0) { sum = 1.0/sum; }
for (std::size_t j = 1; j < nn; ++j) {
G4double y = (*(*((*fIntegral)[i]))[k])[j]/sum;
(*(*((*fIntegral)[i]))[k])[j] = y;
}
beta += dbeta;
beta = std::min(beta, 1.0);
}
}
} else {
for (std::size_t i = 0; i < nVolumes; ++i) {
beta = std::min(beta, (*fBetaLim)[i]);
}
}
pBetaMin = beta;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4StandardCerenkovModel::StepLimitForVolume(G4double& limit)
{
G4double betaMin = (*fBetaLim)[pIndex];
if (pPreStepBeta <= betaMin) { return false; }
// step limitation
betaMin = std::max(betaMin, pPreStepBeta*pMaxBetaChange);
auto const dynPart = pCurrentTrack->GetDynamicParticle();
fParticle = dynPart->GetDefinition();
fPreStepKinE = dynPart->GetKineticEnergy();
fCharge = fParticle->GetPDGCharge()/CLHEP::eplus;
fMass = fParticle->GetPDGMass();
G4double x = limit;
// If the step is smaller than G4ThreeVector::getTolerance(), it may happen
// that the particle does not move. See bug 1992.
if (x < minAllowedStep) { return false; }
// If user has defined an average maximum number of photons to be generated in
// a Step, then calculate the Step length for that number of photons using preStep beta.
G4double nmax = (*fRindex)[pIndex]->GetMaxValue();
fMeanNPhotons = x * AverageNumberOfPhotons(fCharge, pPreStepBeta, nmax);
if (pMaxPhotons < fMeanNPhotons) {
x *= pMaxPhotons/fMeanNPhotons;
x = std::max(x, minAllowedStep);
}
limit = x;
return true;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4StandardCerenkovModel::SampleXRays(std::vector<G4Track*>& out,
const G4Step& step)
{
auto const preStep = step.GetPreStepPoint();
auto const postStep = step.GetPostStepPoint();
G4double kinE = postStep->GetKineticEnergy();
G4double beta = pPreStepBeta;
G4double b = (*fBetaLim)[pIndex];
G4double dbeta = (1.0 - b)/(G4double)(nvec - 1);
G4int idx = std::max(G4int((beta - b)/dbeta), 0);
idx = std::min(idx, nvec - 1);
std::vector<G4double>* v = (*((*fIntegral)[pIndex]))[idx];
G4ThreeVector pos = preStep->GetPosition();
G4ThreeVector dpos = postStep->GetPosition() - pos;
G4ThreeVector dir = dpos.unit();
G4double time = preStep->GetGlobalTime();
G4double dt = postStep->GetGlobalTime() - time;
G4double de = fPreStepKinE - kinE;
// define sub-steps inside the current step
G4double x = (1.0 + fPreStepKinE/fMass);
G4double delim = fMass*pMaxBetaChange*x*x*x;
G4int nn = (G4int)(de/delim) + 1;
x = 1.0/(G4double)nn;
dpos *= x;
dt *= x;
de *= x;
G4double delta = dpos.mag();
fMeanNPhotons *= x;
// produce Cerenkov gamma - loop over sub-steps
G4double ekin = fPreStepKinE;
auto const rindex = (*fRindex)[pIndex];
std::size_t ni = rindex->GetVectorLength();
if (0 == ni) { return; }
G4double emin = rindex->Energy(0);
G4double mean = delta*fCharge*fCharge*(*(*fMeanNumberOfPhotons)[pIndex])[idx];
for (G4int i=0; i<nn; ++i) {
G4int ngamma = (G4int)G4Poisson(mean);
for (G4int j = 0; j < ngamma; ++j) {
G4double q = G4UniformRand();
G4double e = emin;
G4double n = (*rindex)[0];
if (ni > 1) {
for (std::size_t k = 1; k < ni; ++k) {
if ((*v)[k] <= q) {
e = rindex->Energy(k - 1);
e += (rindex->Energy(k) - e)*(q - (*v)[k - 1])/((*v)[k] - (*v)[k - 1]);
n = (*rindex)[k - 1];
n += ((*rindex)[k] - n)*(q - (*v)[k - 1])/((*v)[k] - (*v)[k - 1]);
}
}
}
q = G4UniformRand();
G4double t = time + q*dt;
G4ThreeVector posnew = pos + q*dpos;
G4double minCos = 1.0/(n*beta);
G4double maxSin2 = (1.0 - minCos)*(1.0 + minCos);
G4double cost, sint2;
do {
cost = 1.0 - G4UniformRand()*(1.0 - minCos);
sint2 = (1.0 - cost)*(1.0 + cost);
q = G4UniformRand();
} while(q * maxSin2 > sint2);
G4double sint = std::sqrt(sint2);
G4double phi = G4UniformRand()*CLHEP::twopi;
G4double cosPhi = std::cos(phi);
G4double sinPhi = std::sin(phi);
G4ThreeVector dirnew(sint*cosPhi, sint*sinPhi, cost);
dirnew.rotateUz(dir);
// Determine polarization of new photon
G4ThreeVector photonPolarization(cost*cosPhi, cost*sinPhi, -sint);
// Rotate back to original coord system
photonPolarization.rotateUz(dir);
auto dp = new G4DynamicParticle(fPhoton, dirnew, e);
dp->SetPolarization(photonPolarization);
auto track = new G4Track(dp, t, posnew);
out.push_back(track);
}
pos += dpos;
time += dt;
ekin -= de;
beta = std::sqrt(ekin * (ekin + 2*fMass))/(fMass + ekin);
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4StandardCerenkovModel::ModelDescription(std::ostream& out) const
{
out << "The Cerenkov effect simulates optical photons created by the\n";
out << "passage of charged particles through matter. Materials need\n";
out << "to have the property RINDEX (refractive index) defined." << G4endl;
}
@@ -279,7 +279,7 @@ void G4XrayReflection::SaveHenkeDataAsMaterialProperty()
for (std::size_t i = 0; i < Ephot.size(); ++i) {
G4double lambda = CLHEP::twopi * CLHEP::hbarc / Ephot[i];
G4double lambda_sqr = lambda * lambda;
RealIndex[i] = fmax(0, factor * lambda_sqr * f1[i]); // delta or 1-RealIndex
RealIndex[i] = std::fmax(0, factor * lambda_sqr * f1[i]); // delta or 1-RealIndex
ImagIndex[i] = factor * lambda_sqr * f2[i]; // beta or -ImagIndex
if (GetVerboseLevel() > 2)
G4cout << "Ephot=" << std::setw(10) << Ephot[i] / eV << " eV delta=" << std::setw(10)