//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
////////////////////////////////////////////////////////////////////////////////
//
//  GEANT4 Class source file
//
//  G4RadioactiveDecay
//
//  Author: D.H. Wright (SLAC)
//  Date:   29 August 2017
//
//  Based on the code of F. Lei and P.R. Truscott.
//
////////////////////////////////////////////////////////////////////////////////

#include "G4RadioactiveDecay.hh"
#include "G4RadioactivationMessenger.hh"

#include "G4SystemOfUnits.hh"
#include "G4DynamicParticle.hh"
#include "G4DecayProducts.hh"
#include "G4DecayTable.hh"
#include "G4ParticleChangeForRadDecay.hh"
#include "G4ITDecay.hh"
#include "G4BetaDecayType.hh"
#include "G4BetaMinusDecay.hh"
#include "G4BetaPlusDecay.hh"
#include "G4ECDecay.hh"
#include "G4AlphaDecay.hh"
#include "G4TritonDecay.hh"
#include "G4ProtonDecay.hh"
#include "G4NeutronDecay.hh"
#include "G4SFDecay.hh"
#include "G4VDecayChannel.hh"
#include "G4NuclearDecay.hh"
#include "G4RadioactiveDecayMode.hh"
#include "G4Fragment.hh"
#include "G4Ions.hh"
#include "G4IonTable.hh"
#include "G4BetaDecayType.hh"
#include "Randomize.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4NuclearLevelData.hh"
#include "G4DeexPrecoParameters.hh"
#include "G4LevelManager.hh"
#include "G4ThreeVector.hh"
#include "G4Electron.hh"
#include "G4Positron.hh"
#include "G4Neutron.hh"
#include "G4Gamma.hh"
#include "G4Alpha.hh"
#include "G4Triton.hh"
#include "G4Proton.hh"

#include "G4HadronicProcessType.hh"
#include "G4HadronicProcessStore.hh"
#include "G4HadronicException.hh"
#include "G4LossTableManager.hh"
#include "G4VAtomDeexcitation.hh"
#include "G4UAtomicDeexcitation.hh"
#include "G4PhotonEvaporation.hh"

#include <vector>
#include <sstream>
#include <algorithm>
#include <fstream>

using namespace CLHEP;

G4RadioactiveDecay::G4RadioactiveDecay(const G4String& processName, 
                                       const G4double timeThresholdForRadioactiveDecays)
  : G4VRadioactiveDecay(processName, timeThresholdForRadioactiveDecays)
{
#ifdef G4VERBOSE
  if (GetVerboseLevel() > 1) {
    G4cout << "G4RadioactiveDecay constructor: processName = " << processName
           << G4endl;
  }
#endif

  theRadioactivationMessenger = new G4RadioactivationMessenger(this);

  // Apply default values.
  NSourceBin  = 1;
  SBin[0]     = 0.* s;
  SBin[1]     = 1.* s;    // Convert to ns
  SProfile[0] = 1.;
  SProfile[1] = 0.;
  NDecayBin   = 1;
  DBin[0]     = 0. * s ;
  DBin[1]     = 1. * s;
  DProfile[0] = 1.;
  DProfile[1] = 0.;
  decayWindows[0] = 0;
  G4RadioactivityTable* rTable = new G4RadioactivityTable() ;
  theRadioactivityTables.push_back(rTable);
  NSplit      = 1;
  AnalogueMC = true;
  BRBias = true;
  halflifethreshold = 1000.*nanosecond;
}

void G4RadioactiveDecay::ProcessDescription(std::ostream& outFile) const
{
  outFile << "The G4RadioactiveDecay process performs radioactive decay of\n"
          << "nuclides (G4GenericIon) in biased mode which includes nucleus\n"
          << "duplication, branching ratio biasing, source time convolution\n"
          << "and detector time convolution.  It is designed for use in\n"
          << "activation physics.\n"
          << "The required half-lives and decay schemes are retrieved from\n"
          << "the RadioactiveDecay database which was derived from ENSDF.\n";
}


G4RadioactiveDecay::~G4RadioactiveDecay()
{
  delete theRadioactivationMessenger;
}

G4bool
G4RadioactiveDecay::IsRateTableReady(const G4ParticleDefinition& aParticle)
{
  // Check whether the radioactive decay rates table for the ion has already
  // been calculated.
  G4String aParticleName = aParticle.GetParticleName();
  for (std::size_t i = 0; i < theParentChainTable.size(); ++i) {
    if (theParentChainTable[i].GetIonName() == aParticleName) return true;
  }
  return false;
}

void
G4RadioactiveDecay::GetChainsFromParent(const G4ParticleDefinition& aParticle)
{
  // Retrieve the decay rate table for the specified aParticle
  G4String aParticleName = aParticle.GetParticleName();

  for (std::size_t i = 0; i < theParentChainTable.size(); ++i) {
    if (theParentChainTable[i].GetIonName() == aParticleName) {
      theDecayRateVector = theParentChainTable[i].GetItsRates();
    }
  }
#ifdef G4VERBOSE
  if (GetVerboseLevel() > 1) {
    G4cout << "The DecayRate Table for " << aParticleName << " is selected."
           <<  G4endl;
  }
#endif
}

// ConvolveSourceTimeProfile performs the convolution of the source time profile
// function with a single exponential characterized by a decay constant in the 
// decay chain.  The time profile is treated as a step function so that the 
// convolution integral can be done bin-by-bin.
// This implements Eq. 4.13 of DERA technical note, with SProfile[i] = F(t')

