Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4PolarizationTransition.cc
Go to the documentation of this file.
1// ********************************************************************
2// * License and Disclaimer *
3// * *
4// * The Geant4 software is copyright of the Copyright Holders of *
5// * the Geant4 Collaboration. It is provided under the terms and *
6// * conditions of the Geant4 Software License, included in the file *
7// * LICENSE and available at http://cern.ch/geant4/license . These *
8// * include a list of copyright holders. *
9// * *
10// * Neither the authors of this software system, nor their employing *
11// * institutes,nor the agencies providing financial support for this *
12// * work make any representation or warranty, express or implied, *
13// * regarding this software system or assume any liability for its *
14// * use. Please see the license in the file LICENSE and URL above *
15// * for the full disclaimer and the limitation of liability. *
16// * *
17// * This code implementation is the result of the scientific and *
18// * technical work of the GEANT4 collaboration. *
19// * By using, copying, modifying or distributing the software (or *
20// * any work based on the software) you agree to acknowledge its *
21// * use in resulting scientific publications, and indicate your *
22// * acceptance of all terms of the Geant4 Software license. *
23// ********************************************************************
24//
25//
26// -------------------------------------------------------------------
27// GEANT4 Class file
28//
29// File name: G4PolarizationTransition
30//
31// Author: Jason Detwiler ([email protected])
32//
33// Creation date: Aug 2012
34// -------------------------------------------------------------------
35#include <iostream>
36#include <vector>
37#include "Randomize.hh"
38
40#include "G4Clebsch.hh"
41#include "G4PolynomialPDF.hh"
42#include "G4Fragment.hh"
44#include "G4SystemOfUnits.hh"
45#include "G4Exp.hh"
46
47using namespace std;
48
50 : fVerbose(1), fTwoJ1(0), fTwoJ2(0), fLbar(1), fL(0), fDelta(0),
51 kEps(1.e-15), kPolyPDF(0, nullptr, -1, 1)
52{}
53
55{}
56
58 G4int twoJ2, G4int twoJ1) const
59{
60 G4double fCoeff = G4Clebsch::Wigner3J(2*LL, 2, 2*Lprime, -2, 2*K, 0);
61 if(fCoeff == 0) return 0;
62 fCoeff *= G4Clebsch::Wigner6J(2*LL, 2*Lprime, 2*K, twoJ1, twoJ1, twoJ2);
63 if(fCoeff == 0) return 0;
64 if(((twoJ1+twoJ2)/2 - 1) %2) fCoeff = -fCoeff;
65 return fCoeff*std::sqrt(G4double((2*K+1)*(twoJ1+1)*(2*LL+1)*(2*Lprime+1)));
66}
67
69 G4int LL, G4int Lprime,
70 G4int twoJ2, G4int twoJ1) const
71{
72 G4double fCoeff = G4Clebsch::Wigner3J(2*LL, 2, 2*Lprime, -2, 2*K, 0);
73 if(fCoeff == 0) return 0;
74 fCoeff *= G4Clebsch::Wigner9J(twoJ2, 2*LL, twoJ1, twoJ2, 2*Lprime, twoJ1,
75 2*K2, 2*K, 2*K1);
76 if(fCoeff == 0) return 0;
77 if((Lprime+K2+K1+1) % 2) fCoeff = -fCoeff;
78
79 //AR-13Jun2017 : apply Jason Detwiler's conversion to double
80 // in the argument of sqrt() to avoid integer overflows.
81 return fCoeff*std::sqrt(G4double((twoJ1+1)*(twoJ2+1)*(2*LL+1))
82 *G4double((2*Lprime+1)*(2*K+1)*(2*K1+1)*(2*K2+1)));
83}
84
86{
87 double transFCoeff = FCoefficient(K, fLbar, fLbar, fTwoJ2, fTwoJ1);
88 if(fDelta == 0) return transFCoeff;
89 transFCoeff += 2.*fDelta*FCoefficient(K, fLbar, fL, fTwoJ2, fTwoJ1);
90 transFCoeff += fDelta*fDelta*FCoefficient(K, fL, fL, fTwoJ2, fTwoJ1);
91 return transFCoeff;
92}
93
95 G4int K1) const
96{
97 double transF3Coeff = F3Coefficient(K, K2, K1, fLbar, fLbar, fTwoJ2, fTwoJ1);
98 if(fDelta == 0) return transF3Coeff;
99 transF3Coeff += 2.*fDelta*F3Coefficient(K, K2, K1, fLbar, fL, fTwoJ2, fTwoJ1);
100 transF3Coeff += fDelta*fDelta*F3Coefficient(K, K2, K1, fL, fL, fTwoJ2, fTwoJ1);
101 return transF3Coeff;
102}
103
104G4double G4PolarizationTransition::GenerateGammaCosTheta(const POLAR& pol)
105{
106 std::size_t length = pol.size();
107 // Isotropic case
108 if(length <= 1) return G4UniformRand()*2.-1.;
109
110 // kappa > 0 terms integrate out to zero over phi: 0->2pi, so just need (k,0)
111 // terms to generate cos theta distribution
112 vector<G4double> polyPDFCoeffs(length, 0.0);
113 for(G4int k = 0; k < (G4int)length; k += 2) {
114 if ((pol[k]).size() > 0 ) {
115 if(fVerbose > 1 && std::abs(((pol)[k])[0].imag()) > kEps) {
116 G4cout << "G4PolarizationTransition::GenerateGammaCosTheta WARNING: \n"
117 << " fPolarization["
118 << k << "][0] has imag component: = "
119 << ((pol)[k])[0].real() << " + "
120 << ((pol)[k])[0].imag() << "*i" << G4endl;
121 }
122 G4double a_k = std::sqrt((G4double)(2*k+1))*GammaTransFCoefficient(k)*((pol)[k])[0].real();
123 std::size_t nCoeff = fgLegendrePolys.GetNCoefficients(k);
124 for(std::size_t iCoeff=0; iCoeff < nCoeff; ++iCoeff) {
125 polyPDFCoeffs[iCoeff] += a_k*fgLegendrePolys.GetCoefficient(iCoeff, k);
126 }
127 } else {
128 G4cout << "G4PolarizationTransition::GenerateGammaCosTheta: WARNING: \n"
129 << " size of pol[" << k << "] = " << (pol[k]).size()
130 << " returning isotropic " << G4endl;
131 return G4UniformRand()*2.-1.;
132 }
133 }
134 if(fVerbose > 1 && polyPDFCoeffs[polyPDFCoeffs.size()-1] == 0) {
135 G4cout << "G4PolarizationTransition::GenerateGammaCosTheta: WARNING: "
136 << "got zero highest-order coefficient." << G4endl;
138 }
139 kPolyPDF.SetCoefficients(polyPDFCoeffs);
140 return kPolyPDF.GetRandomX();
141}
142
143G4double G4PolarizationTransition::GenerateGammaPhi(G4double& cosTheta,
144 const POLAR& pol)
145{
146 G4int length = (G4int)pol.size();
147 // Isotropic case
148 G4bool phiIsIsotropic = true;
149 for(G4int i=0; i<length; ++i) {
150 if(((pol)[i]).size() > 1) {
151 phiIsIsotropic = false;
152 break;
153 }
154 }
155 if(phiIsIsotropic) { return G4UniformRand()*CLHEP::twopi; }
156
157 map<G4int, map<G4int, G4double> >* cachePtr = nullptr;
158
159 // Otherwise, P(phi) can be written as a sum of cos(kappa phi + phi_kappa).
160 // Calculate the amplitude and phase for each term
161 std::vector<G4double> amp(length, 0.0);
162 std::vector<G4double> phase(length, 0.0);
163 for(G4int kappa = 0; kappa < length; ++kappa) {
164 G4complex cAmpSum(0.,0.);
165 for(G4int k = kappa + (kappa % 2); k < length; k += 2) {
166 G4int kmax = (G4int)pol[k].size();
167 if(kmax > 0) {
168 if(kappa >= kmax || std::abs(((pol)[k])[kappa]) < kEps) { continue; }
170 if(tmpAmp == 0) { continue; }
171 tmpAmp *= std::sqrt((G4double)(2*k+1))
172 * fgLegendrePolys.EvalAssocLegendrePoly(k, kappa, cosTheta, cachePtr);
173 if(kappa > 0) tmpAmp *= 2.*G4Exp(0.5*(LnFactorial(k-kappa) - LnFactorial(k+kappa)));
174 cAmpSum += ((pol)[k])[kappa]*tmpAmp;
175 } else {
176 if(fVerbose > 1) {
177 G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
178 << " size of pol[" << k << "] = " << (pol[k]).size()
179 << " returning isotropic " << G4endl;
180 }
181 return G4UniformRand()*CLHEP::twopi;
182 }
183 }
184 if(fVerbose > 1 && kappa == 0 && std::abs(cAmpSum.imag()) > kEps) {
185 G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
186 << " Got complex amp for kappa = 0! A = " << cAmpSum.real()
187 << " + " << cAmpSum.imag() << "*i" << G4endl;
188 }
189 amp[kappa] = std::abs(cAmpSum);
190 phase[kappa] = arg(cAmpSum);
191 }
192
193 // Normalize PDF and calc max (note: it's not the true max, but the max
194 // assuming that all of the phases line up at a max)
195 G4double pdfMax = 0.;
196 for(G4int kappa = 0; kappa < length; ++kappa) { pdfMax += amp[kappa]; }
197 if(fVerbose > 1 && pdfMax < kEps) {
198 G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
199 << "got pdfMax = 0 for \n";
201 G4cout << "I suspect a non-allowed transition! Returning isotropic phi..."
202 << G4endl;
203 return G4UniformRand()*CLHEP::twopi;
204 }
205
206 // Finally, throw phi until it falls in the pdf
207 for(std::size_t i=0; i<100; ++i) {
208 G4double phi = G4UniformRand()*CLHEP::twopi;
209 G4double prob = G4UniformRand()*pdfMax;
210 G4double pdfSum = amp[0];
211 for(G4int kappa = 1; kappa < length; ++kappa) {
212 pdfSum += amp[kappa]*std::cos(phi*kappa + phase[kappa]);
213 }
214 if(fVerbose > 1 && pdfSum > pdfMax) {
215 G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
216 << "got pdfSum (" << pdfSum << ") > pdfMax ("
217 << pdfMax << ") at phi = " << phi << G4endl;
218 }
219 if(prob <= pdfSum) { return phi; }
220 }
221 if(fVerbose > 1) {
222 G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
223 << "no phi generated in 1000 throws! Returning isotropic phi..."
224 << G4endl;
225 }
226 return G4UniformRand()*CLHEP::twopi;
227}
228
230 G4NuclearPolarization* nucpol,
231 G4int twoJ1, G4int twoJ2,
232 G4int L0, G4int Lp, G4double mpRatio,
233 G4double& cosTheta, G4double& phi)
234{
235 if(nucpol == nullptr) {
236 if(fVerbose > 1) {
237 G4cout << "G4PolarizationTransition::SampleGammaTransition ERROR: "
238 << "cannot update NULL nuclear polarization" << G4endl;
239 }
240 return;
241 }
242 fTwoJ1 = twoJ1; // add abs to remove negative J
243 fTwoJ2 = twoJ2;
244 fLbar = L0;
245 fL = Lp;
246 fDelta = mpRatio;
247 if(fVerbose > 2) {
248 G4cout << "G4PolarizationTransition: 2J1= " << fTwoJ1 << " 2J2= " << fTwoJ2
249 << " Lbar= " << fLbar << " delta= " << fDelta << " Lp= " << fL
250 << G4endl;
251 G4cout << *nucpol << G4endl;
252 }
253
254 const POLAR& pol = nucpol->GetPolarization();
255
256 if(fTwoJ1 == 0) {
257 nucpol->Unpolarize();
258 cosTheta = 2*G4UniformRand() - 1.0;
259 phi = CLHEP::twopi*G4UniformRand();
260 }
261 else {
262 cosTheta = GenerateGammaCosTheta(pol);
263 if(fVerbose > 2) {
264 G4cout << "G4PolarizationTransition::SampleGammaTransition: cosTheta= "
265 << cosTheta << G4endl;
266 }
267 phi = GenerateGammaPhi(cosTheta, pol);
268 if(fVerbose > 2) {
269 G4cout << "G4PolarizationTransition::SampleGammaTransition: phi= "
270 << phi << G4endl;
271 }
272 }
273
274 if(fTwoJ2 == 0) {
275 nucpol->Unpolarize();
276 return;
277 }
278
279 std::size_t newlength = fTwoJ2+1;
280 //POLAR newPol(newlength);
281 POLAR newPol;
282
283 //map<G4int, map<G4int, G4double> > cache;
284 map<G4int, map<G4int, G4double> >* cachePtr = nullptr;
285 //if(newlength > 10 || pol.size() > 10) cachePtr = &cache;
286
287 for(G4int k2=0; k2<(G4int)newlength; ++k2) {
288 std::vector<G4complex> npolar;
289 npolar.resize(k2+1, 0);
290 //(newPol[k2]).assign(k2+1, 0);
291 for(G4int k1=0; k1<(G4int)pol.size(); ++k1) {
292 for(G4int k=0; k<=k1+k2; k+=2) {
293 // TransF3Coefficient takes the most time. Only calculate it once per
294 // (k, k1, k2) triplet, and wait until the last possible moment to do
295 // so. Break out of the inner loops as soon as it is found to be zero.
296 G4double tF3 = 0.;
297 G4bool recalcTF3 = true;
298 for(G4int kappa2=0; kappa2<=k2; ++kappa2) {
299 G4int ll = (G4int)pol[k1].size();
300 for(G4int kappa1 = 1 - ll; kappa1<ll; ++kappa1) {
301 if(k+k2<k1 || k+k1<k2) continue;
302 G4complex tmpAmp = (kappa1 < 0) ?
303 conj((pol[k1])[-kappa1])*(kappa1 % 2 ? -1.: 1.) : (pol[k1])[kappa1];
304 if(std::abs(tmpAmp) < kEps) continue;
305 G4int kappa = kappa1-(G4int)kappa2;
306 tmpAmp *= G4Clebsch::Wigner3J(2*k1, -2*kappa1, 2*k, 2*kappa, 2*k2, 2*kappa2);
307 if(std::abs(tmpAmp) < kEps) continue;
308 if(recalcTF3) {
309 tF3 = GammaTransF3Coefficient(k, k2, k1);
310 recalcTF3 = false;
311 }
312 if(std::abs(tF3) < kEps) break;
313 tmpAmp *= tF3;
314 if(std::abs(tmpAmp) < kEps) continue;
315 tmpAmp *= ((kappa1+(G4int)k1)%2 ? -1. : 1.)
316 * sqrt((2.*k+1.)*(2.*k1+1.)/(2.*k2+1.));
317 //AR-13Jun2017 Useful for debugging very long computations
318 //G4cout << "G4PolarizationTransition::UpdatePolarizationToFinalState : k1=" << k1
319 // << " ; k2=" << k2 << " ; kappa1=" << kappa1 << " ; kappa2=" << kappa2 << G4endl;
320 tmpAmp *= fgLegendrePolys.EvalAssocLegendrePoly(k, kappa, cosTheta, cachePtr);
321 if(kappa != 0) {
322 tmpAmp *= G4Exp(0.5*(LnFactorial(((G4int)k)-kappa)
323 - LnFactorial(((G4int)k)+kappa)));
324 tmpAmp *= polar(1., phi*kappa);
325 }
326 //(newPol[k2])[kappa2] += tmpAmp;
327 npolar[kappa2] += tmpAmp;
328 }
329 if(!recalcTF3 && std::abs(tF3) < kEps) break;
330 }
331 }
332 }
333 newPol.push_back(npolar);
334 }
335
336 // sanity checks
337 if(fVerbose > 2 && 0.0 == newPol[0][0]) {
338 G4cout << "G4PolarizationTransition::SampleGammaTransition WARNING:"
339 << " P[0][0] is zero!" << G4endl;
340 G4cout << "Old pol is: " << *nucpol << G4endl;
341 DumpTransitionData(newPol);
342 G4cout << "Unpolarizing..." << G4endl;
343 nucpol->Unpolarize();
344 return;
345 }
346 if(fVerbose > 2 && std::abs((newPol[0])[0].imag()) > kEps) {
347 G4cout << "G4PolarizationTransition::SampleGammaTransition WARNING: \n"
348 << " P[0][0] has a non-zero imaginary part! Unpolarizing..."
349 << G4endl;
350 nucpol->Unpolarize();
351 return;
352 }
353 if(fVerbose > 2) {
354 G4cout << "Before normalization: " << G4endl;
355 DumpTransitionData(newPol);
356 }
357 // Normalize and trim
358 std::size_t lastNonEmptyK2 = 0;
359 for(std::size_t k2=0; k2<newlength; ++k2) {
360 G4int lastNonZero = -1;
361 for(std::size_t kappa2=0; kappa2<(newPol[k2]).size(); ++kappa2) {
362 if(k2 == 0 && kappa2 == 0) {
363 lastNonZero = 0;
364 continue;
365 }
366 if(std::abs((newPol[k2])[kappa2]) > 0.0) {
367 lastNonZero = (G4int)kappa2;
368 (newPol[k2])[kappa2] /= (newPol[0])[0];
369 }
370 }
371 while((newPol[k2]).size() != std::size_t (lastNonZero+1)) (newPol[k2]).pop_back();
372 if((newPol[k2]).size() > 0) lastNonEmptyK2 = k2;
373 }
374
375 // Remove zero-value entries
376 while(newPol.size() != lastNonEmptyK2+1) { newPol.pop_back(); }
377 (newPol[0])[0] = 1.0;
378
379 nucpol->SetPolarization(newPol);
380 if(fVerbose > 2) {
381 G4cout << "Updated polarization: " << *nucpol << G4endl;
382 }
383}
384
386{
387 G4cout << "G4PolarizationTransition: ";
388 (fTwoJ1 % 2) ? G4cout << fTwoJ1 << "/2" : G4cout << fTwoJ1/2;
389 G4cout << " --(" << fLbar;
390 if(fDelta != 0) G4cout << " + " << fDelta << "*" << fL;
391 G4cout << ")--> ";
392 (fTwoJ2 % 2) ? G4cout << fTwoJ2 << "/2" : G4cout << fTwoJ2/2;
393 G4cout << ", P = [ { ";
394 for(std::size_t k=0; k<pol.size(); ++k) {
395 if(k>0) G4cout << " }, { ";
396 for(std::size_t kappa=0; kappa<(pol[k]).size(); ++kappa) {
397 if(kappa > 0) G4cout << ", ";
398 G4cout << (pol[k])[kappa].real() << " + " << (pol[k])[kappa].imag() << "*i";
399 }
400 }
401 G4cout << " } ]" << G4endl;
402}
403
G4double G4Exp(G4double initial_x)
Exponential Function double precision.
Definition: G4Exp.hh:180
double G4double
Definition: G4Types.hh:83
bool G4bool
Definition: G4Types.hh:86
std::complex< G4double > G4complex
Definition: G4Types.hh:88
int G4int
Definition: G4Types.hh:85
#define G4endl
Definition: G4ios.hh:57
G4GLOB_DLL std::ostream G4cout
#define G4UniformRand()
Definition: Randomize.hh:52
static G4double Wigner3J(G4double j1, G4double j2, G4double j3, G4double m1, G4double m2, G4double m3)
Definition: G4Clebsch.cc:345
static G4double Wigner6J(G4int twoJ1, G4int twoJ2, G4int twoJ3, G4int twoJ4, G4int twoJ5, G4int twoJ6)
Definition: G4Clebsch.cc:432
static G4double Wigner9J(G4int twoJ1, G4int twoJ2, G4int twoJ3, G4int twoJ4, G4int twoJ5, G4int twoJ6, G4int twoJ7, G4int twoJ8, G4int twoJ9)
Definition: G4Clebsch.cc:533
G4double EvalAssocLegendrePoly(G4int l, G4int m, G4double x, std::map< G4int, std::map< G4int, G4double > > *cache=NULL)
G4double GetCoefficient(size_t i, size_t order)
static size_t GetNCoefficients(size_t order)
std::vector< std::vector< G4complex > > & GetPolarization()
void SetPolarization(std::vector< std::vector< G4complex > > &p)
void DumpTransitionData(const POLAR &pol) const
void SampleGammaTransition(G4NuclearPolarization *np, G4int twoJ1, G4int twoJ2, G4int L0, G4int Lp, G4double mpRatio, G4double &cosTheta, G4double &phi)
G4double F3Coefficient(G4int K, G4int K2, G4int K1, G4int L, G4int Lprime, G4int twoJ2, G4int twoJ1) const
G4double FCoefficient(G4int K, G4int L, G4int Lprime, G4int twoJ2, G4int twoJ1) const
G4double GammaTransF3Coefficient(G4int K, G4int K2, G4int K1) const
G4double GammaTransFCoefficient(G4int K) const
void SetCoefficients(const std::vector< G4double > &v)
G4double GetRandomX()