// This code implementation is the intellectual property of
// the GEANT4 collaboration.
//
// By copying, distributing or modifying the Program (or any work
// based on the Program) you indicate your acceptance of this statement,
// and all its terms.
//
// $Id: G4IMultipleScattering.icc,v 1.1.10.1 1999/12/07 20:50:48 gunter Exp $
// GEANT4 tag $Name: geant4-01-00 $
//
// $Id: 
// -------------------------------------------------------------
//    GEANT 4 class inlined methods file
//
//    For information related to this code contact:
//    CERN, IT Division, ASD Group
//    History: based on object model of
//    2nd December 1995, G.Cosmo
//    ------- G4IMultipleScattering physics process ------
//              by Laszlo Urban, October 1997
// **************************************************************
// 25/11/97: mods for KinEnergy > HighestLimit
//---------------------------------------------------------------
// *****************************************************************
// It is the first implementation of the multiple scattering process
//   using an INTEGRAL APPROACH instead of the differential
//   one used in the standard implementation .
// *****************************************************************
//                by Laszlo Urban, 23 June 1998
// -----------------------------------------------------------------
// 27/10/98: cleanup , L.Urban

inline G4double G4IMultipleScattering::TrueToGeomTransformation(
                              const G4DynamicParticle *aParticle,
                              G4Material *aMaterial,
                              G4double truePathLength)

 // it sets the data member fTransportMeanFreePath and
 // performs the true path length -> geometrical path length
 // transformation 

{
  const G4double factt=1.-1.e-6,tausmall=5.e-5,taubig=50., 
                 minim=1.e-6,smalldroverr=1.e-2,smalldToverT=2.e-2 ;

  G4double KineticEnergy,Tfinal,tau,etau,geomPathLength,range,w1,w2,ww1,ww2 ;
  G4int materialIndex ;
  G4bool isOut ;

  KineticEnergy = aParticle->GetKineticEnergy() ;

  if((lastMaterial == aMaterial) && (lastKineticEnergy == KineticEnergy))
  { ; }
  else
  {
    lastMaterial=aMaterial;
    lastKineticEnergy=KineticEnergy;
    materialIndex = aMaterial->GetIndex() ;

    if(KineticEnergy<LowestKineticEnergy)
    {
      fTransportMeanFreePath = 
                     exp(plowlambda*log((KineticEnergy/LowestKineticEnergy)))*
                     (*theTransportMeanFreePathTable)
                     (materialIndex)->GetValue(LowestKineticEnergy,isOut);
    }
    else 
    {
      if(KineticEnergy>HighestKineticEnergy)
                          KineticEnergy=HighestKineticEnergy;
      fTransportMeanFreePath = (*theTransportMeanFreePathTable)
                               (materialIndex)->GetValue(KineticEnergy,isOut);
    }
  }

  //  do the true -> geom transformation
  if( fTransportMeanFreePath > biglambda )
  {
    geomPathLength = truePathLength ;
    CosTheta = 1. ;
  }
  else 
  {
    const G4ParticleDefinition *theParticle = aParticle->GetDefinition() ;
    G4double range = G4EnergyLossTables::GetPreciseRangeFromEnergy(
                                    theParticle,KineticEnergy,aMaterial) ;       
    if(truePathLength > factt*range)
    {
      geomPathLength = GetIntegralJ(theParticle,
                                    KineticEnergy,aMaterial) ;
      CosTheta = 0. ;
    }
    else
    {  
      if(truePathLength/range < smalldroverr)
      {
        Tfinal = KineticEnergy - truePathLength*
                                    G4EnergyLossTables::GetPreciseDEDX(
                                     theParticle,KineticEnergy,aMaterial) ;     
      }
      else
      {
        Tfinal = G4EnergyLossTables::GetPreciseEnergyFromRange(
                                    theParticle,range-truePathLength,
                                                              aMaterial) ; 
      }

      if((KineticEnergy-Tfinal)> smalldToverT)
      {
        w1 = GetIntegralI(theParticle,KineticEnergy,aMaterial) ;
        w2 = GetIntegralI(theParticle,Tfinal       ,aMaterial) ;
        CosTheta = exp(w2-w1) ;
        if( CosTheta < minim) 
          CosTheta = 0. ;

        ww1 = GetIntegralJ(theParticle,KineticEnergy,aMaterial) ;
        ww2 = GetIntegralJ(theParticle,Tfinal       ,aMaterial) ;

        geomPathLength = ww1 - ww2*CosTheta ;
      }
      else
      {
        tau = truePathLength/fTransportMeanFreePath ;
    
        if(tau<tausmall)
          etau = tau ;
        else
        {
          if(tau>taubig)
            etau = 1. ;
          else
            etau = 1.-exp(-tau) ;
        }

        geomPathLength = fTransportMeanFreePath*etau ;   

        CosTheta = exp(-truePathLength/fTransportMeanFreePath) ;
      } 
    }
  }
  if(geomPathLength>truePathLength)
    geomPathLength = truePathLength ;

  tLast = truePathLength ;
  zLast = geomPathLength ; 

  return geomPathLength ;
}

