// SigmaTotal.cc is a part of the PYTHIA event generator.
// Copyright (C) 2017 Torbjorn Sjostrand.
// PYTHIA is licenced under the GNU GPL version 2, see COPYING for details.
// Please respect the MCnet Guidelines, see GUIDELINES for details.

// Function definitions (not found in the header) for the SigmaTotal class.

#include "Pythia8/SigmaTotal.h"

namespace Pythia8 {

//==========================================================================

// The SigmaTotal class.

// Formulae are taken from:
// G.A. Schuler and T. Sjostrand, Phys. Rev. D49 (1994) 2257,
//   Z. Phys. C73 (1997) 677
// which borrows some total cross sections from
// A. Donnachie and P.V. Landshoff, Phys. Lett. B296 (1992) 227.

// Implemented processes with their process number iProc:
// =  0 : p + p;     =  1 : pbar + p;
// =  2 : pi+ + p;   =  3 : pi- + p;     =  4 : pi0/rho0 + p;
// =  5 : phi + p;   =  6 : J/psi + p;
// =  7 : rho + rho; =  8 : rho + phi;   =  9 : rho + J/psi;
// = 10 : phi + phi; = 11 : phi + J/psi; = 12 : J/psi + J/psi;
// = 13 : Pom + p (preliminary); 14 : gamma + gamma (preliminary);
// = 15 : gamma + p (preliminary).
// For now a neutron is treated like a proton.

//--------------------------------------------------------------------------

// Definitions of static variables.
// Note that a lot of parameters are hardcoded as const here, rather
// than being interfaced for public change, since any changes would
// have to be done in a globally consistent manner. Which basically
// means a rewrite/replacement of the whole class.

// Minimum threshold below which no cross sections will be defined.
const double SigmaTotal::MMIN  = 2.;

// General constants in total cross section parametrization:
// sigmaTot = X * s^epsilon + Y * s^eta (pomeron + reggeon).
const double SigmaTotal::EPSILON = 0.0808;
const double SigmaTotal::ETA     = -0.4525;
const double SigmaTotal::X[] = { 21.70, 21.70, 13.63, 13.63, 13.63,
  10.01, 0.970, 8.56, 6.29, 0.609, 4.62, 0.447, 0.0434};
const double SigmaTotal::Y[] = { 56.08, 98.39, 27.56, 36.02, 31.79,
  1.51, -0.146, 13.08, -0.62, -0.060, 0.030, -0.0028, 0.00028};

// Type of the two incoming hadrons as function of the process number:
// = 0 : p/n ; = 1 : pi/rho/omega; = 2 : phi; = 3 : J/psi.
const int SigmaTotal::IHADATABLE[] = { 0, 0, 1, 1, 1, 2, 3, 1, 1,
  1, 2, 2, 3};
const int SigmaTotal::IHADBTABLE[] = { 0, 0, 0, 0, 0, 0, 0, 1, 2,
  3, 2, 3, 3};

// Hadron-Pomeron coupling beta(t) = beta(0) * exp(b*t).
const double SigmaTotal::BETA0[] = { 4.658, 2.926, 2.149, 0.208};
const double SigmaTotal::BHAD[]  = {   2.3,   1.4,   1.4,  0.23};

// Pomeron trajectory alpha(t) = 1 + epsilon + alpha' * t
const double SigmaTotal::ALPHAPRIME = 0.25;

// Conversion coefficients = 1/(16pi) * (mb <-> GeV^2) * (G_3P)^n,
// with n = 0 elastic, n = 1 single and n = 2 double diffractive.
const double SigmaTotal::CONVERTEL = 0.0510925;
const double SigmaTotal::CONVERTSD = 0.0336;
const double SigmaTotal::CONVERTDD = 0.0084;

// Parameters and coefficients for single diffractive scattering.
const int SigmaTotal::ISDTABLE[] = { 0, 0, 1, 1, 1, 2, 3, 4, 5,
  6, 7, 8, 9};
const double SigmaTotal::CSD[10][8] = {
  { 0.213, 0.0, -0.47, 150., 0.213, 0.0, -0.47, 150., } ,
  { 0.213, 0.0, -0.47, 150., 0.267, 0.0, -0.47, 100., } ,
  { 0.213, 0.0, -0.47, 150., 0.232, 0.0, -0.47, 110., } ,
  { 0.213, 7.0, -0.55, 800., 0.115, 0.0, -0.47, 110., } ,
  { 0.267, 0.0, -0.46,  75., 0.267, 0.0, -0.46,  75., } ,
  { 0.232, 0.0, -0.46,  85., 0.267, 0.0, -0.48, 100., } ,
  { 0.115, 0.0, -0.50,  90., 0.267, 6.0, -0.56, 420., } ,
  { 0.232, 0.0, -0.48, 110., 0.232, 0.0, -0.48, 110., } ,
  { 0.115, 0.0, -0.52, 120., 0.232, 6.0, -0.56, 470., } ,
  { 0.115, 5.5, -0.58, 570., 0.115, 5.5, -0.58, 570.  } };

// Parameters and coefficients for double diffractive scattering.
const int SigmaTotal::IDDTABLE[] = { 0, 0, 1, 1, 1, 2, 3, 4, 5,
  6, 7, 8, 9};
const double SigmaTotal::CDD[10][9] = {
  { 3.11, -7.34,  9.71, 0.068, -0.42, 1.31, -1.37,  35.0,  118., } ,
  { 3.11, -7.10,  10.6, 0.073, -0.41, 1.17, -1.41,  31.6,   95., } ,
  { 3.12, -7.43,  9.21, 0.067, -0.44, 1.41, -1.35,  36.5,  132., } ,
  { 3.13, -8.18, -4.20, 0.056, -0.71, 3.12, -1.12,  55.2, 1298., } ,
  { 3.11, -6.90,  11.4, 0.078, -0.40, 1.05, -1.40,  28.4,   78., } ,
  { 3.11, -7.13,  10.0, 0.071, -0.41, 1.23, -1.34,  33.1,  105., } ,
  { 3.12, -7.90, -1.49, 0.054, -0.64, 2.72, -1.13,  53.1,  995., } ,
  { 3.11, -7.39,  8.22, 0.065, -0.44, 1.45, -1.36,  38.1,  148., } ,
  { 3.18, -8.95, -3.37, 0.057, -0.76, 3.32, -1.12,  55.6, 1472., } ,
  { 4.18, -29.2,  56.2, 0.074, -1.36, 6.67, -1.14, 116.2, 6532.  } };
const double SigmaTotal::SPROTON = 0.880;

// MBR parameters. Integration of MBR cross section.
const int    SigmaTotal::NINTEG = 1000;
const int    SigmaTotal::NINTEG2 = 40;
const double SigmaTotal::HBARC2 = 0.38938;
// MBR: form factor appoximation with two exponents, [FFB1,FFB2] = GeV^-2.
const double SigmaTotal::FFA1 = 0.9;
const double SigmaTotal::FFA2 = 0.1;
const double SigmaTotal::FFB1 = 4.6;
const double SigmaTotal::FFB2 = 0.6;

//--------------------------------------------------------------------------

// Store pointer to Info and initialize data members.

void SigmaTotal::init(Info* infoPtrIn, Settings& settings,
  ParticleData* particleDataPtrIn) {

  // Store pointers.
  infoPtr         = infoPtrIn;
  particleDataPtr = particleDataPtrIn;

  // Normalization of central diffractive cross section.
  zeroAXB    = settings.flag("SigmaTotal:zeroAXB");
  sigAXB2TeV = settings.parm("SigmaTotal:sigmaAXB2TeV");

  // User-set values for cross sections.
  setTotal   = settings.flag("SigmaTotal:setOwn");
  sigTotOwn  = settings.parm("SigmaTotal:sigmaTot");
  sigElOwn   = settings.parm("SigmaTotal:sigmaEl");
  sigXBOwn   = settings.parm("SigmaTotal:sigmaXB");
  sigAXOwn   = settings.parm("SigmaTotal:sigmaAX");
  sigXXOwn   = settings.parm("SigmaTotal:sigmaXX");
  sigAXBOwn  = settings.parm("SigmaTotal:sigmaAXB");

  // User-set values to dampen diffractive cross sections.
  doDampen   = settings.flag("SigmaDiffractive:dampen");
  maxXBOwn   = settings.parm("SigmaDiffractive:maxXB");
  maxAXOwn   = settings.parm("SigmaDiffractive:maxAX");
  maxXXOwn   = settings.parm("SigmaDiffractive:maxXX");
  maxAXBOwn  = settings.parm("SigmaDiffractive:maxAXB");

  // User-set values for handling of elastic sacattering.
  setElastic = settings.flag("SigmaElastic:setOwn");
  bSlope     = settings.parm("SigmaElastic:bSlope");
  rho        = settings.parm("SigmaElastic:rho");
  lambda     = settings.parm("SigmaElastic:lambda");
  tAbsMin    = settings.parm("SigmaElastic:tAbsMin");
  alphaEM0   = settings.parm("StandardModel:alphaEM0");

  // Parameters for diffractive systems.
  sigmaPomP  = settings.parm("Diffraction:sigmaRefPomP");
  mPomP      = settings.parm("Diffraction:mRefPomP");
  pPomP      = settings.parm("Diffraction:mPowPomP");

  // Diffractive mass spectrum starts at m + mMin0 and has a low-mass
  // enhancement, factor cRes, up to around m + mRes0.
  mMin0       = settings.parm("SigmaDiffractive:mMin");
  cResSv      = settings.parm("SigmaDiffractive:lowMEnhance");
  mRes0       = settings.parm("SigmaDiffractive:mResMax");

  // Parameters for MBR model.
  PomFlux     = settings.mode("Diffraction:PomFlux");
  MBReps      = settings.parm("Diffraction:MBRepsilon");
  MBRalpha    = settings.parm("Diffraction:MBRalpha");
  MBRbeta0    = settings.parm("Diffraction:MBRbeta0");
  MBRsigma0   = settings.parm("Diffraction:MBRsigma0");
  m2min       = settings.parm("Diffraction:MBRm2Min");
  dyminSDflux = settings.parm("Diffraction:MBRdyminSDflux");
  dyminDDflux = settings.parm("Diffraction:MBRdyminDDflux");
  dyminCDflux = settings.parm("Diffraction:MBRdyminCDflux");
  dyminSD     = settings.parm("Diffraction:MBRdyminSD");
  dyminDD     = settings.parm("Diffraction:MBRdyminDD");
  dyminCD     = settings.parm("Diffraction:MBRdyminCD");
  dyminSigSD  = settings.parm("Diffraction:MBRdyminSigSD");
  dyminSigDD  = settings.parm("Diffraction:MBRdyminSigDD");
  dyminSigCD  = settings.parm("Diffraction:MBRdyminSigCD");

  // Parameters for gamma+gamma total cross section.
  fracSigmaNDgamma = settings.parm("Photon:sigmaNDfrac");
}

//--------------------------------------------------------------------------

// Function that calculates the relevant properties.

bool SigmaTotal::calc( int idA, int idB, double eCM) {

  // Derived quantities.
  alP2 = 2. * ALPHAPRIME;
  s0   = 1. / ALPHAPRIME;

  // Reset everything to zero to begin with.
  isCalc = false;
  sigTot = sigEl = sigXB = sigAX = sigXX = sigAXB = sigND = bEl = s
    = bA = bB = 0.;

  // Order flavour of incoming hadrons: idAbsA < idAbsB (restore later).
  int idAbsA = abs(idA);
  int idAbsB = abs(idB);
  bool swapped = false;
  if (idAbsA > idAbsB) {
    swap( idAbsA, idAbsB);
    swapped = true;
  }
  double sameSign = (idA * idB > 0);

  // Find process number.
  int iProc                                       = -1;
  if (idAbsA > 1000) {
    iProc                                         = (sameSign) ? 0 : 1;
  } else if (idAbsA > 100 && idAbsB > 1000) {
    iProc                                         = (sameSign) ? 2 : 3;
    if (idAbsA/10 == 11 || idAbsA/10 == 22) iProc = 4;
    if (idAbsA > 300) iProc                       = 5;
    if (idAbsA > 400) iProc                       = 6;
    if (idAbsA > 900) iProc                       = 13;
  } else if (idAbsA > 100) {
    iProc                                         = 7;
    if (idAbsB > 300) iProc                       = 8;
    if (idAbsB > 400) iProc                       = 9;
    if (idAbsA > 300) iProc                       = 10;
    if (idAbsA > 300 && idAbsB > 400) iProc       = 11;
    if (idAbsA > 400) iProc                       = 12;
  } else if (idAbsA == 22 || idAbsB == 22) {
    if (idAbsA == idAbsB) iProc                   = 14;
    if (idAbsA > 1000 || idAbsB > 1000) iProc     = 15;
  }

  if (iProc == -1) return false;

  // Primitive implementation of Pomeron + p.
  if (iProc == 13) {
    s      = eCM*eCM;
    sigTot = sigmaPomP * pow( eCM / mPomP, pPomP);
    sigND  = sigTot;
    isCalc = true;
    return true;
  }

  // Primitive implementation of gamma + gamma.
  if (iProc == 14) {
    s           = eCM*eCM;
    double sEps = pow( s, EPSILON);
    double sEta = pow( s, ETA);
    sigTot = 211e-6 * sEps + 215e-6 * sEta;
    sigND  = fracSigmaNDgamma * sigTot;
    isCalc = true;
    return true;
  }

  // Primitive implementation of gamma + p.
  // Multiplied with a factor (\sim 0.7) as above to get the inelastic part.
  if (iProc == 15) {
    s           = eCM*eCM;
    double sEps = pow( s, EPSILON);
    double sEta = pow( s, ETA);
    sigTot = 67.7e-3 * sEps + 129e-3 * sEta;
    sigND  = fracSigmaNDgamma * sigTot;
    isCalc = true;
    return true;
  }

  // Find hadron masses and check that energy is enough.
  // For mesons use the corresponding vector meson masses.
  int idModA = (idAbsA > 1000) ? idAbsA : 10 * (idAbsA/10) + 3;
  int idModB = (idAbsB > 1000) ? idAbsB : 10 * (idAbsB/10) + 3;
  double mA  = particleDataPtr->m0(idModA);
  double mB  = particleDataPtr->m0(idModB);
  if (eCM < mA + mB + MMIN) {
    infoPtr->errorMsg("Error in SigmaTotal::calc: too low energy");
    return false;
  }

  // Evaluate the total cross section.
  s           = eCM*eCM;
  double sEps = pow( s, EPSILON);
  double sEta = pow( s, ETA);
  sigTot      = X[iProc] * sEps + Y[iProc] * sEta;

  // Slope of hadron form factors.
  int iHadA = IHADATABLE[iProc];
  int iHadB = IHADBTABLE[iProc];
  bA        = BHAD[iHadA];
  bB        = BHAD[iHadB];

  // Elastic slope parameter and cross section.
  bEl   = 2.*bA + 2.*bB + 4.*sEps - 4.2;
  sigEl = CONVERTEL * pow2(sigTot) / bEl;

  // Lookup coefficients for single and double diffraction.
  int iSD = ISDTABLE[iProc];
  int iDD = IDDTABLE[iProc];
  double sum1, sum2, sum3, sum4;

  // Single diffractive scattering A + B -> X + B cross section.
  mMinXBsave      = mA + mMin0;
  double sMinXB   = pow2(mMinXBsave);
  mResXBsave      = mA + mRes0;
  double sResXB   = pow2(mResXBsave);
  double sRMavgXB = mResXBsave * mMinXBsave;
  double sRMlogXB = log(1. + sResXB/sMinXB);
  double sMaxXB   = CSD[iSD][0] * s + CSD[iSD][1];
  double BcorrXB  = CSD[iSD][2] + CSD[iSD][3] / s;
  sum1  = log( (2.*bB + alP2 * log(s/sMinXB))
    / (2.*bB + alP2 * log(s/sMaxXB)) ) / alP2;
  sum2  = cResSv * sRMlogXB / (2.*bB + alP2 * log(s/sRMavgXB) + BcorrXB) ;
  sigXB = CONVERTSD * X[iProc] * BETA0[iHadB] * max( 0., sum1 + sum2);

  // Single diffractive scattering A + B -> A + X cross section.
  mMinAXsave      = mB + mMin0;
  double sMinAX   = pow2(mMinAXsave);
  mResAXsave      = mB + mRes0;
  double sResAX   = pow2(mResAXsave);
  double sRMavgAX = mResAXsave * mMinAXsave;
  double sRMlogAX = log(1. + sResAX/sMinAX);
  double sMaxAX   = CSD[iSD][4] * s + CSD[iSD][5];
  double BcorrAX  = CSD[iSD][6] + CSD[iSD][7] / s;
  sum1  = log( (2.*bA + alP2 * log(s/sMinAX))
    / (2.*bA + alP2 * log(s/sMaxAX)) ) / alP2;
  sum2  = cResSv * sRMlogAX / (2.*bA + alP2 * log(s/sRMavgAX) + BcorrAX) ;
  sigAX = CONVERTSD * X[iProc] * BETA0[iHadA] * max( 0., sum1 + sum2);

  // Order single diffractive correctly.
  if (swapped) {
    swap( bB, bA);
    swap( sigXB, sigAX);
    swap( mMinXBsave, mMinAXsave);
    swap( mResXBsave, mResAXsave);
   }

  // Double diffractive scattering A + B -> X1 + X2 cross section.
  double y0min = log( s * SPROTON / (sMinXB * sMinAX) ) ;
  double sLog  = log(s);
  double Delta0 = CDD[iDD][0] + CDD[iDD][1] / sLog
    + CDD[iDD][2] / pow2(sLog);
  sum1 = (y0min * (log( max( 1e-10, y0min/Delta0) ) - 1.) + Delta0)/ alP2;
  if (y0min < 0.) sum1 = 0.;
  double sMaxXX = s * ( CDD[iDD][3] + CDD[iDD][4] / sLog
    + CDD[iDD][5] / pow2(sLog) );
  double sLogUp = log( max( 1.1, s * s0 / (sMinXB * sRMavgAX) ));
  double sLogDn = log( max( 1.1, s * s0 / (sMaxXX * sRMavgAX) ));
  sum2   = cResSv * log( sLogUp / sLogDn ) * sRMlogAX / alP2;
  sLogUp = log( max( 1.1, s * s0 / (sMinAX * sRMavgXB) ));
  sLogDn = log( max( 1.1, s * s0 / (sMaxXX * sRMavgXB) ));
  sum3   = cResSv * log(sLogUp / sLogDn) * sRMlogXB / alP2;
  double BcorrXX =  CDD[iDD][6] + CDD[iDD][7] / eCM + CDD[iDD][8] / s;
  sum4   = pow2(cResSv) * sRMlogAX * sRMlogXB
    / max( 0.1, alP2 * log( s * s0 / (sRMavgAX * sRMavgXB) ) + BcorrXX);
  sigXX  = CONVERTDD * X[iProc] * max( 0., sum1 + sum2 + sum3 + sum4);

  // Central diffractive scattering A + B -> A + X + B, only p and pbar.
  mMinAXBsave = 1.;
  if ( (idAbsA == 2212 || idAbsA == 2112)
    && (idAbsB == 2212 || idAbsB == 2112) && !zeroAXB) {
    double sMinAXB = pow2(mMinAXBsave);
    double sRefAXB = pow2(2000.);
    sigAXB = sigAXB2TeV * pow( log(0.06 * s / sMinAXB), 1.5 )
           / pow( log(0.06 * sRefAXB / sMinAXB), 1.5 );
  }

  // Option with user-requested damping of diffractive cross sections.
  if (doDampen) {
    sigXB  = sigXB  * maxXBOwn  / (sigXB  + maxXBOwn);
    sigAX  = sigAX  * maxAXOwn  / (sigAX  + maxAXOwn);
    sigXX  = sigXX  * maxXXOwn  / (sigXX  + maxXXOwn);
    sigAXB = sigAXB * maxAXBOwn / (sigAXB + maxAXBOwn);
  }

  // Calculate cross sections in MBR model.
  if (PomFlux == 5) calcMBRxsecs(idA, idB, eCM);

  // Option with user-set values for total and partial cross sections.
  // (Is not done earlier since want diffractive slopes anyway.)
  double sigNDOwn = sigTotOwn - sigElOwn - sigXBOwn - sigAXOwn - sigXXOwn
                  - sigAXBOwn;
  double sigElMax = sigEl;
  if (setTotal && sigNDOwn > 0.) {
    sigTot   = sigTotOwn;
    sigEl    = sigElOwn;
    sigXB    = sigXBOwn;
    sigAX    = sigAXOwn;
    sigXX    = sigXXOwn;
    sigAXB   = sigAXBOwn;
    sigElMax = sigEl;

    // Sub-option to set elastic parameters, including Coulomb contribution.
    if (setElastic) {
      bEl      = bSlope;
      sigEl    = CONVERTEL * pow2(sigTot) * (1. + rho*rho) / bSlope;
      sigElMax = 2. * (sigEl * exp(-bSlope * tAbsMin)
               + alphaEM0 * alphaEM0 / (4. * CONVERTEL * tAbsMin) );
    }
  }

  // Inelastic nondiffractive by unitarity.
  sigND = sigTot - sigEl - sigXB - sigAX - sigXX - sigAXB;
  if (sigND < 0.) infoPtr->errorMsg("Error in SigmaTotal::init: "
    "sigND < 0");
  else if (sigND < 0.4 * sigTot) infoPtr->errorMsg("Warning in "
    "SigmaTotal::init: sigND suspiciously low");

  // Upper estimate of elastic, including Coulomb term, where appropriate.
  sigEl = sigElMax;

  // Done.
  isCalc = true;
  return true;

}

//--------------------------------------------------------------------------

// Calculate parameters in the MBR model.

bool SigmaTotal::calcMBRxsecs( int idA, int idB, double eCM) {

  // Local variables.
  double sigtot, sigel, sigsd, sigdd, sigdpe;

  // MBR parameters locally.
  double eps       = MBReps;
  double alph      = MBRalpha;
  double beta0gev  = MBRbeta0;
  double beta0mb   = beta0gev * sqrt(HBARC2);
  double sigma0mb  = MBRsigma0;
  double sigma0gev = sigma0mb/HBARC2;
  double a1        = FFA1;
  double a2        = FFA2;
  double b1        = FFB1;
  double b2        = FFB2;

  // Calculate total and elastic cross sections.
  double ratio;
  if (eCM <= 1800.0) {
    double sign = (idA * idB > 0);
    sigtot = 16.79 * pow(s, 0.104) + 60.81 * pow(s, -0.32)
           - sign * 31.68 * pow(s, -0.54);
    ratio  = 0.100 * pow(s, 0.06) + 0.421 * pow(s, -0.52)
           + sign * 0.160 * pow(s, -0.6);
  } else {
    double sigCDF = 80.03;
    double sCDF   = pow2(1800.);
    double sF     = pow2(22.);
    sigtot = sigCDF + ( pow2( log(s / sF)) - pow2( log(sCDF / sF)) )
            * M_PI / (3.7 / HBARC2);
    ratio  = 0.066 + 0.0119 * log(s);
  }
  sigel=sigtot*ratio;

  // Integrate SD, DD and DPE(CD) cross sections.
  // Each cross section is obtained from the ratio of two integrals:
  // the Regge cross section and the renormalized flux.
  double cflux, csig, c1, step, f;
  double dymin0 = 0.;

  // Calculate SD cross section.
  double dymaxSD = log(s / m2min);
  cflux          = pow2(beta0gev) / (16. * M_PI);
  csig           = cflux * sigma0mb;

  // SD flux.
  c1             = cflux;
  double fluxsd  = 0.;
  step           = (dymaxSD - dyminSDflux) / NINTEG;
  for (int i = 0; i < NINTEG; ++i) {
    double dy    = dyminSDflux + (i + 0.5) * step;
    f            = exp(2. * eps * dy) * ( (a1 / (b1 + 2. * alph * dy))
                 + (a2 / (b2 + 2. * alph * dy)) );
    f           *= 0.5 * (1. + erf( (dy - dyminSD) / dyminSigSD));
    fluxsd       = fluxsd + step * c1 * f;
  }
  if (fluxsd < 1.) fluxsd = 1.;

  // Regge cross section.
  c1             = csig * pow(s, eps);
  sigsd          = 0.;
  sdpmax         = 0.;
  step           = (dymaxSD - dymin0) / NINTEG;
  for (int i = 0; i < NINTEG; ++i) {
    double dy    = dymin0 + (i + 0.5) * step;
    f            = exp(eps * dy) * ( (a1 / (b1 + 2. * alph * dy))
                 + (a2 / (b2 + 2. * alph * dy)) );
    f           *= 0.5 * (1. + erf( (dy - dyminSD) / dyminSigSD));
    if (f > sdpmax) sdpmax = f;
    sigsd        = sigsd + step * c1 * f;
  }
  sdpmax        *= 1.01;
  sigsd         /= fluxsd;

  // Calculate DD cross section.
  // Note: dymaxDD = ln(s * s0 /mMin^4) with s0 = 1 GeV^2.
  double dymaxDD = log(s / pow2(m2min));
  cflux          = sigma0gev / (16. * M_PI);
  csig           = cflux * sigma0mb;

  // DD flux.
  c1             = cflux / (2. * alph);
  double fluxdd  = 0.;
  step           = (dymaxDD - dyminDDflux) / NINTEG;
  for (int i = 0; i < NINTEG; ++i) {
    double dy    = dyminDDflux + (i + 0.5) * step;
    f            = (dymaxDD - dy) * exp(2. * eps * dy)
                 * ( exp(-2. * alph * dy * exp(-dy))
                 - exp(-2. * alph * dy * exp(dy)) ) / dy;
    f           *= 0.5 * (1. + erf( (dy - dyminDD) / dyminSigDD));
    fluxdd       = fluxdd + step * c1 * f;
  }
  if (fluxdd < 1.) fluxdd = 1.;

  // Regge cross section.
  c1             = csig * pow(s, eps) / (2. * alph);
  ddpmax         = 0.;
  sigdd          = 0.;
  step           = (dymaxDD - dymin0) / NINTEG;
  for (int i = 0; i < NINTEG; ++i) {
    double dy    = dymin0 + (i + 0.5) * step;
    f            = (dymaxDD - dy) * exp(eps * dy)
                 * ( exp(-2. * alph * dy * exp(-dy))
                 - exp(-2. * alph * dy * exp(dy)) ) / dy;
    f           *= 0.5 * (1. + erf( (dy - dyminDD) / dyminSigDD));
    if (f > ddpmax) ddpmax = f;
    sigdd        = sigdd + step * c1 * f;
  }
  ddpmax        *= 1.01;
  sigdd         /= fluxdd;

  // Calculate DPE (CD) cross section.
  double dymaxCD = log(s / m2min);
  cflux          = pow4(beta0gev) / pow2(16. * M_PI);
  csig           = cflux * pow2(sigma0mb / beta0mb);
  double dy1, dy2, f1, f2, step2;

  // DPE flux.
  c1             = cflux;
  double fluxdpe = 0.;
  step           = (dymaxCD - dyminCDflux) / NINTEG;
  for (int i = 0; i < NINTEG; ++i) {
    double dy    = dyminCDflux + (i + 0.5) * step;
    f            = 0.;
    step2        = (dy - dyminCDflux) / NINTEG2;
    for (int j = 0; j < NINTEG2; ++j) {
      double yc  = -0.5 * (dy - dyminCDflux) + (j + 0.5) * step2;
      dy1        = 0.5 * dy - yc;
      dy2        = 0.5 * dy + yc;
      f1         = exp(2. * eps * dy1) * ( (a1 / (b1 + 2. * alph * dy1))
                 + (a2 / (b2 + 2. * alph * dy1)) );
      f2         = exp(2. * eps * dy2) * ( (a1 / (b1 + 2. * alph * dy2))
                 + (a2 / (b2 + 2. * alph * dy2)) );
      f1        *= 0.5 * (1. + erf( (dy1 - 0.5 * dyminCD)
                 / (dyminSigCD / sqrt(2.))) );
      f2        *= 0.5 * (1. + erf( (dy2 - 0.5 *dyminCD)
                 / (dyminSigCD / sqrt(2.))) );
      f         += f1 * f2 * step2;
    }
    fluxdpe     += step * c1 * f;
  }
  if (fluxdpe < 1.) fluxdpe = 1.;

  // Regge cross section.
  c1             = csig * pow(s, eps);
  sigdpe         = 0.;
  dpepmax        = 0;
  step           = (dymaxCD - dymin0) / NINTEG;
  for (int i = 0; i < NINTEG; ++i) {
    double dy    = dymin0 + (i + 0.5) * step;
    f            = 0.;
    step2        = (dy - dymin0) / NINTEG2;
    for (int j = 0; j < NINTEG2; ++j) {
      double yc  = -0.5 * (dy - dymin0) + (j + 0.5) * step2;
      dy1        = 0.5 * dy - yc;
      dy2        = 0.5 * dy + yc;
      f1         = exp(eps * dy1) * ( (a1 / (b1 + 2. * alph * dy1))
                 + (a2 / (b2 + 2. * alph * dy1)) );
      f2         = exp(eps * dy2) * ( (a1 / (b1 + 2. * alph * dy2))
                 + (a2 / (b2 + 2. * alph * dy2)) );
      f1        *= 0.5 * (1. + erf( (dy1 - 0.5 * dyminCD)
                 / (dyminSigCD / sqrt(2.))) );
      f2        *= 0.5 * (1. + erf( (dy2 - 0.5 * dyminCD)
                 /(dyminSigCD / sqrt(2.))) );
      f         += f1 * f2 * step2;
    }
    sigdpe      += step * c1 * f;
    if ( f > dpepmax) dpepmax = f;
  }
  dpepmax       *= 1.01;
  sigdpe        /= fluxdpe;

  // Diffraction done. Now calculate total inelastic cross section.
  sigND  = sigtot - (2. * sigsd + sigdd + sigel + sigdpe);
  sigTot = sigtot;
  sigEl  = sigel;
  sigAX  = sigsd;
  sigXB  = sigsd;
  sigXX  = sigdd;
  sigAXB = sigdpe;

  return true;
}

//==========================================================================

} // end namespace Pythia8
