Geant4  10.03
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 (jasondet@gmail.com)
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"
43 #include "G4NuclearPolarization.hh"
44 #include "G4SystemOfUnits.hh"
45 
46 using namespace std;
47 
49  : fTwoJ1(0), fTwoJ2(0), fLbar(1), fL(0), fDelta(0), kEps(1.e-15),
50  kPolyPDF(0, nullptr, -1, 1)
51 {}
52 
54 {}
55 
57  G4int twoJ2, G4int twoJ1) const
58 {
59  G4double fCoeff = G4Clebsch::Wigner3J(LL, Lprime, K, 1, -1, 0);
60  if(fCoeff == 0) return 0;
61  fCoeff *= G4Clebsch::Wigner6J(2*LL, 2*Lprime, 2*K, twoJ1, twoJ1, twoJ2);
62  if(fCoeff == 0) return 0;
63  if(((twoJ1+twoJ2)/2 - 1) %2) fCoeff = -fCoeff;
64  return fCoeff*sqrt((2*K+1)*(twoJ1+1)*(2*LL+1)*(2*Lprime+1));
65 }
66 
68  G4int LL, G4int Lprime,
69  G4int twoJ2, G4int twoJ1) const
70 {
71  G4double fCoeff = G4Clebsch::Wigner3J(LL, Lprime, K, 1, -1, 0);
72  if(fCoeff == 0) return 0;
73  fCoeff *= G4Clebsch::Wigner9J(twoJ2, 2*LL, twoJ1, twoJ2, 2*Lprime, twoJ1,
74  2*K2, 2*K, 2*K1);
75  if(fCoeff == 0) return 0;
76  if((Lprime+K2+K1+1) % 2) fCoeff = -fCoeff;
77  return fCoeff*sqrt((twoJ1+1)*(twoJ2+1)*(2*LL+1)*(2*Lprime+1)*(2*K+1)*(2*K1+1)*(2*K2+1));
78 }
79 
81  G4int Lbar, G4double delta,
82  G4int Lprime)
83 {
84  fTwoJ1 = twoJ1;
85  fTwoJ2 = twoJ2;
86  fLbar = Lbar;
87  fDelta = delta;
88  fL = Lprime;
89 }
90 
92 {
93  double transFCoeff = FCoefficient(K, fLbar, fLbar, fTwoJ2, fTwoJ1);
94  if(fDelta == 0) return transFCoeff;
95  transFCoeff += 2.*fDelta*FCoefficient(K, fLbar, fL, fTwoJ2, fTwoJ1);
96  transFCoeff += fDelta*fDelta*FCoefficient(K, fL, fL, fTwoJ2, fTwoJ1);
97  return transFCoeff;
98 }
99 
101  G4int K1) const
102 {
103  double transF3Coeff = F3Coefficient(K, K2, K1, fLbar, fLbar, fTwoJ2, fTwoJ1);
104  if(fDelta == 0) return transF3Coeff;
105  transF3Coeff += 2.*fDelta*F3Coefficient(K, K2, K1, fLbar, fL, fTwoJ2, fTwoJ1);
106  transF3Coeff += fDelta*fDelta*F3Coefficient(K, K2, K1, fL, fL, fTwoJ2, fTwoJ1);
107  return transF3Coeff;
108 }
109 
111 {
112  size_t length = pol.size();
113  // Isotropic case
114  if(length == 1) return G4UniformRand()*2.-1.;
115 
116  // kappa > 0 terms integrate out to zero over phi: 0->2pi, so just need (k,0)
117  // terms to generate cos theta distribution
118  vector<G4double> polyPDFCoeffs(length, 0.0);
119  for(size_t k = 0; k < length; k += 2) {
120  if(std::abs(((pol)[k])[0].imag()) > kEps) {
121  G4cout << "Warning: fPolarization[" << k << "][0] has imag component: = "
122  << ((pol)[k])[0].real() << " + "
123  << ((pol)[k])[0].imag() << "*i" << G4endl;
124  }
125  G4double a_k = sqrt(2*k+1)*GammaTransFCoefficient(k)*((pol)[k])[0].real();
126  for(size_t iCoeff=0; iCoeff < fgLegendrePolys.GetNCoefficients(k); ++iCoeff) {
127  polyPDFCoeffs[iCoeff] += a_k*fgLegendrePolys.GetCoefficient(iCoeff, k);
128  }
129  }
130  kPolyPDF.SetCoefficients(polyPDFCoeffs);
131  return kPolyPDF.GetRandomX();
132 }
133 
135  const POLAR& pol)
136 {
137  size_t length = pol.size();
138  // Isotropic case
139  G4bool phiIsIsotropic = true;
140  for(size_t i=0; i<length; ++i) {
141  if(((pol)[i]).size() > 1) {
142  phiIsIsotropic = false;
143  break;
144  }
145  }
146  if(phiIsIsotropic) { return G4UniformRand()*CLHEP::twopi; }
147 
148  // Otherwise, P(phi) can be written as a sum of cos(kappa phi + phi_kappa).
149  // Calculate the amplitude and phase for each term
150  vector<G4double> amp(length, 0.0);
151  vector<G4double> phase(length, 0.0);
152  for(size_t kappa = 0; kappa < length; ++kappa) {
153  G4complex cAmpSum = 0.;
154  for(size_t k = kappa + (kappa % 2); k < length; k += 2) {
155  if(kappa >= length || std::abs(((pol)[k])[kappa]) < kEps) { continue; }
156  G4double tmpAmp = GammaTransFCoefficient(k);
157  if(tmpAmp == 0) { continue; }
158  tmpAmp *= sqrt(2*k+1) * fgLegendrePolys.EvalAssocLegendrePoly(k, kappa, cosTheta);
159  if(kappa > 0) tmpAmp *= 2.*exp(0.5*(LnFactorial(k-kappa) - LnFactorial(k+kappa)));
160  cAmpSum += ((pol)[k])[kappa]*tmpAmp;
161  }
162  if(kappa == 0 && std::abs(cAmpSum.imag()) > kEps) {
163  G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
164  << " got complex amp for kappa = 0! A = " << cAmpSum.real()
165  << " + " << cAmpSum.imag() << "*i" << G4endl;
166  }
167  amp[kappa] = std::abs(cAmpSum);
168  if(amp[kappa] < 0) {
169  G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
170  << "got negative abs for kappa = " << kappa << G4endl;
171  }
172  phase[kappa] = arg(cAmpSum);
173  }
174 
175  // Normalize PDF and calc max (note: it's not the true max, but the max
176  // assuming that all of the phases line up at a max)
177  G4double pdfMax = 0;
178  for(size_t kappa = 0; kappa < amp.size(); ++kappa) { pdfMax += amp[kappa]; }
179  if(pdfMax < kEps) {
180  G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
181  << "got pdfMax = 0 for ";
182  DumpTransitionData(pol);
183  G4cout << "I suspect a non-allowed transition! Returning isotropic phi..."
184  << G4endl;
185  return G4UniformRand()*CLHEP::twopi;
186  }
187 
188  // Finally, throw phi until it falls in the pdf
189  for(size_t i=0; i<1000; ++i) {
191  G4double prob = G4UniformRand()*pdfMax;
192  G4double pdfSum = amp[0];
193  for(size_t kappa = 1; kappa < amp.size(); ++kappa) {
194  pdfSum += amp[kappa]*cos(phi*kappa + phase[kappa]);
195  }
196  if(prob < pdfSum) return phi;
197  if(pdfSum > pdfMax) {
198  G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
199  << "got pdfSum (" << pdfSum << ") > pdfMax ("
200  << pdfMax << ") at phi = " << phi << G4endl;
201  }
202  }
203 
204  G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
205  << "no phi generated in 1000 throws! Returning isotropic phi..." << G4endl;
206  return G4UniformRand()*CLHEP::twopi;
207 }
208 
210  G4double phi,
211  G4Fragment* frag)
212 {
213  if(fTwoJ2 == 0) {
214  frag->SetNuclearPolarization(nullptr);
215  return;
216  }
217  POLAR pol;
219  if(nucpol) { pol = nucpol->GetPolarization(); }
220 
221  size_t length = pol.size();
222 
223  size_t newlength = fTwoJ2+1;
224  POLAR newPol;
225  newPol.resize(newlength);
226 
227  for(size_t k2=0; k2<newlength; ++k2) {
228  (newPol[k2]).assign(k2+1, 0);
229  for(size_t k1=0; k1<length; ++k1) {
230  for(size_t k=0; k<=k1+k2; k+=2) {
231  // TransF3Coefficient takes the most time. Only calculate it once per
232  // (k, k1, k2) triplet, and wait until the last possible moment to do
233  // so. Break out of the inner loops as soon as it is found to be zero.
234  G4double tF3 = 0.;
235  G4bool recalcTF3 = true;
236  for(size_t kappa2=0; kappa2<=k2; ++kappa2) {
237  G4int ll = (pol[k1]).size();
238  for(G4int kappa1 = 1 - ll; kappa1<ll; ++kappa1) {
239  if(k+k2<k1 || k+k1<k2) continue;
240  G4complex tmpAmp = (kappa1 < 0) ?
241  conj((pol[k1])[-kappa1])*(kappa1 % 2 ? -1.: 1.) : (pol[k1])[kappa1];
242  if(std::abs(tmpAmp) < kEps) continue;
243  G4int kappa = kappa1-(G4int)kappa2;
244  tmpAmp *= G4Clebsch::Wigner3J(k1, k, k2, -kappa1, kappa, kappa2);
245  if(std::abs(tmpAmp) < kEps) continue;
246  if(recalcTF3) {
247  tF3 = GammaTransF3Coefficient(k, k2, k1);
248  recalcTF3 = false;
249  }
250  if(std::abs(tF3) < kEps) break;
251  tmpAmp *= GammaTransF3Coefficient(k, k2, k1);
252  if(std::abs(tmpAmp) < kEps) continue;
253  tmpAmp *= ((kappa1+(G4int)k1)%2 ? -1. : 1.)
254  * sqrt((2.*k+1.)*(2.*k1+1.)/(2.*k2+1.));
255  tmpAmp *= fgLegendrePolys.EvalAssocLegendrePoly(k, kappa, cosTheta);
256  if(kappa != 0) {
257  tmpAmp *= G4Exp(0.5*(LnFactorial(((G4int)k)-kappa)
258  - LnFactorial(((G4int)k)+kappa)));
259  tmpAmp *= polar(1., phi*kappa);
260  }
261  (newPol[k2])[kappa2] += tmpAmp;
262  }
263  if(!recalcTF3 && std::abs(tF3) < kEps) break;
264  }
265  }
266  }
267  }
268 
269  // sanity checks
270  if(0.0 == newPol[0][0]) {
271  frag->SetNuclearPolarization(nullptr);
272  return;
273  }
274  if(std::abs((newPol[0])[0].imag()) > kEps) {
275  G4cout << "G4PolarizationTransition::UpdatePolarizationToFinalState WWARNING:"
276  << " P[0][0] has a non-zero imaginary part! Unpolarizing..." << G4endl;
277  frag->SetNuclearPolarization(nullptr);
278  return;
279  }
280 
281  // Normalize and trim
282  size_t lastNonEmptyK2 = 0;
283  for(size_t k2=0; k2<newlength; ++k2) {
284  G4int lastNonZero = -1;
285  for(size_t kappa2=0; kappa2<(newPol[k2]).size(); ++kappa2) {
286  if(k2 == 0 && kappa2 == 0) {
287  lastNonZero = 0;
288  continue;
289  }
290  if(std::abs((newPol[k2])[kappa2]) > 0.0) {
291  lastNonZero = kappa2;
292  (newPol[k2])[kappa2] /= (newPol[0])[0];
293  }
294  }
295  while((newPol[k2]).size() != size_t (lastNonZero+1)) (newPol[k2]).pop_back();
296  if((newPol[k2]).size() > 0) lastNonEmptyK2 = k2;
297  }
298  while(newPol.size() != lastNonEmptyK2+1) { newPol.pop_back(); }
299  (newPol[0])[0] = 1.0;
300 
301  if(!nucpol) {
302  nucpol = new G4NuclearPolarization();
303  nucpol->SetPolarization(newPol);
304  frag->SetNuclearPolarization(nucpol);
305  } else {
306  nucpol->SetPolarization(newPol);
307  }
308 }
309 
311 {
312  G4cout << "G4PolarizationTransition transition: ";
313  (fTwoJ1 % 2) ? G4cout << fTwoJ1 << "/2" : G4cout << fTwoJ1/2;
314  G4cout << " --(" << fLbar;
315  if(fDelta > 0) G4cout << " + " << fDelta << "*" << fL;
316  G4cout << ")--> ";
317  (fTwoJ2 % 2) ? G4cout << fTwoJ2 << "/2" : G4cout << fTwoJ2/2;
318  G4cout << ", P = [ { ";
319  for(size_t k=0; k<pol.size(); ++k) {
320  if(k>0) G4cout << " }, { ";
321  for(size_t kappa=0; kappa<(pol[k]).size(); ++kappa) {
322  if(kappa > 0) G4cout << ", ";
323  G4cout << (pol[k])[kappa].real() << " + " << (pol[k])[kappa].imag() << "*i";
324  }
325  }
326  G4cout << " } ]" << G4endl;
327 }
328 
G4double FCoefficient(G4int K, G4int L, G4int Lprime, G4int twoJ2, G4int twoJ1) const
void SetCoefficients(const std::vector< G4double > &v)
G4double GetRandomX()
G4double GenerateGammaPhi(G4double cosTheta, const POLAR &)
G4double GammaTransF3Coefficient(G4int K, G4int K2, G4int K1) const
G4double LnFactorial(int k) const
static size_t GetNCoefficients(size_t order)
G4double GenerateGammaCosTheta(const POLAR &)
int G4int
Definition: G4Types.hh:78
static constexpr double twopi
Definition: G4SIunits.hh:76
std::vector< std::vector< G4complex > > POLAR
G4LegendrePolynomial fgLegendrePolys
std::complex< G4double > G4complex
Definition: G4Types.hh:81
#define G4UniformRand()
Definition: Randomize.hh:97
G4GLOB_DLL std::ostream G4cout
void SetNuclearPolarization(G4NuclearPolarization *)
Definition: G4Fragment.hh:213
std::vector< std::vector< G4complex > > & GetPolarization()
bool G4bool
Definition: G4Types.hh:79
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 GammaTransFCoefficient(G4int K) const
void SetGammaTransitionData(G4int twoJ1, G4int twoJ2, G4int Lbar, G4double delta=0, G4int Lprime=1)
G4double G4Exp(G4double initial_x)
Exponential Function double precision.
Definition: G4Exp.hh:183
G4double F3Coefficient(G4int K, G4int K2, G4int K1, G4int L, G4int Lprime, G4int twoJ2, G4int twoJ1) const
static G4double Wigner3J(G4double j1, G4double j2, G4double j3, G4double m1, G4double m2, G4double m3)
Definition: G4Clebsch.cc:345
G4double EvalAssocLegendrePoly(G4int l, G4int m, G4double x)
void UpdatePolarizationToFinalState(G4double cosTheta, G4double phi, G4Fragment *)
static const G4int LL[nN]
void DumpTransitionData(const POLAR &pol) const
#define G4endl
Definition: G4ios.hh:61
static G4double Wigner6J(G4int twoJ1, G4int twoJ2, G4int twoJ3, G4int twoJ4, G4int twoJ5, G4int twoJ6)
Definition: G4Clebsch.cc:432
double G4double
Definition: G4Types.hh:76
G4NuclearPolarization * GetNuclearPolarization() const
Definition: G4Fragment.hh:430
G4double GetCoefficient(size_t i, size_t order)
void SetPolarization(std::vector< std::vector< G4complex > > &p)