Develop

The tools of this develop repository are intended to provide a convenient way to develop packages for Pythia 8 that can be installed through the use repository of PYTHIA-CONTRIB. For more details on how the plugin system for Pythia 8 works, specifically how it can be configured by the user, please consult the HTML manual. For a new package to be made public via use, the package must first be submitted, see the package submission section, and a corresponding repository created in the packages repository, see the repository creation section. The basic usage of the develop repository is to create a PYTHIA-CONTRIB package template using the syntax,

./generate PACKAGE BASE/NAME ...

which will create a ready-to-compile package template with name PACKAGE and classes of base-class type BASE with corresponding names NAME, given by the list of BASE/NAME pairs following the package name. By default, the BASE arguments should be taken from the list of available base classes which can be passed to Pythia as a plugin using the Init:plugins setting.

Init:plugins = {libPACKAGE.so::NAME}

This list of base-class templates can be printed with,

./generate --list

which reproduces the list of base classes in the table below, where links to further documentation for each base class are also given. Of note, the UserHooks class provides an omnibus interface to most parts of the Pythia event generation sequence, and should be considered when a more specific base class is not available.

base class Pythia method doc. description
BeamShape setBeamShapePtr HTML Dox. custom shape for the incoming beams
DecayHandler setDecayPtr HTML Dox. decays using an external tool
FragmentationModel setFragmentationPtr† HTML Dox. custom fragmentation model to replace the string model
HeavyIons setHeavyIonsPtr HTML Dox. fully generalized heavy-ion model
HIUserHooks setHIHooks HTML Dox. UserHooks and MergingHooks, but for heavy-ion collisions
LHAup setLHAupPtr HTML Dox. Les Houches Accord user-provided process
Merging setMergingPtr HTML Dox. bridge between the parton-shower history and Pythia
MergingHooks setMergingHooksPtr HTML Dox. parton-shower matching/merging scheme
PDF setPDFPtr HTML Dox. parton distribution functions for the incoming beams
PhaseSpace setSigmaPtr (arg. 2)† HTML Dox. phase-space integrator for a custom SigmaProcess; cannot be used by itself
ResonanceWidths setResonancePtr† HTML Dox. external width calculator for resonances
RndmEngine setRndmEnginePtr HTML Dox. random number generator used by all of Pythia
ShowerModel setShowerModelPtr HTML Dox. custom parton shower, providing both a timelike (FSR) and a spacelike (ISR) shower
SigmaProcess setSigmaPtr (arg. 1)† HTML Dox. new user-provided hard process, optionally used in conjunction with PhaseSpace
UserHooks setUserHooksPtr† HTML Dox. omnibus class which allows interactions with the Pythia event-generation sequence at a number of defined steps

† The corresponding add and insert methods, e.g., addUserHooksPtr, are also available. setSigmaPtr takes the SigmaProcess as its first argument and an optional PhaseSpace as its second.

For the eager package author, a very abbreviated set of commands is given below that will generate, compile, and run an example PYTHIA-CONTRIB package, assuming that Pythia is already installed and pythia8-config is available as a command.

# Generate the template for two user classes, MyUserHooks and MyPDF.
./generate MyPackage UserHooks/MyUserHooks PDF/MyPDF
# Build the package.
cd MyPackage
make -j
# Set the PYTHIA8CONTRIB path.
export PYTHIA8CONTRIB=$PWD/share
# Compile and run the test example for MyUserHooks.
cd share/MyPackage/examples/
make testPlugins
./testPlugins MyUserHooks.cmnd

Further details for each step are given in the remainder of this documentation.

Package Generation

The output of generate will produce a folder with name PACKAGE, with the following structure. Each item of this list is described in further detail in its own dedicated section below.

Example

As an initial example, the following command will produce a PYTHIA-CONTRIB package with the name MyPackage consisting of: a MyUserHooks class of base-class type UserHooks and a MyPDF class of base-class type PDF.

./generate MyPackage UserHooks/MyUserHooks PDF/MyPDF

The package structure for this example is as follows.

MyPackage/
├── configure
├── .configure.inc
├── .gitignore
├── include
│   └── MyPackage
│       ├── MyPDF.h
│       └── MyUserHooks.h
├── Makefile
├── .meta.yaml
├── README.md
├── share
│   └── MyPackage
│       ├── examples
│       │   ├── Makefile
│       │   ├── MyPDF.cmnd
│       │   ├── MyUserHooks.cmnd
│       │   └── testPlugins.cc
│       └── xmldoc
│           ├── Formatting.xml
│           ├── Index.xml
│           └── Settings.xml
└── src
    ├── MyPDF.cc
    ├── MyUserHooks.cc
    └── Package.cc

By default only base classes from the list given by ./generate --list can be provided as BASE arguments to the BASE/NAME list. If a base class is not available, generate will skip that BASE/NAME pair and issue the following warning.

./generate PACKAGE BASE/NAME
WARNING: Unknown template class 'BASE', skipping 'BASE/NAME'.

If the option --nocheck is specified, then the BASE/NAME pair will not be skipped and a minimal template will be produced for that BASE/NAME pair. For more details on working with classes that cannot be loaded via the Init:plugins setting, please see the additional classes section.

Template Code

For each base-class type, the default behavior of generate is to produce template code where all inherited base-class methods are provided. All methods that need to be defined, i.e., pure virtual methods of the base class, are implemented with an ansatz that should be changed by the package author. Methods that are implemented in the base class but may be overridden are provided as comments. In the example above, the following method of MyPDF must be implemented,

  // Update parton densities (defined in src/MyPDF.cc). Must be defined.
  void xfUpdate(int id, double x, double Q2) override;

while the method,

  // // Allow for new scaling factor for VMD PDFs.
  // void setVMDscale(double = 1.) override {}

is optional. The constructor and destructor for every package class must also be defined,

  // Constructor (defined in src/MyPDF.cc).
  MyPDF(Pythia*, Settings*, Logger*);

  // Destructor.
  ~MyPDF() = default;

where a template signature is provided in the header file and a template definition in the source file. The templates produced with the default generate behavior compile out-of-the-box.

Minimal Template

In some cases, the package author might wish to begin with a minimal template, where only the constructor and destructor are already defined. This is possible by passing the --minimal option to generate. The template code generated here will not compile out-of-the-box, as the pure virtual methods of the base class will need to be implemented by the package author. When using the --nocheck option above, full template code will be produced if the base class is known, and by necessity, minimal template code will be produced if the base class is not known.

Modifying a Package

Because the generate command could potentially overwrite already written code, the default behavior of generate is to fail when asked to produce the template for a package directory that already exists. For example, running the command

./generate PACKAGE BASE/NAME

twice will result in the following error message.

ERROR: The 'PACKAGE' package already exists, use '--update' to update.

Here, it is possible to update an already generated package with additional classes using the --update option.

./generate --update PACKAGE BASE/NAME

Any class of name NAME being requested that already exists in the package will be skipped with the following warning.

WARNING: Skipping existing class name 'NAME'.

Testing a Package

