main428

Back to index.

// main428.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
//            Nuclear geometry
//            Validation

// Closure tests for the built-in nuclear geometry models. The tests compare
// sampled one-body radial distributions with their analytic truth shapes.

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

using namespace Pythia8;

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

bool initModel(NucleusModel& model, Pythia& pythia, int id) {
  Info& info = const_cast<Info&>(pythia.info);
  model.initPtr(id, true, info);
  return model.init();
}

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

pair<Hist, Hist> runClosure(string name, function<double()> sampleRadius,
  function<double(double)> truthShape, double rMax, int nBin, int nSample) {

  Hist sampled(name, nBin, 0., rMax);
  Hist truth(name, nBin, 0., rMax);

  for (int iSample = 0; iSample < nSample; ++iSample)
    sampled.fill(sampleRadius());

  for (int iBin = 1; iBin <= truth.getBinNumber(); ++iBin) {
    double rMid = truth.getBinCenter(iBin);
    truth.fill(rMid, max(0., truthShape(rMid)));
  }

  truth.normalizeIntegral();
  sampled.normalizeIntegral();
  return make_pair(truth, sampled);
}


int main() {

  // We need a Pythia object for reading settings and passing
  // info pointer from.
  Pythia pythia("", false);
  // We don't need to initialize the full object, just the
  // random number generator
  pythia.rndm.init(31415);

  // Common settings for all closures
  pythia.readString("HeavyIonA:HardCore = off");
  pythia.readString("HeavyIonB:HardCore = off");
  pythia.readString("HeavyIonA:GaussHardCore = off");
  pythia.readString("HeavyIonB:GaussHardCore = off");

  int nSample = 100000;
  int nBin = 80;

  vector<pair<Hist, Hist> > results;

  // Closure test for GLISSANDO, a selection of different nuclei
  vector<pair<int, string> > glissandoNuclei = {{1000290630, "63Cu"},
    {1000541290, "129Xe"}, {1000791970, "197Au"}, {1000822080, "208Pb"}};
  for (auto n : glissandoNuclei) {
    GLISSANDOModel g;
    initModel(g, pythia, n.first);
    results.push_back(runClosure(
      "GLISSANDO "+n.second,
      [&](){ return g.generateNucleon().pAbs();},
      [&](double r) {
        return r * r / (1. + exp((r - g.R()) / g.a())); },
      g.R() + 20. * g.a(), nBin, nSample));
  }

  // Closure test for Harmonic Oscillator Shell, a selection of light nuclei.
  vector<pair<int, string> > hoShellNuclei = {{1000020040, "4He"},
    {1000030080, "8Li"}, {1000060120, "12C"}, {1000080160, "16O"}};
  for (auto n : hoShellNuclei) {
    HOShellModel h;
    initModel(h, pythia, n.first);
    results.push_back(runClosure(
      "HO shell "+n.second,
      [&](){ return h.generateNucleon().pAbs();},
      [&](double r) {
        return r * r * (1. + (h.A() - 4.) / 6. * r * r / h.C2())
          * exp(-r * r / h.C2()); },
      6.5 * sqrt(h.C2()), nBin, nSample));
  }

  // Closure test for Gaussian, two light nuclei.
  vector<pair<int, string> > gaussianNuclei = {{1000020040, "4He"},
    {1000080160, "16O"}};
  for (auto n : gaussianNuclei) {
    GaussianModel gaussian;
    initModel(gaussian, pythia, n.first);
    results.push_back(runClosure(
      "Gaussian "+n.second,
      [&](){ return gaussian.generateNucleon().pAbs();},
      [&](double r) {
        return exp(-0.5 * pow2(r / gaussian.chargeRadius())); },
      5.5 * gaussian.chargeRadius(), nBin, nSample));
  }

  // Closure test for Hulthen, for deuterons.
  HulthenModel hulthen;
  initModel(hulthen, pythia, 1000010020);
  results.push_back(runClosure(
    "Hulthen deuteron",
    [&](){
      vector<Nucleon> nucleons = hulthen.generate();
      return (nucleons[1].nPos() - nucleons[0].nPos()).pAbs();},
    [&](double r) {
      return exp(-2. * hulthen.a() * r) + exp(-2. * hulthen.b() * r)
        - 2. * exp(-(hulthen.a() + hulthen.b()) * r); },
    12. / hulthen.a(), nBin, nSample));

  // Write a matplotlib script for visual inspection of each closure.
  HistPlot hpl("plot428");
  for (int iResult = 0; iResult < int(results.size()); ++iResult) {
    hpl.frame(iResult == 0 ? "fig428" : "",
      results[iResult].first.getTitle() + " radial closure",
      "$r$ (fm)", "P(r)", 8.0, 5.4);
    hpl.add(results[iResult].second, "e,blue", "sampled");
    hpl.add(results[iResult].first, "-,red", "truth");
    hpl.plot();
  }

  cout << "\n Plot script written to plot428.py." << endl;
  return 0;
}