Files
geant4/source/processes/electromagnetic/adjoint/src/G4AdjointBremsstrahlungModel.cc
T
2021-06-25 16:12:29 +02:00

342 lines
13 KiB
C++

//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
#include "G4AdjointBremsstrahlungModel.hh"
#include "G4AdjointCSManager.hh"
#include "G4AdjointElectron.hh"
#include "G4AdjointGamma.hh"
#include "G4Electron.hh"
#include "G4EmModelManager.hh"
#include "G4Gamma.hh"
#include "G4ParticleChange.hh"
#include "G4PhysicalConstants.hh"
#include "G4SeltzerBergerModel.hh"
#include "G4SystemOfUnits.hh"
#include "G4TrackStatus.hh"
////////////////////////////////////////////////////////////////////////////////
G4AdjointBremsstrahlungModel::G4AdjointBremsstrahlungModel(G4VEmModel* aModel)
: G4VEmAdjointModel("AdjointeBremModel")
{
fDirectModel = aModel;
Initialize();
}
////////////////////////////////////////////////////////////////////////////////
G4AdjointBremsstrahlungModel::G4AdjointBremsstrahlungModel()
: G4VEmAdjointModel("AdjointeBremModel")
{
fDirectModel = new G4SeltzerBergerModel();
Initialize();
}
////////////////////////////////////////////////////////////////////////////////
void G4AdjointBremsstrahlungModel::Initialize()
{
SetUseMatrix(false);
SetUseMatrixPerElement(false);
fEmModelManagerForFwdModels = new G4EmModelManager();
fEmModelManagerForFwdModels->AddEmModel(1, fDirectModel, nullptr, nullptr);
SetApplyCutInRange(true);
fElectron = G4Electron::Electron();
fGamma = G4Gamma::Gamma();
fAdjEquivDirectPrimPart = G4AdjointElectron::AdjointElectron();
fAdjEquivDirectSecondPart = G4AdjointGamma::AdjointGamma();
fDirectPrimaryPart = fElectron;
fSecondPartSameType = false;
fCSManager = G4AdjointCSManager::GetAdjointCSManager();
}
////////////////////////////////////////////////////////////////////////////////
G4AdjointBremsstrahlungModel::~G4AdjointBremsstrahlungModel()
{
if(fEmModelManagerForFwdModels)
delete fEmModelManagerForFwdModels;
}
////////////////////////////////////////////////////////////////////////////////
void G4AdjointBremsstrahlungModel::SampleSecondaries(
const G4Track& aTrack, G4bool isScatProjToProj,
G4ParticleChange* fParticleChange)
{
if(!fUseMatrix)
return RapidSampleSecondaries(aTrack, isScatProjToProj, fParticleChange);
const G4DynamicParticle* theAdjointPrimary = aTrack.GetDynamicParticle();
DefineCurrentMaterial(aTrack.GetMaterialCutsCouple());
G4double adjointPrimKinEnergy = theAdjointPrimary->GetKineticEnergy();
G4double adjointPrimTotalEnergy = theAdjointPrimary->GetTotalEnergy();
if(adjointPrimKinEnergy > GetHighEnergyLimit() * 0.999)
{
return;
}
G4double projectileKinEnergy =
SampleAdjSecEnergyFromCSMatrix(adjointPrimKinEnergy, isScatProjToProj);
// Weight correction
CorrectPostStepWeight(fParticleChange, aTrack.GetWeight(),
adjointPrimKinEnergy, projectileKinEnergy,
isScatProjToProj);
// Kinematic
G4double projectileM0 = fAdjEquivDirectPrimPart->GetPDGMass();
G4double projectileTotalEnergy = projectileM0 + projectileKinEnergy;
G4double projectileP2 =
projectileTotalEnergy * projectileTotalEnergy - projectileM0 * projectileM0;
G4double projectileP = std::sqrt(projectileP2);
// Angle of the gamma direction with the projectile taken from
// G4eBremsstrahlungModel
G4double u;
if(0.25 > G4UniformRand())
u = -std::log(G4UniformRand() * G4UniformRand()) / 0.625;
else
u = -std::log(G4UniformRand() * G4UniformRand()) / 1.875;
G4double theta = u * electron_mass_c2 / projectileTotalEnergy;
G4double sint = std::sin(theta);
G4double cost = std::cos(theta);
G4double phi = twopi * G4UniformRand();
G4ThreeVector projectileMomentum =
G4ThreeVector(std::cos(phi) * sint, std::sin(phi) * sint, cost) *
projectileP; // gamma frame
if(isScatProjToProj)
{ // the adjoint primary is the scattered e-
G4ThreeVector gammaMomentum =
(projectileTotalEnergy - adjointPrimTotalEnergy) *
G4ThreeVector(0., 0., 1.);
G4ThreeVector dirProd = projectileMomentum - gammaMomentum;
G4double cost1 = std::cos(dirProd.angle(projectileMomentum));
G4double sint1 = std::sqrt(1. - cost1 * cost1);
projectileMomentum =
G4ThreeVector(std::cos(phi) * sint1, std::sin(phi) * sint1, cost1) *
projectileP;
}
projectileMomentum.rotateUz(theAdjointPrimary->GetMomentumDirection());
if(!isScatProjToProj)
{ // kill the primary and add a secondary
fParticleChange->ProposeTrackStatus(fStopAndKill);
fParticleChange->AddSecondary(
new G4DynamicParticle(fAdjEquivDirectPrimPart, projectileMomentum));
}
else
{
fParticleChange->ProposeEnergy(projectileKinEnergy);
fParticleChange->ProposeMomentumDirection(projectileMomentum.unit());
}
}
////////////////////////////////////////////////////////////////////////////////
void G4AdjointBremsstrahlungModel::RapidSampleSecondaries(
const G4Track& aTrack, G4bool isScatProjToProj,
G4ParticleChange* fParticleChange)
{
const G4DynamicParticle* theAdjointPrimary = aTrack.GetDynamicParticle();
DefineCurrentMaterial(aTrack.GetMaterialCutsCouple());
G4double adjointPrimKinEnergy = theAdjointPrimary->GetKineticEnergy();
G4double adjointPrimTotalEnergy = theAdjointPrimary->GetTotalEnergy();
if(adjointPrimKinEnergy > GetHighEnergyLimit() * 0.999)
{
return;
}
G4double projectileKinEnergy = 0.;
G4double gammaEnergy = 0.;
G4double diffCSUsed = 0.;
if(!isScatProjToProj)
{
gammaEnergy = adjointPrimKinEnergy;
G4double Emax = GetSecondAdjEnergyMaxForProdToProj(adjointPrimKinEnergy);
G4double Emin = GetSecondAdjEnergyMinForProdToProj(adjointPrimKinEnergy);
if(Emin >= Emax)
return;
projectileKinEnergy = Emin * std::pow(Emax / Emin, G4UniformRand());
diffCSUsed = fCsBiasingFactor * fLastCZ / projectileKinEnergy;
}
else
{
G4double Emax =
GetSecondAdjEnergyMaxForScatProjToProj(adjointPrimKinEnergy);
G4double Emin =
GetSecondAdjEnergyMinForScatProjToProj(adjointPrimKinEnergy, fTcutSecond);
if(Emin >= Emax)
return;
G4double f1 = (Emin - adjointPrimKinEnergy) / Emin;
G4double f2 = (Emax - adjointPrimKinEnergy) / Emax / f1;
projectileKinEnergy =
adjointPrimKinEnergy / (1. - f1 * std::pow(f2, G4UniformRand()));
gammaEnergy = projectileKinEnergy - adjointPrimKinEnergy;
diffCSUsed =
fLastCZ * adjointPrimKinEnergy / projectileKinEnergy / gammaEnergy;
}
// Weight correction:
// First w_corr is set to the ratio between adjoint total CS and fwd total CS
// if this has to be done in the model.
// For the case of forced interaction this will be done in the PostStepDoIt of
// the forced interaction. It is important to set the weight before the
// creation of the secondary
G4double w_corr = fOutsideWeightFactor;
if(fInModelWeightCorr)
{
w_corr = fCSManager->GetPostStepWeightCorrection();
}
// Then another correction is needed due to the fact that a biaised
// differential CS has been used rather than the one consistent with the
// direct model Here we consider the true diffCS as the one obtained by the
// numerical differentiation over Tcut of the direct CS, corrected by the
// Migdal term. Basically any other differential CS could be used here
// (example Penelope).
G4double diffCS = DiffCrossSectionPerVolumePrimToSecond(
fCurrentMaterial, projectileKinEnergy, gammaEnergy);
w_corr *= diffCS / diffCSUsed;
G4double new_weight = aTrack.GetWeight() * w_corr;
fParticleChange->SetParentWeightByProcess(false);
fParticleChange->SetSecondaryWeightByProcess(false);
fParticleChange->ProposeParentWeight(new_weight);
// Kinematic
G4double projectileM0 = fAdjEquivDirectPrimPart->GetPDGMass();
G4double projectileTotalEnergy = projectileM0 + projectileKinEnergy;
G4double projectileP2 =
projectileTotalEnergy * projectileTotalEnergy - projectileM0 * projectileM0;
G4double projectileP = std::sqrt(projectileP2);
// Use the angular model of the forward model to generate the gamma direction
// Dummy dynamic particle to use the model
G4DynamicParticle* aDynPart =
new G4DynamicParticle(fElectron, G4ThreeVector(0., 0., 1.) * projectileP);
// Get the element from the direct model
const G4Element* elm = fDirectModel->SelectRandomAtom(
fCurrentCouple, fElectron, projectileKinEnergy, fTcutSecond);
G4int Z = elm->GetZasInt();
G4double energy = aDynPart->GetTotalEnergy() - gammaEnergy;
G4ThreeVector projectileMomentum =
fDirectModel->GetAngularDistribution()->SampleDirection(aDynPart, energy, Z,
fCurrentMaterial) * projectileP;
G4double phi = projectileMomentum.getPhi();
if(isScatProjToProj)
{ // the adjoint primary is the scattered e-
G4ThreeVector gammaMomentum =
(projectileTotalEnergy - adjointPrimTotalEnergy) *
G4ThreeVector(0., 0., 1.);
G4ThreeVector dirProd = projectileMomentum - gammaMomentum;
G4double cost1 = std::cos(dirProd.angle(projectileMomentum));
G4double sint1 = std::sqrt(1. - cost1 * cost1);
projectileMomentum =
G4ThreeVector(std::cos(phi) * sint1, std::sin(phi) * sint1, cost1) *
projectileP;
}
projectileMomentum.rotateUz(theAdjointPrimary->GetMomentumDirection());
if(!isScatProjToProj)
{ // kill the primary and add a secondary
fParticleChange->ProposeTrackStatus(fStopAndKill);
fParticleChange->AddSecondary(
new G4DynamicParticle(fAdjEquivDirectPrimPart, projectileMomentum));
}
else
{
fParticleChange->ProposeEnergy(projectileKinEnergy);
fParticleChange->ProposeMomentumDirection(projectileMomentum.unit());
}
}
////////////////////////////////////////////////////////////////////////////////
G4double G4AdjointBremsstrahlungModel::DiffCrossSectionPerVolumePrimToSecond(
const G4Material* aMaterial,
G4double kinEnergyProj, // kin energy of primary before interaction
G4double kinEnergyProd // kinetic energy of the secondary particle
)
{
if(!fIsDirectModelInitialised)
{
fEmModelManagerForFwdModels->Initialise(fElectron, fGamma, 1., 0);
fIsDirectModelInitialised = true;
}
return G4VEmAdjointModel::DiffCrossSectionPerVolumePrimToSecond(
aMaterial, kinEnergyProj, kinEnergyProd);
}
////////////////////////////////////////////////////////////////////////////////
G4double G4AdjointBremsstrahlungModel::AdjointCrossSection(
const G4MaterialCutsCouple* aCouple, G4double primEnergy,
G4bool isScatProjToProj)
{
static constexpr G4double maxEnergy = 100. * MeV / 2.718281828459045;
// 2.78.. == std::exp(1.)
if(!fIsDirectModelInitialised)
{
fEmModelManagerForFwdModels->Initialise(fElectron, fGamma, 1., 0);
fIsDirectModelInitialised = true;
}
if(fUseMatrix)
return G4VEmAdjointModel::AdjointCrossSection(aCouple, primEnergy,
isScatProjToProj);
DefineCurrentMaterial(aCouple);
G4double Cross = 0.;
// this gives the constant above
fLastCZ = fDirectModel->CrossSectionPerVolume(
aCouple->GetMaterial(), fDirectPrimaryPart, 100. * MeV, maxEnergy);
if(!isScatProjToProj)
{
G4double Emax_proj = GetSecondAdjEnergyMaxForProdToProj(primEnergy);
G4double Emin_proj = GetSecondAdjEnergyMinForProdToProj(primEnergy);
if(Emax_proj > Emin_proj && primEnergy > fTcutSecond)
Cross = fCsBiasingFactor * fLastCZ * std::log(Emax_proj / Emin_proj);
}
else
{
G4double Emax_proj = GetSecondAdjEnergyMaxForScatProjToProj(primEnergy);
G4double Emin_proj =
GetSecondAdjEnergyMinForScatProjToProj(primEnergy, fTcutSecond);
if(Emax_proj > Emin_proj)
Cross = fLastCZ * std::log((Emax_proj - primEnergy) * Emin_proj /
Emax_proj / (Emin_proj - primEnergy));
}
return Cross;
}