PySLHA’s main programmatic features are the Doc, Block, Particle & Decay, and Process & XSec classes, which map directly on to features of the SLHA data format. We first describe these structures and then the functions which operate on them.
Doc
From PySLHA 3.0.0, the main reading and writing functions in PySLHA accept and return a single Doc object; before this, they passed several dicts of data objects, but the single Doc is more robust and extensible. Doc is a simple structured container which provides access to the main groups of SLHA data types, via its blocks, decays and xsections attributes. A write method bound to the Doc allows convenient file and string I/O of its data, with controllable precision, in addition to the unbound write* functions described in Sect. 2.6.
A SLHA document can be accessed as follows:
In future versions, the Doc (and the following types) will allow storage of SLHA document inline and block comments as well as the parsed data. For now, only non-comment data will be preserved in a read/write cycle.
If it can be imported, the OrderedDict Python type will be used as a base type for the blocks, decays and xsections containers, which have a dict-like interface, in order to ensure that the user-specified ordering of data sections in the SLHA file is not disrupted by manipulation in PySLHA.
Block
From PySLHA 2.0.0 onward, the Block class provides a similar interface to Python’s native dict type, i.e. an instance of Block contains several entries accessed by an index or key, either by named functions or via the square-brackets
operator. Blocks also have an associated name and a Q value indicating the scale at which the contents are defined.
Like dicts, Block instances may store any type of object as the value of an entry, although the SLHA file format means that in practice this is restricted to integers, floating point numbers, and character strings (and sequences of the above). Also like dicts, Blocks support the Python iterator protocol, the get/set-item syntax, and methods such as items(), values(), keys(), and has_key(). Unlike
dicts, Block entries can only be indexed on integers, or on tuples of integers – or, in a special case designed for handling the standard ALPHA block, a block may contain a single unkeyed entry. The ability to use tuples of integers as keys is particularly useful for blocks which represent mixing matrices, e.g.
Adding and accessing multi-value block entries by integer (tuple) keys is unambiguous when done programatically as above. PySLHA of course also has to be able to parse SLHA entry lines into blocks and here some heuristics are necessary to differentiate between keys and values. The approach adopted, via the add_entry and set_value methods, is that if the supplied argument is a string, it will be split into a tuple of strings; these are then automatically converted to numeric types if possible, and consecutive strings are combined together (to allow for string values containing spaces.) The resulting tuple of ints, floats and strings is then parsed from left to right to find the first non-int item: all items before this are treated as the key.
A strength of Python as a language for SLHA block parsing is that the language is very dynamically typed: a variable can hold any type, and dicts, tuples, and other containers can hold heterogeneous entries. This is what makes it possible for blocks to be indexed by integer tuples of any length (or no length) and for values to either be scalar or tuples of mixed types. Hence no special features are needed for SLHA2 support or further extensions to the SLHA standard block content: any block layout representable in the SLHA syntax (and some which are not) can be manipulated using the PySLHA Block class.
Particle and Decay
The Particle and Decay classes are the complements of Block for SUSY particle properties and decay specifications. Particles are defined by a PDG species identifier code [9] and a list of Decays, and optionally may also contain the total decay width and the massFootnote 1 in GeV. In this sense, Particles are representations of the SLHA DECAY block itself, while its entries are each transformed into a fully-fledged Decay object. Each Decay contains a branching ratio and a list of particle ID codes representing the decay daughters. For convenience and familiarity, the PDG ID of the decaying particle and the NDA (number of daughters) number may also be stored but these are not essential: the parent particle has its own fully fledged Particle instance and decay.nda is equivalent to the “more Pythonic” len(decay.ids).
Process and XSec
The SLHA3 draft standardFootnote 2 introduces a new block type, the XSECTION, for storing various calculated cross-sections for different processes. This is supported in PySLHA from version 3.1.0 onwards, via the Process and XSec types. As for the DECAY block, a direct representation of the SLHA text format in memory is not the most natural programming interface, and hence a slightly different structure is used.
The Process class is the closest match to an XSECTION block, and the XSec objects contained within it map to the entries in the block. The main distinction is that a different XSECTION block is used for each energy at which a process cross-section is provided, which the Process is a dict-like object keyed on the concatenated tuple of (sorted) initial- and final-state particle ID codes. A given Process can then contain XSec entries at several energies, as well as the variations in QCD and EW order, factorization and renormalization scale-factors, PDFs, and the computational code which made the calculation. This is both more semantically natural, and avoids the problem of indexing the dictionary on \(\sqrt{s}\), a floating-point number which could easily fail to compare exactly equal to the required key value.
The XSec objects inside a Process are available in the xsecs list attribute. To make look-up of cross-sections which meet certain criteria easier, however, a filtering function has been provided which only requires a subset of the scheme details, e.g. the energy, renormalization scale and code, to be specified. The following example demonstrates how to find the available process index tuples and query them – in this case for a spectrum file which contains cross-sections at 8 TeV but none at 13 TeV:
String representation
Block, Particle and Decay objects can all represent themselves in convenient string form, as shown here for the NMIX matrix, neutralino LSP and gluino entries read from an example mSUGRA file (and accessed via two dicts – more on this later):
File and string I/O
The objects previously described are the in-memory data which can be programmatically manipulated by the user. To read these objects from SLHA files, the functions readSLHA() and readSLHAFile() are supplied.
The format parsing itself lives in the former, which takes an SLHA file’s content as a string argument, and returns two dicts: one containing the blocks keyed by name, and the the other containing the Particle objects keyed by PDG ID code.Footnote 3 The second (“File”) function treats its argument as either a filename string or as a Python file object, from which it loads the file content and passes it to readSLHA(). Both forms take an optional argument, ignorenobr, which if set true will exclude any Decay objects with a branching ratio of 0 from the resulting Particles; it is set false by default. Usage of these functions is demonstrated here:
SLHA format writing from code objects is similarly simple. Having placed Block and Particle objects (the latter containing Decays) into dicts, cf. the return values of the reader functions, they are passed to symmetric writer functions as follows:
The optional precision argument specifies how many decimal places to be written for floating point values such as masses, branching ratios, and widths: the default is 8. Two further functions, writeSLHABlocks(blocks) and writeSLHADecays(decays) exist to separately produce the SLHA output strings for the blocks and decays collections: both accept an optional precision argument, and the optional ignorenobr argument may be passed to the decay writer.
HERWIG/ISAWIG \(\leftrightarrow \) SLHA conversion
An original major motivation for PySLHA was to convert SLHA spectrum/decay files to the format used by the Fortran HERWIG event generator [4, 6] for SUSY simulation. This format was previously only output by the ISAWIG program, which uses the SUSY spectrum generator tools from the ISAJET code [5]. The restriction of HERWIG SUSY simulation to spectra generated by ISAJET/ISASUSY, and the increasing difficulty of building ISAJET (due to reduced availability of CERNLIB and Patchy, and increased standard enforcement in standard Fortran compilers) motivated development of a format converter which would permit other spectrum generators to produce HERWIG-compatible spectrum files.
Although use of ISASUSY (and of HERWIG) has reduced in recent years, PySLHA retains the ability to read and write the ISAWIG format. The readISAWIG() and readISAWIGFile() will parse the ISAWIG format into the PySLHA objects cf. the readSLHA*() ones, and the writeISAWIG() and writeISAWIGFile() functions invert the process. Unlike SLHA, in which the data block format is arbitrarily extensible, the ISAWIG format is fixed and only a subset of data will be written out: the corresponding block entries must be present.
There are some incompletenesses in PySLHA’s ISAWIG support due to a requirement that some SUSY decays be ordered in the file according to their dependence on HERWIG internal matrix elements: it is not clear that these can be handled in full detail without linking against HERWIG. Additionally, R-parity violating couplings are not currently read in or written. These defects will be happily resolved if possible and if there is sufficient interest.
Conversion between the ISAWIG and SLHA formats is made easier by two simple converter scripts, isawig2slha and slha2isawig, so that programming in Python is not necessary to convert a file of one sort into the other.
A side effect of having the ISAWIG \(\leftrightarrow \) SLHA converter machinery in PySLHA is that the library contains two functions, herwigid2pdgid(hwid) and pdgid2herwigid(pdgid), which as their names suggest convert particle ID codes between the standard PDG scheme and the HERWIG internal scheme. This may be useful, although undoubtedly less so as a new generation of C\(++\) event generator codes replace the venerable Fortran ones.