main427
Back to index.
// main427.cc is a part of the PYTHIA event generator.
// Copyright (C) 2025 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;
}