4 /// @file run-single-task.C
5 /// @author Matthias.Richter@ift.uib.no
7 /// @brief Run a single task
9 /// Helper macro to run a single task either locally or on Grid
11 /// aliroot -b -q -l run-single-task.C'("mode", "run", "task", "name", useMC, events, "path", "pattern", "friendPattern", "outputDir", "user")'
13 /// mode: local, full, test
14 /// run: list of run numbers. Or if using AODs with predefined list/AODs in same folder, specifiy as "AOD"
15 /// task: class name of task
17 /// optional arguments
18 /// name: analysis name (default 'myanalysis')
19 /// events: number of events to be processed (default -1 -> all)
20 /// path: data search path for grid analysis (default from configuration file)
21 /// pattern: data search pattern (default from configuration file)
24 /// aliroot -b -q -l run-single-task.C'("full", "146860", "AliAnalysisTaskSample", "myanalysis_LHC11a")'
26 /// aliroot -b -q -l run-single-task.C'("local", "$ALICE_ROOT/test/ppbench/AliESDs.root", "AliAnalysisTaskSample")'
28 /// aliroot -b -q -l run-single-task.C'("local", "AOD", "AddTaskSample.C"")'
30 /// aliroot -b -q -l run-single-task.C'("full", "146860", "AliAnalysisTaskSample", "correlation3p_LHC11a", -1, "/alice/data/2011/LHC11a", "*/pass2_without_SDD/AOD*/*/AliAOD.root")'
33 /// depending on the format of the search pattern either the ESD or AOD input handler is used.
36 /// If the task and classes used by the task are not in an AliRoot library available, e.g.
37 /// for the purpose of development, all header and source files need to be in the local
38 /// directory. The macro searches automatically for dependencies, compiles those and
39 /// copies files to the Grid working directory. In order to make the files accessible in
40 /// the local directory, the files can be just linked.
42 /// for f in <search path>; do ln -s $f; done
46 /// requires only the path to the input file and the task class name. If the specified file is
47 /// a text file (.txt) each line can contain an input file path, all files are chained.
48 /// Note: AOD mode needs to be implemented
51 /// All modes provided by the AliAnalysisAlien plugin can be used, e.g. full, test, offline
52 /// A couple of settings need to be defined in a configuration file 'grid-config.C' which can be
53 /// either in the local directory or home directory.
55 /// const char* alienAPIVersion="V1.1x";
56 /// const char* alienROOTVersion="v5-33-02a";
57 /// const char* alienAliROOTVersion="v5-01-Rev-29";
58 /// const char* defaultGridDataDir="/alice/data/2011/LHC11a";
59 /// const char* defaultDataPattern="*/pass2_without_SDD/*/AliESDs.root";
60 /// {} // note this empty body
62 /// Data path and pattern can also be specified as command line arguments.
63 /// The working directory in the grid home directory of the user is set to
64 /// gridwork/<date>_<time>.
68 ///////////////////////////////////////////////////////////////////////////////////////////////////
73 const char* defaultAnalysisName="myanalysis";
74 const char* includePath="-I. -I$ROOTSYS/include -I$ALICE_ROOT/include";
75 const char* libraryDependencies=
80 "libANALYSISalice.so "
83 TString GetIncludeHeaders(const char* filename, TString& headers, TString& libs, bool loadClass=true);
84 void ErrorConfigurationFile(const char* fileName);
86 void run_single_task(const char* mode,
88 const char* tasknames,
89 const char* analysisName=defaultAnalysisName,
92 const char* gridDataDir=NULL,
93 const char* dataPattern=NULL,
94 const char* friendDataPattern=NULL,
99 ///////////////////////////////////////////////////////////////////////////////////////////////////
100 ///////////////////////////////////////////////////////////////////////////////////////////////////
101 ///////////////////////////////////////////////////////////////////////////////////////////////////
105 if (analysisName==defaultAnalysisName && gDirectory!=NULL) {
106 // NOTE: the direct pointer comparison is on purpose
107 // string comparison not necessary in this special case
109 // fetch the analysis name from the setup file
110 const char* confObjectName="analysis_name";
111 TObject* confObject=gDirectory->FindObject(confObjectName);
113 analysisName=confObject->GetTitle();
117 bool bRunLocal=strcmp(mode, "local")==0;
118 const char* gridConfigFile="grid-config.C";
119 TString strGridConfigFile=gridConfigFile;
120 if (gSystem->AccessPathName(strGridConfigFile)!=0) {
121 strGridConfigFile.Prepend("/");
122 strGridConfigFile.Prepend(gSystem->Getenv("HOME"));
123 if (gSystem->AccessPathName(strGridConfigFile)!=0) {
125 ErrorConfigurationFile(gridConfigFile);
128 strGridConfigFile="";
132 if (strGridConfigFile.IsNull()==0 && !bRunLocal) {
133 cout << "loading grid configuration from file '" << strGridConfigFile << "':" << endl;
134 gROOT->LoadMacro(strGridConfigFile);
135 cout << " alienAPIVersion =" << alienAPIVersion << endl;
136 cout << " alienROOTVersion =" << alienROOTVersion << endl;
137 cout << " alienAliROOTVersion =" << alienAliROOTVersion << endl;
138 cout << " defaultGridDataDir =" << defaultGridDataDir << endl;
139 cout << " defaultDataPattern =" << defaultDataPattern << endl;
140 cout << " defaultFriendDataPattern =" << defaultFriendDataPattern << endl;
142 if (gridDataDir==NULL) gridDataDir=defaultGridDataDir;
143 if (dataPattern==NULL) dataPattern=defaultDataPattern;
144 if (friendDataPattern==NULL) friendDataPattern=defaultFriendDataPattern;
145 } else if (bRunLocal) {
146 if (dataPattern==NULL) {
148 if (strin.EndsWith("AOD"))
150 else if (strin.EndsWith("ESD"))
157 TGrid::Connect("alien://");
159 ///////////////////////////////////////////////////////////////////////////////////////////////////
160 ///////////////////////////////////////////////////////////////////////////////////////////////////
161 ///////////////////////////////////////////////////////////////////////////////////////////////////
163 // make the analysis manager
165 AliAnalysisManager *pManager = new AliAnalysisManager("AnalysisManager");
167 cerr << "failed to created AnalysisManager" << endl;
170 AliInputEventHandler *pInputHandler = NULL;
171 TString strDataPattern(dataPattern);
172 if (strDataPattern.Contains("AOD")) pInputHandler=new AliAODInputHandler;
173 else if (strDataPattern.Contains("ESD")) pInputHandler=new AliESDInputHandler;
175 cerr << "can not determine input type from data pattern '" << dataPattern << "'" << endl;
178 if (!pInputHandler) {
179 cerr << "failed to created input handler" << endl;
182 //pInputHandler->SetReadFriends(kFALSE);
183 pManager->SetInputEventHandler(pInputHandler);
184 pManager->SetNSysInfo(1000);
186 TString ofile=Form("%s.root", analysisName);
188 ///////////////////////////////////////////////////////////////////////////////////////////////////
189 ///////////////////////////////////////////////////////////////////////////////////////////////////
190 ///////////////////////////////////////////////////////////////////////////////////////////////////
192 // load task classes and find and load all dependencies
194 gSystem->AddIncludePath(includePath);
195 TString libraries=libraryDependencies;
196 if (gDirectory!=NULL) {
197 // fetch the analysis libraries from the setup file
198 const char* confObjectName="analysis_libraries";
199 TObject* confObject=gDirectory->FindObject(confObjectName);
201 TString analysisLibraries(confObject->GetTitle());
202 TObjArray* pTokens=analysisLibraries.Tokenize(" ");
204 for (int i=0; i<pTokens->GetEntriesFast(); i++) {
205 if (libraries.Contains(pTokens->At(i)->GetName())==0) {
207 libraries+=pTokens->At(i)->GetName();
214 TObjArray* pTokens=libraries.Tokenize(" ");
216 for (int i=0; i<pTokens->GetEntriesFast(); i++) {
217 if (gSystem->Load(pTokens->At(i)->GetName())==0) {
218 cout << "loading " << pTokens->At(i)->GetName() << endl;
224 TString taskNames=tasknames;
225 TString taskClasses="";
226 TString taskSources="";
227 TString taskHeaders="";
228 TString addTaskMacros="";
229 TString parPackages="";
230 TObjArray* pTaskNames=taskNames.Tokenize(" ");
232 for (int iTaskName=0; iTaskName<pTaskNames->GetEntriesFast(); iTaskName++) {
233 TString taskSource=pTaskNames->At(iTaskName)->GetName();
234 TString taskHeader=pTaskNames->At(iTaskName)->GetName();
235 bool bIsAddTask=false;
236 if (taskSource.EndsWith(".C")) {
237 // suppose that's an 'AddTask' macro
240 } else if (taskSource.EndsWith(".par")) {
242 if (gSystem->AccessPathName(taskSource)!=0) {
243 ::Error("run_single_task", Form("par file '%s' not found in current directory, you might want to set a symbolic link", taskSource.Data()));
247 parPackages+=taskSource;
249 } else if (taskSource.EndsWith(".h")) {
250 taskSource.ReplaceAll(".h", "");
252 taskClasses+=taskSource;
254 } else if (taskSource.EndsWith(".cxx")) {
255 taskHeader.ReplaceAll(".cxx", "");
257 taskClasses+=taskHeader;
261 taskClasses+=taskSource;
265 TString dependencyHeader;
266 TString dependencySource;
267 if (gSystem->AccessPathName(taskHeader)==0) {
268 GetIncludeHeaders(taskHeader, dependencyHeader, libraries);
269 taskHeaders+=" "; taskHeaders+=taskHeader;
271 if (gSystem->AccessPathName(taskSource)==0) {
272 GetIncludeHeaders(taskSource, dependencyHeader, libraries);
273 if (!bIsAddTask) {taskSources+=" "; taskSources+=taskSource;}
274 else {addTaskMacros+=" "; addTaskMacros+=taskSource;}
276 TObjArray* pTokens=dependencyHeader.Tokenize(" ");
278 for (int i=0; i<pTokens->GetEntriesFast(); i++) {
279 TString sourceFile=pTokens->At(i)->GetName();
280 sourceFile.ReplaceAll(".h", ".cxx");
281 if (gSystem->AccessPathName(sourceFile)!=0) continue;
282 if (!dependencySource.IsNull()) dependencySource+=" ";
283 dependencySource+=sourceFile;
284 if (!libraries.IsNull()) libraries+=" ";
285 libraries+=sourceFile;
289 dependencySource.ReplaceAll(taskSource, "");
290 dependencyHeader.ReplaceAll(taskHeader, "");
294 cout << "Tasks: " << taskClasses << endl;
295 cout << "Task files: " << taskSources << addTaskMacros << taskHeaders << endl;
296 cout << "Dependency classes: " << dependencySource << endl;
297 cout << "Dependency headers: " << dependencyHeader << endl;
298 cout << "Dependency libraries: " << libraries << endl;
299 cout << "Packages: " << parPackages << endl;
301 ///////////////////////////////////////////////////////////////////////////////////////////////////
302 ///////////////////////////////////////////////////////////////////////////////////////////////////
303 ///////////////////////////////////////////////////////////////////////////////////////////////////
305 // init for local or GRID analysis
307 AliAnalysisAlien *alienHandler = NULL; // for grid analysis
308 TChain *chain=NULL; // for local analysis
309 TString strInput=input;
311 ///////////////////////////////////////////////////////////////////////////////////////////////////
315 if(strInput.EndsWith("AliESDs.root")){
316 // suppose it's a single ESD file
317 chain = new TChain("esdTree");
318 chain->Add(strInput);
319 } else if(strInput.EndsWith(".txt")) {
320 // Constructs chain from filenames in *.txt
321 // in the form $DIR/AliESDs.root
322 gROOT->LoadMacro("$ALICE_ROOT/PWG0/CreateESDChain.C");
323 // chain can contain up to 200 files, value can be modified to
324 // include a subset of what the *txt file contains
325 chain = CreateESDChainf(strInput.Data(),200);
327 // check if the files are on grid
328 TIter next(chain->GetListOfFiles());
329 TChainElement *chEl = 0;
330 while(( chEl = (TChainElement*)next() )){
331 TString tmp = chEl->GetTitle();
332 if(tmp.BeginsWith("alien://")) {
333 TGrid::Connect("alien://");
337 } else if(strInput.EndsWith("AOD")){
338 // fetch aod tree from the setup macro
339 if (gDirectory!=NULL) {
340 TObject* chainObject=gDirectory->FindObject("aodTree");
342 chain=dynamic_cast<TChain*>(chainObject);
346 cout << "failed to fetch aod tree object from setup" << endl;
350 cerr << "invalid input" << endl;
354 ///////////////////////////////////////////////////////////////////////////////////////////////////
358 alienHandler=new AliAnalysisAlien();
360 cerr << "failed to create alien handler" << endl;
364 // do not check for copying to grid (CLOSE_SE)
365 alienHandler->SetCheckCopy(kFALSE);
367 // Set the run mode (can be "full", "test", "offline", "submit" or "terminate")
368 alienHandler->SetRunMode(mode);
370 // check the versions available on alien with the command 'packages'
371 alienHandler->SetAPIVersion(alienAPIVersion);
372 alienHandler->SetROOTVersion(alienROOTVersion);
373 alienHandler->SetAliROOTVersion(alienAliROOTVersion);
375 //Allow non-default outputs
376 //This is required to set non-default output files with the SetOutputFiles
378 alienHandler->SetDefaultOutputs(kFALSE);
379 if (user && user[0]!=0) alienHandler->SetUser(user);
381 // data alien directory
382 alienHandler->SetGridDataDir(gridDataDir);
384 // Set data search pattern
385 alienHandler->SetDataPattern(dataPattern);
386 alienHandler->SetFriendChainName(friendDataPattern);
388 TObjArray* packageTokens=parPackages.Tokenize(" " );
390 for (int iPackageToken=0; iPackageToken<packageTokens->GetEntriesFast(); iPackageToken++) {
391 alienHandler->EnablePackage(packageTokens->At(iPackageToken)->GetName());
393 delete packageTokens;
397 alienHandler->SetRunPrefix("000"); // real data
399 alienHandler->AddRunNumber(input);
401 // define working and output directories
404 odir=(Form("gridwork/%04d-%02d-%02d_%02d-%02d", dt.GetYear(), dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute()));
405 cout << odir << endl;
406 alienHandler->SetGridWorkingDir(odir); // relative to $HOME
407 alienHandler->SetGridOutputDir("output"); // relative to working dir
408 //alienHandler->SetOverwriteMode(); // overwrites the contents of the working and output directory
410 // workaround for a Root feature: GetIncludePath() appends always
411 // the current Root include path including escaped quotes. Those
412 // quotes make it difficult to pass the output directly. Search for the
413 // last appended include path and truncate
414 TString strIncludePath(gSystem->GetIncludePath());
415 Int_t pos=strIncludePath.Index(includePath);
419 cut=pos+strlen(includePath);
420 } while ((pos=strIncludePath.Index(includePath, cut))>cut);
421 strIncludePath.Resize(cut);
423 alienHandler->AddIncludePath(strIncludePath);
425 // Note: there is no extra source or header file to be transferred if 'AddTask' macros are used
426 alienHandler->SetAnalysisSource(Form("%s %s %s %s", dependencySource.Data(), dependencyHeader.Data(), taskSources.Data(), taskHeaders.Data()));
427 alienHandler->SetAdditionalLibs(Form("%s %s %s", libraries.Data(), taskHeaders.Data(), dependencyHeader.Data()));
429 alienHandler->SetOutputFiles(ofile);
431 // Optionally define the files to be archived.
433 //alienHandler->SetOutputArchive("log_archive.zip:stdout,stderr");
435 // Optionally set a name for the generated analysis macro (default MyAnalysis.C)
436 TString macroName; macroName.Form("run_%s.C",analysisName); macroName.ReplaceAll("-","_");
437 alienHandler->SetAnalysisMacro(macroName);
439 //alienHandler->SetExecutable("comparison.sh");
440 alienHandler->SetExecutable(Form("run_%s.sh",analysisName));
442 alienHandler->SetSplitMaxInputFileNumber(100);
444 // Optionally set number of failed jobs that will trigger killing waiting sub-jobs.
445 alienHandler->SetMaxInitFailed(10);
447 // Optionally resubmit threshold.
448 alienHandler->SetMasterResubmitThreshold(90); // in %
450 alienHandler->SetTTL(30000);// in sec
452 // Optionally set input format (default xml-single)
453 alienHandler->SetInputFormat("xml-single");
455 // Optionally modify the name of the generated JDL (default analysis.jdl)
456 alienHandler->SetJDLName(Form("run_%s.jdl",analysisName));
458 // Optionally modify job price (default 1)
459 alienHandler->SetPrice(1);
461 // Optionally modify split mode (default 'se')
462 alienHandler->SetSplitMode("se");
464 // comment out the next line when using the "terminate" option, unless
465 // you want separate merged files for each run
466 if (strcmp(mode, "terminate")==0) {
467 alienHandler->SetMergeViaJDL(kFALSE);
470 alienHandler->SetOneStageMerging(kFALSE);
471 alienHandler->SetMaxMergeStages(2);
474 // Connect plugin to the analysis manager
476 pManager->SetGridHandler(alienHandler);
479 ///////////////////////////////////////////////////////////////////////////////////////////////////
480 ///////////////////////////////////////////////////////////////////////////////////////////////////
481 ///////////////////////////////////////////////////////////////////////////////////////////////////
483 // create task from the name, create output container, connect slots
485 TObjArray* taskClassTokens=taskClasses.Tokenize(" ");
486 if (taskClassTokens) {
487 for (int iTaskClassToken=0; iTaskClassToken<taskClassTokens->GetEntriesFast(); iTaskClassToken++) {
488 AliAnalysisTaskSE *pTask=NULL;
489 TString taskName=taskClassTokens->At(iTaskClassToken)->GetName();
490 taskName.ReplaceAll(".cxx", "");
491 TClass* pCl=TClass::GetClass(taskName);
493 cerr << "can not load class " << taskName << endl;
496 TObject* p=pCl->New();
498 cerr << "failed to instantiate class " << taskName << endl;
501 pTask=reinterpret_cast<AliAnalysisTaskSE*>(p);
502 pManager->AddTask(pTask);
503 AliAnalysisDataContainer *pContainer=pManager->CreateContainer(analysisName ,TObject::Class(), AliAnalysisManager::kOutputContainer, ofile);
504 pManager->ConnectInput(pTask,0,pManager->GetCommonInputContainer());
505 pManager->ConnectOutput(pTask,1,pContainer);
507 delete taskClassTokens;
509 TObjArray* taskMacroTokens=addTaskMacros.Tokenize(" ");
510 if (taskMacroTokens) {
511 for (int iTaskMacroToken=0; iTaskMacroToken<taskMacroTokens->GetEntriesFast(); iTaskMacroToken++) {
513 TString configuration;
514 configuration.Form("name=%s file=%s %s", analysisName, ofile.Data(), useMC?"mc":"");
515 if (gDirectory) gDirectory->Add(new TNamed("run_single_task_configuration", configuration.Data()));
516 gROOT->Macro(taskMacroTokens->At(iTaskMacroToken)->GetName());
518 delete taskMacroTokens;
521 ///////////////////////////////////////////////////////////////////////////////////////////////////
522 ///////////////////////////////////////////////////////////////////////////////////////////////////
523 ///////////////////////////////////////////////////////////////////////////////////////////////////
528 if (!pManager->InitAnalysis()) {
529 cerr << "failed to initialize analysis" << endl;
532 if (nevents<0) nevents=1000000000;
533 pManager->PrintStatus();
535 pManager->StartAnalysis("local", chain, nevents);
537 pManager->StartAnalysis("grid", nevents);
541 TString GetIncludeHeaders(const char* filename, TString& headers, TString& libs, bool loadClass)
543 // scan the file and add all include headers found by path
544 // to the parameter headers
545 ifstream input(filename);
547 cerr << "failed to open file " << filename << endl;
551 while (!line.ReadLine(input).eof()) {
552 if (!line.Contains("#include") || !line.Contains(".h")) continue;
553 line=line(0, line.Index(".h"));line+=".h";
554 line.Replace(0, line.Index("#include"), "");
555 line.ReplaceAll("#include", "");
556 line.ReplaceAll(" ", "");
557 line.ReplaceAll("\"", "");
558 if (!line.BeginsWith("Ali") && !line.BeginsWith("T")) continue;
559 if (gSystem->AccessPathName(line)!=0) {
560 // not an include file in the current directory, check if class
561 // is available or find library
562 line.ReplaceAll(".h","");
563 //cout << "checking class " << line << endl;
564 if (TClass::GetClass(line)==NULL) {
566 TString resfilename(gSystem->TempDirectory()); resfilename+="/findlib.txt";
567 command.Form("for lib in $ALICE_ROOT/lib/*/lib*.so; do (nm $lib | grep %s | grep ' T ' | grep Class_Name > /dev/null) && echo $lib > %s; done", line.Data(), resfilename.Data());
568 gSystem->Exec(command);
569 ifstream resfile(resfilename.Data());
570 if (resfile.good()) {
572 if (!result.ReadLine(resfile).eof()) {
574 while ((haveSlash=result.First('/'))>=0) result.Replace(0, haveSlash+1, "");
575 if (!libs.Contains(result)) {
576 cout << "loading dependency library '" << result << "' for class '" << line << "'" << endl;
577 gSystem->Load(result);
578 if (!libs.IsNull()) libs+=" ";
582 command="rm "; command+=resfilename;
583 gSystem->Exec(command);
587 if (headers.Contains(line)) {
588 if (!headers.BeginsWith(line)) {
589 headers.ReplaceAll(line, "");
590 if (!headers.IsNull()) headers.Insert(0, " ");
591 if (macroVerbosity>0) cout << "moving " << line << endl;
592 headers.Insert(0, line);
596 if (!headers.IsNull()) headers.Insert(0, " ");
597 if (macroVerbosity>0) cout << "inserting " << line << endl;
598 headers.Insert(0, line);
599 TString source=line; source.ReplaceAll(".h", ".cxx");
600 if (gSystem->AccessPathName(source)==0) {
601 GetIncludeHeaders(source, headers, libs);
603 GetIncludeHeaders(line, headers, libs);
604 if (loadClass && gSystem->AccessPathName(source)==0) {
605 line.ReplaceAll(".h", "");
606 if (TClass::GetClass(line)==NULL) {
608 gROOT->LoadMacro(source);
617 void ErrorConfigurationFile(const char* fileName) {
619 cout << "/// -------------------------------------------------------------------" << endl;
620 cout << "/// Warning: can not find configuration file '" << fileName << "'" << endl;
621 cout << "/// please create a configuration file in either local or HOME directory, or in" << endl;
622 cout << "/// specified location. Below is an example, fill in your preferred defaults." << endl;
623 cout << "/// -------------------------------------------------------------------" << endl;
625 cout << "const char* alienAPIVersion=\"V1.1x\";" << endl;
626 cout << "const char* alienROOTVersion=\"v5-33-02a\";" << endl;
627 cout << "const char* alienAliROOTVersion=\"v5-01-Rev-29\";" << endl;
628 cout << "const char* defaultGridDataDir=\"/alice/data/2011/LHC11f\";" << endl;
629 cout << "const char* defaultDataPattern=\"*ESDs.root\";" << endl;
630 cout << "const char* defaultFriendDataPattern=\"\";" << endl;
631 cout << "{} // note this empty body";