## main245

Back to index.

    // main245.cc is a part of the PYTHIA event generator.
    // Copyright (C) 2026 Torbjorn Sjostrand.
    // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
    // Please respect the MCnet Guidelines, see GUIDELINES for details.

    // Keywords:
    //            External derived class
    //            Parton distribution
    //            Random number generator
    //            Beam momentum
    //            Vertex spread

    // Examples how to write external derived classes
    // that can be handed to Pythia for internal generation.
    // 1) The MIXMAX random number generator; see include/Pythia8Plugins/MixMax.h.
    // 2) A simple derived class for external beam momentum and vertex spread.
    // 3) A simple derived class for external parton distributions.
    // Warning: the parameters are not realistic.
    // Two subruns are made, to compare runtime (and results)
    // for the RanMar and MixMax random number generators.

    #include "Pythia8/Pythia.h"
    #include "Pythia8/BeamShape.h"
    #include "Pythia8Plugins/MixMax.h"

    using namespace Pythia8;

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

    // A derived class to set beam momentum and interaction vertex spread.

    class MyBeamShape : public BeamShape {

    public:

      // Constructor.
      MyBeamShape() {}

      // Initialize beam parameters.
      // In this particular example we will reuse the existing settings names
      // but with modified meaning, so init() in the base class can be kept.
      //virtual void init( Settings& settings, Rndm* rndmPtrIn);

      // Set the two beam momentum deviations and the beam vertex.
      virtual void pick();

    };

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

    // Set the two beam momentum deviations and the beam vertex.
    // Note that momenta are in units of GeV and vertices in mm,
    // always with c = 1, so that e.g. time is in mm/c.

    void MyBeamShape::pick() {

      // Reset all values.
      deltaPxA = deltaPyA = deltaPzA = deltaPxB = deltaPyB = deltaPzB
        = vertexX = vertexY = vertexZ = vertexT = 0.;

      // Set beam A transverse momentum deviation by a two-dimensional Gaussian.
      if (allowMomentumSpread) {
        double totalDev, gauss;
        do {
          totalDev = 0.;
          if (sigmaPxA > 0.) {
            gauss     = rndmPtr->gauss();
            deltaPxA  = sigmaPxA * gauss;
            totalDev += gauss * gauss;
          }
          if (sigmaPyA > 0.) {
            gauss     = rndmPtr->gauss();
            deltaPyA  = sigmaPyA * gauss;
            totalDev += gauss * gauss;
          }
        } while (totalDev > maxDevA * maxDevA);

        // Set beam A longitudinal momentum as a triangular shape.
        // Reuse sigmaPzA to represent maximum deviation in this case.
        if (sigmaPzA > 0.) {
          deltaPzA    = sigmaPzA * ( 1. - sqrt(rndmPtr->flat()) );
          if (rndmPtr->flat() < 0.5) deltaPzA = -deltaPzA;
        }

        // Set beam B transverse momentum deviation by a two-dimensional Gaussian.
        do {
          totalDev = 0.;
          if (sigmaPxB > 0.) {
            gauss     = rndmPtr->gauss();
            deltaPxB  = sigmaPxB * gauss;
            totalDev += gauss * gauss;
          }
          if (sigmaPyB > 0.) {
            gauss     = rndmPtr->gauss();
            deltaPyB  = sigmaPyB * gauss;
            totalDev += gauss * gauss;
          }
        } while (totalDev > maxDevB * maxDevB);

        // Set beam B longitudinal momentum as a triangular shape.
        // Reuse sigmaPzB to represent maximum deviation in this case.
        if (sigmaPzB > 0.) {
          deltaPzB = sigmaPzB * ( 1. - sqrt(rndmPtr->flat()) );
          if (rndmPtr->flat() < 0.5) deltaPzB = -deltaPzB;
        }
      }

      // Set beam vertex location by a two-dimensional Gaussian.
      if (allowVertexSpread) {
        double totalDev, gauss;
        do {
          totalDev = 0.;
          if (sigmaVertexX > 0.) {
            gauss     = rndmPtr->gauss();
            vertexX   = sigmaVertexX * gauss;
            totalDev += gauss * gauss;
          }
          if (sigmaVertexY > 0.) {
            gauss     = rndmPtr->gauss();
            vertexY   = sigmaVertexY * gauss;
            totalDev += gauss * gauss;
          }
        } while (totalDev > maxDevVertex * maxDevVertex);

        // Set beam B longitudinal momentum as a triangular shape.
        // This corresponds to two step-function beams colliding.
        // Reuse sigmaVertexZ to represent maximum deviation in this case.
        if (sigmaVertexZ > 0.) {
          vertexZ     = sigmaVertexZ * ( 1. - sqrt(rndmPtr->flat()) );
          if (rndmPtr->flat() < 0.5) vertexZ = -vertexZ;

          // Set beam collision time flat between +-(sigmaVertexZ - |vertexZ|).
          // This corresponds to two step-function beams colliding (with v = c).
          vertexT = (2. * rndmPtr->flat() - 1.) * (sigmaVertexZ - abs(vertexZ));
        }

        // Add offset to beam vertex.
        vertexX      += offsetX;
        vertexY      += offsetY;
        vertexZ      += offsetZ;
        vertexT      += offsetT;
      }

    }

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

    // A simple scaling PDF. Not realistic; only to check that it works.

    class Scaling : public PDF {

    public:

      // Constructor.
      Scaling(int idBeamIn = 2212) : PDF(idBeamIn) {}

    private:

      // Update PDF values.
      void xfUpdate(int id, double x, double Q2);

    };

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

    // No dependence on Q2, so leave out name for last argument.

    void Scaling::xfUpdate(int, double x, double ) {

      // Valence quarks, carrying 60% of the momentum.
      double dv  = 4. * x * pow3(1. - x);
      double uv  = 2. * dv;

      // Gluons and sea quarks carrying the rest.
      double gl  = 2.  * pow5(1. - x);
      double sea = 0.4 * pow5(1. - x);

      // Update values
      xg    = gl;
      xu    = uv + 0.18 * sea;
      xd    = dv + 0.18 * sea;
      xubar = 0.18 * sea;
      xdbar = 0.18 * sea;
      xs    = 0.08 * sea;
      xc    = 0.04 * sea;
      xb    = 0.02 * sea;

      // idSav = 9 to indicate that all flavours reset.
      idSav = 9;

    }

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

    int main() {

      // Number of events to generate. Max number of errors.
      int nEvent = 10000;
      int nAbort = 5;

      // Comparative statistics.
      double time[2];
      Hist nchRM("charged multiplicity RanMar", 100, -1., 399.);
      Hist nchMM("charged multiplicity MixMax", 100, -1., 399.);

      // Compare runtime (and results) for RanMar and MixMax random numbers.
      for (int iRNG = 0; iRNG < 2; ++iRNG) {
        clock_t start = clock();

        // Pythia generator.
        Pythia pythia;

        // Process selection.
        pythia.readString("HardQCD:all = on");
        pythia.readString("PhaseSpace:pTHatMin = 20.");

        // LHC with acollinear beams in the x plane.
        // Use that default is pp with pz = +-7000 GeV, so this need not be set.
        pythia.readString("Beams:frameType = 3");
        pythia.readString("Beams:pxA = 1.");
        pythia.readString("Beams:pxB = 1.");

        // A class to generate beam parameters according to own parametrization.
        BeamShapePtr myBeamShape = make_shared<MyBeamShape>();

        // Hand pointer to Pythia.
        // If you comment this out you get internal Gaussian-style implementation.
        pythia.setBeamShapePtr( myBeamShape);

        // Set up beam spread parameters - reused by MyBeamShape.
        pythia.readString("Beams:allowMomentumSpread = on");
        pythia.readString("Beams:sigmapxA = 0.1");
        pythia.readString("Beams:sigmapyA = 0.1");
        pythia.readString("Beams:sigmapzA = 5.");
        pythia.readString("Beams:sigmapxB = 0.1");
        pythia.readString("Beams:sigmapyB = 0.1");
        pythia.readString("Beams:sigmapzB = 5.");

        // Set up beam vertex parameters - reused by MyBeamShape.
        pythia.readString("Beams:allowVertexSpread = on");
        pythia.readString("Beams:sigmaVertexX = 0.3");
        pythia.readString("Beams:sigmaVertexY = 0.3");
        pythia.readString("Beams:sigmaVertexZ = 50.");
        // In MyBeamShape the time width is not an independent parameter.
        //pythia.readString("Beams:sigmaTime = 50.");

        // Optionally simplify generation and output.
        //pythia.readString("PartonLevel:all = off");
        pythia.readString("Next:numberShowEvent = 0");

        // A class to do random numbers externally. Hand pointer to Pythia.
        // You can provide four 32-bit unsigned integers to set random sequence.
        if (iRNG == 1)
          pythia.setRndmEnginePtr(make_shared<MixMaxRndm>( 0, 0, 0, 123));

        // Two classes to do the two PDFs externally. Hand pointers to Pythia.
        PDFPtr pdfAPtr = make_shared<Scaling>(2212);
        PDFPtr pdfBPtr = make_shared<Scaling>(2212);
        pythia.setPDFPtr( pdfAPtr, pdfBPtr);

        // If Pythia fails to initialize, exit with error.
        if (!pythia.init()) return 1;

        // Read out nominal energy.
        double eCMnom = pythia.info.eCM();

        // Local histograms.
        Hist eCM("center-of-mass energy deviation", 100, -20., 20.);
        Hist pXsum("net pX offset", 100, -1.0, 1.0);
        Hist pYsum("net pY offset", 100, -1.0, 1.0);
        Hist pZsum("net pZ offset", 100, -10., 10.);
        Hist pZind("individual abs(pZ) offset", 100, -10., 10.);
        Hist vtxX("vertex x position", 100, -1.0, 1.0);
        Hist vtxY("vertex y position", 100, -1.0, 1.0);
        Hist vtxZ("vertex z position", 100, -100., 100.);
        Hist vtxT("vertex time", 100, -100., 100.);
        Hist vtxZT("vertex |x| + |t|", 100, 0., 100.);

        // Begin event loop. Generate event.
        int iAbort = 0;
        for (int iEvent = 0; iEvent < nEvent; ++iEvent) {
          if (!pythia.next()) {

            // List faulty events and quit if many failures.
            pythia.info.list();
            pythia.process.list();
            //pythia.event.list();
            if (++iAbort < nAbort) continue;
            cout << " Event generation aborted prematurely, owing to error!\n";
            break;
          }

          // Fill comparative histograms.
          int nch = 0;
          for (int i = 0; i < pythia.event.size(); ++i)
            if (pythia.event[i].isFinal() && pythia.event[i].isCharged()) ++nch;
          if (iRNG == 0) nchRM.fill( nch );
          else           nchMM.fill( nch );

          // Fill local histograms.
          double eCMnow = pythia.info.eCM();
          eCM.fill( eCMnow - eCMnom);
          pXsum.fill(  pythia.process[0].px() - 2. );
          pYsum.fill(  pythia.process[0].py() );
          pZsum.fill(  pythia.process[0].pz() );
          pZind.fill(  pythia.process[1].pz() - 7000. );
          pZind.fill( -pythia.process[2].pz() - 7000. );
          vtxX.fill(  pythia.process[0].xProd() );
          vtxY.fill(  pythia.process[0].yProd() );
          vtxZ.fill(  pythia.process[0].zProd() );
          vtxT.fill(  pythia.process[0].tProd() );
          double absSum = abs(pythia.process[0].zProd())
                        + abs(pythia.process[0].tProd());
          vtxZT.fill( absSum );

        // End of event loop. Statistics. Histograms.
        }
        pythia.stat();
        cout << eCM << pXsum << pYsum << pZsum << pZind
             << vtxX << vtxY << vtxZ << vtxT << vtxZT;

        // Check time; end of loop over random number generators. Done.
        clock_t stop = clock();
        time[iRNG] = double(stop - start) / double(CLOCKS_PER_SEC);
      }
      cout << nchRM << nchMM;
      cout << "\n ========================================================="
           << "============" << endl;
      cout << fixed << setprecision(3) << "\n Time for run with RanMar is "
           << time[0] << " s and for run with MixMax " << time[1] << " s"<< endl;
      cout << "\n ========================================================="
           << "============" << endl;

      return 0;
    }