To compile and test a package, a working installation of Pythia is required. The following steps are intended to quickly set up such an environment, and should give a basic outline of the process. For more details on installing Pythia, please consult the README included as part of the Pythia source.

Build Pythia

Pythia can be installed by first checking out the Pythia releases repository.

git clone git@gitlab.com:Pythia8/releases.git
cd releases
# Optionally check out a specific version.
# git checkout pythia8318

It is possible to check out a specific version of Pythia, but only versions 8.310 and higher are compatible with the plugin structure of PYTHIA-CONTRIB. Next, configure, build, and install Pythia.

./configure  # Include optional configuration flags here. 
make -j      # Build with an optional number of cores.
make install # Use --prefix options with configure to change installation.
cd ../

By default, the Pythia installation will be placed in the directory in which it was built when make install is called, unless --prefix options are passed to configure.

Build the Package

Once Pythia is installed, make sure that the pythia8-config script can be found by the configure script of the PYTHIA-CONTRIB package being developed. This can either be done by adjusting the system PATH so that pythia8-config is along it, or by directly passing the location of the pythia8-config script to configure. Assuming the installation directory of the previous section, the system path can be updated as follows.

export PATH=$PATH:$PWD/releases/bin

Alternatively, the package configure script can be called with the location specified.

cd MyPackage
./configure --with-pythia8-config=../releases/bin/pythia8-config

Once the path to pythia8-config is specified using either method, the package can be built with make called from the generated MyPackage directory.

cd MyPackage # If not already there from calling ./configure.
make -j      # Build with an optional number of cores.

If the package was built successfully, then the library lib/libPACKAGE.so should exist. In this case, the command above builds the library lib/libMyPackage.so.

Test the Package

Minimal tests for the package are included in the template. First, set the XML path for PYTHIA-CONTRIB, and then change to the examples directory of the package.

export PYTHIA8CONTRIB=$PWD/share
cd share/MyPackage/examples

There is a simple test which can be used to check the loading of each plugin class and to read in settings, either directly specified by the plugin library, or via XML documentation provided by the package. The test takes as arguments command files to pass to Pythia with the readFile command. For each plugin class provided by the package there is a corresponding command file which instructs Pythia to load the plugin. In this example, there are two such configurations, MyPDF.cmnd and MyUserHooks.cmnd. First we test the PDF class.

make testPlugins
./testPlugins MyPDF.cmnd

The output of this test should be the Pythia welcome banner, as well as the settings that were introduced by the package, both directly through the plugin library as well as the XML files. The setting output should look as follows.

 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: library setting MyPackage:registered is 1
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:flag is 0
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:modeopen is 0
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:modepick is 0
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:parm is 0.000000
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:word is value
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:fvec is {1,0}
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:mvec is {0,1}
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:pvec is {0.000000,1.000000}
 PYTHIA Info from Pythia8Contrib::MyPackage::MyPDF::MyPDF: XML setting MyPackage:wvec is {value1,value2}

If any issues are encountered, there will be additional output describing the problem. If the error cannot open shared object file is returned, please see the section on library paths. Next, we can test the UserHooks class and check that the output is similar.

./testPlugins MyUserHooks.cmnd

Documentation

Documentation for the package should be provided in a top-level README.md as a Markdown file. Additional documentation can be provided by the package authors using the mechanism of their choice, but at a minimum, README.md is required. An optional metadata description can also be provided at the top level of the package with a .meta.yaml YAML file. If this file is provided, it will be parsed when building the index of all PYTHIA-CONTRIB packages which are then displayed on the PYTHIA-CONTRIB summary pages. The keywords and syntax of this metadata file are:

Additional Documentation

In Pythia, documentation and data are provided through the share/Pythia8 directory which consists of a number of sub-directories.

In the context of PYTHIA-CONTRIB, it is expected that while package authors might want to follow a structure similar to Pythia, they may also have their own preferences on how to provide documentation for their package. Consequently, other than the required documentation described above, documentation of the package is left to the package authors. However, in the template package, explicit outlines are given for xmldoc and examples as both of these are considered to be sufficiently important: XML documentation because it can be used to define settings, and example programs as these can be used to help test the package as well as provide concrete scenarios for users.

XML Settings

Settings for a package may either be included directly in code through the PYTHIA8_PLUGIN_SETTINGS macro (see package development), or via XML settings. For the XML settings of a package to be enabled, the starting XML file must be specified by the line

PYTHIA8_PLUGIN_XML("PACKAGE/xmldoc/Index.xml") 

which should be included in the package source (typically src/Package.cc). This starting index file is then searched for along the following paths, in this order:

  1. Relative to the PYTHIA8CONTRIB environment variable, i.e., $PYTHIA8CONTRIB/PACKAGE/xmldoc/Index.xml.
  2. Relative to the PYTHIA8DATA environment variable, i.e., $PYTHIA8DATA/../../PACKAGE/xmldoc/Index.xml.
  3. The path itself (whether relative or absolute), i.e., PACKAGE/xmldoc/Index.xml.

If the macro PYTHIA8_PLUGIN_XML is used in the package source but the specified XML index cannot be found, the following error will be issued when loading the plugin library:

PYTHIA Error in Settings::init: settings file PACKAGE/xmldoc/Index.xml not found

When the index is successfully found, all files specified with the <aidx href="FILE">TITLE</aidx> tags within the index will be loaded by the init method of the Pythia Settings object. In the template, the following files are included:

<aidx href="Formatting">Formatting</aidx><br/>
<aidx href="Settings">Settings</aidx><br/>

Here, the files PACKAGE/xmldoc/Formatting.xml and PACKAGE/xmldoc/Settings.xml will be read via the Settings initialization, and any settings defined in these XML files will then be registered.

A number of settings types are available through the Pythia XML settings scheme. It is recommended that all newly defined settings take the prefix PACKAGE, where PACKAGE is the name of the package. This ensures that there will be no settings collisions with other PYTHIA-CONTRIB packages, or with Pythia itself. For each setting type, a number of arguments can be specified. At a minimum, the name of the setting must be specified with the name argument and the default value with the default argument. For settings which are numerical, optional min and max arguments may be provided to limit the values the user may specify. If either of these arguments is missing, then the value will not be limited from below (min) or above (max).

<flag name="PACKAGE:flag" default="off">
This is an example boolean setting which can take the values of either "on" or "off".
</flag>
<modeopen name="PACKAGE:modeopen" default="0" min="0" max="10">
This is an example integer setting that can take any integer value between the limits.
</modeopen>
<modepick name="PACKAGE:modepick" default="0" min="0" max="10">
This is also an example integer setting, the same as "modeopen", but now specific values can be defined with the "option" tags below. Note, these options are not technically enforced, i.e. a user can still choose an integer value not specified below, as long as it remains between the limits.
<option value="0">Example defined option.</option>
</modepick>
<parm name="PACKAGE:parm" default="0.0" min="0.0" max="10.0">
This is an example double-precision setting which can take real-number values between the limits.
</parm>
<word name="PACKAGE:word" default="value">
This is an example character-string setting which can take any string value without blanks.
</word>

