//
// ********************************************************************
// * 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.          *
// ********************************************************************
//
/// \file biasing/ReverseMC01/src/RMC01AnalysisManager.cc
/// \brief Implementation of the RMC01AnalysisManager class
//
//
//////////////////////////////////////////////////////////////
//      Class Name:        RMC01AnalysisManager
//        Author:               L. Desorgher
//         Organisation:         SpaceIT GmbH
//        Contract:        ESA contract 21435/08/NL/AT
//         Customer:             ESA/ESTEC
//////////////////////////////////////////////////////////////

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#include "RMC01AnalysisManager.hh"

#include "RMC01AnalysisManagerMessenger.hh"
#include "RMC01SD.hh"

#include "G4AdjointSimManager.hh"
#include "G4Electron.hh"
#include "G4Gamma.hh"
#include "G4PhysicalConstants.hh"
#include "G4Proton.hh"
#include "G4RunManager.hh"
#include "G4SDManager.hh"
#include "G4SystemOfUnits.hh"
#include "G4THitsCollection.hh"
#include "G4Timer.hh"

RMC01AnalysisManager* RMC01AnalysisManager::fInstance = 0;

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

RMC01AnalysisManager::RMC01AnalysisManager()
  : fAccumulated_edep(0.),
    fAccumulated_edep2(0.),
    fMean_edep(0.),
    fError_mean_edep(0.),
    fRelative_error(0.),
    fElapsed_time(0.),
    fPrecision_to_reach(0.),
    fStop_run_if_precision_reached(true),
    fNb_evt_modulo_for_convergence_test(5000),
    fEdep_rmatrix_vs_electron_prim_energy(0),
    fElectron_current_rmatrix_vs_electron_prim_energy(0),
    fGamma_current_rmatrix_vs_electron_prim_energy(0),
    fEdep_rmatrix_vs_gamma_prim_energy(0),
    fElectron_current_rmatrix_vs_gamma_prim_energy(0),
    fGamma_current_rmatrix_vs_gamma_prim_energy(0),
    fEdep_rmatrix_vs_proton_prim_energy(0),
    fElectron_current_rmatrix_vs_proton_prim_energy(0),
    fProton_current_rmatrix_vs_proton_prim_energy(0),
    fGamma_current_rmatrix_vs_proton_prim_energy(0),
    fFactoryOn(false),
    fPrimSpectrumType(EXPO),
    fAlpha_or_E0(.5 * MeV),
    fAmplitude_prim_spectrum(1.),
    fEmin_prim_spectrum(1. * keV),
    fEmax_prim_spectrum(20. * MeV),
    fAdjoint_sim_mode(true),
    fNb_evt_per_adj_evt(2)
{
  fMsg = new RMC01AnalysisManagerMessenger(this);

  //-------------
  // Timer for convergence vector
  //-------------

  fTimer = new G4Timer();

  //---------------------------------
  // Primary particle ID for normalisation of adjoint results
  //---------------------------------

  fPrimPDG_ID = G4Electron::Electron()->GetPDGEncoding();

  fFileName[0] = "sim";
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

RMC01AnalysisManager::~RMC01AnalysisManager()
{
  ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

RMC01AnalysisManager* RMC01AnalysisManager::GetInstance()
{
  if (fInstance == 0) fInstance = new RMC01AnalysisManager;
  return fInstance;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::BeginOfRun(const G4Run* aRun)
{
  fAccumulated_edep = 0.;
  fAccumulated_edep2 = 0.;
  fNentry = 0.0;
  fRelative_error = 1.;
  fMean_edep = 0.;
  fError_mean_edep = 0.;
  fAdjoint_sim_mode = G4AdjointSimManager::GetInstance()->GetAdjointSimMode();

  if (fAdjoint_sim_mode) {
    fNb_evt_per_adj_evt = aRun->GetNumberOfEventToBeProcessed()
                          / G4AdjointSimManager::GetInstance()->GetNbEvtOfLastRun();
    fConvergenceFileOutput.open("ConvergenceOfAdjointSimulationResults.txt", std::ios::out);
    fConvergenceFileOutput << "Normalised Edep[MeV]\terror[MeV]\tcomputing_time[s]" << std::endl;
  }
  else {
    fConvergenceFileOutput.open("ConvergenceOfForwardSimulationResults.txt", std::ios::out);
    fConvergenceFileOutput << "Edep per event [MeV]\terror[MeV]\tcomputing_time[s]" << std::endl;
  }
  fConvergenceFileOutput.setf(std::ios::scientific);
  fConvergenceFileOutput.precision(6);

  fTimer->Start();
  fElapsed_time = 0.;

  Book();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::EndOfRun(const G4Run* aRun)
{
  fTimer->Stop();
  G4int nb_evt = aRun->GetNumberOfEvent();
  G4double factor = 1. / nb_evt;
  if (!fAdjoint_sim_mode) {
    G4cout << "Results of forward simulation!" << std::endl;
    G4cout << "edep per event [MeV] = " << fMean_edep << std::endl;
    G4cout << "error[MeV] = " << fError_mean_edep << std::endl;
  }

  else {
    G4cout << "Results of reverse/adjoint simulation!" << std::endl;
    G4cout << "normalised edep [MeV] = " << fMean_edep << std::endl;
    G4cout << "error[MeV] = " << fError_mean_edep << std::endl;
    factor = 1. * G4AdjointSimManager::GetInstance()->GetNbEvtOfLastRun() * fNb_evt_per_adj_evt
             / aRun->GetNumberOfEvent();
  }
  Save(factor);
  fConvergenceFileOutput.close();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::BeginOfEvent(const G4Event*)
{
  ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::EndOfEvent(const G4Event* anEvent)
{
  if (fAdjoint_sim_mode)
    EndOfEventForAdjointSimulation(anEvent);
  else
    EndOfEventForForwardSimulation(anEvent);

  // Test convergence. The error is already computed
  //--------------------------------------
  G4int nb_event = anEvent->GetEventID() + 1;
  // G4double factor=1.;
  if (fAdjoint_sim_mode) {
    G4double n_adj_evt = nb_event / fNb_evt_per_adj_evt;
    // nb_event/fNb_evt_per_adj_evt;
    if (n_adj_evt * fNb_evt_per_adj_evt == nb_event) {
      nb_event = static_cast<G4int>(n_adj_evt);
    }
    else
      nb_event = 0;
  }

  if (nb_event > 100 && fStop_run_if_precision_reached && fPrecision_to_reach > fRelative_error) {
    G4cout << fPrecision_to_reach * 100. << "%  Precision reached!" << std::endl;
    fTimer->Stop();
    fElapsed_time += fTimer->GetRealElapsed();
    fConvergenceFileOutput << fMean_edep << '\t' << fError_mean_edep << '\t' << fElapsed_time
                           << std::endl;
    G4RunManager::GetRunManager()->AbortRun(true);
  }

  if (nb_event > 0 && nb_event % fNb_evt_modulo_for_convergence_test == 0) {
    fTimer->Stop();
    fElapsed_time += fTimer->GetRealElapsed();
    fTimer->Start();
    fConvergenceFileOutput << fMean_edep << '\t' << fError_mean_edep << '\t' << fElapsed_time
                           << std::endl;
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::EndOfEventForForwardSimulation(const G4Event* anEvent)
{
  G4SDManager* SDman = G4SDManager::GetSDMpointer();
  G4HCofThisEvent* HCE = anEvent->GetHCofThisEvent();
  RMC01DoubleWithWeightHitsCollection* edepCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("edep")));

  RMC01DoubleWithWeightHitsCollection* electronCurrentCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("current_electron")));

  RMC01DoubleWithWeightHitsCollection* protonCurrentCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("current_proton")));

  RMC01DoubleWithWeightHitsCollection* gammaCurrentCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("current_gamma")));

  // Total energy deposited in Event
  //-------------------------------
  G4double totEdep = 0;
  std::size_t i;
  for (i = 0; i < edepCollection->entries(); ++i)
    totEdep += (*edepCollection)[i]->GetValue() * (*edepCollection)[i]->GetWeight();

  if (totEdep > 0.) {
    fAccumulated_edep += totEdep;
    fAccumulated_edep2 += totEdep * totEdep;
    fNentry += 1.0;
    G4PrimaryParticle* thePrimary = anEvent->GetPrimaryVertex()->GetPrimary();
    G4double E0 = thePrimary->GetG4code()->GetPDGMass();
    G4double P = thePrimary->GetMomentum().mag();
    G4double prim_ekin = std::sqrt(E0 * E0 + P * P) - E0;
    fEdep_vs_prim_ekin->fill(prim_ekin, totEdep);
  }
  ComputeMeanEdepAndError(anEvent, fMean_edep, fError_mean_edep);
  if (fError_mean_edep > 0) fRelative_error = fError_mean_edep / fMean_edep;

  // Particle current on sensitive cylinder
  //-------------------------------------

  for (i = 0; i < electronCurrentCollection->entries(); ++i) {
    G4double ekin = (*electronCurrentCollection)[i]->GetValue();
    G4double weight = (*electronCurrentCollection)[i]->GetWeight();
    fElectron_current->fill(ekin, weight);
  }

  for (i = 0; i < protonCurrentCollection->entries(); ++i) {
    G4double ekin = (*protonCurrentCollection)[i]->GetValue();
    G4double weight = (*protonCurrentCollection)[i]->GetWeight();
    fProton_current->fill(ekin, weight);
  }

  for (i = 0; i < gammaCurrentCollection->entries(); ++i) {
    G4double ekin = (*gammaCurrentCollection)[i]->GetValue();
    G4double weight = (*gammaCurrentCollection)[i]->GetWeight();
    fGamma_current->fill(ekin, weight);
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::EndOfEventForAdjointSimulation(const G4Event* anEvent)
{
  // Output from Sensitive volume computed during the forward tracking phase
  //-----------------------------------------------------------------------
  G4SDManager* SDman = G4SDManager::GetSDMpointer();
  G4HCofThisEvent* HCE = anEvent->GetHCofThisEvent();
  RMC01DoubleWithWeightHitsCollection* edepCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("edep")));

  RMC01DoubleWithWeightHitsCollection* electronCurrentCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("current_electron")));

  RMC01DoubleWithWeightHitsCollection* protonCurrentCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("current_proton")));

  RMC01DoubleWithWeightHitsCollection* gammaCurrentCollection =
    (RMC01DoubleWithWeightHitsCollection*)(HCE->GetHC(SDman->GetCollectionID("current_gamma")));

  // Computation of total energy deposited in fwd tracking phase
  //-------------------------------
  G4double totEdep = 0;
  std::size_t i;
  for (i = 0; i < edepCollection->entries(); ++i)
    totEdep += (*edepCollection)[i]->GetValue() * (*edepCollection)[i]->GetWeight();

  // Output from adjoint tracking phase
  //----------------------------------------------------------------------------

  G4AdjointSimManager* theAdjointSimManager = G4AdjointSimManager::GetInstance();

  size_t nb_adj_track = theAdjointSimManager->GetNbOfAdointTracksReachingTheExternalSurface();
  G4double total_normalised_weight = 0.;

  // We need to loop over the adjoint tracks that have reached the external
  // surface.
  for (std::size_t j = 0; j < nb_adj_track; ++j) {
    G4int pdg_nb = theAdjointSimManager->GetFwdParticlePDGEncodingAtEndOfLastAdjointTrack(j);
    G4double prim_ekin = theAdjointSimManager->GetEkinAtEndOfLastAdjointTrack(j);
    G4double adj_weight = theAdjointSimManager->GetWeightAtEndOfLastAdjointTrack(j);

    // Factor of normalisation to user defined prim spectrum (power law or exp)
    //------------------------------------------------------------------------
    G4double normalised_weight = 0.;
    if (pdg_nb == fPrimPDG_ID && prim_ekin >= fEmin_prim_spectrum
        && prim_ekin <= fEmax_prim_spectrum)
      normalised_weight = adj_weight * PrimDiffAndDirFluxForAdjointSim(prim_ekin);
    total_normalised_weight += normalised_weight;

    // Answer matrices
    //-------------
    G4H1* edep_rmatrix = 0;
    G4H2* electron_current_rmatrix = 0;
    G4H2* gamma_current_rmatrix = 0;
    G4H2* proton_current_rmatrix = 0;

    if (pdg_nb == G4Electron::Electron()->GetPDGEncoding()) {  // e- matrices
      edep_rmatrix = fEdep_rmatrix_vs_electron_prim_energy;
      electron_current_rmatrix = fElectron_current_rmatrix_vs_electron_prim_energy;
      gamma_current_rmatrix = fGamma_current_rmatrix_vs_electron_prim_energy;
    }
    else if (pdg_nb == G4Gamma::Gamma()->GetPDGEncoding()) {
      // gammma answer matrices
      edep_rmatrix = fEdep_rmatrix_vs_gamma_prim_energy;
      electron_current_rmatrix = fElectron_current_rmatrix_vs_gamma_prim_energy;
      gamma_current_rmatrix = fGamma_current_rmatrix_vs_gamma_prim_energy;
    }
    else if (pdg_nb == G4Proton::Proton()->GetPDGEncoding()) {
      // proton answer matrices
      edep_rmatrix = fEdep_rmatrix_vs_proton_prim_energy;
      electron_current_rmatrix = fElectron_current_rmatrix_vs_proton_prim_energy;
      gamma_current_rmatrix = fGamma_current_rmatrix_vs_proton_prim_energy;
      proton_current_rmatrix = fProton_current_rmatrix_vs_proton_prim_energy;
    }
    // Register histo edep vs prim ekin
    //----------------------------------
    if (normalised_weight > 0) fEdep_vs_prim_ekin->fill(prim_ekin, totEdep * normalised_weight);
    // Registering answer matrix
    //---------------------------
    edep_rmatrix->fill(prim_ekin, totEdep * adj_weight / cm2);

    // Registering of current of particles on the sensitive volume
    //------------------------------------------------------------

    for (i = 0; i < electronCurrentCollection->entries(); ++i) {
      G4double ekin = (*electronCurrentCollection)[i]->GetValue();
      G4double weight = (*electronCurrentCollection)[i]->GetWeight();
      fElectron_current->fill(ekin, weight * normalised_weight);
      electron_current_rmatrix->fill(prim_ekin, ekin, weight * adj_weight / cm2);
    }
    for (i = 0; i < protonCurrentCollection->entries(); ++i) {
      G4double ekin = (*protonCurrentCollection)[i]->GetValue();
      G4double weight = (*protonCurrentCollection)[i]->GetWeight();
      fProton_current->fill(ekin, weight * normalised_weight);
      proton_current_rmatrix->fill(prim_ekin, ekin, weight * adj_weight / cm2);
    }
    for (i = 0; i < gammaCurrentCollection->entries(); ++i) {
      G4double ekin = (*gammaCurrentCollection)[i]->GetValue();
      G4double weight = (*gammaCurrentCollection)[i]->GetWeight();
      fGamma_current->fill(ekin, weight * normalised_weight);
      gamma_current_rmatrix->fill(prim_ekin, ekin, weight * adj_weight / cm2);
    }
  }

  // Registering of total energy deposited in Event
  //-------------------------------
  G4bool new_mean_computed = false;
  if (totEdep > 0.) {
    if (total_normalised_weight > 0.) {
      G4double edep = totEdep * total_normalised_weight;

      // Check if the edep is not wrongly too high
      //-----------------------------------------
      G4double new_mean(0.0), new_error(0.0);
      fAccumulated_edep += edep;
      fAccumulated_edep2 += edep * edep;
      fNentry += 1.0;
      ComputeMeanEdepAndError(anEvent, new_mean, new_error);
      G4double new_relative_error = 1.;
      if (new_error > 0) new_relative_error = new_error / new_mean;
      if (fRelative_error < 0.10 && new_relative_error > 1.5 * fRelative_error) {
        G4cout << "Potential wrong adjoint weight!" << std::endl;
        G4cout << "The results of this event will not be registered!" << std::endl;
        G4cout << "previous mean edep [MeV] " << fMean_edep << std::endl;
        G4cout << "previous relative error " << fRelative_error << std::endl;
        G4cout << "new rejected mean edep [MeV] " << new_mean << std::endl;
        G4cout << "new rejected relative error " << new_relative_error << std::endl;
        fAccumulated_edep -= edep;
        fAccumulated_edep2 -= edep * edep;
        fNentry -= 1.0;
        return;
      }
      else {  // accepted
        fMean_edep = new_mean;
        fError_mean_edep = new_error;
        fRelative_error = new_relative_error;
        new_mean_computed = true;
      }
    }

    if (!new_mean_computed) {
      ComputeMeanEdepAndError(anEvent, fMean_edep, fError_mean_edep);
      fRelative_error = (fMean_edep > 0.0) ? fError_mean_edep / fMean_edep : 0.0;
    }
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double RMC01AnalysisManager::PrimDiffAndDirFluxForAdjointSim(G4double prim_energy)
{
  G4double flux = fAmplitude_prim_spectrum;
  if (fPrimSpectrumType == EXPO)
    flux *= std::exp(-prim_energy / fAlpha_or_E0);
  else
    flux *= std::pow(prim_energy, -fAlpha_or_E0);
  return flux;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
/*
void  RMC01AnalysisManager::WriteHisto(G4H1* anHisto,
            G4double scaling_factor, G4String fileName, G4String header_lines)
{ std::fstream FileOutput(fileName, std::ios::out);
  FileOutput<<header_lines;
  FileOutput.setf(std::ios::scientific);
  FileOutput.precision(6);

  for (G4int i =0;i<G4int(anHisto->axis().bins());++i) {
        FileOutput<<anHisto->axis().bin_lower_edge(i)
              <<'\t'<<anHisto->axis().bin_upper_edge(i)
              <<'\t'<<anHisto->bin_height(i)*scaling_factor
              <<'\t'<<anHisto->bin_error(i)*scaling_factor<<std::endl;
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void  RMC01AnalysisManager::WriteHisto(G4H2* anHisto,
            G4double scaling_factor, G4String fileName, G4String header_lines)
{ std::fstream FileOutput(fileName, std::ios::out);
  FileOutput<<header_lines;

  FileOutput.setf(std::ios::scientific);
  FileOutput.precision(6);

  for (G4int i =0;i<G4int(anHisto->axis_x().bins());++i) {
    for (G4int j =0;j<G4int(anHisto->axis_y().bins());++j) {
       FileOutput<<anHisto->axis_x().bin_lower_edge(i)
                     <<'\t'<<anHisto->axis_x().bin_upper_edge(i)
                   <<'\t'<<anHisto->axis_y().bin_lower_edge(i)
                   <<'\t'<<anHisto->axis_y().bin_upper_edge(i)
                   <<'\t'<<anHisto->bin_height(i,j)*scaling_factor
                <<'\t'<<anHisto->bin_error(i,j)*scaling_factor
                                                                  <<std::endl;
        }
  }
}
*/
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::ComputeMeanEdepAndError(const G4Event* anEvent, G4double& mean,
                                                   G4double& error)
{
  G4int nb_event = anEvent->GetEventID() + 1;
  G4double factor = 1.;
  if (fAdjoint_sim_mode) {
    nb_event /= fNb_evt_per_adj_evt;
    factor = 1. * G4AdjointSimManager::GetInstance()->GetNbEvtOfLastRun();
  }

  // VI: error computation now is based on number of entries and not
  //     number of events
  // LD: This is wrong! With the use of fNentry the results were no longer
  //     correctly normalised. The mean and the error should be computed
  //     with nb_event. The old computation has been reset.
  // VI: OK, but let computations be double
  if (nb_event > 0) {
    G4double norm = 1.0 / (G4double)nb_event;
    mean = fAccumulated_edep * norm;
    G4double mean_x2 = fAccumulated_edep2 * norm;
    G4double zz = mean_x2 - mean * mean;
    /*
     G4cout << "Nevt= " << nb_event <<  " mean= " << mean
            << "  mean_x2= " <<  mean_x2 << " x2 - x*x= "
            << zz << G4endl;
    */
    error = factor * std::sqrt(std::max(zz, 0.) * norm);
    mean *= factor;
  }
  else {
    mean = 0;
    error = 0;
  }
  // G4cout << "Aend: " << mean << " " << error << G4endl;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::SetPrimaryExpSpectrumForAdjointSim(const G4String& particle_name,
                                                              G4double omni_fluence, G4double E0,
                                                              G4double Emin, G4double Emax)
{
  fPrimSpectrumType = EXPO;
  if (particle_name == "e-")
    fPrimPDG_ID = G4Electron::Electron()->GetPDGEncoding();
  else if (particle_name == "gamma")
    fPrimPDG_ID = G4Gamma::Gamma()->GetPDGEncoding();
  else if (particle_name == "proton")
    fPrimPDG_ID = G4Proton::Proton()->GetPDGEncoding();
  else {
    G4cout << "The particle that you did select is not in the candidate "
           << "list for primary [e-, gamma, proton]!" << G4endl;
    return;
  }
  fAlpha_or_E0 = E0;
  fAmplitude_prim_spectrum =
    omni_fluence / E0 / (std::exp(-Emin / E0) - std::exp(-Emax / E0)) / 4. / pi;
  fEmin_prim_spectrum = Emin;
  fEmax_prim_spectrum = Emax;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::SetPrimaryPowerLawSpectrumForAdjointSim(const G4String& particle_name,
                                                                   G4double omni_fluence,
                                                                   G4double alpha, G4double Emin,
                                                                   G4double Emax)
{
  fPrimSpectrumType = POWER;
  if (particle_name == "e-")
    fPrimPDG_ID = G4Electron::Electron()->GetPDGEncoding();
  else if (particle_name == "gamma")
    fPrimPDG_ID = G4Gamma::Gamma()->GetPDGEncoding();
  else if (particle_name == "proton")
    fPrimPDG_ID = G4Proton::Proton()->GetPDGEncoding();
  else {
    G4cout << "The particle that you did select is not in the candidate"
           << " list for primary [e-, gamma, proton]!" << G4endl;
    return;
  }

  if (alpha == 1.) {
    fAmplitude_prim_spectrum = omni_fluence / std::log(Emax / Emin) / 4. / pi;
  }
  else {
    G4double p = 1. - alpha;
    fAmplitude_prim_spectrum = omni_fluence / p / (std::pow(Emax, p) - std::pow(Emin, p)) / 4. / pi;
  }

  fAlpha_or_E0 = alpha;
  fEmin_prim_spectrum = Emin;
  fEmax_prim_spectrum = Emax;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::Book()
{
  //----------------------
  // Creation of histograms
  //----------------------

  // Energy binning of the histograms : 60 log bins over [1keV-1GeV]

  G4double emin = 1. * keV;
  G4double emax = 1. * GeV;

  // file_name
  fFileName[0] = "forward_sim";
  if (fAdjoint_sim_mode) fFileName[0] = "adjoint_sim";

  // Histo manager
  G4AnalysisManager* theHistoManager = G4AnalysisManager::Instance();
  theHistoManager->SetDefaultFileType("root");
  G4String extension = theHistoManager->GetFileType();
  fFileName[1] = fFileName[0] + "." + extension;
  theHistoManager->SetFirstHistoId(1);

  G4bool fileOpen = theHistoManager->OpenFile(fFileName[0]);
  if (!fileOpen) {
    G4cout << "\n---> RMC01AnalysisManager::Book(): cannot open " << fFileName[1] << G4endl;
    return;
  }

  // Create directories
  theHistoManager->SetHistoDirectoryName("histo");

  // Histograms for :
  //         1)the forward simulation results
  //         2)the Reverse MC  simulation results normalised to a user spectrum
  //------------------------------------------------------------------------

  G4int idHisto =
    theHistoManager->CreateH1(G4String("Edep_vs_prim_ekin"), G4String("edep vs e- primary energy"),
                              60, emin, emax, "none", "none", G4String("log"));
  fEdep_vs_prim_ekin = theHistoManager->GetH1(idHisto);

  idHisto = theHistoManager->CreateH1(G4String("elecron_current"), G4String("electron"), 60, emin,
                                      emax, "none", "none", G4String("log"));

  fElectron_current = theHistoManager->GetH1(idHisto);

  idHisto = theHistoManager->CreateH1(G4String("proton_current"), G4String("proton"), 60, emin,
                                      emax, "none", "none", G4String("log"));
  fProton_current = theHistoManager->GetH1(idHisto);

  idHisto = theHistoManager->CreateH1(G4String("gamma_current"), G4String("gamma"), 60, emin, emax,
                                      "none", "none", G4String("log"));
  fGamma_current = theHistoManager->GetH1(idHisto);

  // Response matrices for the adjoint simulation only
  //-----------------------------------------------
  if (fAdjoint_sim_mode) {
    // Response matrices for external isotropic e- source
    //--------------------------------------------------

    idHisto = theHistoManager->CreateH1(G4String("Edep_rmatrix_vs_electron_prim_energy"),
                                        G4String("electron RM vs e- primary energy"), 60, emin,
                                        emax, "none", "none", G4String("log"));
    fEdep_rmatrix_vs_electron_prim_energy = theHistoManager->GetH1(idHisto);

    idHisto = theHistoManager->CreateH2(
      G4String("Electron_current_rmatrix_vs_electron_prim_energy"),
      G4String("electron current  RM vs e- primary energy"), 60, emin, emax, 60, emin, emax, "none",
      "none", "none", "none", G4String("log"), G4String("log"));

    fElectron_current_rmatrix_vs_electron_prim_energy = theHistoManager->GetH2(idHisto);

    idHisto = theHistoManager->CreateH2(G4String("Gamma_current_rmatrix_vs_electron_prim_energy"),
                                        G4String("gamma current  RM vs e- primary energy"), 60,
                                        emin, emax, 60, emin, emax, "none", "none", "none", "none",
                                        G4String("log"), G4String("log"));

    fGamma_current_rmatrix_vs_electron_prim_energy = theHistoManager->GetH2(idHisto);

    // Response matrices for external isotropic gamma source

    idHisto = theHistoManager->CreateH1(G4String("Edep_rmatrix_vs_gamma_prim_energy"),
                                        G4String("electron RM vs gamma primary energy"), 60, emin,
                                        emax, "none", "none", G4String("log"));
    fEdep_rmatrix_vs_gamma_prim_energy = theHistoManager->GetH1(idHisto);

    idHisto = theHistoManager->CreateH2(G4String("Electron_current_rmatrix_vs_gamma_prim_energy"),
                                        G4String("electron current  RM vs gamma primary energy"),
                                        60, emin, emax, 60, emin, emax, "none", "none", "none",
                                        "none", G4String("log"), G4String("log"));

    fElectron_current_rmatrix_vs_gamma_prim_energy = theHistoManager->GetH2(idHisto);

    idHisto = theHistoManager->CreateH2(G4String("Gamma_current_rmatrix_vs_gamma_prim_energy"),
                                        G4String("gamma current  RM vs gamma primary energy"), 60,
                                        emin, emax, 60, emin, emax, "none", "none", "none", "none",
                                        G4String("log"), G4String("log"));

    fGamma_current_rmatrix_vs_gamma_prim_energy = theHistoManager->GetH2(idHisto);

    // Response matrices for external isotropic proton source
    idHisto = theHistoManager->CreateH1(G4String("Edep_rmatrix_vs_proton_prim_energy"),
                                        G4String("electron RM vs proton primary energy"), 60, emin,
                                        emax, "none", "none", G4String("log"));
    fEdep_rmatrix_vs_proton_prim_energy = theHistoManager->GetH1(idHisto);

    idHisto = theHistoManager->CreateH2(G4String("Electron_current_rmatrix_vs_proton_prim_energy"),
                                        G4String("electron current  RM vs proton primary energy"),
                                        60, emin, emax, 60, emin, emax, "none", "none", "none",
                                        "none", G4String("log"), G4String("log"));

    fElectron_current_rmatrix_vs_proton_prim_energy = theHistoManager->GetH2(idHisto);

    idHisto = theHistoManager->CreateH2(G4String("Gamma_current_rmatrix_vs_proton_prim_energy"),
                                        G4String("gamma current  RM vs proton primary energy"), 60,
                                        emin, emax, 60, emin, emax, "none", "none", "none", "none",
                                        G4String("log"), G4String("log"));

    fGamma_current_rmatrix_vs_proton_prim_energy = theHistoManager->GetH2(idHisto);

    idHisto = theHistoManager->CreateH2(G4String("Proton_current_rmatrix_vs_proton_prim_energy"),
                                        G4String("proton current  RM vs proton primary energy"), 60,
                                        emin, emax, 60, emin, emax, "none", "none", "none", "none",
                                        G4String("log"), G4String("log"));

    fProton_current_rmatrix_vs_proton_prim_energy = theHistoManager->GetH2(idHisto);
  }
  fFactoryOn = true;
  G4cout << "\n----> Histogram Tree is opened in " << fFileName[1] << G4endl;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RMC01AnalysisManager::Save(G4double scaling_factor)
{
  if (fFactoryOn) {
    G4AnalysisManager* theHistoManager = G4AnalysisManager::Instance();
    // scaling of results
    //-----------------

    for (G4int ind = 1; ind <= theHistoManager->GetNofH1s(); ++ind) {
      theHistoManager->SetH1Ascii(ind, true);
      theHistoManager->ScaleH1(ind, scaling_factor);
    }
    for (G4int ind = 1; ind <= theHistoManager->GetNofH2s(); ++ind)
      theHistoManager->ScaleH2(ind, scaling_factor);

    theHistoManager->Write();
    theHistoManager->CloseFile();
    G4cout << "\n----> Histogram Tree is saved in " << fFileName[1] << G4endl;

    theHistoManager->Clear();
    fFactoryOn = false;
  }
}