G4double
G4RadioactiveDecay::ConvolveSourceTimeProfile(const G4double t, const G4double tau)
{
  G4double convolvedTime = 0.0;
  G4int nbin;
  if ( t > SBin[NSourceBin]) {
    nbin  = NSourceBin;
  } else {
    nbin = 0;

    G4int loop = 0;
    while (t > SBin[nbin]) {  // Loop checking, 01.09.2015, D.Wright
      loop++;
      if (loop > 1000) {
        G4Exception("G4RadioactiveDecay::ConvolveSourceTimeProfile()",
                    "HAD_RDM_100", JustWarning, "While loop count exceeded");
        break;
      }
      ++nbin;
    }
    --nbin;
  }

  // Use expm1 wherever possible to avoid large cancellation errors in
  // 1 - exp(x) for small x
  G4double earg = 0.0;
  if (nbin > 0) {
    for (G4int i = 0; i < nbin; ++i) {
      earg = (SBin[i+1] - SBin[i])/tau;
      if (earg < 100.) {
        convolvedTime += SProfile[i] * std::exp((SBin[i] - t)/tau) *
                         std::expm1(earg);
      } else {
        convolvedTime += SProfile[i] *
          (std::exp(-(t-SBin[i+1])/tau)-std::exp(-(t-SBin[i])/tau));
      }
    }
  }
  convolvedTime -= SProfile[nbin] * std::expm1((SBin[nbin] - t)/tau);
  // tau divided out of final result to provide probability of decay in window

  if (convolvedTime < 0.)  {
    G4cout << " Convolved time =: " << convolvedTime << " reset to zero! " << G4endl;
    G4cout << " t = " << t << " tau = " << tau << G4endl;
    G4cout << SBin[nbin] << " " << SBin[0] << G4endl;
    convolvedTime = 0.;
  }
#ifdef G4VERBOSE
  if (GetVerboseLevel() > 2)
    G4cout << " Convolved time: " << convolvedTime << G4endl;
#endif
  return convolvedTime;
}


////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  GetDecayTime                                                              //
//    Randomly select a decay time for the decay process, following the       //
//    supplied decay time bias scheme.                                        //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

G4double G4RadioactiveDecay::GetDecayTime()
{
  G4double decaytime = 0.;
  G4double rand = G4UniformRand();
  G4int i = 0;

  G4int loop = 0;
  while (DProfile[i] < rand) {  /* Loop checking, 01.09.2015, D.Wright */
    // Entries in DProfile[i] are all between 0 and 1 and arranged in inreaseing order
    // Comparison with rand chooses which time bin to sample  
    ++i;
    loop++;
    if (loop > 100000) {
      G4Exception("G4RadioactiveDecay::GetDecayTime()", "HAD_RDM_100",
                  JustWarning, "While loop count exceeded");
      break;
    }
  }

  rand = G4UniformRand();
  decaytime = DBin[i] + rand*(DBin[i+1]-DBin[i]);
#ifdef G4VERBOSE
  if (GetVerboseLevel() > 2)
    G4cout <<" Decay time: " <<decaytime/s <<"[s]" <<G4endl;
#endif
  return  decaytime;	    
}


G4int G4RadioactiveDecay::GetDecayTimeBin(const G4double aDecayTime)
{
  G4int i = 0;

  G4int loop = 0;
  while (aDecayTime > DBin[i] ) {   /* Loop checking, 01.09.2015, D.Wright */
    ++i;
    loop++;
    if (loop > 100000) {
      G4Exception("G4RadioactiveDecay::GetDecayTimeBin()", "HAD_RDM_100", 
                  JustWarning, "While loop count exceeded");
      break;
    }
  }

  return  i;
}

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  GetMeanLifeTime (required by the base class)                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

G4double G4RadioactiveDecay::GetMeanLifeTime(const G4Track& theTrack,
                                            G4ForceCondition* fc)
{
  // For variance reduction time is set to 0 so as to force the particle
  // to decay immediately.
  // In analogue mode it returns the particle's mean-life.
  G4double meanlife = 0.;
  if (AnalogueMC) meanlife = G4VRadioactiveDecay::GetMeanLifeTime(theTrack, fc); 
  return meanlife;
}


void
G4RadioactiveDecay::SetDecayRate(G4int theZ, G4int theA, G4double theE, 
				G4int theG, std::vector<G4double>& theCoefficients, 
				std::vector<G4double>& theTaos)
//  Why not make this a method of G4RadioactiveDecayRate? (e.g. SetParameters)
{ 
  //fill the decay rate vector 
  ratesToDaughter.SetZ(theZ);
  ratesToDaughter.SetA(theA);
  ratesToDaughter.SetE(theE);
  ratesToDaughter.SetGeneration(theG);
  ratesToDaughter.SetDecayRateC(theCoefficients);
  ratesToDaughter.SetTaos(theTaos);
}