Variable-length lists (or vectors) for each of the above setting types, corresponding to boolean (flag), integer (mode), double-precision (parm), and string (word) values, can also be specified using the vec variations below. The general format for these settings is {value1, value2, ...}.

<fvec name="PACKAGE:fvec" default="{on,off}">
This is an example variable length list of boolean (flag) settings.
</fvec>
<mvec name="PACKAGE:mvec" default="{0,1}" min="0" max="10">
This is an example variable length list of integer (mode) settings.
</mvec>
<pvec name="PACKAGE:pvec" default="{0.0,1.0}" min="0.0" max="10.0">
This is an example variable length list of double-precision (parm) settings.
</pvec>
<wvec name="PACKAGE:wvec" default="{value1,value2}">
This is an example variable length list of character-string (word) settings. Unlike for "word" these string values can contain spaces, but cannot contain commas, ",".
</wvec>

Examples

One of the best ways to demonstrate how to use code is through examples; for context, Pythia has over 100 examples demonstrating various aspects of the program. A very minimal example is provided via the template, testPlugins.cc located in share/PACKAGE/examples. The example creates a Pythia object, reads any user-provided command files, and initializes Pythia.

  Pythia pythia;
  pythia.readString("ProcessLevel:all = off");
  for (int iArg = 1; iArg < argc; ++iArg)
    pythia.readFile(argv[iArg]);
  pythia.init();

For each class with name NAME created by the template, a command file NAME.cmnd is provided.

Init:plugins = {libPACKAGE.so::NAME}

The exceptions are the PhaseSpace, SpaceShower, and TimeShower classes, which cannot be passed to Pythia through Init:plugins on their own. For these classes the test program testNAME.cc is provided instead, which loads the class with make_plugin, and can be built with make testNAME in the examples directory.

In the MyPackage example of the test the package section, the test can be run for the PDF class with the following.

./testPlugins MyPDF.cmnd

This will then load the MyPDF class through the plugin system.

A minimal build system is provided in the examples directory of the template. By default it requires the plugin library to be built, and provides rules to build any main* programs as well as the test* programs. External packages can be linked, for more details see build system. The Makefile first reads the Makefile.inc configuration in the same directory, if it exists. It then tries to find the package's plugin library libPACKAGE.so, first looking in the local path ../../../lib and then along the Pythia library path, and stops with an error if it cannot be found. Finally, it will build any examples with the following compiler flags.

Note that the flags for the package headers path (-IPACKAGE_INCLUDE_PATH), the link-time path to the plugin library (-LPACKAGE_LIB_PATH), and the plugin library (-lPACKAGE) are not passed to the compiler, as it is assumed that the examples load the package's plugins through the Pythia plugin system, rather than using the plugin classes directly. The run-time path to the plugin library (-Wl,-rpath,PACKAGE_LIB_PATH) must still be specified to ensure that it can be found at run time.

Package Development

The PYTHIA-CONTRIB system utilizes the Pythia plugin system. In short, users request the use of a plugin by defining the Init:plugins setting.

Init:plugins = {libPACKAGE1.so::NAME1,libPACKAGE2.so::NAME2,...}

Details on this setting syntax, as well as the Pythia plugin system, are documented in the Pythia HTML manual. At its simplest, the plugin library of a PYTHIA-CONTRIB package consists of two parts: developer-defined plugin classes, derived from the list of available base classes, and macros which allow these plugins to be loaded by Pythia. These macros are summarized here, and are explained in further detail below in plugin library definition. These macros should not be used in header files.

If any of the four single-use macros are called more than once in a package, the package will fail to compile.

Namespace

Because the PYTHIA-CONTRIB system is intended for decentralized development, it is very possible that there are conflicts in class names between different packages. In general, this should not be an issue, since the run-time loading mechanism of the Pythia plugin system protects against this. However, if users wish to link against the plugin library at build time, then this can become an issue. Consequently, it is requested that package authors use the nested namespace Pythia8::Pythia8Contrib::PACKAGE for a package with the name PACKAGE. In the template header and source, this convention is set with the following structure:

namespace Pythia8 {
namespace Pythia8Contrib {
namespace PACKAGE {
// PACKAGE code here.
} // end namespace PACKAGE
} // end namespace Pythia8Contrib
} // end namespace Pythia8

Without using or namespace statements, this means that accessing objects can be somewhat cumbersome,

Pythia8::Pythia8Contrib::PACKAGE::NAME

where NAME is the object being accessed. Consequently, it is suggested that the above namespace code is always used to enclose the body of header and source files. Alternatively, using <NAMESPACE> statements can be used instead, although this is not recommended; these statements should only be used in source files (*.cc) and never included in header files (*.h).

Headers

The headers produced by the template generator are all located in include/PACKAGE. One header, include/PACKAGE/NAME.h, is generated for each BASE/NAME pair passed as an argument to generate. This header is intended to define the structure of the class, without including the implementation, which is intended for the corresponding source file, src/NAME.cc. However, in some cases it may be necessary to provide implementations in the header, e.g., for the case of templated classes or methods. In general, the macros summarized at the start of this section should not be used in the headers.

When a package is installed with the PYTHIA-CONTRIB system, the headers will be placed in the location include/Pythia8Contrib/PACKAGE, i.e., with an additional level of nesting. This should not cause issues, since an additional include path can always be passed to the compiler, -Iinclude/Pythia8Contrib. In this way the package headers should always be included with statements such as,

#include "PACKAGE/NAME.h"

without the need for prefixing the include statement with Pythia8Contrib/. This is what is done in the Makefile for the examples located in share/PACKAGE/examples, where the location of the package is automatically determined to either be local or part of a Pythia installation.

The constructor for the class NAME defined by the header NAME.h must take the following form:

NAME(Pythia* pythiaPtr, Settings* settingsPtr, Logger* loggerPtr) 

There are three pointers which may be passed to the constructor:

When a class is constructed from a Pythia object as a plugin, i.e., from the Init:plugins setting, all three pointers are valid. However, it is also possible for plugins to be created directly in the C++ code, where none of these pointers is required to be valid; further details are given in the following section.

When using the full template produced with generate, all required methods for the class will be included in the corresponding header. For example, the UserHooks base class does not have any pure virtual methods, and so other than the constructor and destructor, no additional methods need be implemented. However, there are a large number of methods that can be overridden from the base class. In the template, each optionally implemented method will be included as a commented-out block of code.

  // // Initialisation after beams have been set by Pythia::init().
  // bool initAfterBeams() override
  // {return true;}

Here, code is given which will override the initAfterBeams method of the base UserHooks class and return the value of true. The general form of the comment will contain the following three sections: a commented description of the method, the call signature of the method, and optionally the implementation of the method if sufficiently succinct for a header. Note that these methods will always end with the override keyword to indicate that this method should be overriding the base-class implementation.

An example of a required method can be found in a PDF derived class where the protected method xfUpdate must be implemented.

  // Update parton densities (defined in src/MyPDF.cc). Must be defined.
  void xfUpdate(int id, double x, double Q2) override;

