Running Compiled Root Jobs in Ganga

Introduction

Ganga supports the submission of ROOT jobs to many backends, including the Grid (LCG,Dirac) via the Root application. This enables users to submit a CINT script which is then run on the specified backend. CINT is an interpreter based on the C++ language, and is an excellant choice for running simple ROOT jobs. However for more involved applications, using pre-compiled libraries may be a better choice.

So, you have written a program using the ROOT API. You've been compiling it locally with gcc and now you want to run this application on the Grid with Ganga. What do you have to do? This HowTo gives a simple example for how to do this in a generic way.

Converting a Compiled Standalone Execuable which uses ROOT libs to CINT

Step 1: Rename your analysis main function

The aim is to be able run your code both via a CINT script, and also via a stand alone exe. To do this (to prevent linking errors, and problems with CINT) rename your main function. In the examples below I call it a_main.

Analysis.cpp

#include "Rtypes.h"
#include <string>

Int_t a_main(int argc, char* argv[]){

  //defaults
  Int_t nFits = 1;
  Int_t seed = 666;

  //rubbishy gnuopts
  for(Int_t i = 1; i < argc; ++i){
    //n:f:
    std::string argStr(argv[i]);
    if(argStr == "-f"){
      nFits = atoi(argv[++i]);
    }else if(argStr == "-s"){
      seed = atoi(argv[++i]);
    }
  }
}

Step 2: CINT Compatible Classes

CINT supports the loading of shared objects via the command gSystem->Load("libFoo"), where libFoo.so exists on the LD_LIBRARY_PATH. However, to use a class from a library in CINT, it must have a dictionary of methods available, and must inherit from TObject. A tutorial on this can be found here.

You can write a single CINT compatible wrapper class that you can call. All other classes in your project do not have to be CINT compatible. Here is an example class for this perpose. It is intended as a wrapper around the main function of a stand alone exe. Note the file extensions used here (.cpp) will be important for the Makefile example given later. Notice also that in the run method, a_main is called.

Main.h

#ifndef MAIN_H
#define MAIN_H

#include "TObject.h"
#include "TList.h"

#include <memory>

extern int a_main(int, char**);

class Main : public TObject {

 public:

  Main(){}//needed by Root IO
  Main(TList* args);
  int run();

private:

  std::auto_ptr<TList> args;

  ClassDef(Main,1)//Needed for Cint

};
#endif

Main.cpp

#include <iostream>
using std::cout;
using std::endl;

#include "Main.h"
#include "TList.h"
#include "TObject.h"
#include "TObjString.h"
#include "TString.h"

#include <cstring>

ClassImp(Main)//needed for Cint

Main::Main(TList* _args):
    args(_args)
{
}

int Main::run(){

  //convert the TList args into a main compatible list
  const Int_t argc = args->GetSize();
  //memory is dynamically allocated
  char** argv = new char*[argc];

  TObjLink *lnk = args->FirstLink();
  Int_t count = 0;
  while (lnk) {
    const char* str = ((TObjString*)lnk->GetObject())->String();
    argv[count] = new char[strlen(str)];
    strcpy(argv[count],str);
    lnk = lnk->Next();
    count++;
  }
  Int_t result = a_main(argc, argv);

  //clean up
  for(int i = 0; i < argc; i++){
    delete argv[i];
  }
  delete argv;

  return result;
}

Step 3: Provide an actual main for using in stand alone mode.

main.cpp

extern int a_main(int,char**);

int main(int argc, char** argv)
{
  return a_main(argc,argv);
}

Step 4: Build using a Makefile

The following assumes that the environment variable ROOTSYS ahs been set correctly.

Makefile

#compiler settings
CXX           = g++
CXXFLAGS      = -O -Wall -fPIC -g -ansi
LD            = g++
LDFLAGS       = -O
SOFLAGS       = -shared

all: $(TARGET)