void G4RadioactiveDecay::
CalculateChainsFromParent(const G4ParticleDefinition& theParentNucleus)
{
  // Use extended Bateman equation to calculate the radioactivities of all
  // progeny of theParentNucleus.  The coefficients required to do this are 
  // calculated using the method of P. Truscott (Ph.D. thesis and
  // DERA Technical Note DERA/CIS/CIS2/7/36/4/10) 11 January 2000.
  // Coefficients are then added to the decay rate table vector

  // Create and initialise variables used in the method.
  theDecayRateVector.clear();

  G4int nGeneration = 0;

  std::vector<G4double> taos;

  // Dimensionless A coefficients of Eqs. 4.24 and 4.25 of the TN
  std::vector<G4double> Acoeffs;

  // According to Eq. 4.26 the first coefficient (A_1:1) is -1
  Acoeffs.push_back(-1.);

  const G4Ions* ion = static_cast<const G4Ions*>(&theParentNucleus);
  G4int A = ion->GetAtomicMass();
  G4int Z = ion->GetAtomicNumber();
  G4double E = ion->GetExcitationEnergy();
  G4double tao = ion->GetPDGLifeTime();
  if (tao < 0.) tao = 1e-100;
  taos.push_back(tao);
  G4int nEntry = 0;

  // Fill the decay rate container (G4RadioactiveDecayRate) with the parent 
  // isotope data
  SetDecayRate(Z,A,E,nGeneration,Acoeffs,taos);   // Fill TP with parent lifetime

  // store the decay rate in decay rate vector
  theDecayRateVector.push_back(ratesToDaughter);
  ++nEntry;

  // Now start treating the secondary generations.
  G4bool stable = false;
  G4int j;
  G4VDecayChannel* theChannel = 0;
  G4NuclearDecay* theNuclearDecayChannel = 0;

  G4ITDecay* theITChannel = 0;
  G4BetaMinusDecay* theBetaMinusChannel = 0;
  G4BetaPlusDecay* theBetaPlusChannel = 0;
  G4AlphaDecay* theAlphaChannel = 0;
  G4ProtonDecay* theProtonChannel = 0;
  G4TritonDecay* theTritonChannel = 0;
  G4NeutronDecay* theNeutronChannel = 0;
  G4SFDecay* theFissionChannel = 0;

  G4RadioactiveDecayMode theDecayMode;
  G4double theBR = 0.0;
  G4int AP = 0;
  G4int ZP = 0;
  G4int AD = 0;
  G4int ZD = 0;
  G4double EP = 0.;
  std::vector<G4double> TP;
  std::vector<G4double> RP;   // A coefficients of the previous generation
  G4ParticleDefinition *theDaughterNucleus;
  G4double daughterExcitation;
  G4double nearestEnergy = 0.0;
  G4int nearestLevelIndex = 0;
  G4ParticleDefinition *aParentNucleus;
  G4IonTable* theIonTable;
  G4DecayTable* parentDecayTable;
  G4double theRate;
  G4double TaoPlus;
  G4int nS = 0;        // Running index of first decay in a given generation
  G4int nT = nEntry;   // Total number of decays accumulated over entire history
  const G4int nMode = G4RadioactiveDecayModeSize;
  G4double brs[nMode];
  //
  theIonTable = G4ParticleTable::GetParticleTable()->GetIonTable();

  G4int loop = 0;
  while (!stable) {   /* Loop checking, 01.09.2015, D.Wright */
    loop++;
    if (loop > 10000) {
      G4Exception("G4RadioactiveDecay::CalculateChainsFromParent()", "HAD_RDM_100",
                  JustWarning, "While loop count exceeded");
      break;
    }
    nGeneration++;
    for (j = nS; j < nT; ++j) {
      // First time through, get data for parent nuclide
      ZP = theDecayRateVector[j].GetZ();
      AP = theDecayRateVector[j].GetA();
      EP = theDecayRateVector[j].GetE();
      RP = theDecayRateVector[j].GetDecayRateC();
      TP = theDecayRateVector[j].GetTaos();
      if (GetVerboseLevel() > 1) {
        G4cout << "G4RadioactiveDecay::CalculateChainsFromParent: daughters of ("
               << ZP << ", " << AP << ", " << EP
               << ") are being calculated,  generation = " << nGeneration
               << G4endl;
      }

      aParentNucleus = theIonTable->GetIon(ZP,AP,EP);
      parentDecayTable = GetDecayTable(aParentNucleus);
      if (nullptr == parentDecayTable) { continue; }

      G4DecayTable* summedDecayTable = new G4DecayTable();
      // This instance of G4DecayTable is for accumulating BRs and decay
      // channels.  It will contain one decay channel per type of decay
      // (alpha, beta, etc.); its branching ratio will be the sum of all
      // branching ratios for that type of decay of the parent.  If the
      // halflife of a particular channel is longer than some threshold,
      // that channel will be inserted specifically and its branching
      // ratio will not be included in the above sums.
      // This instance is not used to perform actual decays.

      for (G4int k = 0; k < nMode; ++k) brs[k] = 0.0;

      // Go through the decay table and sum all channels having the same decay mode
      for (G4int i = 0; i < parentDecayTable->entries(); ++i) {
        theChannel = parentDecayTable->GetDecayChannel(i);
        theNuclearDecayChannel = static_cast<G4NuclearDecay*>(theChannel);
        theDecayMode = theNuclearDecayChannel->GetDecayMode();
        daughterExcitation = theNuclearDecayChannel->GetDaughterExcitation();
        theDaughterNucleus = theNuclearDecayChannel->GetDaughterNucleus();
        AD = ((const G4Ions*)(theDaughterNucleus))->GetAtomicMass();
        ZD = ((const G4Ions*)(theDaughterNucleus))->GetAtomicNumber();  
        const G4LevelManager* levelManager =
          G4NuclearLevelData::GetInstance()->GetLevelManager(ZD,AD);

        // Check each nuclide to see if it is metastable (lifetime > 1 usec) 
        // If so, add it to the decay chain by inserting its decay channel in
        // summedDecayTable.  If not, just add its BR to sum for that decay mode. 
        if (levelManager->NumberOfTransitions() ) {
          nearestEnergy = levelManager->NearestLevelEnergy(daughterExcitation);
          if ((std::abs(daughterExcitation - nearestEnergy) < levelTolerance) &&
	      (std::abs(daughterExcitation - nearestEnergy) > DBL_EPSILON)) {
            // Level half-life is in ns and the threshold is set to 1 micros
            // by default, user can set it via the UI command
            nearestLevelIndex = (G4int)levelManager->NearestLevelIndex(daughterExcitation);
            if (levelManager->LifeTime(nearestLevelIndex)*ns >= halflifethreshold){
              // save the metastable decay channel 
              summedDecayTable->Insert(theChannel);
            } else {
              brs[theDecayMode] += theChannel->GetBR();
            }
          } else {
            brs[theDecayMode] += theChannel->GetBR();
          }
        } else {
          brs[theDecayMode] += theChannel->GetBR();
        }

      } // Combine decay channels (loop i)

      brs[BetaPlus] = brs[BetaPlus]+brs[KshellEC]+brs[LshellEC]+brs[MshellEC]+brs[NshellEC];  // Combine beta+ and EC 
      brs[KshellEC] = brs[LshellEC] = brs[MshellEC] = brs[NshellEC] = 0.0;
      for (G4int i = 0; i < nMode; ++i) {                 // loop over decay modes
        if (brs[i] > 0.) {
          switch (i) {
          case IT:
            // Decay mode is isomeric transition
            theITChannel = new G4ITDecay(aParentNucleus, brs[IT], 0.0, 0.0);

            summedDecayTable->Insert(theITChannel);
            break;

          case BetaMinus:
            // Decay mode is beta-
            theBetaMinusChannel = new G4BetaMinusDecay(aParentNucleus, brs[BetaMinus],
                                                       0.*MeV, 0.*MeV,
                                                       noFloat, allowed);
            summedDecayTable->Insert(theBetaMinusChannel);
            break;

          case BetaPlus:
            // Decay mode is beta+ + EC.
            theBetaPlusChannel = new G4BetaPlusDecay(aParentNucleus, brs[BetaPlus],
                                                     0.*MeV, 0.*MeV,
                                                     noFloat, allowed);
            summedDecayTable->Insert(theBetaPlusChannel);
            break;

          case Alpha:
            // Decay mode is alpha.
            theAlphaChannel = new G4AlphaDecay(aParentNucleus, brs[Alpha], 0.*MeV,
                                               0.*MeV, noFloat);
            summedDecayTable->Insert(theAlphaChannel);
            break;

          case Proton:
            // Decay mode is proton.
            theProtonChannel = new G4ProtonDecay(aParentNucleus, brs[Proton], 0.*MeV,
                                                 0.*MeV, noFloat);
            summedDecayTable->Insert(theProtonChannel);
            break;

          case Neutron:
            // Decay mode is neutron.
            theNeutronChannel = new G4NeutronDecay(aParentNucleus, brs[Neutron], 0.*MeV,
                                                   0.*MeV, noFloat);
            summedDecayTable->Insert(theNeutronChannel);
            break;

          case SpFission:
            // Decay mode is spontaneous fission
            theFissionChannel = new G4SFDecay(aParentNucleus, brs[SpFission], 0.*MeV,
                                              0.*MeV, noFloat);
            summedDecayTable->Insert(theFissionChannel);
            break;
	    
          case BDProton:
            // Not yet implemented
            break;

          case BDNeutron:
            // Not yet implemented
            break;

          case Beta2Minus:
            // Not yet implemented
            break;

          case Beta2Plus:
            // Not yet implemented
            break;

          case Proton2:
            // Not yet implemented
            break;

          case Neutron2:
            // Not yet implemented
            break;

          case Triton:
            // Decay mode is Triton.
            theTritonChannel = new G4TritonDecay(aParentNucleus, brs[Triton], 0.*MeV,
                                                0.*MeV, noFloat);
            summedDecayTable->Insert(theTritonChannel);
            break;

          default:
            break;
          }
        }
      }

      // loop over all branches in summedDecayTable
      //
      for (G4int i = 0; i < summedDecayTable->entries(); ++i){
        theChannel = summedDecayTable->GetDecayChannel(i);
        theNuclearDecayChannel = static_cast<G4NuclearDecay*>(theChannel);
        theBR = theChannel->GetBR();
        theDaughterNucleus = theNuclearDecayChannel->GetDaughterNucleus();

        // First check if the decay of the original nucleus is an IT channel,
        // if true create a new ground-state nucleus
        if (theNuclearDecayChannel->GetDecayMode() == IT && nGeneration == 1) {

          A = ((const G4Ions*)(theDaughterNucleus))->GetAtomicMass();
          Z = ((const G4Ions*)(theDaughterNucleus))->GetAtomicNumber();
          theDaughterNucleus=theIonTable->GetIon(Z,A,0.);
        }
        if (IsApplicable(*theDaughterNucleus) && theBR > 0.0 && 
            aParentNucleus != theDaughterNucleus) { 
          // need to make sure daughter has decay table
          parentDecayTable = GetDecayTable(theDaughterNucleus);
          if (nullptr != parentDecayTable && parentDecayTable->entries() > 0) {
            A = ((const G4Ions*)(theDaughterNucleus))->GetAtomicMass();
            Z = ((const G4Ions*)(theDaughterNucleus))->GetAtomicNumber();
            E = ((const G4Ions*)(theDaughterNucleus))->GetExcitationEnergy();

            TaoPlus = theDaughterNucleus->GetPDGLifeTime();
            if (TaoPlus <= 0.)  TaoPlus = 1e-100;

            // first set the taos, one simply need to add to the parent ones
            taos.clear();
            taos = TP;   // load lifetimes of all previous generations 
            std::size_t k;
            //check that TaoPlus differs from other taos from at least 1.e5 relative difference
            //for (k = 0; k < TP.size(); ++k){
            //if (std::abs((TaoPlus-TP[k])/TP[k])<1.e-5 ) TaoPlus=1.00001*TP[k];
            //}
            taos.push_back(TaoPlus);  // add daughter lifetime to list
            // now calculate the coefficiencies
            //
            // they are in two parts, first the less than n ones
            // Eq 4.24 of the TN
            Acoeffs.clear();
            long double ta1,ta2;
            ta2 = (long double)TaoPlus;
            for (k = 0; k < RP.size(); ++k){
              ta1 = (long double)TP[k];    // loop over lifetimes of all previous generations
              if (ta1 == ta2) {
                theRate = 1.e100;
              } else {
                theRate = ta1/(ta1-ta2);
              }
              theRate = theRate * theBR * RP[k];
              Acoeffs.push_back(theRate);
            }

            // the second part: the n:n coefficiency
            // Eq 4.25 of the TN.  Note Yn+1 is zero apart from Y1 which is -1
            // as treated at line 1013 
            theRate = 0.;
            long double aRate, aRate1;
            aRate1 = 0.L;
            for (k = 0; k < RP.size(); ++k){
              ta1 = (long double)TP[k];
              if (ta1 == ta2 ) {
                aRate = 1.e100;
              } else {
                aRate = ta2/(ta1-ta2);
              }
              aRate = aRate * (long double)(theBR * RP[k]);
              aRate1 += aRate;
            }
            theRate = -aRate1;
            Acoeffs.push_back(theRate); 	      
            SetDecayRate (Z,A,E,nGeneration,Acoeffs,taos);
            theDecayRateVector.push_back(ratesToDaughter);
            nEntry++;
          } // there are entries in the table
        } // nuclide is OK to decay
      } // end of loop (i) over decay table branches

      delete summedDecayTable;

    } // Getting contents of decay rate vector (end loop on j)
    nS = nT;
    nT = nEntry;
    if (nS == nT) stable = true;
  } // while nuclide is not stable

  // end of while loop
  // the calculation completed here


  // fill the first part of the decay rate table
  // which is the name of the original particle (isotope)
  chainsFromParent.SetIonName(theParentNucleus.GetParticleName());

  // now fill the decay table with the newly completed decay rate vector
  chainsFromParent.SetItsRates(theDecayRateVector);

  // finally add the decayratetable to the tablevector
  theParentChainTable.push_back(chainsFromParent);
}

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  SetSourceTimeProfile                                                      //
//     read in the source time profile function (histogram)                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

