//
// ********************************************************************
// * 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.          *
// ********************************************************************
//
// $Id: G4Penelope08RayleighModel.cc,v 1.3 2010/07/28 07:09:16 pandola Exp $
// GEANT4 tag $Name: geant4-09-04-ref-00 $
//
// Author: Luciano Pandola
//
// History:
// --------
// 03 Dec 2009   L Pandola    First implementation
//

#include "G4Penelope08RayleighModel.hh"
#include "G4PenelopeSamplingData.hh"
#include "G4ParticleDefinition.hh"
#include "G4MaterialCutsCouple.hh"
#include "G4ProductionCutsTable.hh"
#include "G4DynamicParticle.hh"
#include "G4PhysicsTable.hh"
#include "G4ElementTable.hh"
#include "G4Element.hh"
#include "G4PhysicsFreeVector.hh"


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


G4Penelope08RayleighModel::G4Penelope08RayleighModel(const G4ParticleDefinition*,
                                             const G4String& nam)
  :G4VEmModel(nam),isInitialised(false),logAtomicCrossSection(0),   
   atomicFormFactor(0),logFormFactorTable(0),pMaxTable(0),samplingTable(0)
{
  fIntrinsicLowEnergyLimit = 100.0*eV;
  fIntrinsicHighEnergyLimit = 100.0*GeV;
  //  SetLowEnergyLimit(fIntrinsicLowEnergyLimit);
  SetHighEnergyLimit(fIntrinsicHighEnergyLimit);
  //
  verboseLevel= 0;
  // Verbosity scale:
  // 0 = nothing 
  // 1 = warning for energy non-conservation 
  // 2 = details of energy budget
  // 3 = calculation of cross sections, file openings, sampling of atoms
  // 4 = entering in methods 

  //build the energy grid. It is the same for all materials
  G4double logenergy = std::log(fIntrinsicLowEnergyLimit/2.);
  G4double logmaxenergy = std::log(1.5*fIntrinsicHighEnergyLimit);
  //finer grid below 160 keV
  G4double logtransitionenergy = std::log(160*keV); 
  G4double logfactor1 = std::log(10.)/250.;
  G4double logfactor2 = logfactor1*10;
  logEnergyGridPMax.push_back(logenergy);
  do{
    if (logenergy < logtransitionenergy)
      logenergy += logfactor1;
    else
      logenergy += logfactor2;
    logEnergyGridPMax.push_back(logenergy);      
  }while (logenergy < logmaxenergy);
}

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

