## main427

Back to index.

    // main427.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.

    // Authors:
    //            Christian Bierlich

    // Keywords:
    //            Heavy ions
    //            Impact parameter
    //            Angantyr
    //            Event weights
    //            Reweighting

    // This test program shows how one can replace the impact parameter
    // generator in Angantyr, through a UserHook. Specifically, the
    // example replaces the existing generator with one that samples
    // impact parameters with unit weights. Unit weights can also easier
    // be obtained with HeavyIon:forceUnitWeight = on.

    #include "Pythia8/Pythia.h"
    #include "Pythia8/HIInfo.h"

    using namespace Pythia8;

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

    // The UnitImpactParameterGenerator generates impact parameters with unit
    // weights. It will sample from b = 0 to HeavyIon::bWidth. For realistic
    // events, this number should be larger than twice the radius of the
    // considered nuclei.

    class UnitImpactParameterGenerator : public ImpactParameterGenerator {

    public:
      // Constructor.
      UnitImpactParameterGenerator() {}

      // Return the impact parameter.
      Vec4 generate(double& weight) const override {
        // Set unit weight.
        weight = 1.;
        // Generate b.
        double b = sqrt(rndmPtr->flat() * width() * width());
        // Generate a random angle.
        double phi = 2.0*M_PI*rndmPtr->flat();
        // Convert to cartesian coordinates.
        return Vec4(b*sin(phi), b*cos(phi), 0., 0.);
      }

      // Return the cross section scale.
      double xSecScale() const override {
        return M_PI*pow2(width());
      }

    };

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

    // MyHIUserHooks works as a wrapper around the impact parameter class (above),
    // functioning to expose it to Angantyr.

    class MyHIUserHooks : public HIUserHooks {

    public:
      // Constructor. Create the impact parameter generator.
      MyHIUserHooks() {
        impactGeneratorPtr = make_shared<UnitImpactParameterGenerator>();
      }

      // Tell Angantyr that you want to replace the impact parameter generator.
      bool hasImpactParameterGenerator() const override {
        return true;
      }

      // Return a pointer to the impact parameter generator.
      shared_ptr<ImpactParameterGenerator> impactParameterGenerator()
        const override {
          return impactGeneratorPtr;
        }

    private:
      // A shared pointer to the new impact parameter generator lives here as a
      // member. The object is kept alive as long as the user hook is kept alive.
      shared_ptr<UnitImpactParameterGenerator> impactGeneratorPtr;
    };

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

    // Read common parameters into the two Pythia objects.
    void readCommonConfiguration(Pythia& pythia) {
      // Set beams. We choose Pb.
      pythia.readString("Beams:idA = 1000822080");
      pythia.readString("Beams:idB = 1000822080");

      // Collision energy.
      pythia.readString("Beams:eCM = 200");
      pythia.readString("Beams:frameType = 1");
      pythia.readString("SoftQCD:all = on");

      // Initialize the Angantyr model cross sections.
      // Warning: if the model changes, inactivate lines below to retune.
      pythia.readString("HeavyIon:SigFitDefAvNDb = 0.78");
      pythia.readString("HeavyIon:SigFitDefPar = 1.24,3.19,0.72");
      pythia.readString("HeavyIon:SigFitNGen = 0");

    }

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

    int main() {
      // We make two Pythia objects, one for each impact parameter
      // selector, so we can compare the results.
      Pythia pythiaDefault;
      readCommonConfiguration(pythiaDefault);
      Pythia pythiaCustom;
      readCommonConfiguration(pythiaCustom);
      // Make the user hook.
      shared_ptr<MyHIUserHooks> hiHookPtr = make_shared<MyHIUserHooks>();
      // Make the hook available to Pythia.
      pythiaCustom.setHIHooks(hiHookPtr);
      // Set the width (i.e. maximal b) of the new impact parameter sampler.
      pythiaCustom.readString("HeavyIon:bWidth = 20");

      // Histogram for the impact parameter. One for the default selector.
      Hist impactDefault("bDef", 15, 0., 20.);
      // One for the default selector, not filled with event weights.
      Hist impactDefaultUnw("bDefUnweighted", 15, 0., 20.);
      // One for the custom selector.
      Hist impactCustom("bCustom", 15, 0., 20.);

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

      // Loop over events.
      int nEvents = 5000;
      double sumWDefault = 0;
      double sumNDefault = 0;
      double sumNCustom = 0;
      for (int iEvent = 0; iEvent < nEvents; ++iEvent) {

        // Event with default impact parameter.
        if (pythiaDefault.next()) {
          // Fill the histograms.
          impactDefault.fill(pythiaDefault.info.hiInfo->b(),
            pythiaDefault.info.weight());
          sumWDefault += pythiaDefault.info.weight();
          impactDefaultUnw.fill(pythiaDefault.info.hiInfo->b());
          sumNDefault += 1.0;
        }

        // Event with custom impact parameter.
        if (pythiaCustom.next()) {
          // No need to use event weights to fill this histogram now,
          // as they are set to 1 in the custom class.
          impactCustom.fill(pythiaCustom.info.hiInfo->b());
          sumNCustom += 1.0;
        }
      }

      // Statistics.
      pythiaDefault.stat();
      pythiaCustom.stat();

      // Write histograms.
      cout << "Run is done. Writing histograms to file." << endl;
      HistPlot hpl("plot427");
      hpl.frame("fig427", "Impact parameter distribution" );
      hpl.add(impactDefault/sumWDefault, "-","Default");
      hpl.add(impactDefaultUnw/sumNDefault, "-","Default (unweighted)");
      hpl.add(impactCustom/sumNCustom, "-","Custom (unweighted)");
      hpl.plot(0, 20, 0, 0.2, false);

      return 0;
    }