void G4RadioactiveDecay::SetSourceTimeProfile(const G4String& filename)
{
  std::ifstream infile ( filename, std::ios::in );
  if (!infile) {
    G4ExceptionDescription ed;
    ed << " Could not open file " << filename << G4endl; 
    G4Exception("G4RadioactiveDecay::SetSourceTimeProfile()", "HAD_RDM_001",
                FatalException, ed);
  }

  G4double bin, flux;
  NSourceBin = -1;

  G4int loop = 0;
  while (infile >> bin >> flux) {  /* Loop checking, 01.09.2015, D.Wright */
    loop++;
    if (loop > 10000) {
      G4Exception("G4RadioactiveDecay::SetSourceTimeProfile()", "HAD_RDM_100",
                  JustWarning, "While loop count exceeded");
      break;
    }
 
    NSourceBin++;
    if (NSourceBin > 99) {
      G4Exception("G4RadioactiveDecay::SetSourceTimeProfile()", "HAD_RDM_002",
                  FatalException, "Input source time file too big (>100 rows)");

    } else {
      SBin[NSourceBin] = bin * s;    // Convert read-in time to ns
      SProfile[NSourceBin] = flux;   // Dimensionless
    }
  }

  AnalogueMC = false;
  infile.close();

#ifdef G4VERBOSE
  if (GetVerboseLevel() > 2)
    G4cout <<" Source Timeprofile Nbin = " << NSourceBin <<G4endl;
#endif
}

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  SetDecayBiasProfile                                                       //
//    read in the decay bias scheme function (histogram)                      //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

void G4RadioactiveDecay::SetDecayBias(const G4String& filename)
{
  std::ifstream infile(filename, std::ios::in);
  if (!infile) G4Exception("G4RadioactiveDecay::SetDecayBias()", "HAD_RDM_001",
                           FatalException, "Unable to open bias data file" );

  G4double bin, flux;
  G4int dWindows = 0;
  G4int i ;

  theRadioactivityTables.clear();

  NDecayBin = -1;

  G4int loop = 0;
  while (infile >> bin >> flux ) {  /* Loop checking, 01.09.2015, D.Wright */
    NDecayBin++;
    loop++;
    if (loop > 10000) {
      G4Exception("G4RadioactiveDecay::SetDecayBias()", "HAD_RDM_100",
                  JustWarning, "While loop count exceeded");
      break;
    }
 
    if (NDecayBin > 99) {
      G4Exception("G4RadioactiveDecay::SetDecayBias()", "HAD_RDM_002",
                  FatalException, "Input bias file too big (>100 rows)" );
    } else {
      DBin[NDecayBin] = bin * s;     // Convert read-in time to ns
      DProfile[NDecayBin] = flux;    // Dimensionless
      if (flux > 0.) {
        decayWindows[NDecayBin] = dWindows;
        dWindows++;
        G4RadioactivityTable *rTable = new G4RadioactivityTable() ;
        theRadioactivityTables.push_back(rTable);
      }
    }
  }
  for ( i = 1; i<= NDecayBin; ++i) DProfile[i] += DProfile[i-1];  // Cumulative flux vs i 
  for ( i = 0; i<= NDecayBin; ++i) DProfile[i] /= DProfile[NDecayBin];
                             // Normalize so entries increase from 0 to 1
  // converted to accumulated probabilities

  AnalogueMC = false;
  infile.close();

#ifdef G4VERBOSE
  if (GetVerboseLevel() > 2)
    G4cout <<" Decay Bias Profile  Nbin = " << NDecayBin <<G4endl;
#endif
}

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  DecayIt                                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