#find out about the installed ROOT
ROOTCONFIG   := $(ROOTSYS)/bin/root-config
ROOTCFLAGS   := $(shell $(ROOTCONFIG) --cflags)
ROOTLDFLAGS  := $(shell $(ROOTCONFIG) --ldflags) $(shell $(ROOTCONFIG) --libs)

#append to options
CXXFLAGS     += $(ROOTCFLAGS)
LDFLAGS      += $(ROOTLDFLAGS)

LIBS = -lRooFit

#source files
ACLICKHEADERS = Main.h
ACLICKSOURCE = Main.cpp
ACLICKOBJS = rootdict.o Main.o

HEADERS =
SOURCE = Analysis.cpp
SOURCE += $(ACLICKSOURCE)
OBJS  = Analysis.o
OBJS += $(ACLICKOBJS)

EXE = foo_exe
SHAREDLIB = libFoo.so

all: $(EXE)

$(EXE): $(HEADERS) $(OBJS) main.o
        $(LD) $(LDFLAGS) $(LIBS) $(OBJS) main.o -o $(EXE)

$(SHAREDLIB):   $(HEADERS) $(OBJS)
        $(LD) $(LDFLAGS) $(LIBS) $(OBJS) -$(SOFLAGS) -o $(SHAREDLIB)

rootdict.cpp:  $(ACLICKHEADERS)
        @echo "Making the rootdict"
        @$(ROOTSYS)/bin/rootcint -f rootdict.cpp -c $(ACLICKHEADERS)

.PHONY clean:
clean:
        @rm -f $(OBJS) rootdict.cpp main.o $(EXE) $(SHAREDLIB)

Step 5: Run the program

make will build the executable directly (foo_exe+ in this case). make libFoo.so will build the shared object. The following CINT script allows this to be run:

runMain.C

void runMain(char* arg1 = "", char* arg2 = "", char* arg3 = "",
             char* arg4 = "", char* arg5 = "", char* arg6 = "",
             char* arg7 = "", char* arg8 = "", char* arg9 = "",
             char* arg10 = "", char* arg11 = "", char* arg12 = "",
             char* arg13 = "")
{
  //load the shared library
  gSystem->Load("libFoo");

  //set up main, eg command line opts
  TList* args = new TList();
  args->Add(new TObjString("foo_exe"));//program name
  args->Add(new TObjString(arg1));
  args->Add(new TObjString(arg2));
  args->Add(new TObjString(arg3));
  args->Add(new TObjString(arg4));
  args->Add(new TObjString(arg5));
  args->Add(new TObjString(arg6));
  args->Add(new TObjString(arg7));
  args->Add(new TObjString(arg8));
  args->Add(new TObjString(arg9));
  args->Add(new TObjString(arg10));
  args->Add(new TObjString(arg11));
  args->Add(new TObjString(arg12));
  args->Add(new TObjString(arg13));


  //run the code
  Main m(args);
  int returnCode = m.run();
  // Prevent ROOT from hanging (encountered with ROOT 5.26.00.b)
  gApplication->Terminate();
}

Final words

Don't forget to include the shared object you have built in your inputsandbox, and set the version of root correctly in the Root application. There is plenty of documentation for the Root application. See help(Root) in ganga for lots of information.
I've attached the files shown in this tutorial to this page.

-- LoicEsteve - 10-Nov-2010 -- WillReece - 01 Feb 2008

Topic attachments
I Attachment History Action Size Date Who Comment
Texttxt example_root_job.py.txt r1 manage 0.9 K 2008-09-09 - 14:00 WillReece Example Ganga Job for Submitting a Root Job
Unknown file formatgz ganga_cint_example.tar.gz r1 manage 1.7 K 2010-11-10 - 12:29 LoicEsteve Copies of all files shown in the tutorial
Edit | Attach | Watch | Print version | History: r3 < r2 < r1 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r3 - 2010-11-10 - LoicEsteve
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    ArdaGrid All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2021 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback