Plugins
- Loading a Plugin
- Plugin Library Paths
- Writing a Plugin Library
Pythia is designed as a standalone package, with no external
dependencies. However, there are many use cases for interfacing
external packages with Pythia. To that end, the
include/Pythia8Plugins
directory provides a number of
header interfaces, which are then demonstrated in
examples
. However, this method of including external
dependencies in Pythia is not always sufficient, and so a plugin
system is also available. The system works as follows (more details
are given later):
- A user creates a standalone plugin library, which implements
derived classes from Pythia. This plugin library can be linked against
external libraries.
- At runtime, a
Pythia
object loads this plugin
library.
- Any new object requested by the user from this plugin library is
created and returned as a shared pointer. The shared pointer is the
same type as the Pythia base class of the plugin object. For example,
consider a plugin library that implements a new
MyPDF
class which derives from the Pythia PDF
class. The plugin
system will then return a shared pointer of type PDF
which allows access to a MyPDF
object.
This type of system has a number of advantages over the header interface
style of plugins.
- The external dependencies only need to be handled once. For
example, consider using HepMC to write out event files. With the
plugin system, we only need to link the standalone plugin library with
HepMC at compile time. Then, any main program we might build after
this does not need to be linked against HepMC (headers or libraries),
but can still write HepMC output to file.
- In some cases, we might want to replace core functionality in
Pythia with code that requires external dependencies. A good example
of this is LHAPDF, which can be used to replace the internal Pythia
PDF sets. Here, we can allow LHAPDF sets to be used seamlessly within
Pythia without introducing any external dependencies to the core
Pythia code.
- We oftentimes want to use libraries that provide duplicate
symbols. This is happens when linking against FORTRAN libraries with
standard common blocks, or libraries that were generated using some
automated tool. An example of this is the matrix element corrections
provided via MG5 for the parton shower. Here, MG5 builds libraries
with identical symbol names and so we cannot link against multiple
versions of these libraries. The plugin system allows us to not only
choose which MG5 matrix element correction library we load at runtime,
but also load multiple libraries without causing symbol
conflicts.
- It is now possible to create a Pythia executable, where all
interactions by the user are through command files, and no code needs
to be compiled.
Within Pythia, the plugin library is already used to provide access to
external libraries such as LHAPDF and MG5 matrix element corrections
in the parton shower.
Loading a Plugin
There are two ways to load plugins within Pythia code. The first is to
provide a list of plugins to Pythia via the Init:plugins
option (see Main Program Settings
for the definition of this option). To give an example, consider
writing a UserHooks
class called MyUserHooks
and compiling this in the plugin library libMyPlugins.so
. To
automatically load this user hook and pass it to your Pythia object, the
following command should be given to Pythia.
Init:plugins = {libMyPlugins.so::MyUserHooks}
Then, when pythia.init()
is called, a MyUserHooks
object will be automatically created and set as the UserHooks
object for the Pythia object via the method
pythia.setUserHooksPtr
. For most plugin object classes of
generic type BaseClass
, the relevant pointer will be set via
the corresponding pythia.setBaseClassPtr
method. For some
class types, there is an option to either set
the
pointer, overwriting any previous pointer, or add
the
pointer to a list of pointers that will all be used. To specify
whether the plugin should be set or added, a third optional keyword
can be specified,
Init:plugins = {libMyPlugins.so::MyUserHooks::set}
where this third keyword can be either set
(the default) or
add
. In this example, if there was a previous
UserHooks
pointer, it would be replaced by a new
MyUserHooks
pointer.
The treatment of this third keyword is different for PDF plugins,
where it specifies the beam type (and the PDF pointer is always set,
rather than added). For example,
Init:plugins = {libMyPlugins.so::MyPDF1::A, libMyPlugins.so::MyPDF2::B}
sets MyPDF1
for beam A, while
MyPDF2
is set for beam B. This beam type can be taken from the
list of arguments to the
Pythia::setPDFPtr
method in
Program Flow, with the
pdf
prefix and Ptr
suffix stripped from the
argument name, for example pdfPomAPtr
becomes
PomA
.
If there are new settings which are defined in the
libMyPlugins.so
library, these settings are registered before the
MyUserHooks
object is created. In this way, it is possible to
pass settings to the MyUserHooks
object from a command file.
This would be done as,
Init:plugins = {libMyPlugins.so::MyUserHooks::set::plugin.cmnd}
where plugin.cmnd
is a standard command file for
Pythia. It is also possible to use the subrun feature of the Pythia
command files.
Init:plugins = {libMyPlugins.so::MyUserHooks::set::plugin.cmnd::2}
This command tells Pythia to load the specific commands for subrun two
from the plugin.cmnd
file.
The plugin functionality is available for a number of Pythia base classes
which can be passed as external pointers to a Pythia object. These include:
PDF
set via Pythia::setPDFPtr
LHAup
set via Pythia::setLHAupPtr
DecayHandler
set via Pythia::setDecayPtr
RndmEngine
set via Pythia::setRndmEnginePtr
UserHooks
set via
Pythia::set/addUserHooksPtr
Merging
set via Pythia::setMergingPtr
MergingHooks
set via
Pythia::setMergingHooksPtr
BeamShape
set via Pythia::setBeamShapePtr
SigmaProcess
set via
Pythia::set/addSigmaPtr
with a null argument for the
PhaseSpace
pointer
PhaseSpace
set via the second argument of
Pythia::set/addSigmaPtr
; the previous plugin must be the
SigmaProcess
which uses this phase-space generator
ResonanceWidths
set via
Pythia::set/addResonancePtr
ShowerModel
set via
Pythia::setShowerModelPtr
HeavyIons
set via Pythia::setHeavyIonsPtr
HIUserHooks
set via Pythia::setHIHooks
The second method for loading a plugin can be used directly in user
code, with or without a Pythia
instance, using a number of
helper functions. The simplest is as follows.
shared_ptr<BaseClass> make_plugin<BaseClass>( string libName, string className)
creates a shared pointer of type BaseClass
from the plugin
library libName
and the plugin class className
.
argument
libName, className :
the plugin library name, typically of the form lib*.so
, and
the plugin class name to load.
shared_ptr<BaseClass> make_plugin<BaseClass>( string libName, string className, Pythia* pythiaPtr)
same as above, but with an optional Pythia
pointer that
is passed to the constructor of the plugin class.
argument
pythiaPtr :
pointer to a Pythia
instance. This pointer is
passed to the constructor of the plugin object, and can be used by
that object.
shared_ptr<BaseClass> make_plugin<BaseClass>( string libName, string className, Pythia* pythiaPtr, Settings* settingsPtr, Logger* loggerPtr)
same as above, but with an optional Settings
and
Logger
pointer.
argument
settingsPtr :
optional pointer to a Settings
instance. This pointer is
passed to the plugin object constructor. If a valid
pythiaPtr
is provided, but no settingsPtr
,
then the settingsPtr
is taken from the
pythiaPtr
object.
argument
loggerPtr :
optional pointer to a Logger
instance, similar to
settingsPtr
.
Plugin classes are always required to take a Pythia
,
Settings
, and Logger
pointer in their
constructors. However, none of these pointers is required to be
valid. If a plugin class requires one of these pointers to be valid,
it is possible to specify this when creating the plugin library (see below).
A plugin library may optionally register new settings in a
Settings
object passed to the plugin library. This allows
the settings to pass a wide range of arguments to a plugin object. The
idea is as follows. First, the plugin library settings are registered
with a Settings
object. Then, the new settings from the
plugin in this Settings
object can be modified by the
user. Finally, the plugin object is created, with the
Settings
object passed to the constructor so that it can
access these specialised settings. The following method registers the
settings from a plugin library.
bool Settings::registerPluginLibrary(string libName, string startFile)
register any settings provided in a plugin library. Typically, this
method is not needed by the user.
argument
libName :
the plugin library name, typically of the form lib*.so
.
argument
startFile (default = ""
) :
read in the settings from all the files listed in this file, and
assumed to be located in the same subdirectory.
The following methods then combine these three steps together into a
single method.
shared_ptr<BaseClass> make_plugin<BaseClass>( string libName, string className, Pythia* pythiaPtr, vector<string>& cmnds)
register the plugin settings with the Pythia
instance and
read the commands of cmnds
into the
pythiaPtr
.
argument
cmnds :
vector of commands to read into Pythia before loading the plugin,
but after registering the plugin settings.
shared_ptr<BaseClass> make_plugin<BaseClass>( string libName, string className, Pythia* pythiaPtr, string fileName, int subrun = SUBRUNDEFAULT)
register the plugin settings with the Pythia
instance and
read the commands of the file fileName
with optional
subrun
.
argument
fileName :
name of the file to read into the Pythia
object.
argument
subrun :
optionally specify the commands from the subrun to use.
The following demonstrates the simplest construction.
PDFPtr pdf = make_plugin<PDF>("libMyPlugins.so", "MyPDF");
To register settings and pass these to the plugin, the following could
be done.
Settings settings;
settings.registerPluginLibrary("libMyPlugins.so");
// Modify the settings here.
PDFPtr pdf = make_plugin<PDF>("libMyPlugins.so", "MyPDF", nullptr,
&settings, nullptr);
Alternatively, if working with a Pythia
object, the
multistep functions can be used.
Pythia pythia;
vector<string> cmnds;
// Fill the command string.
PDFPtr pdf1 = make_plugin<PDF>(
"libMyPlugins.so", "MyPDF", &pythia, cmnds);
// Alternatively, read the commands from a file.
PDFPtr pdf2 = make_plugin<PDF>(
"libMyPlugins.so", "MyPDF", &pythia, "plugin.cmnd");
Note that in this example, two unique objects from the plugin library
are created.
Plugin Library Paths
When libName
is specified for any of the settings,
methods, or functions above, it can be passed as either just the
library name (e.g. libMyPlugins.so
), the library name
with a relative path (e.g. ./libMyPlugins.so
), or the
library name with an absolute path
(e.g. /plugins/libMyPlugins.so
). When the path is
specified (i.e. the latter two options) then that file must exist, as
the plugin library. When no path is specified then the same mechanism
used for resolving library paths at runtime is used. This means that
both the runtime search path specified to the linker at compile time
will be searched (i.e. the RPATH mechanism using
-Wl,rpath
), as well as the path specified via the
environment variable LD_LIBRARY_PATH
.
Writing a Plugin Library
A plugin library consists of two parts: user defined classes
inheriting from any Pythia class, and a function which registers
settings. Arbitrary classes can be defined by the user, as long as they
derive from a Pythia class. Additionally, the constructor for the class
must always take the following form:
MyClass(Pythia* pythiaPtr, Settings* settingsPtr, Logger* loggerPtr)
As discussed above, the validity of these three pointers is not
guaranteed, and so the class must accordingly check their validity if
needed. After a class is defined, it must then be made externally
available so that it can be loaded from the plugin library. It is
possible when making the class externally available to specify that
certain pointers are required. This is done with the following macro.
PYTHIA8_PLUGIN_CLASS(BaseClass, MyClass, RequirePythia, RequireSettings,
RequireLogger)
Here, BaseClass
is whatever class the user wishes the
MyClass>
to be loaded as using the
make_plugin
functions. The argument
ReqirePythia
should be a boolean, and specifies whether
the Pythia
pointer is required by the plugin
class. Similarly, the RequireSettings
and
RequireLogger
arguments must also be booleans specifying
whether the Settings
and Logger
pointers
must be valid or not. Note, this macro can only be used once per
class, and consequently can only be returned as a single base class.
The settings can be registered by a function that follows the
following form:
void registerMySettings(Settings* settingsPtr)
which is then made externally available by the following macro.
PYTHIA8_PLUGIN_SETTINGS(registerMySettings)
This macro can only be used once in a plugin library, and so all
settings must be registered by a single function. Registering settings
in a plugin library is optional.
Users may also provide XML files, similar to the Pythia XML files, to
define both setttings and documentation. This is done with the macro,
PYTHIA8_PLUGIN_XML("pluginLibraryName/xmldoc/startFile.xml")
which takes a string as an argument. The string should provide the
relative path to the XML index for the library, similar to
Pythia8/xmldoc/Index.xml
for Pythia. Here,
pluginLibraryName
should be the name of the plugin
library, and startFile.xml
is the index XML file which
includes all other XML files needed for the plugin library. When a
plugin library is loaded, an attempt will first be made to find the
starting XML file on the path specified by the environment variable
PYTHIA8CONTRIB
, so here the full path would be
PYTHIA8CONTRIB/pluginLibraryName/xmldoc/startFile.xml
. If
this path is not valid, then the path PYTHIA8DATA/../../
will be tried, followed by the provided path itself.
Finally, it is required to define the versions of Pythia that the plugin
library is compatible with. This is done with the macro:
PYTHIA8_PLUGIN_VERSIONS(8310, 8311)
where a comma separated list of integer Pythia versions is passed as
an argument. This list can be variable length, including length
one. In this example, the plugin library is compatible with versions
8.309
and 8.310
. When a user loads a plugin
library the versions from this list are checked against the current
Pythia version to test compatibility. If the current Pythia version is
not found in the list then an error is given when loading the
library. Additionally, the version of Pythia that the plugin library
was compiled against is checked. If the versions between Pythia and
the plugin library do not match a warning is given. This is because in
principle the application binary interface between Pythia versions is
expected to be stable. It is possible that a plugin library compiled
against a different version of Pythia than the version loading the
library might still function correctly. However, this behaviour may be
changed to an error in the future.