G4VParticleChange*
G4RadioactiveDecay::DecayIt(const G4Track& theTrack, const G4Step&)
{
  // Initialize G4ParticleChange object, get particle details and decay table
  fParticleChangeForRadDecay.Initialize(theTrack);
  fParticleChangeForRadDecay.ProposeWeight(theTrack.GetWeight());
  const G4DynamicParticle* theParticle = theTrack.GetDynamicParticle();
  const G4ParticleDefinition* theParticleDef = theParticle->GetDefinition();

  // First check whether RDM applies to the current logical volume
  if (!isAllVolumesMode) {
    if (!std::binary_search(ValidVolumes.begin(), ValidVolumes.end(),
                     theTrack.GetVolume()->GetLogicalVolume()->GetName())) {
#ifdef G4VERBOSE
      if (GetVerboseLevel()>0) {
        G4cout <<"G4RadioactiveDecay::DecayIt : "
               << theTrack.GetVolume()->GetLogicalVolume()->GetName()
               << " is not selected for the RDM"<< G4endl;
        G4cout << " There are " << ValidVolumes.size() << " volumes" << G4endl;
        G4cout << " The Valid volumes are " << G4endl;
        for (std::size_t i = 0; i< ValidVolumes.size(); ++i)
                                  G4cout << ValidVolumes[i] << G4endl;
      }
#endif
      fParticleChangeForRadDecay.SetNumberOfSecondaries(0);

      // Kill the parent particle.
      fParticleChangeForRadDecay.ProposeTrackStatus(fStopAndKill) ;
      fParticleChangeForRadDecay.ProposeLocalEnergyDeposit(0.0);
      ClearNumberOfInteractionLengthLeft();
      return &fParticleChangeForRadDecay;
    }
  }

  // Now check if particle is valid for RDM
  if (!(IsApplicable(*theParticleDef) ) ) { 
    // Particle is not an ion or is outside the nucleuslimits for decay
#ifdef G4VERBOSE
    if (GetVerboseLevel() > 1) {
      G4cout << "G4RadioactiveDecay::DecayIt : "
             << theParticleDef->GetParticleName()
             << " is not an ion or is outside (Z,A) limits set for the decay. "
             << " Set particle change accordingly. "
             << G4endl;
    }
#endif
    fParticleChangeForRadDecay.SetNumberOfSecondaries(0);

    // Kill the parent particle
    fParticleChangeForRadDecay.ProposeTrackStatus(fStopAndKill) ;
    fParticleChangeForRadDecay.ProposeLocalEnergyDeposit(0.0);
    ClearNumberOfInteractionLengthLeft();
    return &fParticleChangeForRadDecay;
  }

  G4DecayTable* theDecayTable = GetDecayTable(theParticleDef);

  if (theDecayTable == nullptr || theDecayTable->entries() == 0) {
    // No data in the decay table.  Set particle change parameters
    // to indicate this.
#ifdef G4VERBOSE
    if (GetVerboseLevel() > 1) {
      G4cout << "G4RadioactiveDecay::DecayIt : "
             << "decay table not defined for "
             << theParticleDef->GetParticleName()
             << ". Set particle change accordingly. "
             << G4endl;
    }
#endif
    fParticleChangeForRadDecay.SetNumberOfSecondaries(0);

    // Kill the parent particle.
    fParticleChangeForRadDecay.ProposeTrackStatus(fStopAndKill) ;
    fParticleChangeForRadDecay.ProposeLocalEnergyDeposit(0.0);
    ClearNumberOfInteractionLengthLeft();
    return &fParticleChangeForRadDecay;

  } else { 
    // Data found.  Try to decay nucleus
    if (AnalogueMC) {
      G4VRadioactiveDecay::DecayAnalog(theTrack, theDecayTable);

    } else {
      // Proceed with decay using variance reduction 
      G4double energyDeposit = 0.0;
      G4double finalGlobalTime = theTrack.GetGlobalTime();
      G4double finalLocalTime = theTrack.GetLocalTime(); 
      G4int index;
      G4ThreeVector currentPosition;
      currentPosition = theTrack.GetPosition();

      fParticleChangeForRadDecay.SetSecondaryWeightByProcess(true);
      
      G4IonTable* theIonTable;
      G4ParticleDefinition* parentNucleus;

      // Get decay chains for the given nuclide
      if (!IsRateTableReady(*theParticleDef))
	CalculateChainsFromParent(*theParticleDef);
      GetChainsFromParent(*theParticleDef);

      // Declare some of the variables required in the implementation
      G4int PZ;
      G4int PA;
      G4double PE;
      G4String keyName;
      std::vector<G4double> PT;
      std::vector<G4double> PR;
      G4double taotime;
      long double decayRate;

      std::size_t i;
      G4int numberOfSecondaries;
      G4int totalNumberOfSecondaries = 0;
      G4double currentTime = 0.;
      G4int ndecaych;
      G4DynamicParticle* asecondaryparticle;
      std::vector<G4DynamicParticle*> secondaryparticles;
      std::vector<G4double> pw;
      std::vector<G4double> ptime;
      pw.clear();
      ptime.clear();

      // Now apply the nucleus splitting
      for (G4int n = 0; n < NSplit; ++n) {
        // Get the decay time following the decay probability function 
        // supplied by user  
        G4double theDecayTime = GetDecayTime();
        G4int nbin = GetDecayTimeBin(theDecayTime);

        // calculate the first part of the weight function  
        G4double weight1 = 1.; 
        if (nbin == 1) {
          weight1 = 1./DProfile[nbin-1] 
                    *(DBin[nbin]-DBin[nbin-1])/NSplit;   // width of window in ns
        } else if (nbin > 1) {
          // Go from nbin to nbin-2 because flux entries in file alternate between 0 and 1 
          weight1 = 1./(DProfile[nbin]-DProfile[nbin-2])
                    *(DBin[nbin]-DBin[nbin-1])/NSplit;
          // weight1 = (probability of choosing one of the bins)*(time width of bin)/NSplit
        }
        // it should be calculated in seconds
        weight1 /= s ;
	    
        // loop over all the possible secondaries of the nucleus
        // the first one is itself.
        for (i = 0; i < theDecayRateVector.size(); ++i) {
          PZ = theDecayRateVector[i].GetZ();
          PA = theDecayRateVector[i].GetA();
          PE = theDecayRateVector[i].GetE();
          PT = theDecayRateVector[i].GetTaos();
          PR = theDecayRateVector[i].GetDecayRateC();

          // The array of arrays theDecayRateVector contains all possible decay
          // chains of a given parent nucleus (ZP,AP,EP) to a given descendant
          // nuclide (Z,A,E).
          //
          // theDecayRateVector[0] contains the decay parameters of the parent
          // nucleus
          //           PZ = ZP
          //           PA = AP
          //           PE = EP
          //           PT[] = {TP}
          //           PR[] = {RP}
          //
          // theDecayRateVector[1] contains the decay of the parent to the first
          // generation daughter (Z1,A1,E1).
          //           PZ = Z1
          //           PA = A1
          //           PE = E1
          //           PT[] = {TP, T1}
          //           PR[] = {RP, R1}
          //
          // theDecayRateVector[2] contains the decay of the parent to the first
          // generation daughter (Z1,A1,E1) and the decay of the first
          // generation daughter to the second generation daughter (Z2,A2,E2).
          //           PZ = Z2
          //           PA = A2
          //           PE = E2
          //           PT[] = {TP, T1, T2}
          //           PR[] = {RP, R1, R2}
          //
          // theDecayRateVector[3] may contain a branch chain
          //           PZ = Z2a
          //           PA = A2a
          //           PE = E2a
          //           PT[] = {TP, T1, T2a}
          //           PR[] = {RP, R1, R2a}
          //
          // and so on.

          // Calculate the decay rate of the isotope.  decayRate is the
          // radioactivity of isotope (PZ,PA,PE) at 'theDecayTime'
          // it will be used to calculate the statistical weight of the 
          // decay products of this isotope

          // For each nuclide, calculate all the decay chains which can reach
          // the parent nuclide
          decayRate = 0.L;
          for (G4int j = 0; j < G4int(PT.size() ); ++j) {
            taotime = ConvolveSourceTimeProfile(theDecayTime,PT[j]);
            decayRate -= PR[j] * (long double)taotime;  // PRs are Acoeffs, taotime is inverse time
            // Eq.4.23 of of the TN
            // note the negative here is required as the rate in the
            // equation is defined to be negative, 
            // i.e. decay away, but we need positive value here.

            // G4cout << j << "\t"<< PT[j]/s << "\t" << PR[j] << "\t" << decayRate << G4endl;
          }

          // At this point any negative decay rates are probably small enough
          // (order 10**-30) that negative values are likely due to cancellation
          // errors.  Set them to zero.
          if (decayRate < 0.0) decayRate = 0.0;

          //  G4cout <<theDecayTime/s <<"\t"<<nbin<<G4endl;
          //  G4cout << theTrack.GetWeight() <<"\t"<<weight1<<"\t"<<decayRate<< G4endl;

          // Add isotope to the radioactivity tables
          // One table for each observation time window specified in
          // SetDecayBias(G4String filename)

          theRadioactivityTables[decayWindows[nbin-1]]
                ->AddIsotope(PZ,PA,PE,weight1*decayRate,theTrack.GetWeight());

          // Now calculate the statistical weight
          // One needs to fold the source bias function with the decaytime
          // also need to include the track weight! (F.Lei, 28/10/10)
          G4double weight = weight1*decayRate*theTrack.GetWeight();

          // decay the isotope 
          theIonTable = (G4IonTable *)(G4ParticleTable::GetParticleTable()->GetIonTable());
          parentNucleus = theIonTable->GetIon(PZ,PA,PE);

          // Create a temprary products buffer.
          // Its contents to be transfered to the products at the end of the loop
          G4DecayProducts* tempprods = nullptr;

          // Decide whether to apply branching ratio bias or not
          if (BRBias) {
            G4DecayTable* decayTable = GetDecayTable(parentNucleus);
	    G4VDecayChannel* theDecayChannel = nullptr;
	    if (nullptr != decayTable) {
	      ndecaych = G4int(decayTable->entries()*G4UniformRand());
              theDecayChannel = decayTable->GetDecayChannel(ndecaych);
	    }

            if (theDecayChannel == nullptr) {
              // Decay channel not found.

              if (GetVerboseLevel() > 0) {
                G4cout << " G4RadioactiveDecay::DoIt : cannot determine decay channel ";
                G4cout << " for this nucleus; decay as if no biasing active. ";
                G4cout << G4endl;
                if (nullptr != decayTable) { decayTable ->DumpInfo(); }
              }
	      // DHW 6 Dec 2010 - do decay as if no biasing to avoid deref of temppprods
              tempprods = DoDecay(*parentNucleus, theDecayTable);
            } else {
              // A decay channel has been identified, so execute the DecayIt.
              G4double tempmass = parentNucleus->GetPDGMass();
              tempprods = theDecayChannel->DecayIt(tempmass);
              weight *= (theDecayChannel->GetBR())*(decayTable->entries());
            }
          } else {
            tempprods = DoDecay(*parentNucleus, theDecayTable);
          }

          // save the secondaries for buffers
          numberOfSecondaries = tempprods->entries();
          currentTime = finalGlobalTime + theDecayTime;
          for (index = 0; index < numberOfSecondaries; ++index) {
            asecondaryparticle = tempprods->PopProducts();
            if (asecondaryparticle->GetDefinition()->GetPDGStable() ) {
              pw.push_back(weight);
              ptime.push_back(currentTime);
              secondaryparticles.push_back(asecondaryparticle);
            }
            // Generate gammas and Xrays from excited nucleus, added by L.Desorgher
            else if (((const G4Ions*)(asecondaryparticle->GetDefinition()))
                      ->GetExcitationEnergy() > 0. && weight > 0.) {  //Compute the gamma
              G4ParticleDefinition* apartDef = asecondaryparticle->GetDefinition();
              AddDeexcitationSpectrumForBiasMode(apartDef,weight,currentTime,pw,
                                                 ptime,secondaryparticles);
            }
          }

          delete tempprods;
        } // end of i loop
      } // end of n loop 

      // now deal with the secondaries in the two stl containers
      // and submmit them back to the tracking manager
      totalNumberOfSecondaries = (G4int)pw.size();
      fParticleChangeForRadDecay.SetNumberOfSecondaries(totalNumberOfSecondaries);
      for (index=0; index < totalNumberOfSecondaries; ++index) { 
        G4Track* secondary = new G4Track(secondaryparticles[index],
                                         ptime[index], currentPosition);
        secondary->SetGoodForTrackingFlag(); 	   
        secondary->SetTouchableHandle(theTrack.GetTouchableHandle());
        secondary->SetWeight(pw[index]); 	   
        fParticleChangeForRadDecay.AddSecondary(secondary); 
      }

      // Kill the parent particle
      fParticleChangeForRadDecay.ProposeTrackStatus(fStopAndKill) ;
      fParticleChangeForRadDecay.ProposeLocalEnergyDeposit(energyDeposit); 
      fParticleChangeForRadDecay.ProposeLocalTime(finalLocalTime);
      // Reset NumberOfInteractionLengthLeft.
      ClearNumberOfInteractionLengthLeft();
    } // end VR decay

    return &fParticleChangeForRadDecay;
  }  // end of data found branch 
} 


