Gaudi logo

5. Getting started

5.1. Overview

In this chapter we walk through one of the example applications (RandomNumber) which are distributed with the framework. We look briefly at the different files and go over the steps needed to compile and execute the code. We also outline where various subjects are covered in more detail in the remainder of the document. Finally we cover briefly the other example applications which are distributed and say a few words on what each one is intended to demonstrate.

5.2. Creating a job

Traditionally, a “job” is the running of a program on a specified set of input data to produce a set of output data, usually in batch.

For the example applications supplied this is essentially a two step process. First the executable must be produced, and secondly the necessary environment variables must be set and the required job options specified, as illustrated in Fig. 5.1.

../_images/job.png

Fig. 5.1 Creating a job from the AlgSequencer example application

The example applications consist of a number of “source code” files which together allow you to generate an executable program. These are:

· The main program.
· Header and implementation files for each of the concrete algorithm classes.
· A CMT requirements file.
· The set of Gaudi libraries.

In order for the job to run as desired you must provide the correct configuration information for the executable. This is done via entries in the job options file.

5.3. The main program

The main program is needed to bootstrap the job. It can be completely general, and can be reused by all Gaudi applications. An example main program, from the package GaudiExamples, is shown in Listing 5.1.

Listing 5.1 The example main program
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Include files
#include "GaudiKernel/SmartIF.h"
#include "GaudiKernel/Bootstrap.h"
#include "GaudiKernel/IAppMgrUI.h"
#include "GaudiKernel/IProperty.h"
#include <iostream>

//--- Example main program
int main(int argc, char** argv) {

    // Create an instance of an application manager
    IInterface* iface = Gaudi::createApplicationMgr();

    SmartIF<IProperty> propMgr ( IID_IProperty, iface );
    SmartIF<IAppMgrUI> appMgr ( IID_IAppMgrUI, iface );

    if( !appMgr.isValid() || !propMgr.isValid() ) {
        std::cout << "Fatal error creating ApplicationMgr " << std::endl;
        return 1;
    }

    // Get the input configuration file from arguments
    std:: string opts = (argc>1) ? argv[1] : "../options/job.opts";

    propMgr->setProperty( "JobOptionsPath", opts );

    // Run the application manager and process events
    appMgr->run();

    // All done - exit
    return 0;
}

It is constructed as follows:

Include

These are needed for the creation of the application manager and Smart interface pointers.

Application Manager instantiation

Line 12 instantiates an ApplicationMgr object. The application manager is essentially the job controller. It is responsible for creating and correctly initialising all of the services and algorithms required, for looping over the input data events and executing the algorithms specified in the job options file, and for terminating the job cleanly.

Retrieval of Interface pointers

The code on lines 14 and 15 retrieves the pointers to the IProperty and IAppMgrUI interfaces of the application manager.

Setting the application manager’s properties

The only property which needs to be set explicitly in the main program is the name of the job options file which contains all of the other configuration information needed to run the job. In this example, the name is the first argument of the program and defaults to “../options/job.opts” (line 23); it is set on line 25.

Program execution

All of the code before line 28 is essentially for setting up the job. Once this is done, a call to appMgr::run() is all that is needed to start the job proper! The steps that occur within this method are discussed briefly in section Section 5.7.

5.4. Configuring the job

The application framework makes use of a job options file for job configuration. Part of the job options file of an example application is shown in Listing 5.2.

Listing 5.2 Part of the job options file for the RootIORead example application
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Include standard option files
#include "$STDOPTS/Common.opts"

// Private Application Configuration options
ApplicationMgr.DLLs += { "GaudiDb", "GaudiRootDb" };
ApplicationMgr.ExtSvc += { "DbEventCnvSvc/RootEvtCnvSvc" };
ApplicationMgr.TopAlg = { "ReadAlg" };

// Set output level threshold (2=DEBUG,3=INFO,4=WARNING,5=ERROR,6=FATAL)
MessageSvc.OutputLevel = 4;
EventSelector.OutputLevel = 2;

// Input File
EventSelector.Input = {"DATAFILE='RootDst.root' TYP='ROOT' OPT='READ'"};
EventSelector.FirstEvent = 1;
ApplicationMgr.EvtMax = 5;

// Persistency service setup:
EventPersistencySvc.CnvServices += { "RootEvtCnvSvc" };

// Setup for ROOT I/O system
RootEvtCnvSvc.DbType = "ROOT";

The format of an options file is discussed fully in Chapter 11. Options may be set both for algorithms and services and the list of available options for standard components is given in Section 20. Here we look briefly at a few of the more commonly used options.

5.4.1. Defining the algorithms to be executed

The option ApplicationMgr.TopAlg (line 7) is a list of algorithms that will be created and controlled directly by the application manager, the so-called top-level algorithms. The syntax is a list of the form:

