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