Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/processes/hadronic/models/de_excitation/photon_evaporation/src/G4PolarizationTransition.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  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 
 39 #include "G4PolarizationTransition.hh"
 40 #include "G4Clebsch.hh"
 41 #include "G4PolynomialPDF.hh"
 42 #include "G4Fragment.hh"
 43 #include "G4NuclearPolarization.hh"
 44 #include "G4SystemOfUnits.hh"
 45 #include "G4Exp.hh"
 46 
 47 using namespace std;
 48 
 49 G4PolarizationTransition::G4PolarizationTransition() 
 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 
 54 G4double G4PolarizationTransition::FCoefficient(G4int K, G4int LL, G4int Lprime, 
 55             G4int twoJ2, G4int twoJ1) const
 56 {
 57   G4double fCoeff = G4Clebsch::Wigner3J(2*LL, 2, 2*Lprime, -2, 2*K, 0);
 58   if(fCoeff == 0) return 0;
 59   fCoeff *= G4Clebsch::Wigner6J(2*LL, 2*Lprime, 2*K, twoJ1, twoJ1, twoJ2);
 60   if(fCoeff == 0) return 0;
 61   if(((twoJ1+twoJ2)/2 - 1) %2) fCoeff = -fCoeff;
 62   return fCoeff*std::sqrt(G4double((2*K+1)*(twoJ1+1)*(2*LL+1)*(2*Lprime+1)));
 63 }
 64 
 65 G4double G4PolarizationTransition::F3Coefficient(G4int K, G4int K2, G4int K1, 
 66              G4int LL, G4int Lprime, 
 67              G4int twoJ2, G4int twoJ1) const
 68 {
 69   G4double fCoeff = G4Clebsch::Wigner3J(2*LL, 2, 2*Lprime, -2, 2*K, 0);
 70   if(fCoeff == 0) return 0;
 71   fCoeff *= G4Clebsch::Wigner9J(twoJ2, 2*LL, twoJ1, twoJ2, 2*Lprime, twoJ1, 
 72         2*K2, 2*K, 2*K1);
 73   if(fCoeff == 0) return 0;
 74   if((Lprime+K2+K1+1) % 2) fCoeff = -fCoeff;
 75 
 76   //AR-13Jun2017 : apply Jason Detwiler's conversion to double 
 77   //               in the argument of sqrt() to avoid integer overflows.
 78   return fCoeff*std::sqrt(G4double((twoJ1+1)*(twoJ2+1)*(2*LL+1))
 79         *G4double((2*Lprime+1)*(2*K+1)*(2*K1+1)*(2*K2+1)));
 80 }
 81 
 82 G4double G4PolarizationTransition::GammaTransFCoefficient(G4int K) const
 83 {
 84   double transFCoeff = FCoefficient(K, fLbar, fLbar, fTwoJ2, fTwoJ1);
 85   if(fDelta == 0) return transFCoeff;
 86   transFCoeff += 2.*fDelta*FCoefficient(K, fLbar, fL, fTwoJ2, fTwoJ1);
 87   transFCoeff += fDelta*fDelta*FCoefficient(K, fL, fL, fTwoJ2, fTwoJ1);
 88   return transFCoeff;
 89 }
 90 
 91 G4double G4PolarizationTransition::GammaTransF3Coefficient(G4int K, G4int K2, 
 92                  G4int K1) const
 93 {
 94   double transF3Coeff = F3Coefficient(K, K2, K1, fLbar, fLbar, fTwoJ2, fTwoJ1);
 95   if(fDelta == 0) return transF3Coeff;
 96   transF3Coeff += 2.*fDelta*F3Coefficient(K, K2, K1, fLbar, fL, fTwoJ2, fTwoJ1);
 97   transF3Coeff += fDelta*fDelta*F3Coefficient(K, K2, K1, fL, fL, fTwoJ2, fTwoJ1);
 98   return transF3Coeff;
 99 }
