Changed scripts for new TrainSetup
[u/mrichter/AliRoot.git] / PWGLF / FORWARD / trains / TrainSetup.C
1 /**
2  * @defgroup pwglf_forward_trains Trains
3  * 
4  * Train specifications 
5  *
6  * @ingroup pwglf_forward
7  */
8 /**
9  * @file   TrainSetup.C
10  * @author Christian Holm Christensen <cholm@master.hehi.nbi.dk>
11  * @date   Tue Oct 16 17:56:57 2012
12  * 
13  * @brief  Base classs for train specifications 
14  * 
15  * @ingroup pwglf_forward_trains
16  */
17 #ifndef TRAINSETUP_C
18 #define TRAINSETUP_C
19 #ifndef __CINT__
20 # include "Helper.C"
21 # include "Option.C"
22 # include <TDatime.h>
23 # include <TUrl.h>
24 # include <TString.h>
25 # include <TApplication.h>
26 # include <TStopwatch.h>
27 # include <AliAnalysisManager.h>
28 # include <AliVEventHandler.h>
29 # include <AliPhysicsSelection.h>
30 # include <AliPhysicsSelectionTask.h>
31 # include <AliCentralitySelectionTask.h>
32 # include <AliESDInputHandler.h>
33 # include <AliAODInputHandler.h>
34 # include <AliAODHandler.h>
35 # include <AliMCEventHandler.h>
36 # include <ctime>
37 #else 
38 struct Helper;
39 struct OptionList;
40 class TDatime;
41 class TUrl;
42 class TString;
43 class TStopwatch;
44 class AliVEventHandler;
45 class AliAnalysisManager;
46 class AliInputEventHandler;
47 #endif
48
49 //====================================================================
50 /** 
51  * Generic set-up of an analysis train 
52  *
53  * See also @ref train_setup_doc
54  *
55  * @ingroup pwglf_forward_trains
56  * 
57  */
58 struct TrainSetup
59 {
60   /** 
61    * Constructor 
62    * 
63    * @param name Name of the train
64    */
65   TrainSetup(const TString& name)
66     : fName(name), 
67       fEscapedName(name),
68       fOptions(),
69       fHelper(0)
70   {
71     fOptions.Add("help", "Show help");
72     fOptions.Add("date", "YYYY-MM-DD HH:MM", "Set date", "now");
73     fOptions.Add("bare-ps", "Use bare physics selection w/o task");
74     fOptions.Add("verbose", "LEVEL", "Set verbosity level", 0);
75     fOptions.Add("url", "URL", "Job location & input URL");
76     fOptions.Add("overwrite", "Allow overwrite");
77     fOptions.Add("events", "N", "Number of events to analyse", -1);
78     fOptions.Add("type", "ESD|AOD|USER", "Input data stype");
79     fOptions.Add("setup", "Only do the setup");
80     fOptions.Add("branches", "Load only requested branches");
81     fEscapedName = EscapeName(fName, "");
82   }
83   TrainSetup(const TrainSetup& o) 
84     : fName(o.fName), 
85       fEscapedName(o.fEscapedName), 
86       fOptions(o.fOptions), 
87       fHelper(o.fHelper)
88   {}
89   TrainSetup& operator=(const TrainSetup& o) 
90   {
91     if (&o == this) return *this;
92     fName        = o.fName;
93     fEscapedName = o.fEscapedName;
94     fOptions     = o.fOptions;
95     fHelper      = o.fHelper;
96     return *this;
97   }
98   
99   /** 
100    * Destructor
101    */
102   virtual ~TrainSetup() {}
103   /* @} */
104   //__________________________________________________________________
105   /** 
106    * @{ 
107    * @name Execution 
108    */
109   /** 
110    * Initialize 
111    * 
112    * @return true on success 
113    */
114   Bool_t Init()
115   {
116     // --- Create the helper -----------------------------------------
117     TString  url     = fOptions.Get("url");
118     Int_t    verbose = fOptions.AsInt("verbose");
119
120     fHelper = Helper::Create(url.Data(), verbose);
121     if (!fHelper) { 
122       Error("Init", "Failed to make the worker for URL %s", url.Data());
123       return false;
124     }
125
126     // --- Check the type, if possible -------------------------------
127     UShort_t type    = fHelper->InputType();
128     Bool_t   mc      = fHelper->IsMC();
129     if (fOptions.Has("type")) { 
130       const TString& it = fOptions.Get("type");
131       if      (it.EqualTo("ESD",TString::kIgnoreCase)) type = Helper::kESD;
132       else if (it.EqualTo("AOD",TString::kIgnoreCase)) type = Helper::kAOD;
133       else if (it.EqualTo("user",TString::kIgnoreCase)) 
134         type = Helper::kUser;
135     }
136
137     // --- Rewrite the escpaed name ----------------------------------
138     if (fOptions.Has("date")) { 
139       TString date = fOptions.Get("date");
140       fEscapedName = EscapeName(fName, date);
141     }
142     
143     // --- Get current directory and set-up sub-directory ------------
144     TString cwd = gSystem->WorkingDirectory();
145     if (!SetupWorkingDirectory()) return false;
146
147     // --- Do initial helper setup -----------------------------------
148     if (!fHelper->PreSetup()) return false;
149
150     // --- Load ROOT libraries ---------------------------------------
151     if (!fHelper->LoadROOT()) return false;
152     
153     // --- Load AliROOT libraries ------------------------------------
154     if (!fHelper->LoadAliROOT()) return false;
155
156     // --- Create analysis manager -----------------------------------
157     AliAnalysisManager *mgr  = CreateAnalysisManager(fEscapedName);
158
159     // In test mode, collect system information on every event 
160     // if (oper == kTest)  mgr->SetNSysInfo(1); 
161     if (verbose  >  0)      mgr->SetDebugLevel(verbose);
162     mgr->SetAutoBranchLoading(!fOptions.Has("branches"));
163     if (fHelper->Mode() == Helper::kLocal) 
164       mgr->SetUseProgressBar(kTRUE, 100);
165    
166     // --- ESD input handler ------------------------------------------
167     AliVEventHandler*  inputHandler = CreateInputHandler(type);
168     if (inputHandler) mgr->SetInputEventHandler(inputHandler);
169     
170     // --- Monte-Carlo ------------------------------------------------
171     AliVEventHandler*  mcHandler = CreateMCHandler(type,mc);
172     if (mcHandler) mgr->SetMCtruthEventHandler(mcHandler);
173     
174     // --- AOD output handler -----------------------------------------
175     AliVEventHandler*  outputHandler = CreateOutputHandler(type);
176     if (outputHandler) mgr->SetOutputEventHandler(outputHandler);
177
178     // --- Include analysis macro path in search path ----------------
179     gROOT->SetMacroPath(Form("%s:%s:$ALICE_ROOT/ANALYSIS/macros",
180                              cwd.Data(), gROOT->GetMacroPath()));
181
182     // --- Physics selction - only for ESD ---------------------------
183     if (type == Helper::kESD) CreatePhysicsSelection(mc, mgr);
184     
185     // --- Create centrality task ------------------------------------
186     CreateCentralitySelection(mc, mgr);
187
188     // --- Create tasks ----------------------------------------------
189     CreateTasks(mgr);
190
191     // --- Post set-up initialization of helper ----------------------
192     if (!fHelper->PostSetup()) return false;
193
194     // --- Set debug level on defined tasks --------------------------
195     if (verbose > 0) {
196       TIter next(mgr->GetTasks());
197       AliAnalysisTask* sub = 0;
198       while ((sub = static_cast<AliAnalysisTask*>(next()))) { 
199         AliAnalysisTaskSE* se = dynamic_cast<AliAnalysisTaskSE*>(sub);
200         if (!se) continue;
201         se->SetDebugLevel(verbose);
202       }
203     }
204
205     // --- Print this setup ------------------------------------------
206     Print();
207
208     // --- Initialise the train --------------------------------------
209     if (!mgr->InitAnalysis())  {
210       gSystem->ChangeDirectory(cwd.Data());
211       Error("Init","Failed to initialise train");
212       return false;
213     }
214
215     // --- Enable progress bar ---------------------------------------
216     if (fHelper->Mode() != Helper::kGrid) 
217       mgr->SetUseProgressBar(true, 100);
218
219     // --- Save setup to disk ----------------------------------------
220     SaveSetup(true);
221
222     // --- Some information ------------------------------------------
223     mgr->PrintStatus();
224     if (fHelper->Mode() != Helper::kLocal) {
225       TIter next(mgr->GetTasks());
226       AliAnalysisTask* sub = 0;
227       while ((sub = static_cast<AliAnalysisTask*>(next()))) { 
228         sub->Print();
229       }
230     }
231     return true;
232   }
233   void PrintTimer(TStopwatch& timer, const char* where)
234   {
235     timer.Stop();
236     Double_t t = timer.RealTime();
237     Int_t    h = Int_t(t / 3600); t -= h * 3600;
238     Int_t    m = Int_t(t /   60); t -= m *   60;
239     if (t < 0) t = 0;
240     Info(where, "took %4d:%02d:%06.3f", h, m, t);
241   }
242     
243   Bool_t Run()
244   {
245     TString cwd = gSystem->WorkingDirectory();
246     Bool_t status = false;
247     TStopwatch timer;
248     timer.Start();
249     try {
250       if (!Init()) throw TString("Failed to intialize the train");
251       PrintTimer(timer, "Initialization");
252       timer.Continue();
253       
254       // Check if we're asked to only do the setup 
255       if (fOptions.Has("setup")) {
256         status = true;
257         throw TString("Only did setup, no running");
258       }
259
260       // if (r) SaveSetup(*r, nEvents, asShell);
261       
262       Long64_t nEvents = fOptions.AsLong("events", -1);
263       Long64_t ret     = fHelper->Run(nEvents);
264       PrintTimer(timer, "Processing");
265       timer.Continue();
266       
267       // Make sure we go back 
268       gSystem->ChangeDirectory(cwd.Data());
269       
270       // Return. 
271       if (ret < 0) throw TString("Analysis failed");
272
273       status = true;
274     }
275     catch (TString& e) {
276       if (status) Warning("Run", "%s", e.Data());
277       else        Error("Run", "%s", e.Data());
278     }
279     if (fOptions.Has("date")) {
280       TString escaped = EscapeName(fName, "");
281       gSystem->Exec(Form("rm -f last_%s", escaped.Data()));
282       gSystem->Exec(Form("ln -sf %s last_%s", 
283                          fEscapedName.Data(), escaped.Data()));
284     }
285     PrintTimer(timer, "Finish");
286     timer.Continue();
287     return status;
288   }
289   /** 
290    * Get the options 
291    * 
292    * 
293    * @return Reference ot the options 
294    */
295   OptionList& Options() { return fOptions; }
296   /** 
297    * Print information to standard output 
298    * 
299    */
300   void Print(Option_t* ="") const
301   {
302     std::cout << "Train: " << fName << " (" << fEscapedName << ")" 
303               << std::endl;
304     fOptions.Show(std::cout);
305     if (fHelper) fHelper->Print();
306   }
307   /** 
308    * Show the help 
309    * 
310    * @param o 
311    * 
312    * @return 
313    */
314   Bool_t Help(std::ostream& o=std::cout, bool asProg=false)
315   {
316     if (!fOptions.Has("help")) return true;
317
318     if (asProg) 
319       o << "Usage: runTrain2 --name=NAME --class=CLASS [OPTIONS]";
320     else 
321       o << "Usage: RunTrain(NAME, CLASS, OPTIONS)";
322     
323     o << "\n\nOptions:\n\n";
324     if (asProg) {
325       OptionList tmp(fOptions);
326       tmp.Add("name", "STRING", "Name of train", fName);
327       tmp.Add("class", "NAME", "Name of setup class", "");
328       tmp.Help(o,"  --");
329     }
330     else 
331       fOptions.Help(o, "  ");
332     
333     o << "\n";
334
335     if (!fHelper && fOptions.Has("url")) {
336       TString url = fOptions.Get("url");
337       fHelper = Helper::Create(url.Data());
338     }
339     if (fHelper) { 
340       o << fHelper->Desc() << " URL form:\n\n"
341         << "    " << fHelper->UrlHelp() << "\n\n"
342         << "Options:\n";
343       fHelper->Options().Help(o, "    ");
344       o << "\n";
345     }
346     else { 
347       o << "Possible URL forms:\n\n";
348       Helper::ShowUrlHelp("LocalHelper");
349       Helper::ShowUrlHelp("ProofHelper");
350       Helper::ShowUrlHelp("LiteHelper");
351       Helper::ShowUrlHelp("AAFHelper");
352       Helper::ShowUrlHelp("AAFPluginHelper");
353       Helper::ShowUrlHelp("GridHelper");
354       o << "\n";
355     }
356     return false;
357   }
358   /** 
359    * Run train.  This will AcLic compile the setup script, create
360    * an object of that type with the given name, and then pass the 
361    * options to it.  Then, it will run the setup.
362    * 
363    * @param name  Train name 
364    * @param cls   Class name 
365    * @param opts  Comma seperated list of options
366    * 
367    * @return true on success
368    */
369   static Bool_t Main(const TString& name, const TString& cls, 
370                      const TCollection* opts, 
371                      Bool_t asProg=true)
372   {
373     Bool_t ret = false;
374     try {
375       if (cls.IsNull()) 
376         throw TString("No class name specified");
377       if (name.IsNull()) 
378         throw TString("No train name specified");
379
380       Int_t error = 0;
381       Int_t r1 = gROOT->LoadMacro(Form("%s.C++g", cls.Data()), &error);
382       if (r1 < 0 || error) 
383         throw TString::Format("Failed to load setup %s: %d", cls.Data(), error);
384
385       // Make our object using the interpreter 
386       TString create = TString::Format("new %s(\"%s\")", 
387                                        cls.Data(), name.Data());
388       gROOT->ProcessLine("gSystem->RedirectOutput(\"/dev/null\",\"w\");");
389       Long_t retP = gROOT->ProcessLine(create, &error);
390       gROOT->ProcessLine("gSystem->RedirectOutput(0);");
391       if (!retP || error) 
392         throw TString::Format("Failed to make object of class %s: "
393                               "0x%08lx/%d\n\t%s", 
394                               cls.Data(), retP, error, create.Data());
395
396       TrainSetup* train = reinterpret_cast<TrainSetup*>(retP);
397     
398       // Now parse the options 
399       if (!train->Options().Parse(opts)) 
400         throw TString("Failed to parse options");
401
402       // Check if we got a help request
403       if (train->Options().Has("help")) { 
404         train->Help(std::cout, asProg);
405         ret = true;
406         throw TString("");
407       }
408
409       // return train->Init();
410       ret = train->Run();
411     }
412     catch (TString& e) { 
413       if (!e.IsNull()) Error("Main", "%s", e.Data());
414     }
415     if (gApplication && asProg) {
416       gSystem->Sleep(3);
417       gApplication->Terminate(ret ? 0 : 1);
418     }
419     return ret;
420   }
421 protected:
422   //__________________________________________________________________
423   /** 
424    * @{ 
425    * @Name Overloadable behaviour 
426    */
427   //------------------------------------------------------------------
428   /** 
429    * Create the analysis manager 
430    * 
431    * @param name Name of the analysis 
432    * 
433    * @return Created analysis manager 
434    */
435   virtual AliAnalysisManager* CreateAnalysisManager(const char* name)
436   {
437     return new AliAnalysisManager(name,"Analysis Train");
438   }
439   //------------------------------------------------------------------
440   /** 
441    * Create input handler 
442    * 
443    * @param type 
444    * 
445    * @return 
446    */
447   virtual AliVEventHandler* CreateInputHandler(UShort_t type)
448   {
449     switch (type) {
450     case Helper::kESD:  return new AliESDInputHandler(); 
451     case Helper::kAOD:  return new AliAODInputHandler(); 
452     case Helper::kUser: return 0;
453     }
454     return 0;
455   }
456   //------------------------------------------------------------------
457   /** 
458    * Create MC input handler 
459    * 
460    * @param type  Run type (ESD or AOD)
461    * @param mc    Assume monte-carlo input 
462    * 
463    * @return 
464    */
465   virtual AliVEventHandler* CreateMCHandler(UShort_t /*type*/, bool mc)
466   {
467     if (!mc) return 0;
468     AliMCEventHandler* mcHandler = new AliMCEventHandler();
469     mcHandler->SetReadTR(true); 
470     return mcHandler;
471   }
472   //------------------------------------------------------------------
473   /** 
474    * Create output event handler 
475    * 
476    * @param type 
477    * 
478    * @return 
479    */
480   virtual AliVEventHandler* CreateOutputHandler(UShort_t type)
481   {
482     AliAODHandler* ret = new AliAODHandler();
483     switch (type) { 
484     case Helper::kESD: 
485       ret->SetOutputFileName("AliAOD.root");
486       break;
487     case Helper::kAOD: 
488       ret->SetOutputFileName("AliAOD.pass2.root");
489       break;
490     case Helper::kUser: 
491       break;
492     }
493     
494     return ret;
495   }
496   //------------------------------------------------------------------
497   /** 
498    * Create physics selection, and add to manager
499    * 
500    * @param mc Whether this is for MC 
501    * @param mgr Manager
502    */
503   virtual void CreatePhysicsSelection(Bool_t mc, AliAnalysisManager* mgr)
504   {
505     if (fOptions.Has("bare-ps")) {
506       AliInputEventHandler* input = 
507         dynamic_cast<AliInputEventHandler*> (mgr->GetInputEventHandler());
508       if (!input) return;
509
510       AliPhysicsSelection* ps = new AliPhysicsSelection();
511       if (mc) ps->SetAnalyzeMC();
512
513       input->SetEventSelection(ps);
514
515       return;
516     }
517     gROOT->Macro(Form("AddTaskPhysicsSelection.C(%d)", mc));
518     mgr->RegisterExtraFile("event_stat.root");
519   }
520   //------------------------------------------------------------------
521   /** 
522    * Create centrality selection, and add to manager
523    * 
524    * @param mc Whether this is for MC 
525    * @param mgr Manager
526    */
527   virtual void CreateCentralitySelection(Bool_t mc, AliAnalysisManager* mgr)
528   {
529     gROOT->Macro("AddTaskCentrality.C");
530     const char* name = "CentralitySelection";
531     AliCentralitySelectionTask* ctask = 
532       dynamic_cast<AliCentralitySelectionTask*>(mgr->GetTask(name));
533     if (!ctask) return;
534     if (mc) ctask->SetMCInput();
535   }
536   //------------------------------------------------------------------
537   /** 
538    * Create analysis tasks.  Must be overloaded by sub-class
539    * 
540    * @param mgr  Manager
541    */
542   virtual void CreateTasks(AliAnalysisManager* mgr)=0;
543   virtual const Char_t* ClassName() const = 0;
544   /* @} */
545   //__________________________________________________________________
546   /** 
547    * @{ 
548    * @name Utility functions 
549    */
550   /** 
551    * Escape bad elements of the name 
552    * 
553    * @param name   Name to escape 
554    * @param datime Date and Time 
555    * 
556    * @return escaped name 
557    */  
558   static TString EscapeName(const char* name, const TString& datimeStr)
559   {
560     TString escaped = name;
561     char  c[] = { ' ', '/', '@', 0 };
562     char* p   = c;
563     while (*p) { 
564       char tmp[] = { *p, '\0' };
565       escaped.ReplaceAll(tmp, "_");
566       p++;
567     }
568     if (!datimeStr.IsNull()) {
569       TDatime datime;
570       if (datimeStr.EqualTo("now", TString::kIgnoreCase)) 
571         datime.Set();
572       else {
573         // Try various formats 
574         struct tm t;
575         const char* formats[] = { "%Ec", // Locale 
576                                   "%c", // Locale 
577                                   "%Ex EX", // Locale 
578                                   "%x %X", // Locale 
579                                   "%Y%m%d_%H%M", // YYYYMMDD_HHMM
580                                   "%F %R", // ISO standard, no seconds 
581                                   0 };
582         const char** f = formats;
583         Bool_t found = false;
584         while (*f && !found) { 
585           // Reset needed fields 
586           t.tm_year  = 0;
587           t.tm_mon   = 0;
588           t.tm_mday  = 0;
589           t.tm_hour  = 0;
590           t.tm_min   = 0;
591           // Stop processing on first match 
592           if (strptime(datimeStr.Data(), *f, &t) != 0) found = true;
593           f++;
594         }
595         if (found) {
596           t.tm_mon += 1; // Return 0-based month
597           datime.Set(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, 0); 
598         }
599       }
600       if (datime.GetYear() <= 1995 ||
601           datime.GetMonth() == 0 || 
602           datime.GetDay() == 0) return escaped;
603       escaped.Append(Form("_%04d%02d%02d_%02d%02d", 
604                           datime.GetYear(), 
605                           datime.GetMonth(), 
606                           datime.GetDay(), 
607                           datime.GetHour(), 
608                           datime.GetMinute()));
609     }
610     return escaped;
611   }    
612   /** 
613    * Make our working directory if so requested 
614    * 
615    * @return true on success
616    */
617   Bool_t SetupWorkingDirectory()
618   {
619     // Get the name of the target directory 
620     TString& nam = fEscapedName;
621
622     // Check if the directory exists already 
623     Bool_t exists = gSystem->AccessPathName(nam.Data()) == 0;
624     if (fHelper->Operation() == Helper::kTerminate && !exists) {
625       Error("SetupWorkingDirectory", "File/directory %s does not exists", 
626             nam.Data());
627       return false;
628     }
629
630     Bool_t overwrite = fOptions.Has("overwrite");
631     // If we're not allowed to overwrite, then complain
632     if (!overwrite && exists) {
633       Error("SetupWorkingDirectory", "File/directory %s already exists", 
634             nam.Data());
635       return false;
636     }
637
638     // Make the target directory if it doesn't exists 
639     if (!exists) {
640       if (gSystem->MakeDirectory(nam.Data())) {
641         Error("SetupWorkingDirectory", "Failed to make directory '%s'", 
642               nam.Data());
643         return false;
644       }
645     }
646       
647     // Change directory to target directory 
648     if (!gSystem->ChangeDirectory(nam.Data())) { 
649       Error("SetupWorkingDirectory", "Failed to change directory to %s", 
650             nam.Data());
651       return false;
652     }
653     Info("SetupWorkingDirectory", "Made subdirectory %s, and cd'ed there", 
654          nam.Data());
655     return true;
656   }
657   /** 
658    * Save the setup as a ROOT script and possibly also a shell script
659    * 
660    * @param asShellScript If true, also save as shell script
661    */
662   virtual void SaveSetup(Bool_t asShellScript)
663   {
664     OptionList tmp(fOptions);
665     const OptionList* uopts = (fHelper ? &fHelper->Options() : 0);
666     if (tmp.Find("overwrite")) tmp.Set("overwrite");
667     if (tmp.Find("date") && fEscapedName.Length() > fName.Length()+1) {
668       Int_t n = fName.Length()+1;
669       tmp.Set("date", fEscapedName(n, fEscapedName.Length()-n));
670     }
671     if (asShellScript) 
672       SaveSetupShell("rerun", ClassName(), fName, tmp, uopts);
673     SaveSetupROOT("ReRun", ClassName(), fName, tmp, uopts);
674     if (fHelper) fHelper->AuxSave(fEscapedName, asShellScript);
675   }
676   /** 
677    * Save a setup as a shell script 
678    * 
679    * @param out   Output name of shell script 
680    * @param cls   Class of the train 
681    * @param name  Name of the train
682    * @param opts  Option list
683    */
684   static void SaveSetupShell(const TString& out, const TString& cls,
685                              const TString& name, const OptionList& opts,
686                              const OptionList* uopts)
687   {
688     std::ofstream o(Form("%s.sh", out.Data()));
689     o << "#!/bin/bash\n\n"
690       << "class=\"" << cls << "\"\n"
691       << "name=\"" << name << "\"\n\n"
692       << "# Available options\n"
693       << "# \n";
694     opts.Help(o, "#    --");
695     if (uopts) {
696       o << "#\n"
697         << "# Available URI options\n"
698         << "# \n";
699       uopts->Help(o, "#      ");
700     }
701     o << "#\n"
702       << "opts=(--class=$class \\\n"
703       << "  --name=$name";
704     opts.Store(o, " \\\n  --", "", true);
705     o << ")\n\n"
706       << "echo \"Running runTrain ${opts[@]} $@\"\n"
707       << "runTrain \"${opts[@]}\" $@\n\n"
708       << "# EOF" << std::endl;
709     o.close();
710     gSystem->Exec(Form("chmod a+x %s.sh", out.Data()));
711   }
712   /** 
713    * Save a setup as a ROOT script 
714    * 
715    * @param out   Output name of shell script 
716    * @param cls   Class of the train 
717    * @param name  Name of the train
718    * @param opts  Option list
719    */
720   static void SaveSetupROOT(const TString& out, const TString& cls,
721                             const TString& name, const OptionList& opts,
722                             const OptionList* uopts)
723   {
724     OptionList tmp(opts);
725     tmp.Remove("url");
726
727     std::ofstream o(Form("%s.C", out.Data()));
728     o << "// Available options:\n"
729       << "// \n";
730     tmp.Help(o, "//     ");
731     if (uopts) {
732       o << "// \n"
733         << "// Available URI options\n";
734       uopts->Help(o, "//      ");
735     }
736     o << "//\n"
737       << "Bool_t " << out << "()\n" 
738       << "{\n"
739       << "  TString name(\"" << name << "\");\n"
740       << "  TString cls(\"" << cls << "\");\n"
741       << "  TUrl    uri(\"" << opts.Get("url") << "\");\n"
742       << "  \n"
743       << "  TString opts(";
744     tmp.Store(o, "\"", ",\"\n               ", false);
745     o << ");\n\n"
746       << "  TString path(";
747     TString     path(gROOT->GetMacroPath());
748     TObjArray*  elements = path.Tokenize(":");
749     TObjString* element = 0;
750     TIter       next(elements);
751     while ((element = static_cast<TObjString*>(next()))) {
752       if (element->String().IsNull()) continue;
753       o << "\n               \"" << element->GetName() << ":\"";
754     }
755     elements->Delete();
756     o << ");\n"
757       << "  path.Append(\"$ALICE_ROOT/PWGLF/FORWARD/trains\");\n"
758       << "  gROOT->SetMacroPath(path);\n\n"
759       << "  gROOT->LoadMacro(\"RunTrain.C\");\n\n"
760       << "  return RunTrain(name, cls, uri, opts);\n"
761       << "}\n" 
762       << "//\n"
763       << "// EOF\n" 
764       << "//" << std::endl;
765     o.close();
766   }    
767   /* @} */
768   TString      fName;
769   TString      fEscapedName;
770   OptionList   fOptions;
771   Helper*      fHelper;
772 };
773 #endif