Note that here no implementation is given, which is instead relegated to the source file.

Source

For each developer-defined class with name NAME, there is a corresponding src/NAME.cc source file. This file is intended to include any detailed implementations that are not suitable for the corresponding include/PACKAGE/NAME.h header. In the generated template, an implementation for the constructor is always provided, which demonstrates how to use methods of the passed Pythia pointer, access settings from the passed Settings pointer, and write messages via the passed Logger pointer. The following provides an abbreviated template for derived class NAME of base class BASE.

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

// Constructor.

NAME::NAME(
  Pythia* pythiaPtrIn, Settings* settingsPtrIn,
  Logger* loggerPtrIn) : BASE() {

  // If a pointer is not needed its name (but not type) can be
  // commented out above, e.g. change pythiaPtrIn to /*pythiaPtrIn*/,
  // to prevent compiler warnings. If a pointer is not needed, ensure
  // it is specified as not required in the corresponding call to the
  // macro PYTHIA8_PLUGIN_CLASS.

  // Include whatever code is needed here to construct the plugin
  // class. Below, we give some examples of how the pointer arguments
  // can be used.

  // Access and print the settings as defined by the XML files.
  loggerPtrIn->INFO_MSG("XML setting PACKAGE:flag is " + to_string(
      settingsPtrIn->flag("PACKAGE:flag")));
  // ...

}

As discussed in the headers section, the passed pointers may not be required to be valid. This is specified by the macro PYTHIA8_PLUGIN_CLASS, which registers the class so that it can be accessed from the plugin library. The usage of PYTHIA8_PLUGIN_CLASS is as follows.

PYTHIA8_PLUGIN_CLASS(BASE, NAME,
  true, // Require the Pythia pointer to be valid.
  true, // Require the Settings pointer to be valid.
  true) // Require the Logger pointer to be valid.

Here, BASE is the base class, e.g., UserHooks, while NAME is the derived class to be registered, e.g., MyUserHooks. If an argument is true then that type of pointer must be a valid pointer when using the Pythia plugin system to load the plugin. If false, then that pointer may be passed as a null pointer. If a pointer is specified by the package author as not being required, then it is important that the validity of that pointer is checked in the implemented constructor for the derived class.

In the Pythia plugin system, plugins may be loaded either through settings or through using make_plugin in code, with four different possible use cases:

  1. Via user-defined settings, using the format Init:plugins = {libPACKAGE.so::NAME}. Here, all three pointers passed will always be valid, and correspond to the Pythia object loading the plugin.
  2. Via the following make_plugin method called in C++, where no pointers are passed:

    shared_ptr<BASE> make_plugin<BASE>(string PACKAGE, string NAME)
    

    Here, the PACKAGE string would be given as libPACKAGE.so and NAME is the name of the plugin class to be loaded. All three pointers loaded with this method are null. If any of the PYTHIA8_PLUGIN_CLASS pointer arguments are set as true, then this method will fail and return a null pointer. 3. Via the following make_plugin method called in C++, where now a Pythia pointer is passed:

    shared_ptr<BASE> make_plugin<BASE>(string PACKAGE, string NAME, Pythia* pythiaPtr)
    

    If the Pythia pointer is valid, then all three pointers to the plugin constructor will be valid and taken from the Pythia pointer. If the Pythia pointer is null and any of the pointers are required, then this method will fail and return a null pointer. 4. Via the following make_plugin method called in C++, where all three pointers are passed individually:

    shared_ptr<BASE> make_plugin<BASE>(string PACKAGE, string NAME, Pythia* pythiaPtr, Settings* settingsPtr, Logger* loggerPtr)
    

    If pythiaPtr is valid, and either settingsPtr or loggerPtr is null, then the corresponding pointer from the pythiaPtr will be passed to the plugin class constructor instead. If any of the pointers are required by the PYTHIA8_PLUGIN_CLASS call, and that corresponding pointer is null, then this method will fail and return a null pointer.

In addition to the required implementation of the constructor, a number of methods may be required, dependent upon the base class. The generated template source file for each developer-defined class will contain an outline of how to implement the required methods. In the example above for the PDF derived class, an implementation of the required xfUpdate method is given.

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

// Calculates the PDF distributions for the specified parton id at (x,
// Q^2) and stores the resulting values in the corresponding
// fields. If id == 9, the values should be calculated for all
// relevant partons. The user may choose to always calculate the value
// for all partons, in which case they should set idSav = 9. The PDF
// class flexibly handles antiparticle and isospin symmetries, so
// xfUpdate should always give the values that correspond to the
// particle with positive id and isospin.

void NAME::xfUpdate(int /*id*/, double /*x*/, double /*Q2*/) {

  // // Update the values (currently 0 is used as a dummy value).
  // xg     = 0;
  // ...

  // // idSav = 9 to indicate that all flavours reset, otherwise use id.
  // idSav = 9;

}

Optional methods with more lengthy implementations are also provided in the template source file. For a UserHooks derived class, the following commented-out code implements the optional multiplySigmaBy method:

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

// multiplySigmaBy allows the user to introduce a multiplicative factor
// that modifies the cross section of a hard process. Since it is called
// from before the event record is generated in full, the normal analysis
// does not work. The code here provides a rather extensive summary of
// which methods actually do work. It is a convenient starting point for
// writing your own derived routine.

// double NAME::multiplySigmaBy(const SigmaProcess* sigmaProcessPtr,
//   const PhaseSpace* phaseSpacePtr, bool inEvent) {
// 
//   // Process code, necessary when some to be treated differently.
//   int code       = sigmaProcessPtr->code();
// 
//   // Final multiplicity, i.e. whether 2 -> 1 or 2 -> 2.
//   int nFinal     = sigmaProcessPtr->nFinal();
//
//   // Incoming x1 and x2 to the hard collision, and factorization scale.
//   double x1      = phaseSpacePtr->x1();
//   double x2      = phaseSpacePtr->x2();
//   double Q2Fac   = sigmaProcessPtr->Q2Fac();
//   // ...
//
//   // Dummy statement.
//   return 1.;
//
// }

This commented code provides a brief outline of how the method could be implemented, including access of relevant data members from the class.

Thread Safety

By default, plugin classes are assumed to be thread-unsafe to prevent difficult-to-debug errors when running in a threaded environment. Consequently, plugin classes cannot be loaded by PythiaParallel unless the PYTHIA8_PLUGIN_PARALLEL macro is called with true for a plugin library, see the plugin library definition section for details. In some cases, plugin classes may need to lock a thread to prevent data races on shared resources, e.g., histograms. To enable this, every class derived from PhysicsBase has access to mutexPtr, a mutex pointer shared across all threads of the top-level PythiaParallel instance. Whenever this mutex is locked, the following code pattern will prevent threading:

mutexPtr->lock();
// Thread unsafe code can be written here.
mutexPtr->unlock();
// Thread safe code can be written here.

All classes that can be passed to a Pythia instance by an external pointer are derived from PhysicsBase and have access to this mutexPtr except PDF, BeamShape, and RndmEngine.

For many plugin classes that are threaded, it is necessary to combine the output of threads of PythiaParallel together again. This can be accomplished for classes inheriting from PhysicsBase by defining onStat.

void NAME::onStat(vector<PhysicsBase*> pluginPtrs, Pythia* pythiaPtrIn) { 
  // Loop over the plugin from each thread. 
  for (PhysicsBase* pluginPtr : pluginPtrs) { 
    // Skip the plugin that this method was called for. 
    if (pluginPtr == this) continue; 
    // Access all other plugins. 
    NAME* pluginNow = dynamic_cast<NAME*>(pluginPtr); 
    // Perform whatever merging is necessary here. 
  } 
  // Call onStat for this plugin-class instance. 
  onStat(); 
}

Here, pluginPtrs is a vector of the plugin-class pointer from each thread. Each of these plugin-class pointers then needs to be recast back to the plugin class. Finally, the onStat() for this instance of the plugin should be called, with no arguments. This ensures that the onStat behavior is the same between a single Pythia instance, and PythiaParallel. The onStat method defined above is called when PythiaParallel::stat() is called.

Plugin Library Definition

The remaining four macros given at the introduction of this package development section can only be called once per plugin library, unlike PYTHIA8_PLUGIN_CLASS which must be called per class definition. If called more than once in the package code, the package will fail to compile. These macros define properties of the plugin library and can be called anywhere in the source files. However, it is preferred that they are all called in a single source file, which in the generated template is done in src/Package.cc. The first macro,

PYTHIA8_PLUGIN_VERSIONS(8310, 8311, ...)

defines the versions of Pythia that the plugin library is compatible with. Any number of versions can be passed. When a plugin library is loaded by Pythia, a check is made to see if the version of Pythia loading the plugin library is compatible. If it is not, a warning is issued, and a null pointer is returned for any class being constructed from that plugin library.

The second macro,

PYTHIA8_PLUGIN_PARALLEL(true)

is optional, and declares if the plugin library is compatible with PythiaParallel. If not called, the plugin library is assumed to be incompatible with PythiaParallel which is the same as calling PYTHIA8_PLUGIN_PARALLEL with an argument of false. See thread safety for what must be true of the plugin classes before this can be set to true.

The third macro,

PYTHIA8_PLUGIN_XML("PACKAGE/xmldoc/Index.xml") 

is optional, but if called, requires XML files for the plugin library to be present. Any starting index file can be passed, but in the template the file PACKAGE/xmldoc/Index.xml is used as the convention. More details on how settings can be defined via XML, as well as the search path for the starting XML file are given in the XML settings section of this documentation.

Finally, settings can be registered directly in the C++ code, rather than via XML files, through interactions with a Settings object with the following macro:

PYTHIA8_PLUGIN_SETTINGS(PACKAGESettings)

The method PACKAGESettings can take any name, but must be defined with the following call signature:

void PACKAGESettings(Settings* settingsPtr)

When this method is called, settingsPtr is guaranteed to be valid and does not need to be checked. The PACKAGESettings method is always called after XML settings are loaded.

The following add methods for the Settings class allow settings to be added to the settings database. Possible setting types are boolean (flag), integer (mode), double (parm), string (word), vector of booleans (fvec), vector of integers (mvec), vector of doubles (pvec), and vector of strings (wvec). It is recommended that all settings begin with the PACKAGE prefix, to prevent collisions with settings from other PYTHIA-CONTRIB packages, as well as Pythia itself. The arguments to these methods are as follows:

The methods themselves are:

// Add a boolean setting (flag).
settingsPtr->addFlag(string keyIn, bool defaultIn);
// Add an integer setting (mode).
settingsPtr->addMode(string keyIn, int defaultIn, bool hasMinIn,
  bool hasMaxIn, int minIn, int maxIn, bool optOnlyIn = false);
// Add a double setting (parm).
settingsPtr->addParm(string keyIn, double defaultIn, bool hasMinIn,
  bool hasMaxIn, double minIn, double maxIn);
// Add a string setting (word).
settingsPtr->addWord(string keyIn, string defaultIn);
// Add a vector of booleans setting (fvec).
settingsPtr->addFVec(string keyIn, vector<bool> defaultIn);
// Add a vector of integers setting (mvec).
settingsPtr->addMVec(string keyIn, vector<int> defaultIn, bool hasMinIn,
  bool hasMaxIn, int minIn, int maxIn);
// Add a vector of doubles setting (pvec).
settingsPtr->addPVec(string keyIn, vector<double> defaultIn, bool hasMinIn,
  bool hasMaxIn, double minIn, double maxIn);
// Add a vector of strings setting (wvec).
settingsPtr->addWVec(string keyIn, vector<string> defaultIn);

Formatting

No formatting requirements are defined for PYTHIA-CONTRIB packages, although it is recommended to follow the Pythia formatting style. The generated templates follow the Pythia coding style, which is provided in the somewhat lengthy CODINGSTYLE file distributed with the Pythia source. For convenience a few points are summarized here.

Build System

There are any number of build system tools available, and in many cases package authors have strong preferences for certain systems over others. Consequently, there are no restrictions on what build systems package authors use for their package. However, in many cases, package authors may want a build system out-of-the-box, and so we provide such a build system based on the Pythia build system. This build system has relatively few dependencies, other than standard POSIX tools. First we describe the interaction that the build system (whatever that system is) must have with PYTHIA-CONTRIB, then we describe how to customize the provided build system in more detail.

PYTHIA-CONTRIB Interaction

The top-level build system for PYTHIA-CONTRIB consists of a configure script and a Makefile. Each PYTHIA-CONTRIB package is then expected to consist of a folder in the top level of PYTHIA-CONTRIB. The general build procedure for PYTHIA-CONTRIB is as follows:

./configure [OPTIONS]
make
make install

This will then configure and build all packages in the top level of PYTHIA-CONTRIB. It is expected that the package-provided build system optionally has a configure script, and must have a Makefile. Further specifications for each of these components are given below.

Configure Requirements

The top-level PYTHIA-CONTRIB configure script is designed so that users can configure all the PYTHIA-CONTRIB packages at the top level without needing to configure each package individually. To this end, the top-level configure script takes options of the form:

--with-DEP[=DIR]       : Use the external dependency DEP  with
                         its top directory optionally set by DIR. If the
                         enabled DEP requires other external packages,
                         these packages will also be enabled.
--with-DEP-bin=DIR     : Set the path to the DEP binaries.
--with-DEP-lib=DIR     : Set the path to the DEP libraries.
--with-DEP-include=DIR : Set the path to the DEP headers.
--with-DEP-config=BIN  : Set the package configuration script BIN.

and passes these on to the configure script for each package. Note that here, DEP should be passed in all lower case.

