WHIZARD is hosted by Hepforge, IPPP Durham
Previous Up Next

Chapter 12  User Code Plug-Ins

Note that the user-code plug-in mechanism has been currently (for version 2.2.0) disabled, as the huge refactoring of the code between versions 2.1.X and 2.2.X has completely changed many of the interfaces. We plan to bring the interface for user code for spectra, structure functions and event shapes, cuts and observables back online as soon as possible, at latest for version 2.4.0.

12.1  The plug-in mechanism

The capabilities of WHIZARD and its SINDARIN command language are not always sufficient to adapt to all users’ needs. To make the program more versatile, there are several spots in the workflow where the user may plug in his/her own code, to enhance or modify the default behavior.

User code can be injected, without touching WHIZARD’s source code, in the following places:

  • Cuts, weights, analysis, etc.:
    • Cut functions that operate on a whole subevent.
    • Observable (e.g., event shapes) calculated from a whole subevent.
    • Observable calculated for a particle or particle pair.
  • Spectra and structure functions.

Additional plug-in locations may be added in the future.

User code is loaded dynamically by WHIZARD. There are two possibilities:

  1. The user codes the required procedures in one or more Fortran source files that are present in the working directory of the WHIZARD program. WHIZARD is called with the -u flag:
    whizard -u --user-src=user-source-code-file
    The file must have the extension .f90, and the file name must be specified without extension.

    There may be an arbitrary number of user source-code files. The compilation is done in order of appearance. If the name of the user source-code file is user.f90, the flag --user-src can be omitted.

    This tells the program to compile and dynamically link the code at runtime. The basename of the linked library is user.

    If a compiled (shared) library with that name already exists, it is taken as-is. If the user code changes or the library becomes invalid for other reasons, recompilation of the user-code files can be forced by the flag --rebuild-user or by the generic -r flag.

  2. The user codes and compiles the required procedures him/herself. They should be provided in form of a library, where the interfaces of the individual procedures follow C calling conventions and exactly match the required interfaces as described in the following sections. The library must be compiled in such a way that it can be dynamically linked. If the calling conventions are met, the actual user code may be written in any programming language. E.g., it may be coded in Fortran, C, or C++ (with extern(C) specifications).

    WHIZARD is called with the -u flag and is given the name of the user library as

    whizard -u --user-lib=user-library-file

The library file should either be a dynamically loadable (shared) library with appropriate extension (.so on Linux), or a libtool archive (.la).

The user-provided procedures may have arbitrary names; the user just has to avoid clashes with procedures from the Fortran runtime library or from the operating-system environment.

12.2  Data Types Used for Communication

Since the user-code interface is designed to be interoperable with C, it communicates with WHIZARD only via C-interoperable data types. The basic data types (Fortran: integer and real kinds) c_int and c_double are usually identical with the default kinds on the Fortran side. If necessary, explicit conversion may be inserted.

For transferring particle data, we are using a specific derived type c_prt_t which has the following content:

  type, bind(C) :: c_prt_t
     integer(c_int) :: type
     integer(c_int) :: pdg
     integer(c_int) :: polarized
     integer(c_int) :: h
     real(c_double) :: pe
     real(c_double) :: px
     real(c_double) :: py
     real(c_double) :: pz
     real(c_double) :: p2
  end type c_prt_t

The meaning of the entries is as follows:

The type of the particle. The common type codes are 1=incoming, 2=outgoing, and 3=composite. A composite particle in a subevent is created from a combination of individual particle momenta, e.g., in jet clustering. If the status code is not defined, it is set to zero.
The particle identification code as proposed by the Particle Data Group. If undefined, it is zero.
If nonzero, the particle is polarized. The only polarization scheme supported at this stage is helicity. If zero, particle polarization is ignored.
If the particle is polarized, this is the helicity. 0 for a scalar, ± 1 for a spin-1/2 fermion, −1,0,1 for a spin-1 boson.
The energy in GeV.
The transversal momentum components in GeV.
The longitudinal momentum component in GeV.
The invariant mass squared of the actual momentum in GeV2.

WHIZARD does not provide tools for manipulating c_prt_t objects directly. However, the four-momentum can be used in Lorentz-algebra calculations from the lorentz module. To this end, this module defines the transformational functions vector4_from_c_prt and vector4_to_c_prt.

12.3  User-defined Observables and Functions

12.3.1  Cut function

Instead of coding a cut expression in SINDARIN, it may be coded in Fortran, or in any other language with a C-compatible interface. A user-defined cut expression is referenced in SINDARIN as follows:

cuts = user_cut (name-string) [subevent]

The name-string is an expression that evaluates to string, the name of the function to call in the user code. The subevent is a subevent expression, analogous to the built-in cut definition syntax. The result of the user_cut function is a logical value in SINDARIN. It is true if the event passes the cut, false otherwise.

If coded in Fortran, the actual user-cut function in the user-provided source code has the following form:

function user_cut_fun (prt, n_prt) result (iflag) bind(C)
   use iso_c_binding
   use c_particles
   type(c_prt_t), dimension(*), intent(in) :: prt
   integer(c_int), intent(in) :: n_prt
   integer(c_int) :: iflag
   ! ... code that evaluates iflag
end function user_cut_fun

Here, user_cut_fun can be replaced by an arbitrary name by which the function is referenced as name-string above. The bind(C) attribute in the function declaration is mandatory.

The argument prt is an array of objects of type c_prt_t, as described above. The integer n_prt is the number of entries in the array. It is passed separately in order to determine the actual size of the assumed-size prt array.