ApplicationMgr.TopAlg = { "Type1/Name1", "Type2/Name2" };

The line above instructs the application manager to create two top level algorithms. One of type Type1 called “Name1” and one of type Type2 called “Name2”.

In the case where the name of the algorithm is the same as the algorithm’s type (i.e. class), only the class name is necessary. In the example, an instance of the class “ReadAlg” will be created with name “ReadAlg”.

5.4.2. Defining the job input

Event data input is controlled by an EventSelector. The EventSelector uses a storage technology dependent data persistency service to load the data into the transient event data store, with the help of converters which are able to convert the data from the technology dependent persistent representation, to the technology independent representation in the transient data store.

In order to set up this mechanism, one needs a number of job options:

- Line 14 defines the input data file, and the persistency technology (ROOT I/O in this example).
- Line 6 tells the application manager to create a new event conversion service, to be called RootEvtCnvSvc. Note that this is just a name for our convenience, the service is of type DbEventCnvSvc and does not (yet) know that it will deal with ROOT technology. The configuration of RootEvtCnvSvc to use the ROOT I/O technology is done in line 22.
- Line 19 tells the event persistency service (EventPersistencySvc created by the application manager by default) to use the RootEvtCnvSvc to do the conversion between persistent and transient data representations.
- Line 5 tells the application manager which additional libraries to load in order to find the required conversion service. In this example, the GaudiDb library contains the DbEventCnvSvc class, the GaudiRootDb library contains the ROOT specific database drivers.
- Finally, the options on lines 15 and 16 tell the EventSelector to start reading sequentially from the first event in the file, for five events.

In the special case where no event input is required (e.g. for event generation), one can replace the above options by the two options:

ApplicationMgr.EvtMax = 20; // events to be processed (default is 10)
ApplicationMgr.EvtSel = "NONE"; // do not use any event input

A discussion of event I/O can be found in Section 11. Converters and the conversion process are described in Section 14.

5.4.3. Defining job output

One can consider three types of job output: event data (including event collections and n-tuples), statistical data (histograms) and printout. Here we discuss only the simplest (printout); histograms are discussed in Section 10, event data in Section 7.10.1, event collections in Section 12.3.1.

Printout in Gaudi is handled by the message service (described in Section 12), which allows to control the amount of printout according to severity level. The global threshold for printout is set by the option on line 10 - in this example only messages of severity level WARNING or above will be printed. This can be over-ridden for individual algorithms or services, as in line 11, where the threshold for EventSelector is set to DEBUG.

5.5. Algorithms

The subject of specialising the Algorithm base class to do something useful will be covered in detail in Section 6. Here we will limit ourselves to looking at an example HelloWorld class.

5.5.1. The HelloWorld.h header file

The HelloWorld class definition is shown in Listing 5.3

Listing 5.3 The header file of the class: HelloWorld
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Include files
#include "GaudiKernel/Algorithm.h"
// Required for inheritance
#include "GaudiKernel/Property.h"
#include "GaudiKernel/MsgStream.h"

class HelloWorld : public Algorithm {

public:

  /// Constructor of this form must be provided
  HelloWorld(const std::string& name, ISvcLocator* pSvcLocator);

  /// Three mandatory member functions of any algorithm
  StatusCode initialize();
  StatusCode execute();
  StatusCode finalize();

private:

  /// These data members are used in the execution of this algorithm
  /// and are set in the initialisation phase by the job options service
  int m_int;
  double m_double;
  std::string m_string;
};

Note the following:

· The class is derived from the Algorithm base class as must be all specialised algorithm classes. This implies that the Algorithm.h file must be included (line 6).
· All derived algorithm classes must provide a constructor with the parameters shown in line 9 . The first parameter is the name of the algorithm and is used amongst other things to locate any options that may have been specified in the job options file.
· The HistoAlgorithm class has three (private) data members, defined in lines 18 to 20. These are properties that can be set via the job options file.
· The three methods on lines 12 to 14 must be implemented, since they are pure virtual in the base class.

5.6. The HelloWorld implementation file

The implementation file contains the actual code for the constructor and for the methods: initialize(), execute() and finalize(). It also contains two lines of code for the HelloWorld factory, which we will discuss in section Section 6.3.1

The constructor

must call the base class constructor, passing on its two arguments. As usual, member variables should be initialised. Here we declare and initialise the member variables that we wish to be set by the job options service. This is done by calling the declareProperty() method.
1
2
3
4
5
6
7
HelloWorld::HelloWorld(const std::string& name, ISvcLocator* ploc) : Algorithm(name, ploc) {
    //-----------------------------------------------------------------------
    // Declare the algorithm's properties
    declareProperty( "Int", m_int = 100 );
    declareProperty( "Double", m_double = 100.);
    declareProperty( "String", m_string = std::string("one hundred"));
}
Initialisation