G4Penelope08RayleighModel::~G4Penelope08RayleighModel()
{
  std::map <const G4int,G4PhysicsFreeVector*>::iterator i;
  if (logAtomicCrossSection)
    {
      for (i=logAtomicCrossSection->begin();i != logAtomicCrossSection->end();i++)
	if (i->second) delete i->second;
      delete logAtomicCrossSection;
     }

   if (atomicFormFactor)
     {
       for (i=atomicFormFactor->begin();i != atomicFormFactor->end();i++)
	 if (i->second) delete i->second;
       delete atomicFormFactor;
     }

  ClearTables();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4Penelope08RayleighModel::ClearTables()
{
   std::map <const G4Material*,G4PhysicsFreeVector*>::iterator i;
 
   if (logFormFactorTable)
     {
       for (i=logFormFactorTable->begin(); i != logFormFactorTable->end(); i++)
	 if (i->second) delete i->second;
       delete logFormFactorTable;
       logFormFactorTable = 0; //zero explicitely
     }

   if (pMaxTable)
     {
       for (i=pMaxTable->begin(); i != pMaxTable->end(); i++)
	 if (i->second) delete i->second;
       delete pMaxTable;
       pMaxTable = 0; //zero explicitely
     }

   std::map<const G4Material*,G4PenelopeSamplingData*>::iterator ii;
   if (samplingTable)
     {
       for (ii=samplingTable->begin(); ii != samplingTable->end(); ii++)
	 if (ii->second) delete ii->second;
       delete samplingTable;
       samplingTable = 0; //zero explicitely
     }     

   return;
}

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

void G4Penelope08RayleighModel::Initialise(const G4ParticleDefinition* ,
					 const G4DataVector& )
{
  if (verboseLevel > 3)
    G4cout << "Calling G4Penelope08RayleighModel::Initialise()" << G4endl;

  //clear tables depending on materials, not the atomic ones
  ClearTables();
  
  //create new tables
  //
  // logAtomicCrossSection and atomicFormFactor are created only once,
  // since they are never cleared
  if (!logAtomicCrossSection)
    logAtomicCrossSection = new std::map<const G4int,G4PhysicsFreeVector*>;
  if (!atomicFormFactor)
    atomicFormFactor = new std::map<const G4int,G4PhysicsFreeVector*>;

  if (!logFormFactorTable)
    logFormFactorTable = new std::map<const G4Material*,G4PhysicsFreeVector*>;
  if (!pMaxTable)
    pMaxTable = new std::map<const G4Material*,G4PhysicsFreeVector*>;
  if (!samplingTable)
    samplingTable = new std::map<const G4Material*,G4PenelopeSamplingData*>;


  if (verboseLevel > 0) {
    G4cout << "Penelope08 Rayleigh model is initialized " << G4endl
	   << "Energy range: "
	   << LowEnergyLimit() / keV << " keV - "
	   << HighEnergyLimit() / GeV << " GeV"
	   << G4endl;
  }

  if(isInitialised) return;
  fParticleChange = GetParticleChangeForGamma();
  isInitialised = true;
}

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

G4double G4Penelope08RayleighModel::ComputeCrossSectionPerAtom(const G4ParticleDefinition*,
							       G4double energy,
							       G4double Z,
							       G4double,
							       G4double,
							       G4double)
{
  // Cross section of Rayleigh scattering in Penelope2008 is calculated by the EPDL97 
  // tabulation, Cuellen et al. (1997), with non-relativistic form factors from Hubbel 
  // et al. J. Phys. Chem. Ref. Data 4 (1975) 471; Erratum ibid. 6 (1977) 615.

   if (verboseLevel > 3)
    G4cout << "Calling CrossSectionPerAtom() of G4Penelope08RayleighModel" << G4endl;
 
   G4int iZ = (G4int) Z;

   //read data files
   if (!logAtomicCrossSection->count(iZ))
     ReadDataFile(iZ);
   //now it should be ok
   if (!logAtomicCrossSection->count(iZ))
     {
       G4cout << "Problem in G4Penelope08RayleighModel::ComputeCrossSectionPerAtom" 
	      << G4endl;
       G4Exception();
     }

   G4double cross = 0;

   G4PhysicsFreeVector* atom = logAtomicCrossSection->find(iZ)->second;
   if (!atom)
     {
       G4cout << "Problem in G4Penelope08RayleighModel::ComputeCrossSectionPerAtom" 
	 << G4endl;
       G4Exception();
     }
   G4double logene = std::log(energy);
   G4double logXS = atom->Value(logene);
   cross = std::exp(logXS);

   if (verboseLevel > 2)
    G4cout << "Rayleigh cross section at " << energy/keV << " keV for Z=" << Z << 
      " = " << cross/barn << " barn" << G4endl;
    return cross;
}


//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4Penelope08RayleighModel::BuildFormFactorTable(const G4Material* material)
{
 
  /*
    1) get composition and equivalent molecular density
  */
  
  G4int nElements = material->GetNumberOfElements();
  const G4ElementVector* elementVector = material->GetElementVector();
  const G4double* fractionVector = material->GetFractionVector();

  std::vector<G4double> *StechiometricFactors = new std::vector<G4double>;
  for (G4int i=0;i<nElements;i++)
    {
      G4double fraction = fractionVector[i];
      G4double atomicWeigth = (*elementVector)[i]->GetA()/(g/mole);
      StechiometricFactors->push_back(fraction/atomicWeigth);
    }
  //Find max
  G4double MaxStechiometricFactor = 0.;
  for (G4int i=0;i<nElements;i++)
    {
      if ((*StechiometricFactors)[i] > MaxStechiometricFactor)
        MaxStechiometricFactor = (*StechiometricFactors)[i];
    }
  if (MaxStechiometricFactor<1e-16)
    {
      G4cout << "G4Penelope08RayleighModel::BuildFormFactorTable" << G4endl;
      G4cout << "Problem with the mass composition of " << material->GetName() << G4endl;
      G4Exception();
    }
  //Normalize
  for (G4int i=0;i<nElements;i++)
    (*StechiometricFactors)[i] /=  MaxStechiometricFactor;
 
  // Equivalent atoms per molecule
  G4double atomsPerMolecule = 0;
  for (G4int i=0;i<nElements;i++)
    atomsPerMolecule += (*StechiometricFactors)[i]; 
 
  /*
    CREATE THE FORM FACTOR TABLE
  */
  G4PhysicsFreeVector* theFFVec = new G4PhysicsFreeVector(logQSquareGrid.size());
  theFFVec->SetSpline(true);

  for (size_t k=0;k<logQSquareGrid.size();k++)
    {
      G4double ff2 = 0; //squared form factor
      for (G4int i=0;i<nElements;i++)
	{
	  G4int iZ = (G4int) (*elementVector)[i]->GetZ();
	  G4PhysicsFreeVector* theAtomVec = atomicFormFactor->find(iZ)->second;
	  G4double f = (*theAtomVec)[k]; //the q-grid is always the same	    
	  ff2 += f*f*(*StechiometricFactors)[i];
	}
      if (ff2)
	theFFVec->PutValue(k,logQSquareGrid[k],std::log(ff2)); //NOTICE: THIS IS log(Q^2) vs. log(F^2)
    }
  logFormFactorTable->insert(std::make_pair(material,theFFVec));

  delete StechiometricFactors;
  
  return;
}


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

void G4Penelope08RayleighModel::SampleSecondaries(std::vector<G4DynamicParticle*>* ,
						const G4MaterialCutsCouple* couple,
						const G4DynamicParticle* aDynamicGamma,
						G4double,
						G4double)
{
  // Sampling of the Rayleigh final state (namely, scattering angle of the photon) 
  // from the Penelope2008 model. The scattering angle is sampled from the atomic 
  // cross section dOmega/d(cosTheta) from Born ("Atomic Phyisics", 1969), disregarding 
  // anomalous scattering effects. The Form Factor F(Q) function which appears in the 
  // analytical cross section is retrieved via the method GetFSquared(); atomic data 
  // are tabulated for F(Q). Form factor for compounds is calculated according to 
  // the additivity rule. The sampling from the F(Q) is made via a Rational Inverse 
  // Transform with Aliasing (RITA) algorithm; RITA parameters are calculated once 
  // for each material and managed by G4PenelopeSamplingData objects.
  // The sampling algorithm (rejection method) has efficiency 67% at low energy, and 
  // increases with energy. For E=100 keV the efficiency is 100% and 86% for 
  // hydrogen and uranium, respectively.

  if (verboseLevel > 3)
    G4cout << "Calling SamplingSecondaries() of G4Penelope08RayleighModel" << G4endl;

  G4double photonEnergy0 = aDynamicGamma->GetKineticEnergy();
 
  if (photonEnergy0 <= fIntrinsicLowEnergyLimit)
    {
      fParticleChange->ProposeTrackStatus(fStopAndKill);
      fParticleChange->SetProposedKineticEnergy(0.);
      fParticleChange->ProposeLocalEnergyDeposit(photonEnergy0);
      return ;
    }

  G4ParticleMomentum photonDirection0 = aDynamicGamma->GetMomentumDirection();
  
  const G4Material* theMat = couple->GetMaterial();
  
  //1) Verify if tables are ready
  if (!pMaxTable || !samplingTable)
    {
      G4cout << " G4Penelope08RayleighModel::SampleSecondaries" << G4endl;
      G4cout << "Looks like the model is _not_ properly initialized" << G4endl;
      G4Exception();
    }
  
  //2) retrieve or build the sampling table
  if (!(samplingTable->count(theMat)))
    InitializeSamplingAlgorithm(theMat);
  G4PenelopeSamplingData* theDataTable = samplingTable->find(theMat)->second;
  
  //3) retrieve or build the pMax data
  if (!pMaxTable->count(theMat))
    GetPMaxTable(theMat);
  G4PhysicsFreeVector* thePMax = pMaxTable->find(theMat)->second;

  G4double cosTheta = 1.0;
  
  //OK, ready to go!
  G4double qmax = 2.0*photonEnergy0/electron_mass_c2; //this is non-dimensional now

  if (qmax < 1e-10) //very low momentum transfer
    {
      G4bool loopAgain=false;
      do
	{
	  loopAgain = false;
	  cosTheta = 1.0-2.0*G4UniformRand();
	  G4double G = 0.5*(1+cosTheta*cosTheta);
	  if (G4UniformRand()>G)
	    loopAgain = true;
	}while(loopAgain);
    }
  else //larger momentum transfer
    {
      size_t nData = theDataTable->GetNumberOfStoredPoints();
      G4double LastQ2inTheTable = theDataTable->GetX(nData-1);
      G4double q2max = std::min(qmax*qmax,LastQ2inTheTable);

      G4bool loopAgain = false;
      G4double MaxPValue = thePMax->Value(photonEnergy0);
      G4double xx=0;
      
      //Sampling by rejection method. The rejection function is 
      //G = 0.5*(1+cos^2(theta))
      //
      do{
	loopAgain = false;
	G4double RandomMax = G4UniformRand()*MaxPValue;
	xx = theDataTable->SampleValue(RandomMax);
	//xx is a random value of q^2 in (0,q2max),sampled according to 
	//F(Q^2) via the RITA algorithm
	if (xx > q2max)
	  loopAgain = true;
	cosTheta = 1.0-2.0*xx/q2max;
	G4double G = 0.5*(1+cosTheta*cosTheta);
	if (G4UniformRand()>G)
	  loopAgain = true;
      }while(loopAgain);
    }
  
  G4double sinTheta = std::sqrt(1-cosTheta*cosTheta);
 
  // Scattered photon angles. ( Z - axis along the parent photon)
  G4double phi = twopi * G4UniformRand() ;
  G4double dirX = sinTheta*std::cos(phi);
  G4double dirY = sinTheta*std::sin(phi);
  G4double dirZ = cosTheta;
  
  // Update G4VParticleChange for the scattered photon 
  G4ThreeVector photonDirection1(dirX, dirY, dirZ);

  photonDirection1.rotateUz(photonDirection0);
  fParticleChange->ProposeMomentumDirection(photonDirection1) ;
  fParticleChange->SetProposedKineticEnergy(photonEnergy0) ;
 
  return;
}


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

void G4Penelope08RayleighModel::ReadDataFile(const G4int Z)
{
  if (verboseLevel > 2)
    {
      G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
      G4cout << "Going to read Rayleigh data files for Z=" << Z << G4endl;
    }

  char* path = getenv("G4LEDATA");
  if (!path)
    {
      G4String excep = "G4Penelope08RayleighModel - G4LEDATA environment variable not set!";
      G4Exception(excep);
    }

  /*
    Read first the cross section file
  */
  std::ostringstream ost;
  if (Z>9)
    ost << path << "/penelope/rayleigh/pdgra" << Z << ".p08";
  else
    ost << path << "/penelope/rayleigh/pdgra0" << Z << ".p08";
  std::ifstream file(ost.str().c_str());
  if (!file.is_open())
    {
      G4String excep = "G4Penelope08RayleighModel - data file " + G4String(ost.str()) + " not found!";
      G4Exception(excep);
    }
  G4int readZ =0;
  size_t nPoints= 0;
  file >> readZ >> nPoints;
  //check the right file is opened.
  if (readZ != Z || nPoints <= 0)
    {
      G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
      G4cout << "Corrupted data file for Z=" << Z << G4endl;
      G4Exception();
    }  
  G4PhysicsFreeVector* theVec = new G4PhysicsFreeVector((size_t)nPoints);
  G4double ene=0,f1=0,f2=0,xs=0;
  for (size_t i=0;i<nPoints;i++)
    {
      file >> ene >> f1 >> f2 >> xs;
      //dimensional quantities
      ene *= eV;
      xs *= cm2;
      theVec->PutValue(i,std::log(ene),std::log(xs));
      if (file.eof() && i != (nPoints-1)) //file ended too early
	{
	  G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
	  G4cout << "Corrupted data file for Z=" << Z << G4endl;
	  G4cout << "Found less than " << nPoints << "entries " <<G4endl;
	  G4Exception();
	}
    }
  if (!logAtomicCrossSection)
    {
      G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
      G4cout << "Problem with allocation of logAtomicCrossSection data table " << G4endl;
      G4Exception();
    }
  logAtomicCrossSection->insert(std::make_pair(Z,theVec));
  file.close();

  /*
    Then read the form factor file
  */
  std::ostringstream ost2;
  if (Z>9)
    ost2 << path << "/penelope/rayleigh/pdaff" << Z << ".p08";
  else
    ost2 << path << "/penelope/rayleigh/pdaff0" << Z << ".p08";
  file.open(ost2.str().c_str());
  if (!file.is_open())
    {
      G4String excep = "G4Penelope08RayleighModel - data file " + G4String(ost2.str()) + " not found!";
      G4Exception(excep);
    }
  file >> readZ >> nPoints;
  //check the right file is opened.
  if (readZ != Z || nPoints <= 0)
    {
      G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
      G4cout << "Corrupted data file for Z=" << Z << G4endl;
      G4Exception();
    }  
  G4PhysicsFreeVector* theFFVec = new G4PhysicsFreeVector((size_t)nPoints);
  G4double q=0,ff=0,incoh=0;
  G4bool fillQGrid = false;
  //fill this vector only the first time.
  if (!logQSquareGrid.size())
    fillQGrid = true;
  for (size_t i=0;i<nPoints;i++)
    {
      file >> q >> ff >> incoh;
      //q and ff are dimensionless (q is in units of (m_e*c)
      theFFVec->PutValue(i,q,ff);
      if (fillQGrid)
	{
	  logQSquareGrid.push_back(2.0*std::log(q));
	}
      if (file.eof() && i != (nPoints-1)) //file ended too early
	{
	  G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
	  G4cout << "Corrupted data file for Z=" << Z << G4endl;
	  G4cout << "Found less than " << nPoints << "entries " <<G4endl;
	  G4Exception();
	}
    }
  if (!atomicFormFactor)
    {
      G4cout << "G4Penelope08RayleighModel::ReadDataFile()" << G4endl;
      G4cout << "Problem with allocation of atomicFormFactor data table " << G4endl;
      G4Exception();
    }
  atomicFormFactor->insert(std::make_pair(Z,theFFVec));
  file.close();
  return;
}

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

G4double G4Penelope08RayleighModel::GetFSquared(const G4Material* mat, const G4double QSquared)
{
  G4double f2 = 0;
  G4double logQSquared = std::log(QSquared);
  //last value of the table
  G4double maxlogQ2 = logQSquareGrid[logQSquareGrid.size()-1];
  //If the table has not been built for the material, do it!
  if (!logFormFactorTable->count(mat))
    BuildFormFactorTable(mat);
  
  //now it should  be all right
  G4PhysicsFreeVector* theVec = logFormFactorTable->find(mat)->second;

  if (!theVec)
    {
      G4cout << " G4Penelope08RayleighModel::GetFSquared()" << G4endl;
      G4cout << "Unable to retrieve F squared table for " << mat->GetName();
      G4Exception();
    }
  if (logQSquared < -20) // Q < 1e-9
    {
      G4double logf2 = (*theVec)[0]; //first value of the table
      f2 = std::exp(logf2);
    }
  else if (logQSquared > maxlogQ2)
    f2 =0;
  else
    {
      //log(Q^2) vs. log(F^2)
      G4double logf2 = theVec->Value(logQSquared);
      f2 = std::exp(logf2);

    }
  if (verboseLevel > 3)
    {
      G4cout << "G4Penelope08RayleighModel::GetFSquared() in " << mat->GetName() << G4endl;  
      G4cout << "Q^2 = " <<  QSquared << " (units of 1/(m_e*c); F^2 = " << f2 << G4endl;
    }
  return f2;
}

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

void G4Penelope08RayleighModel::InitializeSamplingAlgorithm(const G4Material* mat)
{
  G4double q2min = 0;
  G4double q2max = 0;
  const size_t np = 150; //hard-coded in Penelope
  for (size_t i=1;i<logQSquareGrid.size();i++)
    {
      G4double Q2 = std::exp(logQSquareGrid[i]);
      if (GetFSquared(mat,Q2) >  1e-35)
	{
	  q2max = std::exp(logQSquareGrid[i-1]);
	}
    }
  
  size_t nReducedPoints = np/4;

  //check for errors
  if (np < 16)
    {
      G4cout << "G4Penelope08RayleighModel::InitializeSamplingAlgorithm" << G4endl;
      G4cout << "Too few points to initialize the sampling algorithm" << G4endl;
      G4Exception();
    }
  if (q2min > (q2max-1e-10))
    {
      G4cout << "G4Penelope08RayleighModel::InitializeSamplingAlgorithm" << G4endl;
      G4cout << "Too narrow grid to initialize the sampling algorithm" << G4endl;
      G4Exception();
    }

  //This is subroutine RITAI0 of Penelope
  //Create an object of type G4Penelope08RayleighSamplingData --> store in a map::Material*

  //temporary vectors --> Then everything is stored in G4PenelopeSamplingData
  G4DataVector* x = new G4DataVector();
  
  /*******************************************************************************
    Start with a grid of NUNIF points uniformly spaced in the interval q2min,q2max
  ********************************************************************************/
  size_t NUNIF = std::min(std::max(((size_t)8),nReducedPoints),np/2);
  const G4int nip = 51; //hard-coded in Penelope

  G4double dx = (q2max-q2min)/((G4double) NUNIF-1);
  x->push_back(q2min);
  for (size_t i=1;i<NUNIF-1;i++)
    {
      G4double app = q2min + i*dx;
      x->push_back(app); //increase 
    }
  x->push_back(q2max);
  
  if (verboseLevel> 3)
    G4cout << "Vector x has " << x->size() << " points, while NUNIF = " << NUNIF << G4endl;

  //Allocate temporary storage vectors
  G4DataVector* area = new G4DataVector();
  G4DataVector* a = new G4DataVector();
  G4DataVector* b = new G4DataVector();
  G4DataVector* c = new G4DataVector();
  G4DataVector* err = new G4DataVector();

  for (size_t i=0;i<NUNIF-1;i++) //build all points but the last
    {      
      //Temporary vectors for this loop
      G4DataVector* pdfi = new G4DataVector();
      G4DataVector* pdfih = new G4DataVector();
      G4DataVector* sumi = new G4DataVector();

      G4double dxi = ((*x)[i+1]-(*x)[i])/(G4double (nip-1));
      G4double pdfmax = 0;
      for (G4int k=0;k<nip;k++)
	{
	  G4double xik = (*x)[i]+k*dxi;
	  G4double pdfk = std::max(GetFSquared(mat,xik),0.);
	  pdfi->push_back(pdfk);
	  pdfmax = std::max(pdfmax,pdfk);	
	  if (k < (nip-1))
	    {
	      G4double xih = xik + 0.5*dxi;
	      G4double pdfIK = std::max(GetFSquared(mat,xih),0.);
	      pdfih->push_back(pdfIK);
	      pdfmax = std::max(pdfmax,pdfIK);
	    }
	}
    
      //Simpson's integration
      G4double cons = dxi*0.5*(1./3.);
      sumi->push_back(0.);
      for (G4int k=1;k<nip;k++)
	{
	  G4double previous = (*sumi)[k-1];
	  G4double next = previous + cons*((*pdfi)[k-1]+4.0*(*pdfih)[k-1]+(*pdfi)[k]);
	  sumi->push_back(next);
	}
    
      G4double lastIntegral = (*sumi)[sumi->size()-1];
      area->push_back(lastIntegral);
      //Normalize cumulative function
      G4double factor = 1.0/lastIntegral;
      for (size_t k=0;k<sumi->size();k++)
	(*sumi)[k] *= factor;
      
      //When the PDF vanishes at one of the interval end points, its value is modified
      if ((*pdfi)[0] < 1e-35) 
	(*pdfi)[0] = 1e-5*pdfmax;
      if ((*pdfi)[pdfi->size()-1] < 1e-35)
	(*pdfi)[pdfi->size()-1] = 1e-5*pdfmax;

      G4double pli = (*pdfi)[0]*factor;
      G4double pui = (*pdfi)[pdfi->size()-1]*factor;
      G4double B_temp = 1.0-1.0/(pli*pui*dx*dx);
      G4double A_temp = (1.0/(pli*dx))-1.0-B_temp;
      G4double C_temp = 1.0+A_temp+B_temp;
      if (C_temp < 1e-35)
	{
	  a->push_back(0.);
	  b->push_back(0.);
	  c->push_back(1.);	  
	}
      else
	{
	  a->push_back(A_temp);
	  b->push_back(B_temp);
	  c->push_back(C_temp);
	}

      //OK, now get ERR(I), the integral of the absolute difference between the rational interpolation 
      //and the true pdf, extended over the interval (X(I),X(I+1))
      G4int icase = 1; //loop code
      G4bool reLoop = false;
      err->push_back(0.);
      do
	{
	  reLoop = false;
	  (*err)[i] = 0.; //zero variable
	  for (G4int k=0;k<nip;k++)
	    {
	      G4double rr = (*sumi)[k];
	      G4double pap = (*area)[i]*(1.0+((*a)[i]+(*b)[i]*rr)*rr)*(1.0+((*a)[i]+(*b)[i]*rr)*rr)/
		((1.0-(*b)[i]*rr*rr)*(*c)[i]*((*x)[i+1]-(*x)[i]));
	      if (k == 0 || k == nip-1)
		(*err)[i] += 0.5*std::fabs(pap-(*pdfi)[k]);
	      else
		(*err)[i] += std::fabs(pap-(*pdfi)[k]);
	    }
	  (*err)[i] *= dxi;
      
	  //If err(I) is too large, the pdf is approximated by a uniform distribution
	  if ((*err)[i] > 0.1*(*area)[i] && icase == 1) 
	    {
	      (*b)[i] = 0;
	      (*a)[i] = 0;
	      (*c)[i] = 1.;
	      icase = 2;
	      reLoop = true;
	    }
	}while(reLoop);

      if (pdfi) delete pdfi;
      if (pdfih) delete pdfih;
      if (sumi) delete sumi;
    } //end of first loop over i

  //Now assign last point
  (*x)[x->size()-1] = q2max;
  a->push_back(0.);
  b->push_back(0.);
  c->push_back(0.);
  err->push_back(0.);
  area->push_back(0.);

  if (x->size() != NUNIF || a->size() != NUNIF || 
      err->size() != NUNIF || area->size() != NUNIF)
    {
      G4cout << "G4Penelope08RayleighModel::InitializeSamplingAlgorithm" << G4endl;
      G4cout << "Problem in building the Table for Sampling: array dimensions do not match " << G4endl;
      G4Exception();
    }
  
  /*******************************************************************************
   New grid points are added by halving the sub-intervals with the largest absolute error
  This is done up to np=150 points in the grid
  ********************************************************************************/
  do
    {
      G4double maxError = 0.0;
      size_t iErrMax = 0;
      for (size_t i=0;i<err->size()-2;i++) 
	{
	  //maxError is the lagest of the interval errors err[i]
	  if ((*err)[i] > maxError)
	    {
	      maxError = (*err)[i];
	      iErrMax = i;
	    }
	}
      
      //OK, now I have to insert one new point in the position iErrMax
      G4double newx = 0.5*((*x)[iErrMax]+(*x)[iErrMax+1]);
      
      x->insert(x->begin()+iErrMax+1,newx);
      //Add place-holders in the other vectors
      area->insert(area->begin()+iErrMax+1,0.);
      a->insert(a->begin()+iErrMax+1,0.);
      b->insert(b->begin()+iErrMax+1,0.);
      c->insert(c->begin()+iErrMax+1,0.);
      err->insert(err->begin()+iErrMax+1,0.);
        
      //Now calculate the other parameters
      for (size_t i=iErrMax;i<=iErrMax+1;i++)
	{
	  //Temporary vectors for this loop
	  G4DataVector* pdfi = new G4DataVector();
	  G4DataVector* pdfih = new G4DataVector();
	  G4DataVector* sumi = new G4DataVector();
	  
	  G4double dx = (*x)[i+1]-(*x)[i];
	  G4double dxi = ((*x)[i+1]-(*x)[i])/(G4double (nip-1));
	  G4double pdfmax = 0;
	  for (G4int k=0;k<nip;k++)
	    {
	      G4double xik = (*x)[i]+k*dxi;
	      G4double pdfk = std::max(GetFSquared(mat,xik),0.);
	      pdfi->push_back(pdfk);
	      pdfmax = std::max(pdfmax,pdfk);	
	      if (k < (nip-1))
		{
		  G4double xih = xik + 0.5*dxi;
		  G4double pdfIK = std::max(GetFSquared(mat,xih),0.);
		  pdfih->push_back(pdfIK);
		  pdfmax = std::max(pdfmax,pdfIK);
		}
	    }
	  
	  //Simpson's integration
	  G4double cons = dxi*0.5*(1./3.);
	  sumi->push_back(0.);
	  for (G4int k=1;k<nip;k++)
	    {
	      G4double previous = (*sumi)[k-1];
	      G4double next = previous + cons*((*pdfi)[k-1]+4.0*(*pdfih)[k-1]+(*pdfi)[k]);
	      sumi->push_back(next);
	    }
	  G4double lastIntegral = (*sumi)[sumi->size()-1];
	  (*area)[i] = lastIntegral;
	  
	  //Normalize cumulative function
	  G4double factor = 1.0/lastIntegral;
	  for (size_t k=0;k<sumi->size();k++)
	    (*sumi)[k] *= factor;
	  
	  //When the PDF vanishes at one of the interval end points, its value is modified
	  if ((*pdfi)[0] < 1e-35) 
	    (*pdfi)[0] = 1e-5*pdfmax;
	  if ((*pdfi)[pdfi->size()-1] < 1e-35)
	    (*pdfi)[pdfi->size()-1] = 1e-5*pdfmax;
	  
	  G4double pli = (*pdfi)[0]*factor;
	  G4double pui = (*pdfi)[pdfi->size()-1]*factor;
	  G4double B_temp = 1.0-1.0/(pli*pui*dx*dx);
	  G4double A_temp = (1.0/(pli*dx))-1.0-B_temp;
	  G4double C_temp = 1.0+A_temp+B_temp;
	  if (C_temp < 1e-35)
	    {
	      (*a)[i]= 0.;
	      (*b)[i] = 0.;
	      (*c)[i] = 0;
	    }
	  else
	    {
	      (*a)[i]= A_temp;
	      (*b)[i] = B_temp;
	      (*c)[i] = C_temp;
	    }
	  //OK, now get ERR(I), the integral of the absolute difference between the rational interpolation 
	  //and the true pdf, extended over the interval (X(I),X(I+1))
	  G4int icase = 1; //loop code
	  G4bool reLoop = false;
	  do
	    {
	      reLoop = false;
	      (*err)[i] = 0.; //zero variable
	      for (G4int k=0;k<nip;k++)
		{
		  G4double rr = (*sumi)[k];
		  G4double pap = (*area)[i]*(1.0+((*a)[i]+(*b)[i]*rr)*rr)*(1.0+((*a)[i]+(*b)[i]*rr)*rr)/
		    ((1.0-(*b)[i]*rr*rr)*(*c)[i]*((*x)[i+1]-(*x)[i]));
		  if (k == 0 || k == nip-1)
		    (*err)[i] += 0.5*std::fabs(pap-(*pdfi)[k]);
		  else
		    (*err)[i] += std::fabs(pap-(*pdfi)[k]);
		}
	      (*err)[i] *= dxi;
	      
	      //If err(I) is too large, the pdf is approximated by a uniform distribution
	      if ((*err)[i] > 0.1*(*area)[i] && icase == 1) 
		{
		  (*b)[i] = 0;
		  (*a)[i] = 0;
		  (*c)[i] = 1.;
		  icase = 2;
		  reLoop = true;
		}
	    }while(reLoop);
	  if (pdfi) delete pdfi;
	  if (pdfih) delete pdfih;
	  if (sumi) delete sumi;
	}
    }while(x->size() < np);

  if (x->size() != np || a->size() != np || 
      err->size() != np || area->size() != np)
    {
      G4cout << "G4Penelope08RayleighModel::InitializeSamplingAlgorithm" << G4endl;
      G4cout << "Problem in building the extended Table for Sampling: array dimensions do not match " << G4endl;
      G4Exception();
    }

  /*******************************************************************************
   Renormalization
  ********************************************************************************/
  G4double ws = 0;
  for (size_t i=0;i<np-1;i++)
    ws += (*area)[i];
  ws = 1.0/ws;
  G4double errMax = 0;
  for (size_t i=0;i<np-1;i++)
    {
      (*area)[i] *= ws;
      (*err)[i] *= ws;
      errMax = std::max(errMax,(*err)[i]);
    }

  //Vector with the normalized cumulative distribution
  G4DataVector* PAC = new G4DataVector();
  PAC->push_back(0.);
  for (size_t i=0;i<np-1;i++)
    {
      G4double previous = (*PAC)[i];
      PAC->push_back(previous+(*area)[i]);
    }
  (*PAC)[PAC->size()-1] = 1.;
		     
  /*******************************************************************************
  Pre-calculated limits for the initial binary search for subsequent sampling
  ********************************************************************************/

  //G4DataVector* ITTL = new G4DataVector();
  std::vector<size_t> *ITTL = new std::vector<size_t>;
  std::vector<size_t> *ITTU = new std::vector<size_t>;

  //Just create place-holders
  for (size_t i=0;i<np;i++)
    {
      ITTL->push_back(0);
      ITTU->push_back(0);
    }

  G4double bin = 1.0/(np-1);
  (*ITTL)[0]=0;
  for (size_t i=1;i<(np-1);i++)
    {
      G4double ptst = i*bin; 
      G4bool found = false;
      for (size_t j=(*ITTL)[i-1];j<np && !found;j++)
	{
	  if ((*PAC)[j] > ptst)
	    {
	      (*ITTL)[i] = j-1;
	      (*ITTU)[i-1] = j;
	      found = true;
	    }
	}
    }
  (*ITTU)[ITTU->size()-2] = ITTU->size()-1;
  (*ITTU)[ITTU->size()-1] = ITTU->size()-1;
  (*ITTL)[ITTL->size()-1] = ITTU->size()-2;

  if (ITTU->size() != np || ITTU->size() != np)
    {
      G4cout << "G4Penelope08RayleighModel::InitializeSamplingAlgorithm" << G4endl;
      G4cout << "Problem in building the Limit Tables for Sampling: array dimensions do not match " << G4endl;
      G4Exception();
    }


  /********************************************************************************
    Copy tables
  ********************************************************************************/
  G4PenelopeSamplingData* theTable = new G4PenelopeSamplingData(np);
  for (size_t i=0;i<np;i++)
    {
      theTable->AddPoint((*x)[i],(*PAC)[i],(*a)[i],(*b)[i],(*ITTL)[i],(*ITTU)[i]);
    }

  if (verboseLevel > 2)
    {
      G4cout << "*************************************************************************" << G4endl;
      G4cout << "Sampling table for Penelope Rayleigh scattering in " << mat->GetName() << G4endl;
      theTable->DumpTable();
    } 
  samplingTable->insert(std::make_pair(mat,theTable));

 
  //Clean up temporary vectors
  if (x) delete x;
  if (a) delete a;
  if (b) delete b;
  if (c) delete c;
  if (err) delete err;
  if (area) delete area;
  if (PAC) delete PAC;
  if (ITTL) delete ITTL;
  if (ITTU) delete ITTU;

  //DONE!
  return;
  
}

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

void G4Penelope08RayleighModel::GetPMaxTable(const G4Material* mat)
{
  if (!pMaxTable)
    {
      G4cout << "G4Penelope08RayleighModel::BuildPMaxTable" << G4endl;
      G4cout << "Going to instanziate the pMaxTable !" << G4endl;
      G4cout << "That should _not_ be here! " << G4endl;
      pMaxTable = new std::map<const G4Material*,G4PhysicsFreeVector*>;
    }
  //check if the table is already there
  if (pMaxTable->count(mat))
    return;

  //otherwise build it
  if (!samplingTable)
    {
      G4cout << "G4Penelope08RayleighModel::BuildPMaxTable" << G4endl;
      G4cout << "WARNING: samplingTable is not properly instantiated" << G4endl;
      G4Exception();
    }

  if (!samplingTable->count(mat))
    InitializeSamplingAlgorithm(mat);      
  
  G4PenelopeSamplingData *theTable = samplingTable->find(mat)->second;
  size_t tablePoints = theTable->GetNumberOfStoredPoints();

  size_t nOfEnergyPoints = logEnergyGridPMax.size();
  G4PhysicsFreeVector* theVec = new G4PhysicsFreeVector(nOfEnergyPoints);

  const size_t nip = 51; //hard-coded in Penelope

  for (size_t ie=0;ie<logEnergyGridPMax.size();ie++)
    {
      G4double energy = std::exp(logEnergyGridPMax[ie]);
      G4double Qm = 2.0*energy/electron_mass_c2; //this is non-dimensional now
      G4double Qm2 = Qm*Qm;
      G4double firstQ2 = theTable->GetX(0);
      G4double lastQ2 = theTable->GetX(tablePoints-1);
      G4double thePMax = 0;
      
      if (Qm2 > firstQ2)
	{
	  if (Qm2 < lastQ2)
	    {
	      //bisection to look for the index of Qm
	      size_t lowerBound = 0;
	      size_t upperBound = tablePoints-1;
	      while (lowerBound <= upperBound)
		{
		  size_t midBin = (lowerBound + upperBound)/2;
		  if( Qm2 < theTable->GetX(midBin))
		    { upperBound = midBin-1; }
		  else
		    { lowerBound = midBin+1; }
		}
	      //upperBound is the output (but also lowerBounf --> should be the same!)
	      G4double Q1 = theTable->GetX(upperBound);
 	      G4double Q2 = Qm2;
	      G4double DQ = (Q2-Q1)/((G4double)(nip-1));
	      G4double theA = theTable->GetA(upperBound);
	      G4double theB = theTable->GetB(upperBound);
	      G4double thePAC = theTable->GetPAC(upperBound);
	      G4DataVector* fun = new G4DataVector();
	      for (size_t k=0;k<nip;k++)
		{
		  G4double qi = Q1 + k*DQ;
		  G4double tau = (qi-Q1)/
		    (theTable->GetX(upperBound+1)-Q1);
		  G4double con1 = 2.0*theB*tau; 
		  G4double ci = 1.0+theA+theB;
		  G4double con2 = ci-theA*tau;
		  G4double etap = 0;
		  if (std::fabs(con1) > 1.0e-16*std::fabs(con2))
		    etap = con2*(1.0-std::sqrt(1.0-2.0*tau*con1/(con2*con2)))/con1;
		  else
		    etap = tau/con2;
		  G4double theFun = (theTable->GetPAC(upperBound+1)-thePAC)*
		    (1.0+(theA+theB*etap)*etap)*(1.0+(theA+theB*etap)*etap)/
		    ((1.0-theB*etap*etap)*ci*(theTable->GetX(upperBound+1)-Q1));
		  fun->push_back(theFun);
		}
	      //Now intergrate numerically the fun Cavalieri-Simpson's method
	      G4DataVector* sum = new G4DataVector;
	      G4double CONS = DQ*(1./12.);
	      G4double HCONS = 0.5*CONS;
	      sum->push_back(0.);
	      G4double secondPoint = (*sum)[0] + 
		(5.0*(*fun)[0]+8.0*(*fun)[1]-(*fun)[2])*CONS;
	      sum->push_back(secondPoint);
	      for (size_t hh=2;hh<nip-1;hh++)
		{
		  G4double previous = (*sum)[hh-1];
		  G4double next = previous+(13.0*((*fun)[hh-1]+(*fun)[hh])-
					    (*fun)[hh+1]-(*fun)[hh-2])*HCONS;
		  sum->push_back(next);
		}
	      G4double last = (*sum)[nip-2]+(5.0*(*fun)[nip-1]+8.0*(*fun)[nip-2]-
					     (*fun)[nip-3])*CONS;
	      sum->push_back(last);	 
	      thePMax = thePAC + (*sum)[sum->size()-1]; //last point
	      if (fun) delete fun;
	      if (sum) delete sum;
	    }
	  else
	    {
	      thePMax = 1.0;
	    }	 
	}
      else
	{
	  thePMax = theTable->GetPAC(0);
	}

      //Write number in the table
      theVec->PutValue(ie,energy,thePMax);
  }
  
  pMaxTable->insert(std::make_pair(mat,theVec));
  return;

}

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

void G4Penelope08RayleighModel::DumpFormFactorTable(const G4Material* mat)
{
  G4cout << "*****************************************************************" << G4endl;
  G4cout << "G4Penelope08RayleighModel: Form Factor Table for " << mat->GetName() << G4endl;
  //try to use the same format as Penelope-Fortran, namely Q (/m_e*c) and F
  G4cout <<  "Q/(m_e*c)                 F(Q)     " << G4endl;
  G4cout << "*****************************************************************" << G4endl;
  if (!logFormFactorTable->count(mat))
    BuildFormFactorTable(mat);
  
  G4PhysicsFreeVector* theVec = logFormFactorTable->find(mat)->second;
  for (size_t i=0;i<theVec->GetVectorLength();i++)
    {
      G4double logQ2 = theVec->GetLowEdgeEnergy(i);
      G4double Q = std::exp(0.5*logQ2);
      G4double logF2 = (*theVec)[i];
      G4double F = std::exp(0.5*logF2);
      G4cout << Q << "              " << F << G4endl;
    }
  //DONE
  return;
}