Additionally, the option --nowarn is passed, which tells the package-level configure script to not issue warnings for --with options that are not available. In this way, if package A requires the dependency fastjet3, while package B does not, package B will not warn that it is being passed an invalid option --with-fastjet3. The top-level configure will skip calling the package-level configure script if it is missing, and will issue a warning instead that the package must be configured manually, following documentation that should be provided in the package README.md.

It is expected that if a package is configured, then a package-level Makefile.inc will be present. This file may be empty if a custom build system is being used, but is intended to be included by the package-level Makefile. The PYTHIA-CONTRIB configure script will skip running the package-level configure if the package-level Makefile.inc is present, unless the --force option is passed.

To summarize, the package-level configure script is optional. If it does exist, it should adhere to the following specifications:

Make Requirements

The top-level PYTHIA-CONTRIB build system uses a Makefile to build all the packages. It does this by calling

make -C PACKAGE contrib

for each package with name PACKAGE. The package-level Makefile should then perform the following actions when called with target contrib:

If the library lib/libPACKAGE.so is not produced, then it is assumed that the build has failed. Note that the .so suffix does not change between build platforms, e.g., the .dylib suffix common to macOS is not used. This is because plugin libraries are intended to be loaded dynamically at run time as set by configuration files or strings, and so to ensure that the configuration files are not platform-dependent, the same library suffix is used across all platforms.

Configuration Customization

When using the provided build system, the following settings with the syntax KEY=VALUE can be used to configure external packages in the optional .configure.inc file provided at the top level of the package. It is possible to define multiple external packages in .configure.inc, and by default a number of common packages are defined in the template .configure.inc. Each external package configuration starts with a line beginning with PKG=. If options for a package are missing, then they are taken as the default value. Note, each configuration key must begin the line and follow the syntax KEY=VALUE. The following keys are allowed (defaults, if defined, are given in brackets after the key):

By default, a number of common external dependencies are fully defined in .configure.inc. It is suggested that the following names are used for these common dependencies (links to the corresponding website for each dependency are also given):

Make Customization

For each external dependency specified with the configure script, a number of variables are available within the Makefile both at the top level of the package, as well as in share/PACKAGE/examples. The defined variables are as follows. Note that, other than DEP_USE, these variables can be empty -- when either the dependency has not been configured, or if the dependency does not require that variable.

Here, replace DEP with (in all caps) the name of the relevant dependency, e.g., PYTHIA8.

The following lines in .configure.inc define and require Pythia as a dependency:

PKG=PYTHIA8
CFG=pythia8-config
INC_PATH=--cxxflags
LIB_PATH=--libs
INC_DEPS=Pythia8/Plugins.h
REQUIRE=true

As an example, consider a Pythia installation at /hep/pythia/8.3XX. By running ./configure, and assuming /hep/pythia/8.3XX/bin/pythia8-config is along PATH, the following lines will be generated in the Makefile.inc:

PYTHIA8_USE=true
PYTHIA8_CONFIG=pythia8-config
PYTHIA8_BIN=/hep/pythia/8.3XX/bin/
PYTHIA8_INCLUDE=-I/hep/pythia/8.3XX/include
PYTHIA8_LIB=-L/hep/pythia/8.3XX/lib -Wl,-rpath,/hep/pythia/8.3XX/lib

Note that here the Pythia library is not included as part of the dependency. However, if the line,

LIB_DEPS=pythia8

is added to .configure.inc then the PYTHIA8_LIB variable will now include -lpythia8.

PYTHIA8_LIB=-L/hep/pythia/8.3XX/lib -Wl,-rpath,/hep/pythia/8.3XX/lib -lpythia8

Assuming a dependency has been defined, it is then possible to modify the Makefile to link appropriately with the dependency.

<target>: <prerequisites> 
ifeq ($(DEP_USE),true)
    <recipe, given the package exists>
else
    <recipe, given the package does not exist, or, error message>
    $(error Error: $@ requires DEP)
endif

Here, DEP should be changed to whatever dependency is being required. For the specific example of linking the plugin library against a dependency, the plugin library rule would be changed as follows:

ifeq ($(DEP_USE),true)
$(LOCAL_LIB)/libPACKAGE.so: $(OBJECTS)
    $(CXX) $^ -o $@ $(CXX_COMMON) $(CXX_SHARED) $(CXX_SONAME)$(notdir $@)\
     $(DEP_INCLUDE) $(DEP_LIB)
else
     $(error Error: $@ requires DEP)
endif

Package Submission

Packages for PYTHIA-CONTRIB are recommended to be installed by users through the use repository. The abbreviated package installation procedure is as follows:

./enable PACKAGE[/VERSION] ... [OPTIONS]
./configure [OPTIONS]
make
make install

Here, PACKAGE is the package name, followed by an optional package version, and additional options which can be passed. If no version is given, then the latest version is taken. For a package to be available via the enable command from PYTHIA-CONTRIB, it must have a repository as part of the packages subgroup within PYTHIA-CONTRIB. To request a repository for a new package, please open an issue on the develop repository. The creation of this issue requires a freely available gitlab.com account. The issue should contain the following information:

Package Naming

Before creating a package, it is recommended to first check which names are already used in the packages subgroup of PYTHIA-CONTRIB. While no strict naming restrictions are enforced, the following conventions are encouraged:

Repository Creation

After submitting an issue requesting a package repository as described above, the repository will be created after review by the PYTHIA-CONTRIB maintainers. This review does not require that the package actually exist at this point, but rather is simply intended to check the package name is reasonable and that the package belongs in PYTHIA-CONTRIB, given the package description. Initially, the repository will be created as a private repository until the package authors are ready for the first release. After the creation of the repository, two or more owners will be assigned to the repository:

  1. The package author requesting the repository.
  2. The PYTHIA-CONTRIB maintainers.

The PYTHIA-CONTRIB maintainers will not make any changes to the repository without the express permission of the requesting package author, and are included in the case of orphaned packages or when package authors grant permission for minor maintenance to be performed on the package. The goal is that package authors will have complete autonomy over their package repository, while still allowing the PYTHIA-CONTRIB maintainers to update the system as needed. After a repository is created, the requesting package author can add additional package authors to the repository via their corresponding gitlab.com accounts. The package authors can then begin committing to their repository. Note, GitLab includes a whole suite of features including CI/CD, project wikis, issue trackers, etc. There are a number of GitLab introductions, and GitLab itself provides introductory tutorials.

Version Tagging

Once a repository is created, the package authors can then develop their code as they see fit. However, for the enable script of PYTHIA-CONTRIB to be able to interact with the package repository, the repository must be made public, and versions of the package must be identified via git tags. This is done as follows:

git tag VERSION

Here, VERSION is the name of the version that is being tagged, e.g., v8318.01. It is also possible to create an annotated tag,

git tag -a VERSION -m 'MESSAGE'

where MESSAGE is now a message associated with the tag. Once a tag has been created locally, it must be pushed back to the remote repository on gitlab.com.

git push origin --tags

It is also possible to push a single tag to the remote repository:

git push origin VERSION

