Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/processes/hadronic/models/particle_hp/src/G4FissionProductYieldDist.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 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 /*
 27  * File:   G4FissionProductYieldDist.cc
 28  * Author: B. Wendt (wendbryc@isu.edu)
 29  *
 30  * Created on May 11, 2011, 12:04 PM
 31  */
 32 
 33 /* * * * * * * * * * * * * * * *   References   * * * * * * * * * * * * * * * *
 34  *                                                                            *
 35  *  1.  "Systematics of fission fragment total kinetic energy release",       *
 36  *      V. E. Viola, K. Kwiatkowski, and M. Walker, Physical Review C, 31.4,  *
 37  *      April 1985                                                            *
 38  *  2.  "Reactor Handbook", United States Atomic Energy Commission,           *
 39  *      III.A:Physics, 1962                                                   *
 40  *  3.  "Properties of the Alpha Particles Emitted in the Spontaneous Fission *
 41  *      of Cf252", Z. Fraenkel and S. G. Thompson, Physical Review Letters,   *
 42  *      13.14, October 1964                                                   *
 43  *                                                                            *
 44  * * * * * * * * * * * * * * * *   References   * * * * * * * * * * * * * * * */
 45 
 46 // #include <ios>
 47 // #include <iostream>
 48 
 49 #include "G4Alpha.hh"
 50 #include "G4Gamma.hh"
 51 #include "G4Ions.hh"
 52 #include "G4Neutron.hh"
 53 // #include "G4NeutronHPNames.hh"
 54 #include "G4ArrayOps.hh"
 55 #include "G4DynamicParticle.hh"
 56 #include "G4DynamicParticleVector.hh"
 57 #include "G4ENDFTapeRead.hh"
 58 #include "G4ENDFYieldDataContainer.hh"
 59 #include "G4Exp.hh"
 60 #include "G4FFGDebuggingMacros.hh"
 61 #include "G4FFGDefaultValues.hh"
 62 #include "G4FFGEnumerations.hh"
 63 #include "G4FPYNubarValues.hh"
 64 #include "G4FPYSamplingOps.hh"
 65 #include "G4FPYTreeStructures.hh"
 66 #include "G4FissionProductYieldDist.hh"
 67 #include "G4HadFinalState.hh"
 68 #include "G4NucleiProperties.hh"
 69 #include "G4ParticleTable.hh"
 70 #include "G4Pow.hh"
 71 #include "G4ReactionProduct.hh"
 72 #include "G4SystemOfUnits.hh"
 73 #include "G4ThreeVector.hh"
 74 #include "G4UImanager.hh"
 75 #include "Randomize.hh"
 76 #include "globals.hh"
 77 
 78 using CLHEP::pi;
 79 
 80 #ifdef G4MULTITHREADED
 81 #  include "G4AutoLock.hh"
 82 G4Mutex G4FissionProductYieldDist::fissprodMutex = G4MUTEX_INITIALIZER;
 83 #endif
 84 
 85 G4FissionProductYieldDist::G4FissionProductYieldDist(G4int WhichIsotope,
 86                                                      G4FFGEnumerations::MetaState WhichMetaState,
 87                                                      G4FFGEnumerations::FissionCause WhichCause,
 88                                                      G4FFGEnumerations::YieldType WhichYieldType,
 89                                                      std::istringstream& dataStream)
 90   : Isotope_(WhichIsotope),
 91     MetaState_(WhichMetaState),
 92     Cause_(WhichCause),
 93     YieldType_(WhichYieldType),
 94     Verbosity_(G4FFGDefaultValues::Verbosity)
 95 {
 96   G4FFG_FUNCTIONENTER__
 97 
 98   try {
 99     // Initialize the class
100     Initialize(dataStream);
101   }
102   catch (std::exception& e) {
103     G4FFG_FUNCTIONLEAVE__
104     throw e;
105   }
106 
107   G4FFG_FUNCTIONLEAVE__
108 }
109 
110 G4FissionProductYieldDist::G4FissionProductYieldDist(G4int WhichIsotope,
111                                                      G4FFGEnumerations::MetaState WhichMetaState,
112                                                      G4FFGEnumerations::FissionCause WhichCause,
113                                                      G4FFGEnumerations::YieldType WhichYieldType,
114                                                      G4int Verbosity,
115                                                      std::istringstream& dataStream)
116   : Isotope_(WhichIsotope),
117     MetaState_(WhichMetaState),
118     Cause_(WhichCause),
119     YieldType_(WhichYieldType),
120     Verbosity_(Verbosity)
121 {
122   G4FFG_FUNCTIONENTER__
123 
124   try {
125     // Initialize the class
126     Initialize(dataStream);
127   }
128   catch (std::exception& e) {
129     G4FFG_FUNCTIONLEAVE__
130     throw e;
131   }
132 
133   G4FFG_FUNCTIONLEAVE__
134 }
135 
136 void G4FissionProductYieldDist::Initialize(std::istringstream& dataStream)
137 {
138   G4FFG_FUNCTIONENTER__
139 
140   IncidentEnergy_ = 0.0;
141   TernaryProbability_ = 0;
142   AlphaProduction_ = 0;
143   SetNubar();
144 
145   // Set miscellaneous variables
146   AlphaDefinition_ = static_cast<G4Ions*>(G4Alpha::Definition());
147   NeutronDefinition_ = static_cast<G4Ions*>(G4Neutron::Definition());
148   GammaDefinition_ = G4Gamma::Definition();
149   SmallestZ_ = SmallestA_ = LargestZ_ = LargestA_ = nullptr;
150 
151   // Construct G4NeutronHPNames: provides access to the element names
152   ElementNames_ = new G4ParticleHPNames;
153   // Get the pointer to G4ParticleTable: stores all G4Ions
154   IonTable_ = G4IonTable::GetIonTable();
155   // Construct the pointer to the random engine
156   // TODO Make G4FPSamplingOps a singleton so that only one instance is used across all classes
157   RandomEngine_ = new G4FPYSamplingOps;
158 
159   try {
160     // Read in and sort the probability data
161     ENDFData_ = new G4ENDFTapeRead(dataStream, YieldType_, Cause_, Verbosity_);
162     //        ENDFData_ = new G4ENDFTapeRead(MakeDirectoryName(),
163     //                                       MakeFileName(Isotope_, MetaState_),
164     //                                       YieldType_,
165     //                                       Cause_);
166     YieldEnergyGroups_ = ENDFData_->G4GetNumberOfEnergyGroups();
167     DataTotal_ = new G4double[YieldEnergyGroups_];
168     MaintainNormalizedData_ = new G4double[YieldEnergyGroups_];
169     YieldEnergies_ = new G4double[YieldEnergyGroups_];
170     G4ArrayOps::Copy(YieldEnergyGroups_, YieldEnergies_, ENDFData_->G4GetEnergyGroupValues());
171     MakeTrees();
172     ReadProbabilities();
173   }
174   catch (std::exception& e) {
175     delete ElementNames_;
176     delete RandomEngine_;
177 
178     G4FFG_FUNCTIONLEAVE__
179     throw e;
180   }
181 
182   G4FFG_FUNCTIONLEAVE__
183 }
184 
185 G4DynamicParticleVector* G4FissionProductYieldDist::G4GetFission()
186 {
187   G4FFG_FUNCTIONENTER__
188 
189 #ifdef G4MULTITHREADED
190   G4AutoLock lk(&G4FissionProductYieldDist::fissprodMutex);
191 #endif
192 
193   // Check to see if the user has set the alpha production to a somewhat
194   // reasonable level
195   CheckAlphaSanity();
196 
197   // Generate the new G4DynamicParticle pointers to identify key locations in
198   // the G4DynamicParticle chain that will be passed to the G4FissionEvent
199   G4ReactionProduct* FirstDaughter = nullptr;
200   G4ReactionProduct* SecondDaughter = nullptr;
201   auto Alphas = new std::vector<G4ReactionProduct*>;
202   auto Neutrons = new std::vector<G4ReactionProduct*>;
203   auto Gammas = new std::vector<G4ReactionProduct*>;
204 
205   // Generate all the nucleonic fission products
206   // How many nucleons do we have to work with?
207   // TK modified 131108
208   const G4int ParentA = (Isotope_ / 10) % 1000;
209   const G4int ParentZ = ((Isotope_ / 10) - ParentA) / 1000;
210   RemainingA_ = ParentA;
211   RemainingZ_ = ParentZ;
212 
213   // Don't forget the extra nucleons depending on the fission cause
214   switch (Cause_) {
215     case G4FFGEnumerations::NEUTRON_INDUCED:
216       ++RemainingA_;
217       break;
218 
219     case G4FFGEnumerations::PROTON_INDUCED:
220       ++RemainingZ_;
221       break;
222 
223     case G4FFGEnumerations::GAMMA_INDUCED:
224     case G4FFGEnumerations::SPONTANEOUS:
225     default:
226       // Nothing to do here
227       break;
228   }
229 
230   // Ternary fission can be set by the user. Thus, it is necessary to
231   // sample the alpha particle first and the first daughter product
232   // second. See the discussion in
233   // G4FissionProductYieldDist::G4GetFissionProduct() for more information
234   // as to why the fission events are sampled this way.
235   GenerateAlphas(Alphas);
236 
237   // Generate the first daughter product
238   FirstDaughter = new G4ReactionProduct(GetFissionProduct());
239   RemainingA_ -= FirstDaughter->GetDefinition()->GetAtomicMass();
240   RemainingZ_ -= FirstDaughter->GetDefinition()->GetAtomicNumber();
241   if ((Verbosity_ & G4FFGEnumerations::DAUGHTER_INFO) != 0) {
242     G4FFG_SPACING__
243     G4FFG_LOCATION__
244 
245     G4cout << " -- First daughter product sampled" << G4endl;
246     G4FFG_SPACING__
247     G4cout << "  Name:       " << FirstDaughter->GetDefinition()->GetParticleName() << G4endl;
248     G4FFG_SPACING__
249     G4cout << "  Z:          " << FirstDaughter->GetDefinition()->GetAtomicNumber() << G4endl;
250     G4FFG_SPACING__
251     G4cout << "  A:          " << FirstDaughter->GetDefinition()->GetAtomicMass() << G4endl;
252     G4FFG_SPACING__
253     G4cout << "  Meta State: " << (FirstDaughter->GetDefinition()->GetPDGEncoding() % 10) << G4endl;
254   }
255 
256   GenerateNeutrons(Neutrons);
257 
258   // Now that all the nucleonic particles have been generated, we can
259   // calculate the composition of the second daughter product.
260   G4int NewIsotope = RemainingZ_ * 1000 + RemainingA_;
261   SecondDaughter =
262     new G4ReactionProduct(GetParticleDefinition(NewIsotope, G4FFGEnumerations::GROUND_STATE));
263   if ((Verbosity_ & G4FFGEnumerations::DAUGHTER_INFO) != 0) {
264     G4FFG_SPACING__
265     G4FFG_LOCATION__
266 
267     G4cout << " -- Second daughter product sampled" << G4endl;
268     G4FFG_SPACING__
269     G4cout << "  Name:       " << SecondDaughter->GetDefinition()->GetParticleName() << G4endl;
270     G4FFG_SPACING__
271     G4cout << "  Z:          " << SecondDaughter->GetDefinition()->GetAtomicNumber() << G4endl;
272     G4FFG_SPACING__
273     G4cout << "  A:          " << SecondDaughter->GetDefinition()->GetAtomicMass() << G4endl;
274     G4FFG_SPACING__
275     G4cout << "  Meta State: " << (SecondDaughter->GetDefinition()->GetPDGEncoding() % 10)
276            << G4endl;
277   }
278 
279   // Calculate how much kinetic energy will be available
280   // 195 to 205 MeV are available in a fission reaction, but about 20 MeV
281   // are from delayed sources. We are concerned only with prompt sources,
282   // so sample a Gaussian distribution about 20 MeV and subtract the
283   // result from the total available energy. Also, the energy of fission
284   // neutrinos is neglected. Fission neutrinos would add ~11 MeV
285   // additional energy to the fission. (Ref 2)
286   // Finally, add in the kinetic energy contribution of the fission
287   // inducing particle, if any.
288   const G4double TotalKE = RandomEngine_->G4SampleUniform(195.0, 205.0) * MeV
289                            - RandomEngine_->G4SampleGaussian(20.0, 3.0) * MeV + IncidentEnergy_;
290   RemainingEnergy_ = TotalKE;
291 
292   // Calculate the energies of the alpha particles and neutrons
293   // Once again, since the alpha particles are user defined, we must
294   // sample their kinetic energy first. SampleAlphaEnergies() returns the
295   // amount of energy consumed by the alpha particles, so remove the total
296   // energy alloted to the alpha particles from the available energy
297   SampleAlphaEnergies(Alphas);
298 
299   // Second, the neutrons are sampled from the Watt fission spectrum.
300   SampleNeutronEnergies(Neutrons);
301 
302   // Calculate the total energy available to the daughter products
303   // A Gaussian distribution about the average calculated energy with
304   // a standard deviation of 1.5 MeV (Ref. 2) is used. Since the energy
305   // distribution is dependant on the alpha particle generation and the
306   // Watt fission sampling for neutrons, we only have the left-over energy
307   // to work with for the fission daughter products.
308   G4double FragmentsKE = 0.;
309   G4int icounter = 0;
310   G4int icounter_max = 1024;
311   do {
312     icounter++;
313     if (icounter > icounter_max) {
314       G4cout << "Loop-counter exceeded the threshold value at " << __LINE__ << "th line of "
315              << __FILE__ << "." << G4endl;
316       break;
317     }
318     FragmentsKE = RandomEngine_->G4SampleGaussian(RemainingEnergy_, 1.5 * MeV);
319   } while (FragmentsKE > RemainingEnergy_);  // Loop checking, 11.05.2015, T. Koi
320 
321   // Make sure that we don't produce any sub-gamma photons
322   if ((RemainingEnergy_ - FragmentsKE) / (100 * keV) < 1.0) {
323     FragmentsKE = RemainingEnergy_;
324   }
325 
326   // This energy has now been allotted to the fission fragments.
327   // Subtract FragmentsKE from the total available fission energy.
328   RemainingEnergy_ -= FragmentsKE;
329 
330   // Sample the energies of the gamma rays
331   // Assign the remainder, if any, of the energy to the gamma rays
332   SampleGammaEnergies(Gammas);
333 
334   // Prepare to balance the momenta of the system
335   // We will need these for sampling the angles and balancing the momenta
336   // of all the fission products
337   G4double NumeratorSqrt, NumeratorOther, Denominator;
338   G4double Theta, Phi, Magnitude;
339   G4ThreeVector Direction;
340   G4ParticleMomentum ResultantVector(0, 0, 0);
341 
342   if (!Alphas->empty()) {
343     // Sample the angles of the alpha particles and neutrons, then calculate
344     // the total moment contribution to the system
345     // The average angle of the alpha particles with respect to the
346     // light fragment is dependent on the ratio of the kinetic energies.
347     // This equation was determined by performing a fit on data from
348     // Ref. 3 using the website:
349     // http://soft.arquimedex.com/linear_regression.php
350     //
351     // RatioOfKE    Angle (rad)     Angle (degrees)
352     // 1.05         1.257           72
353     // 1.155        1.361           78
354     // 1.28         1.414           81
355     // 1.5          1.518           87
356     // 1.75         1.606           92
357     // 1.9          1.623           93
358     // 2.2          1.728           99
359     // This equation generates the angle in radians. If the RatioOfKE is
360     // greater than 2.25 the angle defaults to 1.3963 rad (100 degrees)
361     G4double MassRatio =
362       FirstDaughter->GetDefinition()->GetPDGMass() / SecondDaughter->GetDefinition()->GetPDGMass();
363 
364     // Invert the mass ratio if the first daughter product is the lighter fragment
365     if (MassRatio < 1) {
366       MassRatio = 1 / MassRatio;
367     }
368 
369     // The empirical equation is valid for mass ratios up to 2.75
370     if (MassRatio > 2.75) {
371       MassRatio = 2.75;
372     }
373     const G4double MeanAlphaAngle = 0.3644 * MassRatio * MassRatio * MassRatio
374                                     - 1.9766 * MassRatio * MassRatio + 3.8207 * MassRatio - 0.9917;
375 
376     // Sample the directions of the alpha particles with respect to the
377     // light fragment. For the moment we will assume that the light
378     // fragment is traveling along the z-axis in the positive direction.
379     const G4double MeanAlphaAngleStdDev = 0.0523598776;
380     G4double PlusMinus;
381 
382     for (auto& Alpha : *Alphas) {
383       PlusMinus = std::acos(RandomEngine_->G4SampleGaussian(0, MeanAlphaAngleStdDev)) - (pi / 2);
384       Theta = MeanAlphaAngle + PlusMinus;
385       if (Theta < 0) {
386         Theta = 0.0 - Theta;
387       }
388       else if (Theta > pi) {
389         Theta = (2 * pi - Theta);
390       }
391       Phi = RandomEngine_->G4SampleUniform(-pi, pi);
392 
393       Direction.setRThetaPhi(1.0, Theta, Phi);
394       Magnitude = std::sqrt(2 * Alpha->GetKineticEnergy() * Alpha->GetDefinition()->GetPDGMass());
395       Alpha->SetMomentum(Direction * Magnitude);
396       ResultantVector += Alpha->GetMomentum();
397     }
398   }
399 
400   // Sample the directions of the neutrons.
401   if (!Neutrons->empty()) {
402     for (auto& Neutron : *Neutrons) {
403       Theta = std::acos(RandomEngine_->G4SampleUniform(-1, 1));
404       Phi = RandomEngine_->G4SampleUniform(-pi, pi);
405 
406       Direction.setRThetaPhi(1.0, Theta, Phi);
407       Magnitude =
408         std::sqrt(2 * Neutron->GetKineticEnergy() * Neutron->GetDefinition()->GetPDGMass());
409       Neutron->SetMomentum(Direction * Magnitude);
410       ResultantVector += Neutron->GetMomentum();
411     }
412   }
413 
414   // Sample the directions of the gamma rays
415   if (!Gammas->empty()) {
416     for (auto& Gamma : *Gammas) {
417       Theta = std::acos(RandomEngine_->G4SampleUniform(-1, 1));
418       Phi = RandomEngine_->G4SampleUniform(-pi, pi);
419 
420       Direction.setRThetaPhi(1.0, Theta, Phi);
421       Magnitude = Gamma->GetKineticEnergy() / CLHEP::c_light;
422       Gamma->SetMomentum(Direction * Magnitude);
423       ResultantVector += Gamma->GetMomentum();
424     }
425   }
426 
427   // Calculate the momenta of the two daughter products
428   G4ReactionProduct* LightFragment;
429   G4ReactionProduct* HeavyFragment;
430   G4ThreeVector LightFragmentDirection;
431   G4ThreeVector HeavyFragmentDirection;
432   G4double ResultantX, ResultantY, ResultantZ;
433   ResultantX = ResultantVector.getX();
434   ResultantY = ResultantVector.getY();
435   ResultantZ = ResultantVector.getZ();
436 
437   if (FirstDaughter->GetDefinition()->GetPDGMass() < SecondDaughter->GetDefinition()->GetPDGMass())
438   {
439     LightFragment = FirstDaughter;
440     HeavyFragment = SecondDaughter;
441   }
442   else {
443     LightFragment = SecondDaughter;
444     HeavyFragment = FirstDaughter;
445   }
446   const G4double LightFragmentMass = LightFragment->GetDefinition()->GetPDGMass();
447   const G4double HeavyFragmentMass = HeavyFragment->GetDefinition()->GetPDGMass();
448 
449   LightFragmentDirection.setRThetaPhi(1.0, 0, 0);
450 
451   // Fit the momenta of the daughter products to the resultant vector of
452   // the remaining fission products. This will be done in the Cartesian
453   // coordinate system, not spherical. This is done using the following
454   // table listing the system momenta and the corresponding equations:
455   //              X               Y               Z
456   //
457   //      A       0               0               P
458   //
459   //      B       -R_x            -R_y            -P - R_z
460   //
461   //      R       R_x             R_y             R_z
462   //
463   // v = sqrt(2*m*k)  ->  k = v^2/(2*m)
464   // tk = k_A + k_B
465   // k_L = P^2/(2*m_L)
466   // k_H = ((-R_x)^2 + (-R_y)^2 + (-P - R_z)^2)/(2*m_H)
467   // where:
468   // P: momentum of the light daughter product
469   // R: the remaining fission products' resultant vector
470   // v: momentum
471   // m: mass
472   // k: kinetic energy
473   // tk: total kinetic energy available to the daughter products
474   //
475   // Below is the solved form for P, with the solution generated using
476   // the WolframAlpha website:
477   // http://www.wolframalpha.com/input/?i=
478   // solve+((-x)^2+%2B+(-y)^2+%2B+(-P-z)^2)%2F(2*B)+%2B+L^2%2F(2*A)+%3D+k+
479   // for+P
480   //
481   //
482   // nsqrt = sqrt(m_L*(m_L*(2*m_H*tk - R_x^2 - R_y^2) +
483   //                   m_H*(2*m_H*tk - R_x^2 - R_y^2 - R_z^2))
484   NumeratorSqrt = std::sqrt(
485     LightFragmentMass
486     * (LightFragmentMass
487          * (2 * HeavyFragmentMass * FragmentsKE - ResultantX * ResultantX - ResultantY * ResultantY)
488        + HeavyFragmentMass
489            * (2 * HeavyFragmentMass * FragmentsKE - ResultantX * ResultantX
490               - ResultantY * ResultantY - ResultantZ - ResultantZ)));
491 
492   // nother = m_L*R_z
493   NumeratorOther = LightFragmentMass * ResultantZ;
494 
495   // denom = m_L + m_H
496   Denominator = LightFragmentMass + HeavyFragmentMass;
497 
498   // P = (nsqrt + nother) / denom
499   const G4double LightFragmentMomentum = (NumeratorSqrt + NumeratorOther) / Denominator;
500   const G4double HeavyFragmentMomentum =
501     std::sqrt(ResultantX * ResultantX + ResultantY * ResultantY
502               + G4Pow::GetInstance()->powN(LightFragmentMomentum + ResultantZ, 2));
503 
504   // Finally! We now have everything we need for the daughter products
505   LightFragment->SetMomentum(LightFragmentDirection * LightFragmentMomentum);
506   HeavyFragmentDirection.setX(-ResultantX);
507   HeavyFragmentDirection.setY(-ResultantY);
508   HeavyFragmentDirection.setZ((-LightFragmentMomentum - ResultantZ));
509   // Don't forget to normalize the vector to the unit sphere
510   HeavyFragmentDirection.setR(1.0);
511   HeavyFragment->SetMomentum(HeavyFragmentDirection * HeavyFragmentMomentum);
512 
513   if ((Verbosity_ & (G4FFGEnumerations::DAUGHTER_INFO | G4FFGEnumerations::MOMENTUM_INFO)) != 0) {
514     G4FFG_SPACING__
515     G4FFG_LOCATION__
516 
517     G4cout << " -- Daugher product momenta finalized" << G4endl;
518     G4FFG_SPACING__
519   }
520 
521   // Load all the particles into a contiguous set
522   // TK modifed 131108
523   // G4DynamicParticleVector* FissionProducts = new G4DynamicParticleVector(2 + Alphas->size() +
524   // Neutrons->size() + Gammas->size());
525   auto FissionProducts = new G4DynamicParticleVector();
526   // Load the fission fragments
527   FissionProducts->push_back(MakeG4DynamicParticle(LightFragment));
528   FissionProducts->push_back(MakeG4DynamicParticle(HeavyFragment));
529   // Load the neutrons
530   for (auto& Neutron : *Neutrons) {
531     FissionProducts->push_back(MakeG4DynamicParticle(Neutron));
532   }
533   // Load the gammas
534   for (auto& Gamma : *Gammas) {
535     FissionProducts->push_back(MakeG4DynamicParticle(Gamma));
536   }
537   // Load the alphas
538   for (auto& Alpha : *Alphas) {
539     FissionProducts->push_back(MakeG4DynamicParticle(Alpha));
540   }
541 
542   // Rotate the system to a random location so that the light fission fragment
543   // is not always traveling along the positive z-axis
544   // Sample Theta and Phi.
545   G4ThreeVector RotationAxis;
546 
547   Theta = std::acos(RandomEngine_->G4SampleUniform(-1, 1));
548   Phi = RandomEngine_->G4SampleUniform(-pi, pi);
549   RotationAxis.setRThetaPhi(1.0, Theta, Phi);
550 
551   // We will also check the net momenta
552   ResultantVector.set(0.0, 0.0, 0.0);
553   for (auto& FissionProduct : *FissionProducts) {
554     Direction = FissionProduct->GetMomentumDirection();
555     Direction.rotateUz(RotationAxis);
556     FissionProduct->SetMomentumDirection(Direction);
557     ResultantVector += FissionProduct->GetMomentum();
558   }
559 
560   // Warn if the sum momenta of the system is not within a reasonable
561   // tolerance
562   G4double PossibleImbalance = ResultantVector.mag();
563   if (PossibleImbalance > 0.01) {
564     std::ostringstream Temp;
565     Temp << "Momenta imbalance of ";
566     Temp << PossibleImbalance / (MeV / CLHEP::c_light);
567     Temp << " MeV/c in the system";
568     G4Exception("G4FissionProductYieldDist::G4GetFission()", Temp.str().c_str(), JustWarning,
569                 "Results may not be valid");
570   }
571 
572   // Clean up
573   delete FirstDaughter;
574   delete SecondDaughter;
575   G4ArrayOps::DeleteVectorOfPointers(*Neutrons);
576   G4ArrayOps::DeleteVectorOfPointers(*Gammas);
577   G4ArrayOps::DeleteVectorOfPointers(*Alphas);
578 
579   G4FFG_FUNCTIONLEAVE__
580   return FissionProducts;
581 }
582 
583 G4Ions* G4FissionProductYieldDist::G4GetFissionProduct()
584 {
585   G4FFG_FUNCTIONENTER__
586 
587   G4Ions* Product = FindParticle(RandomEngine_->G4SampleUniform());
588 
589   G4FFG_FUNCTIONLEAVE__
590   return Product;
591 }
592 
593 void G4FissionProductYieldDist::G4SetAlphaProduction(G4double WhatAlphaProduction)
594 {
595   G4FFG_FUNCTIONENTER__
596 
597   AlphaProduction_ = WhatAlphaProduction;
598 
599   G4FFG_FUNCTIONLEAVE__
600 }
601 
602 void G4FissionProductYieldDist::G4SetEnergy(G4double WhatIncidentEnergy)
603 {
604   G4FFG_FUNCTIONENTER__
605 
606   if (Cause_ != G4FFGEnumerations::SPONTANEOUS) {
607     IncidentEnergy_ = WhatIncidentEnergy;
608   }
609   else {
610     IncidentEnergy_ = 0 * GeV;
611   }
612 
613   G4FFG_FUNCTIONLEAVE__
614 }
615 
616 void G4FissionProductYieldDist::G4SetTernaryProbability(G4double WhatTernaryProbability)
617 {
618   G4FFG_FUNCTIONENTER__
619 
620   TernaryProbability_ = WhatTernaryProbability;
621 
622   G4FFG_FUNCTIONLEAVE__
623 }
624 
625 void G4FissionProductYieldDist::G4SetVerbosity(G4int Verbosity)
626 {
627   G4FFG_FUNCTIONENTER__
628 
629   Verbosity_ = Verbosity;
630 
631   ENDFData_->G4SetVerbosity(Verbosity_);
632   RandomEngine_->G4SetVerbosity(Verbosity_);
633 
634   G4FFG_FUNCTIONLEAVE__
635 }
636 
637 void G4FissionProductYieldDist::CheckAlphaSanity()
638 {
639   G4FFG_FUNCTIONENTER__
640 
641   // This provides comfortable breathing room at 16 MeV per alpha
642   if (AlphaProduction_ > 10) {
643     AlphaProduction_ = 10;
644   }
645   else if (AlphaProduction_ < -7) {
646     AlphaProduction_ = -7;
647   }
648 
649   G4FFG_FUNCTIONLEAVE__
650 }
651 
652 G4Ions* G4FissionProductYieldDist::FindParticle(G4double RandomParticle)
653 {
654   G4FFG_FUNCTIONENTER__
655 
656   // Determine which energy group is currently in use
657   G4bool isExact = false;
658   G4bool lowerExists = false;
659   G4bool higherExists = false;
660   G4int energyGroup;
661   for (energyGroup = 0; energyGroup < YieldEnergyGroups_; energyGroup++) {
662     if (IncidentEnergy_ == YieldEnergies_[energyGroup]) {
663       isExact = true;
664       break;
665     }
666 
667     if (energyGroup == 0 && IncidentEnergy_ < YieldEnergies_[energyGroup]) {
668       // Break if the energy is less than the lowest energy
669       higherExists = true;
670       break;
671     }
672     if (energyGroup == YieldEnergyGroups_ - 1) {
673       // The energy is greater than any values in the yield data.
674       lowerExists = true;
675       break;
676     }
677     // Break if the energy is less than the lowest energy
678     if (IncidentEnergy_ > YieldEnergies_[energyGroup]) {
679       energyGroup--;
680       lowerExists = true;
681       higherExists = true;
682       break;
683     }
684   }
685 
686   // Determine which particle it is
687   G4Ions* FoundParticle = nullptr;
688   if (isExact || YieldEnergyGroups_ == 1) {
689     // Determine which tree contains the random value
690     G4int tree;
691     for (tree = 0; tree < TreeCount_; tree++) {
692       // Break if a tree is identified as containing the random particle
693       if (RandomParticle <= Trees_[tree].ProbabilityRangeEnd[energyGroup]) {
694         break;
695       }
696     }
697     ProbabilityBranch* Branch = Trees_[tree].Trunk;
698 
699     // Iteratively traverse the tree until the particle addressed by the random
700     // variable is found
701     G4bool RangeIsSmaller;
702     G4bool RangeIsGreater;
703     while ((RangeIsSmaller = (RandomParticle < Branch->ProbabilityRangeBottom[energyGroup]))
704            || (RangeIsGreater = (RandomParticle > Branch->ProbabilityRangeTop[energyGroup])))
705     // Loop checking, 11.05.2015, T. Koi
706     {
707       if (RangeIsSmaller) {
708         Branch = Branch->Left;
709       }
710       else {
711         Branch = Branch->Right;
712       }
713     }
714 
715     FoundParticle = Branch->Particle;
716   }
717   else if (lowerExists && higherExists) {
718     // We need to do some interpolation
719     FoundParticle = FindParticleInterpolation(RandomParticle, energyGroup);
720   }
721   else {
722     // We need to do some extrapolation
723     FoundParticle = FindParticleExtrapolation(RandomParticle, lowerExists);
724   }
725 
726   // Return the particle
727   G4FFG_FUNCTIONLEAVE__
728   return FoundParticle;
729 }
730 
731 G4Ions* G4FissionProductYieldDist::FindParticleExtrapolation(G4double RandomParticle,
732                                                              G4bool LowerEnergyGroupExists)
733 {
734   G4FFG_FUNCTIONENTER__
735 
736   G4Ions* FoundParticle = nullptr;
737   G4int NearestEnergy;
738   G4int NextNearestEnergy;
739 
740   // Check to see if we are extrapolating above or below the data set
741   if (LowerEnergyGroupExists) {
742     NearestEnergy = YieldEnergyGroups_ - 1;
743     NextNearestEnergy = NearestEnergy - 1;
744   }
745   else {
746     NearestEnergy = 0;
747     NextNearestEnergy = 1;
748   }
749 
750   for (G4int Tree = 0; Tree < TreeCount_ && FoundParticle == nullptr; Tree++) {
751     FoundParticle = FindParticleBranchSearch(Trees_[Tree].Trunk, RandomParticle, NearestEnergy,
752                                              NextNearestEnergy);
753   }
754 
755   G4FFG_FUNCTIONLEAVE__
756   return FoundParticle;
757 }
758 
759 G4Ions* G4FissionProductYieldDist::FindParticleInterpolation(G4double RandomParticle,
760                                                              G4int LowerEnergyGroup)
761 {
762   G4FFG_FUNCTIONENTER__
763 
764   G4Ions* FoundParticle = nullptr;
765   G4int HigherEnergyGroup = LowerEnergyGroup + 1;
766 
767   for (G4int Tree = 0; Tree < TreeCount_ && FoundParticle == nullptr; Tree++) {
768     FoundParticle = FindParticleBranchSearch(Trees_[Tree].Trunk, RandomParticle, LowerEnergyGroup,
769                                              HigherEnergyGroup);
770   }
771 
772   G4FFG_FUNCTIONLEAVE__
773   return FoundParticle;
774 }
775 
776 G4Ions* G4FissionProductYieldDist::FindParticleBranchSearch(ProbabilityBranch* Branch,
777                                                             G4double RandomParticle,
778                                                             G4int EnergyGroup1, G4int EnergyGroup2)
779 {
780   G4FFG_RECURSIVE_FUNCTIONENTER__
781 
782   G4Ions* Particle;
783 
784   // Verify that the branch exists
785   if (Branch == nullptr) {
786     Particle = nullptr;
787   }
788   else if (EnergyGroup1 >= Branch->IncidentEnergiesCount
789            || EnergyGroup2 >= Branch->IncidentEnergiesCount || EnergyGroup1 == EnergyGroup2
790            || Branch->IncidentEnergies[EnergyGroup1] == Branch->IncidentEnergies[EnergyGroup2])
791   {
792     // Set NULL if any invalid conditions exist
793     Particle = nullptr;
794   }
795   else {
796     // Everything check out - proceed
797     G4Ions* FoundParticle = nullptr;
798     G4double Intercept;
799     G4double Slope;
800     G4double RangeAtIncidentEnergy;
801     G4double Denominator =
802       Branch->IncidentEnergies[EnergyGroup1] - Branch->IncidentEnergies[EnergyGroup2];
803 
804     // Calculate the lower probability bounds
805     Slope =
806       (Branch->ProbabilityRangeBottom[EnergyGroup1] - Branch->ProbabilityRangeBottom[EnergyGroup2])
807       / Denominator;
808     Intercept =
809       Branch->ProbabilityRangeBottom[EnergyGroup1] - Slope * Branch->IncidentEnergies[EnergyGroup1];
810     RangeAtIncidentEnergy = Slope * IncidentEnergy_ + Intercept;
811 
812     // Go right if the particle is below the probability bounds
813     if (RandomParticle < RangeAtIncidentEnergy) {
814       FoundParticle =
815         FindParticleBranchSearch(Branch->Left, RandomParticle, EnergyGroup1, EnergyGroup2);
816     }
817     else {
818       // Calculate the upper probability bounds
819       Slope =
820         (Branch->ProbabilityRangeTop[EnergyGroup1] - Branch->ProbabilityRangeTop[EnergyGroup2])
821         / Denominator;
822       Intercept =
823         Branch->ProbabilityRangeTop[EnergyGroup1] - Slope * Branch->IncidentEnergies[EnergyGroup1];
824       RangeAtIncidentEnergy = Slope * IncidentEnergy_ + Intercept;
825 
826       // Go left if the particle is above the probability bounds
827       if (RandomParticle > RangeAtIncidentEnergy) {
828         FoundParticle =
829           FindParticleBranchSearch(Branch->Right, RandomParticle, EnergyGroup1, EnergyGroup2);
830       }
831       else {
832         // If the particle is bounded then we found it!
833         FoundParticle = Branch->Particle;
834       }
835     }
836 
837     Particle = FoundParticle;
838   }
839 
840   G4FFG_RECURSIVE_FUNCTIONLEAVE__
841   return Particle;
842 }
843 
844 void G4FissionProductYieldDist::GenerateAlphas(std::vector<G4ReactionProduct*>* Alphas)
845 {
846   G4FFG_FUNCTIONENTER__
847 
848   // Throw the dice to determine if ternary fission occurs
849   G4bool MakeAlphas = RandomEngine_->G4SampleUniform() <= TernaryProbability_;
850   if (MakeAlphas) {
851     G4int NumberOfAlphasToProduce;
852 
853     // Determine how many alpha particles to produce for the ternary fission
854     if (AlphaProduction_ < 0) {
855       NumberOfAlphasToProduce = RandomEngine_->G4SampleIntegerGaussian(AlphaProduction_ * -1, 1,
856                                                                        G4FFGEnumerations::POSITIVE);
857     }
858     else {
859       NumberOfAlphasToProduce = (G4int)AlphaProduction_;
860     }
861 
862     // TK modifed 131108
863     // Alphas->resize(NumberOfAlphasToProduce);
864     for (int i = 0; i < NumberOfAlphasToProduce; i++) {
865       // Set the G4Ions as an alpha particle
866       Alphas->push_back(new G4ReactionProduct(AlphaDefinition_));
867 
868       // Remove 4 nucleons (2 protons and 2 neutrons) for each alpha added
869       RemainingZ_ -= 2;
870       RemainingA_ -= 4;
871     }
872   }
873 
874   G4FFG_FUNCTIONLEAVE__
875 }
876 
877 void G4FissionProductYieldDist::GenerateNeutrons(std::vector<G4ReactionProduct*>* Neutrons)
878 {
879   G4FFG_FUNCTIONENTER__
880 
881   G4int NeutronProduction;
882   NeutronProduction =
883     RandomEngine_->G4SampleIntegerGaussian(Nubar_, NubarWidth_, G4FFGEnumerations::POSITIVE);
884 
885   // TK modifed 131108
886   // Neutrons->resize(NeutronProduction);
887   for (int i = 0; i < NeutronProduction; i++) {
888     // Define the fragment as a neutron
889     Neutrons->push_back(new G4ReactionProduct(NeutronDefinition_));
890 
891     // Remove 1 nucleon for each neutron added
892     RemainingA_--;
893   }
894 
895   G4FFG_FUNCTIONLEAVE__
896 }
897 
898 G4Ions* G4FissionProductYieldDist::GetParticleDefinition(G4int Product,
899                                                          // TK modified 131108
900                                                          // G4FFGEnumerations::MetaState MetaState )
901                                                          G4FFGEnumerations::MetaState /*MetaState*/)
902 {
903   G4FFG_DATA_FUNCTIONENTER__
904 
905   G4Ions* Temp;
906 
907   // Break Product down into its A and Z components
908   G4int A = Product % 1000;  // Extract A
909   G4int Z = (Product - A) / 1000;  // Extract Z
910 
911   // Check to see if the particle is registered using the PDG code
912   // TODO Add metastable state when supported by G4IonTable::GetIon()
913   Temp = static_cast<G4Ions*>(IonTable_->GetIon(Z, A));
914 
915   // Removed in favor of the G4IonTable::GetIon() method
916   //    // Register the particle if it does not exist
917   //    if(Temp == NULL)
918   //    {
919   //        // Define the particle properties
920   //        G4String Name = MakeIsotopeName(Product, MetaState);
921   //        // Calculate the rest mass using a function already in Geant4
922   //        G4double Mass = G4NucleiProperties::
923   //                        GetNuclearMass((double)A, (double)Z );
924   //        G4double Charge = Z*eplus;
925   //        G4int BaryonNum = A;
926   //        G4bool Stable = TRUE;
927   //
928   //        // I am unsure about the following properties:
929   //        //     2*Spin, Parity, C-conjugation, 2*Isospin, 2*Isospin3, G-parity.
930   //        // Perhaps is would be a good idea to have a physicist familiar with
931   //        // Geant4 nomenclature to review and correct these properties.
932   //        Temp = new G4Ions (
933   //            // Name           Mass           Width          Charge
934   //               Name,          Mass,          0.0,           Charge,
935   //
936   //            // 2*Spin         Parity         C-conjugation  2*Isospin
937   //               0,             1,             0,             0,
938   //
939   //            // 2*Isospin3     G-parity       Type           Lepton number
940   //               0,             0,             "nucleus",     0,
941   //
942   //            // Baryon number  PDG encoding   Stable         Lifetime
943   //               BaryonNum,     PDGCode,       Stable,        -1,
944   //
945   //            // Decay table    Shortlived     SubType        Anti_encoding
946   //               NULL,          FALSE,        "generic",     0,
947   //
948   //            // Excitation
949   //               0.0);
950   //        Temp->SetPDGMagneticMoment(0.0);
951   //
952   //        // Declare that there is no anti-particle
953   //        Temp->SetAntiPDGEncoding(0);
954   //
955   //        // Define the processes to use in transporting the particles
956   //        std::ostringstream osAdd;
957   //        osAdd << "/run/particle/addProcManager " << Name;
958   //        G4String cmdAdd = osAdd.str();
959   //
960   //        // set /control/verbose 0
961   //        G4int tempVerboseLevel = G4UImanager::GetUIpointer()->GetVerboseLevel();
962   //        G4UImanager::GetUIpointer()->SetVerboseLevel(0);
963   //
964   //        // issue /run/particle/addProcManage
965   //        G4UImanager::GetUIpointer()->ApplyCommand(cmdAdd);
966   //
967   //        // retrieve  /control/verbose
968   //        G4UImanager::GetUIpointer()->SetVerboseLevel(tempVerboseLevel);
969   //    }
970 
971   G4FFG_DATA_FUNCTIONLEAVE__
972   return Temp;
973 }
974 
975 G4String G4FissionProductYieldDist::MakeDirectoryName()
976 {
977   G4FFG_FUNCTIONENTER__
978 
979   // Generate the file location starting in the Geant4 data directory
980   std::ostringstream DirectoryName;
981   DirectoryName << G4FindDataDir("G4NEUTRONHPDATA") << G4FFGDefaultValues::ENDFFissionDataLocation;
982 
983   // Return the directory structure
984   G4FFG_FUNCTIONLEAVE__
985   return DirectoryName.str();
986 }
987 
988 G4String G4FissionProductYieldDist::MakeFileName(G4int Isotope,
989                                                  G4FFGEnumerations::MetaState MetaState)
990 {
991   G4FFG_FUNCTIONENTER__
992 
993   // Create the unique identifying name for the particle
994   std::ostringstream FileName;
995 
996   // Determine if a leading 0 is needed (ZZZAAA or 0ZZAAA)
997   if (Isotope < 100000) {
998     FileName << "0";
999   }
1000 
1001   // Add the name of the element and the extension
1002   FileName << MakeIsotopeName(Isotope, MetaState) << ".fpy";
1003 
1004   G4FFG_FUNCTIONLEAVE__
1005   return FileName.str();
1006 }
1007 
1008 G4DynamicParticle*
1009 G4FissionProductYieldDist::MakeG4DynamicParticle(G4ReactionProduct* ReactionProduct)
1010 {
1011   G4FFG_DATA_FUNCTIONENTER__
1012 
1013   auto DynamicParticle =
1014     new G4DynamicParticle(ReactionProduct->GetDefinition(), ReactionProduct->GetMomentum());
1015 
1016   G4FFG_DATA_FUNCTIONLEAVE__
1017   return DynamicParticle;
1018 }
1019 
1020 G4String G4FissionProductYieldDist::MakeIsotopeName(G4int Isotope,
1021                                                     G4FFGEnumerations::MetaState MetaState)
1022 {
1023   G4FFG_DATA_FUNCTIONENTER__
1024 
1025   // Break Product down into its A and Z components
1026   G4int A = Isotope % 1000;
1027   G4int Z = (Isotope - A) / 1000;
1028 
1029   // Create the unique identifying name for the particle
1030   std::ostringstream IsotopeName;
1031 
1032   IsotopeName << Z << "_" << A;
1033 
1034   // If it is metastable then append "m" to the name
1035   if (MetaState != G4FFGEnumerations::GROUND_STATE) {
1036     IsotopeName << "m";
1037 
1038     // If it is a second isomeric state then append "2" to the name
1039     if (MetaState == G4FFGEnumerations::META_2) {
1040       IsotopeName << "2";
1041     }
1042   }
1043   // Add the name of the element and the extension
1044   IsotopeName << "_" << ElementNames_->GetName(Z - 1);
1045 
1046   G4FFG_DATA_FUNCTIONLEAVE__
1047   return IsotopeName.str();
1048 }
1049 
1050 void G4FissionProductYieldDist::MakeTrees()
1051 {
1052   G4FFG_FUNCTIONENTER__
1053 
1054   // Allocate the space
1055   // We will make each tree a binary search
1056   // The maximum number of iterations required to find a single fission product
1057   // based on it's probability is defined by the following:
1058   //      x = number of fission products
1059   //      Trees       = T(x)  = ceil( ln(x) )
1060   //      Rows/Tree   = R(x)  = ceil(( sqrt( (8 * x / T(x)) + 1) - 1) / 2)
1061   //      Maximum     = M(x)  = T(x) + R(x)
1062   //      Results: x    =>    M(x)
1063   //               10         5
1064   //               100        10
1065   //               1000       25
1066   //               10000      54
1067   //               100000     140
1068   TreeCount_ = (G4int)ceil((G4double)log((G4double)ENDFData_->G4GetNumberOfFissionProducts()));
1069   Trees_ = new ProbabilityTree[TreeCount_];
1070 
1071   // Initialize the range of each node
1072   for (G4int i = 0; i < TreeCount_; i++) {
1073     Trees_[i].ProbabilityRangeEnd = new G4double[YieldEnergyGroups_];
1074     Trees_[i].Trunk = nullptr;
1075     Trees_[i].BranchCount = 0;
1076     Trees_[i].IsEnd = FALSE;
1077   }
1078   // Mark the last tree as the ending tree
1079   Trees_[TreeCount_ - 1].IsEnd = TRUE;
1080 
1081   G4FFG_FUNCTIONLEAVE__
1082 }
1083 
1084 void G4FissionProductYieldDist::ReadProbabilities()
1085 {
1086   G4FFG_DATA_FUNCTIONENTER__
1087 
1088   G4int ProductCount = ENDFData_->G4GetNumberOfFissionProducts();
1089   BranchCount_ = 0;
1090   G4ArrayOps::Set(YieldEnergyGroups_, DataTotal_, 0.0);
1091 
1092   // Loop through all the products
1093   for (G4int i = 0; i < ProductCount; i++) {
1094     // Acquire the data and sort it
1095     SortProbability(ENDFData_->G4GetYield(i));
1096   }
1097 
1098   // Generate the true normalization factor, since round-off errors may result
1099   // in non-singular normalization of the data files. Also, reset DataTotal_
1100   // since it is used by Renormalize() to set the probability segments.
1101   G4ArrayOps::Divide(YieldEnergyGroups_, MaintainNormalizedData_, 1.0, DataTotal_);
1102   G4ArrayOps::Set(YieldEnergyGroups_, DataTotal_, 0.0);
1103 
1104   // Go through all the trees one at a time
1105   for (G4int i = 0; i < TreeCount_; i++) {
1106     Renormalize(Trees_[i].Trunk);
1107     // Set the max range of the tree to DataTotal
1108     G4ArrayOps::Copy(YieldEnergyGroups_, Trees_[i].ProbabilityRangeEnd, DataTotal_);
1109   }
1110 
1111   G4FFG_DATA_FUNCTIONLEAVE__
1112 }
1113 
1114 void G4FissionProductYieldDist::Renormalize(ProbabilityBranch* Branch)
1115 {
1116   G4FFG_RECURSIVE_FUNCTIONENTER__
1117 
1118   // Check to see if Branch exists. Branch will be a null pointer if it
1119   // doesn't exist
1120   if (Branch != nullptr) {
1121     // Call the lower branch to set the probability segment first, since it
1122     // supposed to have a lower probability segment that this node
1123     Renormalize(Branch->Left);
1124 
1125     // Set this node as the next sequential probability segment
1126     G4ArrayOps::Copy(YieldEnergyGroups_, Branch->ProbabilityRangeBottom, DataTotal_);
1127     G4ArrayOps::Multiply(YieldEnergyGroups_, Branch->ProbabilityRangeTop, MaintainNormalizedData_);
1128     G4ArrayOps::Add(YieldEnergyGroups_, Branch->ProbabilityRangeTop, DataTotal_);
1129     G4ArrayOps::Copy(YieldEnergyGroups_, DataTotal_, Branch->ProbabilityRangeTop);
1130 
1131     // Now call the upper branch to set those probability segments
1132     Renormalize(Branch->Right);
1133   }
1134 
1135   G4FFG_RECURSIVE_FUNCTIONLEAVE__
1136 }
1137 
1138 void G4FissionProductYieldDist::SampleAlphaEnergies(std::vector<G4ReactionProduct*>* Alphas)
1139 {
1140   G4FFG_FUNCTIONENTER__
1141 
1142   // The condition of sampling more energy from the fission products than is
1143   // alloted is statistically unfavorable, but it could still happen. The
1144   // do-while loop prevents such an occurrence from happening
1145   G4double MeanAlphaEnergy = 16.0;
1146   G4double TotalAlphaEnergy;
1147 
1148   do {
1149     G4double AlphaEnergy;
1150     TotalAlphaEnergy = 0;
1151 
1152     // Walk through the alpha particles one at a time and sample each's
1153     // energy
1154     for (auto& Alpha : *Alphas) {
1155       AlphaEnergy =
1156         RandomEngine_->G4SampleGaussian(MeanAlphaEnergy, 2.35, G4FFGEnumerations::POSITIVE) * MeV;
1157       // Assign the energy to the alpha particle
1158       Alpha->SetKineticEnergy(AlphaEnergy);
1159 
1160       // Add up the total amount of kinetic energy consumed.
1161       TotalAlphaEnergy += AlphaEnergy;
1162     }
1163 
1164     // If true, decrement the mean alpha energy by 0.1 and try again.
1165     MeanAlphaEnergy -= 0.1;
1166   } while (TotalAlphaEnergy >= RemainingEnergy_);  // Loop checking, 11.05.2015, T. Koi
1167 
1168   // Subtract the total amount of energy that was assigned.
1169   RemainingEnergy_ -= TotalAlphaEnergy;
1170 
1171   G4FFG_FUNCTIONLEAVE__
1172 }
1173 
1174 void G4FissionProductYieldDist::SampleGammaEnergies(std::vector<G4ReactionProduct*>* Gammas)
1175 {
1176   G4FFG_FUNCTIONENTER__
1177 
1178   // Make sure that there is energy to assign to the gamma rays
1179   if (RemainingEnergy_ != 0) {
1180     G4double SampleEnergy;
1181 
1182     // Sample from RemainingEnergy until it is all gone. Also,
1183     // RemainingEnergy should not be smaller than
1184     // G4FFGDefaultValues::MeanGammaEnergy. This will prevent the
1185     // sampling of a fractional portion of the Gaussian distribution
1186     // in an attempt to find a new gamma ray energy.
1187     G4int icounter = 0;
1188     G4int icounter_max = 1024;
1189     while (RemainingEnergy_
1190            >= G4FFGDefaultValues::MeanGammaEnergy)  // Loop checking, 11.05.2015, T. Koi
1191     {
1192       icounter++;
1193       if (icounter > icounter_max) {
1194         G4cout << "Loop-counter exceeded the threshold value at " << __LINE__ << "th line of "
1195                << __FILE__ << "." << G4endl;
1196         break;
1197       }
1198       SampleEnergy = RandomEngine_->G4SampleGaussian(G4FFGDefaultValues::MeanGammaEnergy, 1.0 * MeV,
1199                                                      G4FFGEnumerations::POSITIVE);
1200       // Make sure that we didn't sample more energy than was available
1201       if (SampleEnergy <= RemainingEnergy_) {
1202         // If this energy assignment would leave less energy than the
1203         // 'intrinsic' minimal energy of a gamma ray then just assign
1204         // all of the remaining energy
1205         if (RemainingEnergy_ - SampleEnergy < 100 * keV) {
1206           SampleEnergy = RemainingEnergy_;
1207         }
1208 
1209         // Create the new particle
1210         Gammas->push_back(new G4ReactionProduct());
1211 
1212         // Set the properties
1213         Gammas->back()->SetDefinition(GammaDefinition_);
1214         Gammas->back()->SetTotalEnergy(SampleEnergy);
1215 
1216         // Calculate how much is left
1217         RemainingEnergy_ -= SampleEnergy;
1218       }
1219     }
1220 
1221     // If there is anything left over, the energy must be above 100 keV but
1222     // less than G4FFGDefaultValues::MeanGammaEnergy. Arbitrarily assign
1223     // RemainingEnergy to a new particle
1224     if (RemainingEnergy_ > 0) {
1225       SampleEnergy = RemainingEnergy_;
1226       Gammas->push_back(new G4ReactionProduct());
1227 
1228       // Set the properties
1229       Gammas->back()->SetDefinition(GammaDefinition_);
1230       Gammas->back()->SetTotalEnergy(SampleEnergy);
1231 
1232       // Calculate how much is left
1233       RemainingEnergy_ -= SampleEnergy;
1234     }
1235   }
1236 
1237   G4FFG_FUNCTIONLEAVE__
1238 }
1239 
1240 void G4FissionProductYieldDist::SampleNeutronEnergies(std::vector<G4ReactionProduct*>* Neutrons)
1241 {
1242   G4FFG_FUNCTIONENTER__
1243 
1244   // The condition of sampling more energy from the fission products than is
1245   // alloted is statistically unfavorable, but it could still happen. The
1246   // do-while loop prevents such an occurrence from happening
1247   G4double TotalNeutronEnergy = 0.;
1248   G4double NeutronEnergy = 0.;
1249 
1250   // Make sure that we don't sample more energy than is available
1251   G4int icounter = 0;
1252   G4int icounter_max = 1024;
1253   do {
1254     icounter++;
1255     if (icounter > icounter_max) {
1256       G4cout << "Loop-counter exceeded the threshold value at " << __LINE__ << "th line of "
1257              << __FILE__ << "." << G4endl;
1258       break;
1259     }
1260     TotalNeutronEnergy = 0;
1261 
1262     // Walk through the neutrons one at a time and sample the energies.
1263     // The gamma rays have not yet been sampled, so the last neutron will
1264     // have a NULL value for NextFragment
1265     for (auto& Neutron : *Neutrons) {
1266       // Assign the energy to the neutron
1267       NeutronEnergy = RandomEngine_->G4SampleWatt(Isotope_, Cause_, IncidentEnergy_);
1268       Neutron->SetKineticEnergy(NeutronEnergy);
1269 
1270       // Add up the total amount of kinetic energy consumed.
1271       TotalNeutronEnergy += NeutronEnergy;
1272     }
1273   } while (TotalNeutronEnergy > RemainingEnergy_);  // Loop checking, 11.05.2015, T. Koi
1274 
1275   // Subtract the total amount of energy that was assigned.
1276   RemainingEnergy_ -= TotalNeutronEnergy;
1277 
1278   G4FFG_FUNCTIONLEAVE__
1279 }
1280 
1281 void G4FissionProductYieldDist::SetNubar()
1282 {
1283   G4FFG_FUNCTIONENTER__
1284 
1285   G4int* WhichNubar;
1286   G4int* NubarWidth;
1287   G4double XFactor, BFactor;
1288 
1289   if (Cause_ == G4FFGEnumerations::SPONTANEOUS) {
1290     WhichNubar = const_cast<G4int*>(&SpontaneousNubar_[0][0]);
1291     NubarWidth = const_cast<G4int*>(&SpontaneousNubarWidth_[0][0]);
1292   }
1293   else {
1294     WhichNubar = const_cast<G4int*>(&NeutronInducedNubar_[0][0]);
1295     NubarWidth = const_cast<G4int*>(&NeutronInducedNubarWidth_[0][0]);
1296   }
1297 
1298   XFactor = G4Pow::GetInstance()->powA(10.0, -13.0);
1299   BFactor = G4Pow::GetInstance()->powA(10.0, -4.0);
1300   Nubar_ = *(WhichNubar + 1) * IncidentEnergy_ * XFactor + *(WhichNubar + 2) * BFactor;
1301   while (*WhichNubar != -1)  // Loop checking, 11.05.2015, T. Koi
1302   {
1303     if (*WhichNubar == Isotope_) {
1304       Nubar_ = *(WhichNubar + 1) * IncidentEnergy_ * XFactor + *(WhichNubar + 2) * BFactor;
1305 
1306       break;
1307     }
1308     WhichNubar += 3;
1309   }
1310 
1311   XFactor = G4Pow::GetInstance()->powN((G4double)10, -6);
1312   NubarWidth_ = *(NubarWidth + 1) * XFactor;
1313   while (*WhichNubar != -1)  // Loop checking, 11.05.2015, T. Koi
1314   {
1315     if (*WhichNubar == Isotope_) {
1316       NubarWidth_ = *(NubarWidth + 1) * XFactor;
1317 
1318       break;
1319     }
1320     WhichNubar += 2;
1321   }
1322 
1323   G4FFG_FUNCTIONLEAVE__
1324 }
1325 
1326 void G4FissionProductYieldDist::SortProbability(G4ENDFYieldDataContainer* YieldData)
1327 {
1328   G4FFG_DATA_FUNCTIONENTER__
1329 
1330   // Initialize the new branch
1331   auto NewBranch = new ProbabilityBranch;
1332   NewBranch->IncidentEnergiesCount = YieldEnergyGroups_;
1333   NewBranch->Left = nullptr;
1334   NewBranch->Right = nullptr;
1335   NewBranch->Particle = GetParticleDefinition(YieldData->GetProduct(), YieldData->GetMetaState());
1336   NewBranch->IncidentEnergies = new G4double[YieldEnergyGroups_];
1337   NewBranch->ProbabilityRangeTop = new G4double[YieldEnergyGroups_];
1338   NewBranch->ProbabilityRangeBottom = new G4double[YieldEnergyGroups_];
1339   G4ArrayOps::Copy(YieldEnergyGroups_, NewBranch->ProbabilityRangeTop,
1340                    YieldData->GetYieldProbability());
1341   G4ArrayOps::Copy(YieldEnergyGroups_, NewBranch->IncidentEnergies, YieldEnergies_);
1342   G4ArrayOps::Add(YieldEnergyGroups_, DataTotal_, YieldData->GetYieldProbability());
1343 
1344   // Check to see if the this is the smallest/largest particle. First, check
1345   // to see if this is the first particle in the system
1346   if (SmallestZ_ == nullptr) {
1347     SmallestZ_ = SmallestA_ = LargestZ_ = LargestA_ = NewBranch->Particle;
1348   }
1349   else {
1350     G4bool IsSmallerZ = NewBranch->Particle->GetAtomicNumber() < SmallestZ_->GetAtomicNumber();
1351     G4bool IsSmallerA = NewBranch->Particle->GetAtomicMass() < SmallestA_->GetAtomicMass();
1352     G4bool IsLargerZ = NewBranch->Particle->GetAtomicNumber() > LargestZ_->GetAtomicNumber();
1353     G4bool IsLargerA = NewBranch->Particle->GetAtomicMass() > LargestA_->GetAtomicMass();
1354 
1355     if (IsSmallerZ) {
1356       SmallestZ_ = NewBranch->Particle;
1357     }
1358 
1359     if (IsLargerZ) {
1360       LargestA_ = NewBranch->Particle;
1361     }
1362 
1363     if (IsSmallerA) {
1364       SmallestA_ = NewBranch->Particle;
1365     }
1366 
1367     if (IsLargerA) {
1368       LargestA_ = NewBranch->Particle;
1369     }
1370   }
1371 
1372   // Place the new branch
1373   // Determine which tree the new branch goes into
1374   auto WhichTree = (G4int)floor((G4double)(BranchCount_ % TreeCount_));
1375   ProbabilityBranch** WhichBranch = &(Trees_[WhichTree].Trunk);
1376   Trees_[WhichTree].BranchCount++;
1377 
1378   // Search for the position
1379   // Determine where the branch goes
1380   G4int BranchPosition = (G4int)floor((G4double)(BranchCount_ / TreeCount_)) + 1;
1381 
1382   // Run through the tree until the end branch is reached
1383   while (BranchPosition > 1)  // Loop checking, 11.05.2015, T. Koi
1384   {
1385     if ((BranchPosition & 1) != 0) {
1386       // If the 1's bit is on then move to the next 'right' branch
1387       WhichBranch = &((*WhichBranch)->Right);
1388     }
1389     else {
1390       // If the 1's bit is off then move to the next 'down' branch
1391       WhichBranch = &((*WhichBranch)->Left);
1392     }
1393 
1394     BranchPosition >>= 1;
1395   }
1396 
1397   *WhichBranch = NewBranch;
1398   BranchCount_++;
1399 
1400   G4FFG_DATA_FUNCTIONLEAVE__
1401 }
1402 
1403 G4FissionProductYieldDist::~G4FissionProductYieldDist()
1404 {
1405   G4FFG_FUNCTIONENTER__
1406 
1407   // Burn each tree, one by one
1408   G4int WhichTree = 0;
1409   while (static_cast<int>(Trees_[WhichTree].IsEnd) != TRUE)  // Loop checking, 11.05.2015, T. Koi
1410   {
1411     BurnTree(Trees_[WhichTree].Trunk);
1412     delete Trees_[WhichTree].Trunk;
1413     delete[] Trees_[WhichTree].ProbabilityRangeEnd;
1414     WhichTree++;
1415   }
1416 
1417   // Delete each dynamically allocated variable
1418   delete ENDFData_;
1419   delete[] Trees_;
1420   delete[] DataTotal_;
1421   delete[] MaintainNormalizedData_;
1422   delete ElementNames_;
1423   delete RandomEngine_;
1424 
1425   G4FFG_FUNCTIONLEAVE__
1426 }
1427 
1428 void G4FissionProductYieldDist::BurnTree(ProbabilityBranch* Branch)
1429 {
1430   G4FFG_RECURSIVE_FUNCTIONENTER__
1431 
1432   // Check to see it Branch exists. Branch will be a null pointer if it
1433   // doesn't exist
1434   if (Branch != nullptr) {
1435     // Burn down before you burn up
1436     BurnTree(Branch->Left);
1437     delete Branch->Left;
1438     BurnTree(Branch->Right);
1439     delete Branch->Right;
1440 
1441     delete[] Branch->IncidentEnergies;
1442     delete[] Branch->ProbabilityRangeTop;
1443     delete[] Branch->ProbabilityRangeBottom;
1444   }
1445 
1446   G4FFG_RECURSIVE_FUNCTIONLEAVE__
1447 }
1448