File indexing completed on 2024-05-10 02:21:25
0001 #include "SimG4Core/CustomPhysics/interface/G4muDarkBremsstrahlungModel.h"
0002 #include "SimG4Core/CustomPhysics/interface/G4muDarkBremsstrahlung.h"
0003 #include "SimG4Core/CustomPhysics/interface/G4APrime.h"
0004
0005
0006 #include "G4ProcessTable.hh"
0007 #include "G4ProcTblElement.hh"
0008 #include "G4MuonMinus.hh"
0009 #include "G4MuonPlus.hh"
0010 #include "G4ProductionCutsTable.hh"
0011 #include <CLHEP/Units/SystemOfUnits.h>
0012
0013 #include "TFile.h"
0014 #include "TTree.h"
0015
0016 #include <gsl/gsl_math.h>
0017 #include <gsl/gsl_integration.h>
0018
0019 using namespace std;
0020
0021 G4muDarkBremsstrahlungModel::G4muDarkBremsstrahlungModel(const G4String& scalefile,
0022 const G4double biasFactor,
0023 const G4ParticleDefinition* p,
0024 const G4String& nam)
0025 : G4VEmModel(nam),
0026 mgfile(scalefile),
0027 cxBias(biasFactor),
0028 particle(nullptr),
0029 isMuon(true),
0030 probsup(1.0),
0031 isInitialised(false),
0032 method("forward_only"),
0033 mg_loaded(false) {
0034 if (p) {
0035 SetParticle(p);
0036 }
0037 theAPrime = G4APrime::APrime();
0038 MA = G4APrime::APrime()->GetPDGMass() / CLHEP::GeV;
0039 muonMass = G4MuonMinus::MuonMinusDefinition()->GetPDGMass() / CLHEP::GeV;
0040 highKinEnergy = HighEnergyLimit();
0041 lowKinEnergy = LowEnergyLimit();
0042 fParticleChange = nullptr;
0043 }
0044
0045 G4muDarkBremsstrahlungModel::~G4muDarkBremsstrahlungModel() {
0046 size_t n = partialSumSigma.size();
0047 if (n > 0) {
0048 for (size_t i = 0; i < n; i++) {
0049 delete partialSumSigma[i];
0050 }
0051 }
0052 }
0053
0054 void G4muDarkBremsstrahlungModel::SetParticle(const G4ParticleDefinition* p) {
0055 particle = p;
0056
0057 if ((p == G4MuonPlus::MuonPlus()) || (p == G4MuonMinus::MuonMinus())) {
0058 isMuon = true;
0059 } else {
0060 isMuon = false;
0061 }
0062 }
0063
0064 void G4muDarkBremsstrahlungModel::Initialise(const G4ParticleDefinition* p, const G4DataVector& cuts) {
0065 if (p) {
0066 SetParticle(p);
0067 }
0068
0069 highKinEnergy = HighEnergyLimit();
0070 lowKinEnergy = LowEnergyLimit();
0071 const G4ProductionCutsTable* theCoupleTable = G4ProductionCutsTable::GetProductionCutsTable();
0072
0073 if (theCoupleTable) {
0074 G4int numOfCouples = theCoupleTable->GetTableSize();
0075 G4int nn = partialSumSigma.size();
0076 G4int nc = cuts.size();
0077 if (nn > 0) {
0078 for (G4int ii = 0; ii < nn; ii++) {
0079 G4DataVector* a = partialSumSigma[ii];
0080 if (a)
0081 delete a;
0082 }
0083 partialSumSigma.clear();
0084 }
0085 if (numOfCouples > 0) {
0086 for (G4int i = 0; i < numOfCouples; i++) {
0087 G4double cute = DBL_MAX;
0088 if (i < nc)
0089 cute = cuts[i];
0090 const G4MaterialCutsCouple* couple = theCoupleTable->GetMaterialCutsCouple(i);
0091 const G4Material* material = couple->GetMaterial();
0092 G4DataVector* dv = ComputePartialSumSigma(material, 0.5 * highKinEnergy, std::min(cute, 0.25 * highKinEnergy));
0093 partialSumSigma.push_back(dv);
0094 }
0095 }
0096 }
0097
0098 if (isInitialised)
0099 return;
0100 fParticleChange = GetParticleChangeForLoss();
0101 isInitialised = true;
0102 }
0103
0104 void G4muDarkBremsstrahlungModel::LoadMG()
0105
0106 {
0107 TFile* f = TFile::Open(mgfile.c_str());
0108 TTree* tree = (TTree*)f->Get("Events");
0109 TLorentzVector* mvec = new TLorentzVector();
0110 TLorentzVector* avec = new TLorentzVector();
0111 TLorentzVector* nvec = new TLorentzVector();
0112 tree->SetBranchAddress("IncidentParticle", &mvec);
0113 tree->SetBranchAddress("APrime", &avec);
0114 tree->SetBranchAddress("Nucleus", &nvec);
0115 int entries = tree->GetEntries();
0116 int start = int(G4UniformRand() * entries);
0117 for (int i = start; i < int(start + entries / 10.); i++) {
0118 if (i < entries) {
0119 tree->GetEntry(i);
0120 } else {
0121 tree->GetEntry(i - entries);
0122 }
0123 frame evnt;
0124 evnt.fEl = (TLorentzVector*)mvec->Clone();
0125 evnt.cm = (TLorentzVector*)avec->Clone();
0126 *evnt.cm = *evnt.cm + *evnt.fEl;
0127 TLorentzVector* ebeam = (TLorentzVector*)nvec->Clone();
0128 *ebeam = *ebeam + *evnt.cm;
0129 evnt.E = round(ebeam->Z() * 10.) / 10.;
0130 if (mgdata.count(evnt.E) == 0) {
0131 mgdata[evnt.E];
0132 }
0133 mgdata[evnt.E].push_back(evnt);
0134 }
0135 f->Close();
0136 }
0137
0138 void G4muDarkBremsstrahlungModel::MakePlaceholders() {
0139
0140 for (const auto& iter : mgdata) {
0141 energies.push_back(std::make_pair(iter.first, iter.second.size()));
0142 }
0143
0144 for (uint64_t i = 0; i < energies.size(); i++) {
0145 energies[i].second = int(G4UniformRand() * mgdata[energies[i].first].size());
0146 }
0147 }
0148
0149 frame G4muDarkBremsstrahlungModel::GetMadgraphData(double E0)
0150
0151 {
0152 frame cmdata;
0153 uint64_t i = 0;
0154 bool pass = false;
0155
0156
0157 while (!pass) {
0158 i++;
0159 if ((E0 <= energies[i].first) || (i >= energies.size())) {
0160 pass = true;
0161 }
0162 }
0163
0164 if (i == energies.size()) {
0165 i--;
0166 }
0167
0168
0169
0170
0171 if (energies[i].second >= double(mgdata[energies[i].first].size())) {
0172 energies[i].second = 0;
0173 }
0174
0175
0176 cmdata = mgdata[energies[i].first].at(energies[i].second);
0177
0178
0179 energies[i].second = energies[i].second + 1;
0180
0181 return cmdata;
0182 }
0183
0184 G4double G4muDarkBremsstrahlungModel::DsigmaDx(double x, void* pp) {
0185 ParamsForChi* params = (ParamsForChi*)pp;
0186
0187 G4double beta = sqrt(1 - (params->MMA) * (params->MMA) / (params->EE0) / (params->EE0));
0188 G4double num = 1. - x + x * x / 3.;
0189 G4double denom = (params->MMA) * (params->MMA) * (1. - x) / x + (params->MMu) * (params->MMu) * x;
0190 G4double DsDx = beta * num / denom;
0191
0192 return DsDx;
0193 }
0194
0195 G4double G4muDarkBremsstrahlungModel::chi(double t, void* pp) {
0196 ParamsForChi* params = (ParamsForChi*)pp;
0197
0198
0199
0200
0201
0202
0203
0204 G4double Mel = 5.1E-04;
0205 G4double MUp = 2.79;
0206 G4double Mpr = 0.938;
0207 G4double d = 0.164 / pow((params->AA), 2. / 3.);
0208 G4double ap = 773.0 / Mel / pow((params->ZZ), 2. / 3.);
0209 G4double a = 111.0 / Mel / pow((params->ZZ), 1. / 3.);
0210 G4double G2el = (params->ZZ) * (params->ZZ) * a * a * a * a * t * t / (1.0 + a * a * t) / (1.0 + a * a * t) /
0211 (1.0 + t / d) / (1.0 + t / d);
0212 G4double G2in = (params->ZZ) * ap * ap * ap * ap * t * t / (1.0 + ap * ap * t) / (1.0 + ap * ap * t) /
0213 (1.0 + t / 0.71) / (1.0 + t / 0.71) / (1.0 + t / 0.71) / (1.0 + t / 0.71) / (1.0 + t / 0.71) /
0214 (1.0 + t / 0.71) / (1.0 + t / 0.71) / (1.0 + t / 0.71) *
0215 (1.0 + t * (MUp * MUp - 1.0) / 4.0 / Mpr / Mpr) * (1.0 + t * (MUp * MUp - 1.0) / 4.0 / Mpr / Mpr);
0216 G4double G2 = G2el + G2in;
0217 G4double ttmin = (params->MMA) * (params->MMA) * (params->MMA) * (params->MMA) / 4.0 / (params->EE0) / (params->EE0);
0218
0219 G4double Under = G2 * (t - ttmin) / t / t;
0220
0221 return Under;
0222 }
0223
0224 G4double G4muDarkBremsstrahlungModel::ComputeCrossSectionPerAtom(
0225 const G4ParticleDefinition*, G4double E0, G4double Z, G4double A, G4double cut, G4double)
0226
0227 {
0228 G4double cross = 0.0;
0229 if (E0 < CLHEP::keV || E0 < cut) {
0230 return cross;
0231 }
0232
0233 E0 = E0 / CLHEP::GeV;
0234 if (E0 < 2. * MA)
0235 return 0.;
0236
0237
0238 gsl_integration_workspace* w = gsl_integration_workspace_alloc(1000);
0239
0240 G4double result, error;
0241 G4double tmin = MA * MA * MA * MA / (4. * E0 * E0);
0242 G4double tmax = MA * MA;
0243
0244 gsl_function F;
0245 ParamsForChi alpha = {1.0, 1.0, 1.0, 1.0, 1.0};
0246 F.function = &G4muDarkBremsstrahlungModel::chi;
0247 F.params = α
0248 alpha.AA = A;
0249 alpha.ZZ = Z;
0250 alpha.MMA = MA;
0251 alpha.MMu = muonMass;
0252 alpha.EE0 = E0;
0253
0254
0255 gsl_integration_qags(&F, tmin, tmax, 0, 1e-7, 1000, w, &result, &error);
0256
0257 G4double ChiRes = result;
0258 gsl_integration_workspace_free(w);
0259
0260
0261 gsl_integration_workspace* dxspace = gsl_integration_workspace_alloc(1000);
0262 gsl_function G;
0263 G.function = &DsigmaDx;
0264 G.params = α
0265 G4double xmin = 0;
0266 G4double xmax = muonMass > MA ? 1. - muonMass / E0 : 1. - MA / E0;
0267 G4double res, err;
0268
0269 gsl_integration_qags(&G, xmin, xmax, 0, 1e-7, 1000, dxspace, &res, &err);
0270
0271 G4double DsDx = res;
0272 gsl_integration_workspace_free(dxspace);
0273
0274 G4double GeVtoPb = 3.894E08;
0275 G4double alphaEW = 1.0 / 137.0;
0276 G4double epsilBench = 1;
0277
0278 cross = GeVtoPb * 4. * alphaEW * alphaEW * alphaEW * epsilBench * epsilBench * ChiRes * DsDx * CLHEP::picobarn;
0279 if (cross < 0.) {
0280 cross = 0.;
0281 }
0282
0283 cross = cross * cxBias;
0284 return cross;
0285 }
0286
0287 G4DataVector* G4muDarkBremsstrahlungModel::ComputePartialSumSigma(const G4Material* material,
0288 G4double kineticEnergy,
0289 G4double cut)
0290
0291
0292
0293
0294 {
0295 G4int nElements = material->GetNumberOfElements();
0296 const G4ElementVector* theElementVector = material->GetElementVector();
0297 const G4double* theAtomNumDensityVector = material->GetAtomicNumDensityVector();
0298 G4DataVector* dv = new G4DataVector();
0299 G4double cross = 0.0;
0300
0301 for (G4int i = 0; i < nElements; i++) {
0302 cross += theAtomNumDensityVector[i] *
0303 ComputeCrossSectionPerAtom(
0304 particle, kineticEnergy, (*theElementVector)[i]->GetZ(), (*theElementVector)[i]->GetN(), cut);
0305 dv->push_back(cross);
0306 }
0307 return dv;
0308 }
0309
0310 void G4muDarkBremsstrahlungModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
0311 const G4MaterialCutsCouple* couple,
0312 const G4DynamicParticle* dp,
0313 G4double tmin,
0314 G4double maxEnergy)
0315
0316 {
0317
0318 G4bool state = false;
0319 G4String pname = "muDBrem";
0320 G4ProcessTable* ptable = G4ProcessTable::GetProcessTable();
0321 ptable->SetProcessActivation(pname, state);
0322 if (mg_loaded == false) {
0323 LoadMG();
0324 MakePlaceholders();
0325 mg_loaded = true;
0326 }
0327 G4double E0 = dp->GetTotalEnergy();
0328 G4double tmax = min(maxEnergy, E0);
0329 if (tmin >= tmax) {
0330 return;
0331 }
0332
0333 E0 = E0 / CLHEP::GeV;
0334
0335 frame data = GetMadgraphData(E0);
0336 double EAcc, Pt, P, PhiAcc;
0337 if (method == "forward_only") {
0338 EAcc = (data.fEl->E() - muonMass) / (data.E - muonMass - MA) * (E0 - muonMass - MA);
0339 EAcc = muonMass + EAcc;
0340 Pt = data.fEl->Pt();
0341 P = sqrt(EAcc * EAcc - muonMass * muonMass);
0342 PhiAcc = data.fEl->Phi();
0343 int i = 0;
0344 while (Pt * Pt + muonMass * muonMass > EAcc * EAcc)
0345 {
0346 i++;
0347 data = GetMadgraphData(E0);
0348 EAcc = (data.fEl->E() - muonMass) / (data.E - muonMass - MA) * (E0 - muonMass - MA);
0349 EAcc = muonMass + EAcc;
0350 Pt = data.fEl->Pt();
0351 P = sqrt(EAcc * EAcc - muonMass * muonMass);
0352 PhiAcc = data.fEl->Phi();
0353
0354 if (i > 10000) {
0355 break;
0356 }
0357 }
0358 } else if (method == "cm_scaling") {
0359 TLorentzVector* el = new TLorentzVector(data.fEl->X(), data.fEl->Y(), data.fEl->Z(), data.fEl->E());
0360 double ediff = data.E - E0;
0361 TLorentzVector* newcm = new TLorentzVector(data.cm->X(), data.cm->Y(), data.cm->Z() - ediff, data.cm->E() - ediff);
0362 el->Boost(-1. * data.cm->BoostVector());
0363 el->Boost(newcm->BoostVector());
0364 double newE = (data.fEl->E() - muonMass) / (data.E - muonMass - MA) * (E0 - muonMass - MA);
0365 el->SetE(newE);
0366 EAcc = el->E();
0367 Pt = el->Pt();
0368 P = el->P();
0369 PhiAcc = el->Phi();
0370 } else {
0371 EAcc = E0;
0372 P = dp->GetTotalMomentum();
0373 Pt = sqrt(dp->Get4Momentum().px() * dp->Get4Momentum().px() + dp->Get4Momentum().py() * dp->Get4Momentum().py());
0374 PhiAcc = dp->Get4Momentum().phi();
0375 }
0376
0377 EAcc = EAcc * CLHEP::GeV;
0378
0379 G4double muon_mass_mev = muonMass * CLHEP::GeV;
0380 G4double momentum = sqrt(EAcc * EAcc - muon_mass_mev * muon_mass_mev);
0381 G4ThreeVector newDirection;
0382 double ThetaAcc = std::asin(Pt / P);
0383 newDirection.set(std::sin(ThetaAcc) * std::cos(PhiAcc), std::sin(ThetaAcc) * std::sin(PhiAcc), std::cos(ThetaAcc));
0384 newDirection.rotateUz(dp->GetMomentumDirection());
0385 newDirection.setMag(momentum);
0386
0387 G4ThreeVector direction = (dp->GetMomentum() - newDirection);
0388 G4DynamicParticle* dphoton = new G4DynamicParticle(theAPrime, direction);
0389 vdp->push_back(dphoton);
0390
0391
0392 G4double finalKE = EAcc - muon_mass_mev;
0393
0394
0395 bool makeSecondary = true;
0396 if (makeSecondary) {
0397 fParticleChange->ProposeTrackStatus(fStopAndKill);
0398 fParticleChange->SetProposedKineticEnergy(0.0);
0399 G4DynamicParticle* mu =
0400 new G4DynamicParticle(const_cast<G4ParticleDefinition*>(particle), newDirection.unit(), finalKE);
0401 vdp->push_back(mu);
0402
0403 } else {
0404 fParticleChange->SetProposedMomentumDirection(newDirection.unit());
0405 fParticleChange->SetProposedKineticEnergy(finalKE);
0406 }
0407 }
0408
0409
0410
0411 const G4Element* G4muDarkBremsstrahlungModel::SelectRandomAtom(const G4MaterialCutsCouple* couple) {
0412
0413
0414 const G4Material* material = couple->GetMaterial();
0415 G4int nElements = material->GetNumberOfElements();
0416 const G4ElementVector* theElementVector = material->GetElementVector();
0417
0418 const G4Element* elm = nullptr;
0419
0420 if (1 < nElements) {
0421 --nElements;
0422 G4DataVector* dv = partialSumSigma[couple->GetIndex()];
0423 G4double rval = G4UniformRand() * ((*dv)[nElements]);
0424
0425 elm = (*theElementVector)[nElements];
0426 for (G4int i = 0; i < nElements; ++i) {
0427 if (rval <= (*dv)[i]) {
0428 elm = (*theElementVector)[i];
0429 break;
0430 }
0431 }
0432 } else {
0433 elm = (*theElementVector)[0];
0434 }
0435
0436 SetCurrentElement(elm);
0437 return elm;
0438 }
0439
0440 void G4muDarkBremsstrahlungModel::SetMethod(std::string method_in) {
0441 method = method_in;
0442 return;
0443 }