Tags can be very useful for any number of reasons, and so the PYTHIA-CONTRIB system tries to be minimally invasive in how tags are used for versioning. All version tags must begin with v[0-9], i.e., the letter v followed by a digit. The remainder of the version tag can then take on whatever form the package authors wish. Any tag that does not follow this convention will not be interpreted by enable from PYTHIA-CONTRIB as a version tag. In this way, package authors can use tags that are not included as releases. It is important to note that the enable script does not require a version number to be passed for a package. When this occurs, enable sorts all the v[0-9] tags from the repository by version (as with sort -V) and takes the highest tag as the most recent version. Care must be taken with versioning schemes, as the version ordering treats the numbers in a tag as numbers rather than text, e.g., v10 is more recent than v9.

While no requirements are made on the version tag format, other than beginning with v[0-9], the following versioning conventions are recommended:

Following this convention, package authors can develop separate versions of a package which are compatible with different versions of Pythia, and users will understand at a glance which version of Pythia that version of a package was developed for. Additionally, the most recent version will be interpreted by the enable script as the tag with the largest package version number for the most recent version of Pythia, e.g., given the tags v8311.01 and v8310.06 the most recent version would be taken as v8311.01 and not v8310.06.

Testing

Extensive testing is an important part of any package development. For Pythia, roughly fifty tests are run before a development branch is merged, including running all the tests with checks for floating-point exceptions and memory leaks as well as using a variety of compilers. This is done through the automated continuous-integration/continuous-deployment (CI/CD) system provided by gitlab.com. These tests are performed using Docker containers. A number of Pythia Docker containers are available through Docker Hub. For development, the container pythia8/dev:test is typically used, which contains a number of external packages. Pythia can be built with this container as follows.

# Pull the Pythia source.
git clone git@gitlab.com:Pythia8/releases.git
cd releases

# Start the Docker container (the cap flag allows use of GDB).
docker run -i -t -v "$PWD:$PWD" -w $PWD -u `id -u` \
  --cap-add=SYS_PTRACE --rm pythia8/dev:test bash --norc

# Configure Pythia with all packages.
export PATH=$PATH:/hep/fastjet3/bin:/hep/hepmc3/bin:/hep/lhapdf5/bin
export PATH=$PATH:/hep/rivet/bin:/hep/yoda/bin
./configure --with-evtgen=/hep/evtgen --with-fastjet3 \
  --with-hepmc2=/hep/hepmc2 --with-hepmc3 --with-lhapdf5 \
  --with-lhapdf6=/hep/lhapdf6 --with-powheg-bin=/hep/powheg/ \
  --with-rivet --with-root --with-gzip --with-python --with-mg5mes \
  --with-openmp --with-highfive=/hep/highfive --with-hdf5=/hep/mpich/ \
  --with-mpich=/hep/mpich/

# Build Pythia.
make -j6

# Build and run the examples.
cd examples
./runmains

This container, or perhaps a more lightweight container, could be used for package testing, depending upon the preferences of the package authors.

Because testing is integral to package development, this is left to the package authors and not the PYTHIA-CONTRIB maintainers. The PYTHIA-CONTRIB maintainers will assume all versions provided by package authors have also been tested and validated by the package authors. However, if it becomes clear from issues submitted by users that a specific package is broken, the PYTHIA-CONTRIB maintainers may choose to exclude this package from the enable script until the latest version of the package is fixed.

Tests run within a package repository typically only call make all within the package. Before publishing a package, the package build should be tested with the use installation of enable, configure, make, and make install. This installation calls make contrib within the package, as described in the make requirements section, and assumes the package sits directly below the use top-level directory.

Prior to the release of a new Pythia version, the internal Pythia developers will provide package authors with a Pythia release candidate. Ideally, changes to the plugin interfaces between versions will be minimal, and so package authors should not need to make major changes between Pythia releases, other than updating the PYTHIA8_PLUGIN_VERSIONS macro. The minimal testing functionality available via the optional test key of .meta.yaml, see the documentation section for more details, is intended to partially automate these checks. The PYTHIA-CONTRIB maintainers will run these tests prior to a Pythia release, and notify package authors whether their package is compatible with the upcoming Pythia release. The results of these tests will be made available to package authors via the CI of the private PYTHIA-CONTRIB website repository. This automatic testing functionality is currently not stable, and may change in the future.

Index Generation

An index of all the available PYTHIA-CONTRIB packages is available at contrib.pythia.org. For each package, the index is generated as follows:

Advanced Usage

In this section we provide additional documentation for topics that in general lie outside the intended scope of the PYTHIA-CONTRIB development system, but that package authors may nevertheless find relevant.

Additional Classes

The primary use case for PYTHIA-CONTRIB is through the available base classes listed above. However, as mentioned in the package generation example, it is possible to create a class that uses any class available in Pythia as a base class. To create a template, the --nocheck option should be passed to generate. This type of plugin class cannot be directly passed to Pythia using the Init:plugins setting, and instead must be loaded using the make_plugin method, which was described in the source implementation section. This means that while some custom C++ code will need to be written by the user to access the plugin, that code itself does not need to link against the plugin library, since its run-time loading is still handled by the Pythia plugin system.

If settings are required by the plugin library, then before calling the make_plugin method, the settings of the plugin library need to be registered with a Settings object. This can be done by the following method of the Settings class.

bool Settings::registerPluginLibrary(string PACKAGE, string INDEX = "")

Here, PACKAGE is the name of the plugin library optionally with path, and INDEX is the index file for the XML documentation, again with optional path. If INDEX is not provided, and PYTHIA8_PLUGIN_XML is defined in the plugin library, then this location will be used. In this way it is possible to override the default XML documentation location for the plugin library. If PYTHIA8_PLUGIN_SETTINGS is defined within the plugin library, then the registered settings function will also be called.

For convenience, the registerPluginLibrary call has been combined with the basic make_plugin methods into the following make_plugin method:

shared_ptr<BASE> make_plugin<BASE>(string PACKAGE, string NAME, Pythia* pythiaPtr, const vector<string>& CMNDS)

This method does the following:

  1. Calls registerPluginLibrary on the Settings object from the provided pythiaPtr. At this point, all settings from the plugin library will be available.
  2. Reads in the settings passed by CMNDS using the readString method of the pythiaPtr.
  3. Creates an object of type NAME using the method,
    shared_ptr<BASE> make_plugin<BASE>(string PACKAGE, string NAME, Pythia* pythiaPtr)
    

    and then returns a shared pointer of base class BASE.

In this way it is possible to create with a single method an object from a plugin library, while still passing valid settings to be used in the creation of that object. A similar method is also available where the settings are passed via a file rather than a vector of strings.

shared_ptr<BASE> make_plugin<BASE>(string PACKAGE, string NAME, Pythia* pythiaPtr, string FILE, int SUBRUN = SUBRUNDEFAULT)

Here, FILE is now the settings file and SUBRUN is the subrun to use from that file, where all settings are read if subruns are not being used and SUBRUN is not passed as an argument. When using generate with the --nocheck argument and an unknown base class, a test example share/PACKAGE/examples/testNAME.cc is created, which uses these two methods to create an instance of the plugin class.