The result iflag is an integer. A nonzero value indicates true (i.e., the event passes the cut), zero value indicates false. (We do not use boolean values in the interface because their interoperability might be problematic on some systems.)

12.3.2  Event-shape function

An event-shape function is similar to a cut function. It takes a subevent as argument and returns a real (i.e., C double) variable. It can be used for defining subevent observables, event weights, or the event scale, as in

analysis = record hist-id (user_event_fun (name-string) [subevent])


scale = user_event_fun (name-string) [subevent]

The corresponding Fortran source code has the form

function user_event_fun (prt, n_prt) result (rval) bind(C)
   use iso_c_binding
   use c_particles
   type(c_prt_t), dimension(*), intent(in) :: prt
   integer(c_int), intent(in) :: n_prt
   real(c_double) :: rval
   ! ... code that evaluates rval
end function user_event_fun

with user_event_fun replaced by name-string.

12.3.3  Observable

In SINDARIN, an observable-type function is a function of one or two particle objects that returns a real value. The particle objects result from scanning over subevents. In the SINDARIN code, the observable is used like a variable; the particle-object arguments are implictly assigned.

A user-defined observable is used analogously, e.g.,

cuts = all user_obs (name-string) > 0 [subevent]

The user observable is defined, as Fortran code, as either a unary or as a binary C-double-valued function of c_prt_t objects. The use in SINDARIN (unary or binary) must match the definition.

function user_obs_unary (prt1) result (rval) bind(C)
  use iso_c_binding
  use c_particles
  type(c_prt_t), intent(in) :: prt1
  real(c_double) :: rval
  ! ... code that evaluates rval
end function user_obs_unary


function user_obs_binary (prt1, prt2) result (rval) bind(C)
  use iso_c_binding
  use c_particles
  type(c_prt_t), intent(in) :: prt1, prt2
  real(c_double) :: rval
  ! ... code that evaluates rval
end function user_obs_binary

with user_obs_unary/binary replaced by name-string.

12.3.4  Examples

For an example, we implement three different ways of computing the transverse momentum of a particle. This observable is actually built into WHIZARD, so the examples are not particularly useful. However, implementing kinematical functions that are not supported (yet) by WHIZARD (and not easily computed via SINDARIN expressions) proceeds along the same lines.


The first function is a complete cut which can be used as

cuts = user_cut("ptcut") [subevt]

It is equivalent to

cuts = all Pt > 50 [subevt]

The implementation reads

function ptcut (prt, n_prt) result (iflag) bind(C)
  use iso_c_binding
  use c_particles
  use lorentz
  type(c_prt_t), dimension(*), intent(in) :: prt
  integer(c_int), intent(in) :: n_prt
  integer(c_int) :: iflag
  logical, save :: first = .true.
  if (all (transverse_part (vector4_from_c_prt (prt(1:n_prt))) > 50)) then
     iflag = 1
     iflag = 0
  end if
end function ptcut

The procedure makes use of the kinematical functions in the lorentz module, after transforming the particles into a vector4 array.

Event Shape

Similar functionality can be achieved by implementing an event-shape function. The function computes the minimum pT among all particles in the subevent. The SINDARIN expression reads

cuts = user_event_shape("pt_min") [subevt] > 50

and the function is coded as

function pt_min (prt, n_prt) result (rval) bind(C)
  use iso_c_binding
  use c_particles
  use lorentz
  type(c_prt_t), dimension(*), intent(in) :: prt
  integer(c_int), intent(in) :: n_prt
  real(c_double) :: rval
  rval = minval (transverse_part (vector4_from_c_prt (prt(1:n_prt))))
end function pt_min


The third (and probably simplest) user implementation of the pT cut computes a single-particle observable. Here, the usage is

cuts = all user_obs("ptval") > 50 [subevt]

and the subroutine reads

function ptval (prt1) result (rval) bind(C)
  use iso_c_binding
  use c_particles
  use lorentz
  type(c_prt_t), intent(in) :: prt1
  real(c_double) :: rval
  rval = transverse_part (vector4_from_c_prt (prt1))
end function ptval

12.4  User Code and Static Executables

In Sec. 5.4.7 we describe how to build a static executable that can be submitted to batch jobs, e.g., on the grid, where a compiler may not be available.

If there is user plug-in code, it would require the same setup of libtool, compiler and linker on the target host, as physical process code. To avoid this, it is preferable to link the user code statically with the executable, which is then run as a monolithic program.

This is actually simple. Two conditions have to be met:

  1. The WHIZARD job that creates the executable has to be given the appropriate options (-u, --user-src, --user-lib) such that the user code is dynamically compiled and linked.
  2. The compile command in the SINDARIN script which creates the executable takes options that list the procedures which the stand-alone program should access:
    compile as "executable-name" {
       $user_procs_cut = "
       $user_procs_event_shape = "
       $user_procs_obs1 = "
       $user_procs_obs2 = "
       $user_procs_sf = "
    The values of these option variables are comma-separated lists of procedure names, grouped by their nature. obs1 and obs2 refer to unary and binary observables, respectively. The strfun-names are the names of the user-defined spectra or structure functions as they would appear in the SINDARIN file which uses them.

With these conditions met, the stand-alone executable will have the user code statically linked, and it will be able to use exactly those user-defined routines that have been listed in the various option strings. (It is possible nevertheless, to plug in additional user code into the stand-alone executable, using the same options as for the original WHIZARD program.)

Previous Up Next