inline G4double G4IMultipleScattering::GetContinuousStepLimit(
                                     const G4Track& track,                  
                                     G4double,
                                     G4double currentMinimumStep,
                                     G4double&)
{
  G4double zPathLength,tPathLength ;
  const G4DynamicParticle* aParticle ;
  // this process is not a candidate for selection!!!!!!!!!
  SetGPILSelection(NotCandidateForSelection) ;

  tPathLength = currentMinimumStep ;
  aParticle = track.GetDynamicParticle() ;
 
  zPathLength = TrueToGeomTransformation(aParticle, 
                                    track.GetMaterial(),tPathLength);                 
  return zPathLength ;
  }

inline G4double G4IMultipleScattering::GetMeanFreePath(const G4Track&,
                                                G4double,
                                                G4ForceCondition* condition)
 //  it does not limit the Step size , but it sets condition to
 //   Forced , because the PostStepDoIt always has to be called
{
  *condition = Forced ;

  return DBL_MAX ;
}

inline G4VParticleChange* G4IMultipleScattering::AlongStepDoIt(
                                       const G4Track& track,const G4Step& Step)          
 //   only a geom path->true path transformation is performed
{
  const G4double Tlowlimit=100.*keV ;
  const G4double fact = 1.-1.e-10 ;
  //!! const G4double tausmall=5.e-5,taubig=0.9999,trueBig=5. ;
  const G4double tausmall=5.e-5,taubig=0.9999,trueBig=9.21034 ;
  G4double tau ,geomPathLength, truePathLength ;

  aParticleChange.Initialize(track);

  geomPathLength = track.GetStepLength() ;

  //Store this value for later use in PostStepDoIt
  GeomStepFinal = geomPathLength ;

  if(geomPathLength == zLast)
  { 
    truePathLength = tLast ;
  }
  else
  {
    if( fTransportMeanFreePath > biglambda )
    {
      truePathLength = track.GetStepLength() ;
      CosTheta = 1. ;
    }
    else
    {
      G4double T = track.GetDynamicParticle()->GetKineticEnergy() ;
      if(T < Tlowlimit)
      { // spec. low energy msc code
        G4double range = G4EnergyLossTables::GetPreciseRangeFromEnergy(
                           track.GetDynamicParticle()->GetDefinition(),
                           T,track.GetMaterial()) ;
        G4double alfa = 1.+range/fTransportMeanFreePath ;
        G4double z = geomPathLength ;  
        //protection: z can not be greater than zmax !!!!!!!
        G4double zmax = fact*range/alfa ;
        if(z > zmax) 
          z = zmax ;
     
        if(z == zmax)
        {
          truePathLength = range ;
          CosTheta = 0. ;
        }
        else 
        {
          truePathLength = range*
               (1.-exp(log(1.-alfa*z/range)/alfa)) ;
          CosTheta = (1.-alfa*z/range)/(1.-truePathLength/range) ;
        }
      }
      else
      {   
        tau = geomPathLength/fTransportMeanFreePath ;

        if(tau<tausmall)
          truePathLength = fTransportMeanFreePath*tau*(1.+0.5*tau) ;
        else
        {
          if(tau<taubig)
            truePathLength = -fTransportMeanFreePath*log(1.-tau) ;
          else
            truePathLength = fTransportMeanFreePath*trueBig ;
        }
        CosTheta = exp(-truePathLength/fTransportMeanFreePath) ;
      } 
    }
  }

  if(truePathLength<geomPathLength)
    truePathLength = geomPathLength ;

  aParticleChange.SetTrueStepLength(truePathLength) ;

  return &aParticleChange ;
}


inline G4bool G4IMultipleScattering::IsApplicable(
                                    const G4ParticleDefinition& particle)
{
  return(particle.GetPDGCharge() != 0.);
}