100 
101 G4double G4PolarizationTransition::GenerateGammaCosTheta(const POLAR& pol) 
102 {
103   std::size_t length = pol.size();
104   // Isotropic case
105   if(length <= 1) return G4UniformRand()*2.-1.;
106 
107   // kappa > 0 terms integrate out to zero over phi: 0->2pi, so just need (k,0)
108   // terms to generate cos theta distribution
109   std::vector<G4double> polyPDFCoeffs(length, 0.0);
110   for(G4int k = 0; k < (G4int)length; k += 2) {
111     if ((pol[k]).size() > 0 ) {
112       if(fVerbose > 1 && std::abs(((pol)[k])[0].imag()) > kEps) {
113         G4cout << "G4PolarizationTransition::GenerateGammaCosTheta WARNING: \n"
114        << "          fPolarization[" 
115        << k << "][0] has imag component: = " 
116        << ((pol)[k])[0].real() << " + " 
117          << ((pol)[k])[0].imag() << "*i" << G4endl;
118       }
119       G4double a_k = std::sqrt((G4double)(2*k+1))*GammaTransFCoefficient(k)*((pol)[k])[0].real();
120       std::size_t nCoeff = fgLegendrePolys.GetNCoefficients(k);
121       for(std::size_t iCoeff=0; iCoeff < nCoeff; ++iCoeff) {
122         polyPDFCoeffs[iCoeff] += a_k*fgLegendrePolys.GetCoefficient(iCoeff, k);
123       }
124     } else {
125       G4cout << "G4PolarizationTransition::GenerateGammaCosTheta: WARNING: \n"
126              << " size of pol[" << k << "] = " << (pol[k]).size()
127              << " returning isotropic " << G4endl;
128       return G4UniformRand()*2.-1.; 
129     }
130   }
131   if(fVerbose > 1 && polyPDFCoeffs[polyPDFCoeffs.size()-1] == 0) {
132     G4cout << "G4PolarizationTransition::GenerateGammaCosTheta: WARNING: "
133            << "got zero highest-order coefficient." << G4endl;
134     DumpTransitionData(pol);
135   }
136   kPolyPDF.SetCoefficients(polyPDFCoeffs);
137   return kPolyPDF.GetRandomX();
138 }
139 
140 G4double G4PolarizationTransition::GenerateGammaPhi(G4double& cosTheta,
141                 const POLAR& pol)
142 {
143   G4int length = (G4int)pol.size();
144   // Isotropic case
145   G4bool phiIsIsotropic = true;
146   for(G4int i=0; i<length; ++i) {
147     if(((pol)[i]).size() > 1) {
148       phiIsIsotropic = false;
149       break;
150     }
151   }
152   if(phiIsIsotropic) { return G4UniformRand()*CLHEP::twopi; }
153   // Otherwise, P(phi) can be written as a sum of cos(kappa phi + phi_kappa).
154   // Calculate the amplitude and phase for each term
155   std::vector<G4double> amp(length, 0.0);
156   std::vector<G4double> phase(length, 0.0);
157   for(G4int kappa = 0; kappa < length; ++kappa) {
158     G4complex cAmpSum(0.,0.);
159     for(G4int k = kappa + (kappa % 2); k < length; k += 2) {
160       G4int kmax = (G4int)pol[k].size();
161       if(kmax > 0) {
162   if(kappa >= kmax || std::abs(((pol)[k])[kappa]) < kEps) { continue; }
163         G4double tmpAmp = GammaTransFCoefficient(k);
164         if(tmpAmp == 0) { continue; }
165         tmpAmp *= std::sqrt((G4double)(2*k+1)) 
166     * fgLegendrePolys.EvalAssocLegendrePoly(k, kappa, cosTheta);
167         if(kappa > 0) tmpAmp *= 2.*G4Exp(0.5*(LnFactorial(k-kappa) - LnFactorial(k+kappa)));
168         cAmpSum += ((pol)[k])[kappa]*tmpAmp;
169       } else {
170   if(fVerbose > 1) {
171     G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
172      << " size of pol[" << k << "] = " << (pol[k]).size()
173      << " returning isotropic " << G4endl;
174   }
175         return G4UniformRand()*CLHEP::twopi;
176       }
177     }
178     if(fVerbose > 1 && kappa == 0 && std::abs(cAmpSum.imag()) > kEps) {
179       G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
180        << "    Got complex amp for kappa = 0! A = " << cAmpSum.real() 
181        << " + " << cAmpSum.imag() << "*i" << G4endl;
182     }
183     amp[kappa] = std::abs(cAmpSum);
184     phase[kappa] = arg(cAmpSum);
185   }
186 
187   // Normalize PDF and calc max (note: it's not the true max, but the max
188   // assuming that all of the phases line up at a max)
189   G4double pdfMax = 0.;
190   for(G4int kappa = 0; kappa < length; ++kappa) { pdfMax += amp[kappa]; }
191   if(fVerbose > 1 && pdfMax < kEps) {
192     G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING "
193      << "got pdfMax = 0 for \n";
194     DumpTransitionData(pol);
195     G4cout << "I suspect a non-allowed transition! Returning isotropic phi..." 
196      << G4endl;
197     return G4UniformRand()*CLHEP::twopi;
198   }
199 
200   // Finally, throw phi until it falls in the pdf
201   for(std::size_t i=0; i<100; ++i) {
202     G4double phi = G4UniformRand()*CLHEP::twopi;
203     G4double prob = G4UniformRand()*pdfMax;
204     G4double pdfSum = amp[0];
205     for(G4int kappa = 1; kappa < length; ++kappa) {
206       pdfSum += amp[kappa]*std::cos(phi*kappa + phase[kappa]);
207     }
208     if(fVerbose > 1 && pdfSum > pdfMax) {
209       G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
210              << "got pdfSum (" << pdfSum << ") > pdfMax (" 
211        << pdfMax << ") at phi = " << phi << G4endl;
212     }
213     if(prob <= pdfSum) { return phi; }
214   }
215   if(fVerbose > 1) {
216     G4cout << "G4PolarizationTransition::GenerateGammaPhi: WARNING: \n"
217      << "no phi generated in 1000 throws! Returning isotropic phi..." 
218            << G4endl;
219   }
220   return G4UniformRand()*CLHEP::twopi;
221 }
222 
223 void G4PolarizationTransition::SampleGammaTransition(
224                    G4NuclearPolarization* nucpol, 
225        G4int twoJ1, G4int twoJ2, 
226                    G4int L0, G4int Lp, G4double mpRatio, 
227        G4double& cosTheta, G4double& phi)
228 {
229   if(nucpol == nullptr) {
230     if(fVerbose > 1) {
231       G4cout << "G4PolarizationTransition::SampleGammaTransition ERROR: "
232              << "cannot update NULL nuclear polarization" << G4endl;
233     }
234     return;
235   }
236   fTwoJ1 = twoJ1;  // add abs to remove negative J
237   fTwoJ2 = twoJ2;
238   fLbar  = L0;
239   fL     = Lp;
240   fDelta = mpRatio;
241   if(fVerbose > 2) {
242     G4cout << "G4PolarizationTransition: 2J1= " << fTwoJ1 << " 2J2= " << fTwoJ2
243      << " Lbar= " << fLbar << " delta= " << fDelta << " Lp= " << fL 
244            << G4endl;
245     G4cout << *nucpol << G4endl;
246   }
247 
248   const POLAR& pol = nucpol->GetPolarization();
249 
250   if(fTwoJ1 == 0) {
251     nucpol->Unpolarize();
252     cosTheta = 2*G4UniformRand() - 1.0;
253     phi = CLHEP::twopi*G4UniformRand();
254   }
255   else {
256     cosTheta = GenerateGammaCosTheta(pol);
257     phi = GenerateGammaPhi(cosTheta, pol);
258     if (fVerbose > 2) {
259       G4cout << "G4PolarizationTransition::SampleGammaTransition: cosTheta= "
260        << cosTheta << " phi= " << phi << G4endl;
261     }
262   }
263 
264   if(fTwoJ2 == 0) {
265     nucpol->Unpolarize();
266     return;
267   }
268 
269   std::size_t newlength = fTwoJ2+1;
270   //POLAR newPol(newlength);
271   POLAR newPol;
272 
273   for(G4int k2=0; k2<(G4int)newlength; ++k2) {
274     std::vector<G4complex> npolar;
275     npolar.resize(k2+1, 0);
276     //(newPol[k2]).assign(k2+1, 0);
277     for(G4int k1=0; k1<(G4int)pol.size(); ++k1) {
278       for(G4int k=0; k<=k1+k2; k+=2) {
279         // TransF3Coefficient takes the most time. Only calculate it once per
280         // (k, k1, k2) triplet, and wait until the last possible moment to do
281         // so. Break out of the inner loops as soon as it is found to be zero.
282         G4double tF3 = 0.;
283         G4bool recalcTF3 = true;
284         for(G4int kappa2=0; kappa2<=k2; ++kappa2) {
285           G4int ll = (G4int)pol[k1].size();
286           for(G4int kappa1 = 1 - ll; kappa1<ll; ++kappa1) {
287             if(k+k2<k1 || k+k1<k2) continue;
288             G4complex tmpAmp = (kappa1 < 0) ?  
289         conj((pol[k1])[-kappa1])*(kappa1 % 2 ? -1.: 1.) : (pol[k1])[kappa1];
290             if(std::abs(tmpAmp) < kEps) continue;
291             G4int kappa = kappa1-(G4int)kappa2;
292             tmpAmp *= G4Clebsch::Wigner3J(2*k1, -2*kappa1, 2*k, 2*kappa, 2*k2, 2*kappa2);
293             if(std::abs(tmpAmp) < kEps) continue;
294             if(recalcTF3) {
295               tF3 = GammaTransF3Coefficient(k, k2, k1);
296               recalcTF3 = false;
297             }
298             if(std::abs(tF3) < kEps) break;
299             tmpAmp *= tF3;
300             if(std::abs(tmpAmp) < kEps) continue;
301             tmpAmp *= ((kappa1+(G4int)k1)%2 ? -1. : 1.) 
302         * sqrt((2.*k+1.)*(2.*k1+1.)/(2.*k2+1.));
303             //AR-13Jun2017 Useful for debugging very long computations
304             //G4cout << "G4PolarizationTransition::UpdatePolarizationToFinalState : k1=" << k1 
305             //       << " ; k2=" << k2 << " ; kappa1=" << kappa1 << " ; kappa2=" << kappa2 << G4endl;
306             tmpAmp *= fgLegendrePolys.EvalAssocLegendrePoly(k, kappa, cosTheta);
307             if(kappa != 0) {
308               tmpAmp *= G4Exp(0.5*(LnFactorial(((G4int)k)-kappa) 
309            - LnFactorial(((G4int)k)+kappa)));
310               tmpAmp *= polar(1., phi*kappa);
311             }
312             npolar[kappa2] += tmpAmp;
313           }
314           if(!recalcTF3 && std::abs(tF3) < kEps) break;
315         }
316       }
317     }
318     newPol.push_back(std::move(npolar));
319   }
320 
321   // sanity checks
322   if(fVerbose > 2 && 0.0 == newPol[0][0]) {
323     G4cout << "G4PolarizationTransition::SampleGammaTransition WARNING:"
324      << " P[0][0] is zero!" << G4endl;
325     G4cout << "Old pol is: " << *nucpol << G4endl;
326     DumpTransitionData(newPol);
327     G4cout << "Unpolarizing..." << G4endl;
328     nucpol->Unpolarize();
329     return;
330   }
331   if(fVerbose > 2 && std::abs((newPol[0])[0].imag()) > kEps) {
332     G4cout << "G4PolarizationTransition::SampleGammaTransition WARNING: \n"
333      << " P[0][0] has a non-zero imaginary part! Unpolarizing..." 
334      << G4endl;
335     nucpol->Unpolarize();
336     return;
337   }
338   if(fVerbose > 2) {
339     G4cout << "Before normalization: " << G4endl;
340     DumpTransitionData(newPol);
341   }
342   // Normalize and trim
343   std::size_t lastNonEmptyK2 = 0;
344   for(std::size_t k2=0; k2<newlength; ++k2) {
345     G4int lastNonZero = -1;
346     for(std::size_t kappa2=0; kappa2<(newPol[k2]).size(); ++kappa2) {
347       if(k2 == 0 && kappa2 == 0) {
348         lastNonZero = 0;
349         continue;
350       }
351       if(std::abs((newPol[k2])[kappa2]) > 0.0) {
352         lastNonZero = (G4int)kappa2;
353         (newPol[k2])[kappa2] /= (newPol[0])[0];
354       }
355     }
356     while((newPol[k2]).size() != std::size_t (lastNonZero+1)) (newPol[k2]).pop_back();
357     if((newPol[k2]).size() > 0) lastNonEmptyK2 = k2;
358   }
359 
360   // Remove zero-value entries 
361   while(newPol.size() != lastNonEmptyK2+1) { newPol.pop_back(); }
362   (newPol[0])[0] = 1.0;
363 
364   nucpol->SetPolarization(newPol);
365   if(fVerbose > 2) {
366     G4cout << "Updated polarization: " << *nucpol << G4endl;
367   }
368 }
369 
370 void G4PolarizationTransition::DumpTransitionData(const POLAR& pol) const
371 {
372   G4cout << "G4PolarizationTransition: ";
373   (fTwoJ1 % 2) ? G4cout << fTwoJ1 << "/2" : G4cout << fTwoJ1/2;
374   G4cout << " --(" << fLbar;
375   if(fDelta != 0) G4cout << " + " << fDelta << "*" << fL;
376   G4cout << ")--> ";
377   (fTwoJ2 % 2) ? G4cout << fTwoJ2 << "/2" : G4cout << fTwoJ2/2;
378   G4cout << ", P = [ { ";
379   for(std::size_t k=0; k<pol.size(); ++k) {
380     if(k>0) G4cout << " }, { ";
381     for(std::size_t kappa=0; kappa<(pol[k]).size(); ++kappa) {
382       if(kappa > 0) G4cout << ", ";
383       G4cout << (pol[k])[kappa].real() << " + " << (pol[k])[kappa].imag() << "*i";
384     }
385   }
386   G4cout << " } ]" << G4endl;
387 }
388 
389