// Add gamma, X-ray, conversion and auger electrons for bias mode
void 
G4RadioactiveDecay::AddDeexcitationSpectrumForBiasMode(G4ParticleDefinition* apartDef,
                                        G4double weight,G4double currentTime,
                                        std::vector<double>& weights_v,
                                        std::vector<double>& times_v,
                                        std::vector<G4DynamicParticle*>& secondaries_v)
{
  G4double elevel=((const G4Ions*)(apartDef))->GetExcitationEnergy();
  G4double life_time=apartDef->GetPDGLifeTime();
  G4ITDecay* anITChannel = 0;

  while (life_time < halflifethreshold && elevel > 0.) {
    decayIT->SetupDecay(apartDef);
    G4DecayProducts* pevap_products = decayIT->DecayIt(0.);
    G4int nb_pevapSecondaries = pevap_products->entries();

    G4DynamicParticle* a_pevap_secondary = 0;
    G4ParticleDefinition* secDef = 0;
    for (G4int ind = 0; ind < nb_pevapSecondaries; ind++) {
      a_pevap_secondary= pevap_products->PopProducts();
      secDef = a_pevap_secondary->GetDefinition();

      if (secDef->GetBaryonNumber() > 4) {
        elevel = ((const G4Ions*)(secDef))->GetExcitationEnergy();
        life_time = secDef->GetPDGLifeTime();
        apartDef = secDef;
        if (secDef->GetPDGStable() ) {
          weights_v.push_back(weight);
          times_v.push_back(currentTime);
          secondaries_v.push_back(a_pevap_secondary);
        }
      } else {
        weights_v.push_back(weight);
        times_v.push_back(currentTime);
        secondaries_v.push_back(a_pevap_secondary);
      }
    }

    delete anITChannel;
    delete pevap_products;
  }
}