The application manager invokes the sysInitialize() method of the algorithm base class which, in turn, invokes the initialize() method of the base class, the setProperties() method, and finally the initialize() method of the concrete algorithm class. As a consequence all of an algorithm’s properties will have been set before its initialize() method is invoked, and all of the standard services such as the message service are available. This is discussed in more detail in Section 6.

Looking at the code in the example (Listing 5.4) we see that we are now able to print out the values of the algorithm’s properties, using the message service and the MsgStream utility class. A local MsgStream object is created (line 3 ), which uses the Algorithm’s standard message service via the msgSvc() accessor, and the algorithm’s name via the name() accessor. The use of these is discussed in more detail in Section 12.

Note that the job will stop if the initialize() method of any algorithm does not return StatusCode::SUCCESS. This is to avoid processing with a badly configured application.:

Listing 5.4 Example of initialize() method
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
StatusCode HelloWorld::initialize() {
//----------------------------------------------------------------------
    MsgStream log(msgSvc(), name());
    log << MSG::INFO << "initializing...." << endreq;
    log << MSG::INFO << "Property Int = " << m_int << endreq;
    log << MSG::INFO << "Property Double = " << m_double << endreq;
    log << MSG::INFO << "Property String = " << m_string << endreq;

    m_initialized = true;
    return StatusCode::SUCCESS;
}
execution

The execute() method is called by the application manager once for every event. This is where most of the real action should take place. The trivial HelloWorld class just prints out a message… Note that the method must return StatusCode::SUCCESS on successful completion. If a particular algorithm returns a FAILURE status code more than a (configurable) maximum number of times, the application manager will decide that this algorithm is badly configured and jump to the finalisation stage before all events have been processed.
1
2
3
4
5
6
7
StatusCode HelloWorld::execute() {
//----------------------------------------------------------------------
    MsgStream log( msgSvc(), name() );
    log << MSG::INFO << "executing...." << endreq;

    return StatusCode::SUCCESS;
}
Finalisation

The finalize() method is called at the end of the job. In this trivial example a message is printed.
1
2
3
4
5
6
7
StatusCode HelloWorld::finalize() {
//----------------------------------------------------------------------
    MsgStream log(msgSvc(), name());
    log << MSG::INFO << "finalizing...." << endreq;

    return StatusCode::SUCCESS;
}

5.7. Job execution

From the main program and the CMT requirements file we can make an executable, as explained in section Section 4.6. This executable together with the file of job options form a job which may be submitted for batch or run interactively. Fig. 5.2 shows a trace of an example program execution. The diagram is not intended to be complete, merely to illustrate a few of the points mentioned earlier in the chapter.

../_images/Exampletrace.png

Fig. 5.2 A sequence diagram showing a part of the execution of an example program

1. The application manager instantiates the required services and initialises them. The message service is done first to allow the other services to use it, and the job options service is second so that the other services may be configured at run time.
2. The algorithms which have been declared to the application manager within the job options (via the TopAlg option) are created. We denote these algorithms “top-level” as they are the only ones controlled directly by the application manager. For illustration purposes we instantiate an EmptyAlgorithm and a HistoAlgorithm.
3. The top-level algorithms are initialised. Their properties (if they have any) are set and they may make use of the message service. If any algorithm fails to initialise, the job is stopped.
4. The application manager now starts to loop over events. After each event is read, it executes each of the top level algorithms in order. The order of execution of the algorithms is the order in which they appear in the TopAlg option. This will continue until the required number of events has been processed, unless one or more of the algorithms return a FAILURE status code more than the maximum number of times, in which case the application manager will jump to the finalisation stage before all events have been processed.
5. After the required data sample has been read the application manager finalises each top level algorithm.
6. Services are finalised.
7. All objects are deleted and resources freed. The program terminates.

5.8. Examples distributed with Gaudi

A number of examples is included in the current release of the framework, in the GaudiExamples package. The package has some sub-directories in addition to the standard ones shown in Fig. 17.2. The options sub-directory contains files of standard job options common to many examples. These files are included in the job options of the specific examples when necessary. The specific job options files can be found in the home sub-directory.

The code of the examples is in sub-directories of the src directory, one sub-directory per example. The intention is that each example demonstrates how to make use of some part of the functionality of the framework. The list of available examples is shown in Table 5.1.

Table 5.1 List of examples available in Gaudi release v9

Example Name

Target Functionality

AlgSequencer

Illustraing the use of the sequencer algorithm provided with teh GaudiAlg package

AlgTool

Example implementation and use of a Gaudi Tool

Common

Actually not a complete example: contains the main program used in the examples

GPython

Exercise the Python scripting packages

Properties

Trivial algorithm showing how to set and retrieve Properties

RandomNumber

Example of use of the Random Number service

RootIO

Two examples, reading and writing persistend data with ROOT I/O