It is interesting to note that with this method it is possible to create a custom class that inherits from the full Pythia class, and then load it using the plugin system.

./generate --nocheck MyRecursion Pythia/MyPythia
Pythia pythia;
vector<string> cmnds = {};
shared_ptr<Pythia> plugin = make_plugin<Pythia>("libMyRecursion.so", "MyPythia", &pythia, cmnds);

In this way, fully customized versions of Pythia can be provided through the PYTHIA-CONTRIB package.

Python Packages

The PYTHIA-CONTRIB system does not yet support Python modules. However, the Pythia Python bindings are bidirectional; classes defined in Python that inherit from C++ classes can be passed back as C++ objects to Pythia. For example, it is possible to create a UserHooks class entirely in Python.

#==========================================================================

# Write own derived UserHooks class.

class MyUserHooks(pythia8.UserHooks):

    # Make sure to initialize base class.
    def __init__(self):
        pythia8.UserHooks.__init__(self)

    # Allow cross section to be modified.
    def canModifySigma(self): return True

    # Double the cross section.
    def multiplySigmaBy(self, sigmaProcessPtr, phaseSpacePtr, inEvent): return 2.

Here, the class simply doubles the cross section. This can then be passed back to a Pythia object in Python code as follows:

# Generator.
pythia = pythia8.Pythia()

# Pass the user hook back to Pythia.
myUserHooks = MyUserHooks()
pythia.setUserHooksPtr(myUserHooks)

Note that it is not yet possible to load a class from a Python module, i.e., write a derived class in Python and then load that class in C++ code using the Pythia plugin system. However, this may be possible in the future using Python's ability to be embedded in C.

Library Paths

The Pythia and PYTHIA-CONTRIB systems are designed to work out-of-the-box without user manipulation of library paths, i.e., fixing the error cannot open shared object file reported by Pythia, even when the plugin library is in a valid location. When this occurs, there are a number of possible resolutions.

  1. Set LD_LIBRARY_PATH to include the folder containing the plugin library, e.g., LD_LIBRARY_PATH=$LD_LIBRARY_PATH:PACKAGE_PATH where PACKAGE_PATH is the relative or absolute path to the plugin library folder. Note, LD_LIBRARY_PATH is not supported in macOS: use instead the variable DYLD_LIBRARY_PATH.
  2. Link or move the plugin library into the same directory as the shared Pythia library, libpythia8.so or libpythia8.dylib.
  3. Link or move the plugin library into the same directory as the executable.
  4. Recompile Pythia with the flag -Wl,--disable-new-dtags passed to the linker via the compiler. This can be done by modifying the following line of Makefile.inc below. If Pythia 8.312 or later is being used, this should be done automatically, yielding:
    CXX_SHARED=-shared -Wl,--disable-new-dtags
    

Details on why this flag needs to be passed are given below.

When debugging library path issues, it can be helpful to have further knowledge of the library path resolution. The LD_DEBUG variable can be set as follows, and a full report of the library path resolution will be given.

LD_DEBUG=libs ./EXE

Here, EXE is the executable being run. Example output from this, assuming the MyPackage example, might look something like the following, depending on the system and local development area:

     16201: find library=libMyPackage.so [0]; searching
     16201:  search path=../lib/tls/x86_64:../lib/tls:../lib/x86_64:../lib:/home/contrib/develop/releases/lib       (RPATH from file /home/contrib/develop/releases/lib/libpythia8.so)
     16201:   trying file=../lib/tls/x86_64/libMyPackage.so
     16201:   trying file=../lib/tls/libMyPackage.so
     16201:   trying file=../lib/x86_64/libMyPackage.so
     16201:   trying file=../lib/libMyPackage.so
     16201:   trying file=/home/contrib/develop/releases/lib/libMyPackage.so
     16201:  search path=../../../lib/tls/x86_64:../../../lib/tls:../../../lib/x86_64:../../../lib:/home/contrib/develop/releases/lib       (RPATH from file ./testPlugins)
     16201:   trying file=../../../lib/tls/x86_64/libMyPackage.so
     16201:   trying file=../../../lib/tls/libMyPackage.so
     16201:   trying file=../../../lib/x86_64/libMyPackage.so
     16201:   trying file=../../../lib/libMyPackage.so
     16201: 
     16201: 
     16201: calling init: ../../../lib/libMyPackage.so
     16201: 

From this print-out we see that a number of paths were tried, and then finally the library was found along the relative path ../../../lib. Note that the path /home/contrib/develop/releases/lib was tried before ../../../lib. This is because this is passed as a path to the linker when the shared Pythia library is compiled; this is also why resolution 2 outlined above should work. In general, if the plugin library is not being found, this is a very useful way to see which paths are being tried, and their ordering.

The use of the disable-new-dtags flag of resolution 4 above is necessary because of a change in the default linker behavior for a number of Linux distributions and the difference between RPATH and RUNPATH. The library path typically resolves along the following order:

  1. RPATH: this is the run-time search path that is hard-coded into compiled libraries or executables and is typically set by passing the flag -Wl,-rpath,PATH to the linker through the compiler.
  2. LD_LIBRARY_PATH: is the environment variable used by the dynamic link loader if the library is not found along RPATH. A similar, but not identical, variable DYLD_LIBRARY_PATH is available for macOS.
  3. RUNPATH: this is also passed via the flag -Wl,-rpath,PATH at compile time, similar to RPATH.

For older systems, when rpath is passed to the compiler, setting RPATH is the default behavior, while for newer systems RUNPATH is set instead. Consequently, depending on whether RPATH or RUNPATH is preferred, the following options should be used:

For some systems, it is possible that the disable-new-dtags and enable-new-dtags options are not available, in which case the rpath flag will typically set RPATH and not RUNPATH. Note that it is also possible to avoid passing rpath flags to the linker by instead setting the LD_RUN_PATH environment variable before running the linker.

There is a critical difference between RPATH and RUNPATH: RPATH is transitive while RUNPATH is not. In other words, the RPATH for a dependency will be used in addition to the RPATH for the binary itself. Conversely, if RUNPATH is specified, then only that RUNPATH for that binary will be used, even if a dependency specifies an RPATH. From the Linux Foundation reference specifications, the following is noted about the lack of the transitive property for RUNPATH:

The set of directories specified by a given DT_RUNPATH entry is used to find only the immediate dependencies of the executable or shared object containing the DT_RUNPATH entry. That is, it is used only for those dependencies contained in the DT_NEEDED entries of the dynamic structure containing the DT_RUNPATH entry, itself. One object's DT_RUNPATH entry does not affect the search for any other object's dependencies.

This has important implications for PYTHIA-CONTRIB. If Pythia is compiled with RUNPATH, then even if RPATH is correctly specified for the executable calling Pythia, Pythia will not use this path at run time. Similarly, if the executable is compiled with RUNPATH then Pythia will not search along this path. Consequently, for Pythia to search along a path specified by the executable, both Pythia and the executable must be compiled using RPATH and not RUNPATH.