]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWGLF/FORWARD/trains/TrainSetup.C
Merge branch 'master' of https://git.cern.ch/reps/AliRoot
[u/mrichter/AliRoot.git] / PWGLF / FORWARD / trains / TrainSetup.C
1 /**
2  * @defgroup pwglf_forward_trains Trains.
3  * 
4  * Train specifications. 
5  * See also @ref train_setup_doc
6  */
7 /**
8  * @file   TrainSetup.C
9  * @author Christian Holm Christensen <cholm@master.hehi.nbi.dk>
10  * @date   Tue Oct 16 17:56:57 2012
11  * 
12  * @brief  Base classs for train specifications 
13  * 
14  * @ingroup pwglf_forward_trains
15  */
16 #ifndef TRAINSETUP_C
17 #define TRAINSETUP_C
18 #ifndef __CINT__
19 # include "Railway.C"
20 # include "Option.C"
21 # include <TDatime.h>
22 # include <TUrl.h>
23 # include <TString.h>
24 # include <TApplication.h>
25 # include <TStopwatch.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 Railway;
38 struct OptionList;
39 class TDatime;
40 class TUrl;
41 class TString;
42 class TStopwatch;
43 class AliVEventHandler;
44 class AliAnalysisManager;
45 class AliInputEventHandler;
46 #endif
47
48 //====================================================================
49 /** 
50  * Generic set-up of an analysis train 
51  *
52  * See also @ref train_setup_doc
53  *
54  * @ingroup pwglf_forward_trains
55  * 
56  */
57 struct TrainSetup
58 {
59   /** 
60    * Constructor 
61    * 
62    * @param name Name of the train
63    */
64   TrainSetup(const TString& name)
65     : fName(name), 
66       fEscapedName(name),
67       fDatimeString(""),
68       fOptions(),
69       fRailway(0),
70       fMonitored("")
71   {
72     fOptions.Add("help", "Show help", false);
73     fOptions.Add("date", "YYYY-MM-DD HH:MM", "Set date", "now");
74     fOptions.Add("bare-ps", "Use bare physics selection w/o task", false);
75     fOptions.Add("verbose", "LEVEL", "Set verbosity level", 0);
76     fOptions.Add("url", "URL", "Job location & input URL", "");
77     fOptions.Add("overwrite", "Allow overwrite", false);
78     fOptions.Add("events", "N", "Number of events to analyse", -1);
79     fOptions.Add("type", "ESD|AOD|USER", "Input data stype", "");
80     fOptions.Add("setup", "Only do the setup", false);
81     fOptions.Add("branches", "Load only requested branches", false);
82     fDatimeString = "";
83     fEscapedName  = EscapeName(fName, fDatimeString);
84   }
85   /** 
86    * Copy constructor 
87    * 
88    * @param o Object to copy from 
89    */
90   TrainSetup(const TrainSetup& o) 
91     : fName(o.fName), 
92       fEscapedName(o.fEscapedName), 
93       fDatimeString(o.fDatimeString),
94       fOptions(o.fOptions), 
95       fRailway(o.fRailway),
96       fMonitored(o.fMonitored)
97   {}
98   /** 
99    * Assignment operator
100    * 
101    * @param o Object to assign from 
102    * 
103    * @return Reference to this object
104    */
105   TrainSetup& operator=(const TrainSetup& o) 
106   {
107     if (&o == this) return *this;
108     fName         = o.fName;
109     fEscapedName  = o.fEscapedName;
110     fDatimeString = o.fDatimeString;
111     fOptions      = o.fOptions;
112     fRailway       = o.fRailway;
113     fMonitored    = o.fMonitored;
114     return *this;
115   }
116   
117   /** 
118    * Destructor
119    */
120   virtual ~TrainSetup() {}
121   /* @} */
122   //__________________________________________________________________
123   /** 
124    * @{ 
125    * @name Execution 
126    */
127   /** 
128    * Initialize 
129    * 
130    * @return true on success 
131    */
132   Bool_t Init()
133   {
134     // --- Create the helper -----------------------------------------
135     TString  url     = fOptions.Get("url");
136     Int_t    verbose = fOptions.AsInt("verbose");
137
138     fRailway = Railway::Create(url.Data(), verbose);
139     if (!fRailway) { 
140       Error("Init", "Failed to make the worker for URL %s", url.Data());
141       return false;
142     }
143
144     // --- Check the type, if possible -------------------------------
145     UShort_t type    = fRailway->InputType();
146     Bool_t   mc      = fRailway->IsMC();
147     if (fOptions.Has("type")) { 
148       const TString& it = fOptions.Get("type");
149       if      (it.EqualTo("ESD",TString::kIgnoreCase)) type = Railway::kESD;
150       else if (it.EqualTo("AOD",TString::kIgnoreCase)) type = Railway::kAOD;
151       else if (it.EqualTo("user",TString::kIgnoreCase)) 
152         type = Railway::kUser;
153     }
154
155     // --- Rewrite the escpaed name ----------------------------------
156     if (fOptions.Has("date")) { 
157       fDatimeString = fOptions.Get("date");
158       fEscapedName  = EscapeName(fName, fDatimeString);
159     }
160     
161     // --- Get current directory and set-up sub-directory ------------
162     TString cwd = gSystem->WorkingDirectory();
163     if (!SetupWorkingDirectory()) return false;
164
165     // --- Do initial helper setup -----------------------------------
166     if (!fRailway->PreSetup()) return false;
167
168     // --- Load ROOT libraries ---------------------------------------
169     if (!fRailway->LoadROOT()) return false;
170     
171     // --- Load AliROOT libraries ------------------------------------
172     if (!fRailway->LoadAliROOT()) return false;
173
174     // --- Create analysis manager -----------------------------------
175     AliAnalysisManager *mgr  = CreateAnalysisManager(fEscapedName);
176
177     // In test mode, collect system information on every event 
178     // if (oper == kTest)  mgr->SetNSysInfo(1); 
179     if (verbose  >  0)      mgr->SetDebugLevel(verbose);
180     mgr->SetAutoBranchLoading(!fOptions.Has("branches"));
181     if (fRailway->Mode() == Railway::kLocal) 
182       mgr->SetUseProgressBar(kTRUE, 100);
183    
184     // --- ESD input handler ------------------------------------------
185     AliVEventHandler*  inputHandler = CreateInputHandler(type);
186     if (inputHandler) mgr->SetInputEventHandler(inputHandler);
187     
188     // --- Monte-Carlo ------------------------------------------------
189     AliVEventHandler*  mcHandler = CreateMCHandler(type,mc);
190     if (mcHandler) mgr->SetMCtruthEventHandler(mcHandler);
191     
192     // --- AOD output handler -----------------------------------------
193     AliVEventHandler*  outputHandler = CreateOutputHandler(type);
194     if (outputHandler) mgr->SetOutputEventHandler(outputHandler);
195
196     // --- Include analysis macro path in search path ----------------
197     gROOT->SetMacroPath(Form("%s:%s:$ALICE_ROOT/ANALYSIS/macros",
198                              cwd.Data(), gROOT->GetMacroPath()));
199
200     // --- Physics selction - only for ESD ---------------------------
201     if (type == Railway::kESD) CreatePhysicsSelection(mc, mgr);
202     
203     // --- Create centrality task ------------------------------------
204     CreateCentralitySelection(mc);
205
206     // --- Create tasks ----------------------------------------------
207     CreateTasks(mgr);
208
209     // --- Create monitor objects ------------------------------------
210     CreateMonitors();
211
212     // --- Post set-up initialization of helper ----------------------
213     if (!fRailway->PostSetup()) return false;
214
215     // --- Set debug level on defined tasks --------------------------
216     if (verbose > 0) {
217       TIter next(mgr->GetTasks());
218       AliAnalysisTask* sub = 0;
219       while ((sub = static_cast<AliAnalysisTask*>(next()))) { 
220         AliAnalysisTaskSE* se = dynamic_cast<AliAnalysisTaskSE*>(sub);
221         if (!se) continue;
222         se->SetDebugLevel(verbose);
223       }
224     }
225
226     // --- Print this setup ------------------------------------------
227     Print();
228
229     // --- Initialise the train --------------------------------------
230     if (!mgr->InitAnalysis())  {
231       gSystem->ChangeDirectory(cwd.Data());
232       Error("Init","Failed to initialise train");
233       return false;
234     }
235
236     // --- Enable progress bar ---------------------------------------
237     if (fRailway->Mode() != Railway::kGrid) 
238       mgr->SetUseProgressBar(true, 100);
239
240     // --- Save setup to disk ----------------------------------------
241     SaveSetup(true);
242
243     // --- Some information ------------------------------------------
244     mgr->PrintStatus();
245     if (fRailway->Mode() != Railway::kLocal) {
246       TIter next(mgr->GetTasks());
247       AliAnalysisTask* sub = 0;
248       while ((sub = static_cast<AliAnalysisTask*>(next()))) { 
249         sub->Print();
250       }
251     }
252     return true;
253   }
254   /** 
255    * Print timer information
256    * 
257    * @param timer The timer
258    * @param where Where this was called from 
259    */
260   void PrintTimer(TStopwatch& timer, const char* where)
261   {
262     timer.Stop();
263     Double_t t = timer.RealTime();
264     Int_t    h = Int_t(t / 3600); t -= h * 3600;
265     Int_t    m = Int_t(t /   60); t -= m *   60;
266     if (t < 0) t = 0;
267     Info(where, "took %4d:%02d:%06.3f", h, m, t);
268   }
269   /** 
270    * Run this train 
271    * 
272    * @return true on success 
273    */    
274   Bool_t Run()
275   {
276     TString cwd = gSystem->WorkingDirectory();
277     Bool_t status = false;
278     TStopwatch timer;
279     timer.Start();
280     try {
281       if (!Init()) throw TString("Failed to intialize the train");
282       PrintTimer(timer, "Initialization");
283       timer.Continue();
284       
285       // Check if we're asked to only do the setup 
286       if (fOptions.Has("setup")) {
287         status = true;
288         throw TString("Only did setup, no running");
289       }
290
291       // if (r) SaveSetup(*r, nEvents, asShell);
292       
293       Long64_t nEvents = fOptions.AsLong("events", -1);
294       Long64_t ret     = fRailway->Run(nEvents);
295       PrintTimer(timer, "Processing");
296       timer.Continue();
297       
298       // Make sure we go back 
299       gSystem->ChangeDirectory(cwd.Data());
300       
301       // Return. 
302       if (ret < 0) throw TString("Analysis failed");
303
304       status = true;
305     }
306     catch (TString& e) {
307       if (status) Warning("Run", "%s", e.Data());
308       else        Error("Run", "%s", e.Data());
309     }
310     if (fOptions.Has("date")) {
311       TString tmp     = "";
312       TString escaped = EscapeName(fName, tmp);
313       gSystem->Exec(Form("rm -f last_%s", escaped.Data()));
314       gSystem->Exec(Form("ln -sf %s last_%s", 
315                          fEscapedName.Data(), escaped.Data()));
316     }
317     PrintTimer(timer, "Finish");
318     timer.Continue();
319     return status;
320   }
321   /** 
322    * Get the options 
323    * 
324    * 
325    * @return Reference ot the options 
326    */
327   OptionList& Options() { return fOptions; }
328   /** 
329    * Print information to standard output 
330    * 
331    */
332   void Print(Option_t* ="") const
333   {
334     std::cout << "Train: " << fName << " (" << fEscapedName << ")" 
335               << std::endl;
336     fOptions.Show(std::cout);
337     if (fRailway) fRailway->Print();
338   }
339   /** 
340    * Show the help 
341    * 
342    * @param o      Output stream
343    * @param asProg If true, output as program options 
344    * 
345    * @return 
346    */
347   Bool_t Help(std::ostream& o=std::cout, bool asProg=false)
348   {
349     if (!fOptions.Has("help")) return true;
350
351     if (!asProg) 
352       o << "Usage: RunTrain(NAME, CLASS, OPTIONS)";
353     
354     o << "\n\nTrain Options:\n";
355     fOptions.Help(o, asProg ? "  --" : "  ");
356     o << "\n";
357
358     if (!fRailway && fOptions.Has("url")) {
359       TString url = fOptions.Get("url");
360       fRailway = Railway::Create(url.Data());
361     }
362     if (fRailway) { 
363       o << fRailway->Desc() << " URL form:\n\n"
364         << "    " << fRailway->UrlHelp() << "\n\n"
365         << "Options:\n";
366       fRailway->Options().Help(o, "    ");
367       o << "\n";
368     }
369     else { 
370       o << "Possible URL forms:\n\n";
371       Railway::ShowUrlHelp("LocalRailway");
372       Railway::ShowUrlHelp("ProofRailway");
373       Railway::ShowUrlHelp("LiteRailway");
374       Railway::ShowUrlHelp("AAFRailway");
375       Railway::ShowUrlHelp("AAFPluginRailway");
376       Railway::ShowUrlHelp("GridRailway");
377       o << "\n";
378     }
379     return false;
380   }
381   /** 
382    * Run train.  This will AcLic compile the setup script, create
383    * an object of that type with the given name, and then pass the 
384    * options to it.  Then, it will run the setup.
385    * 
386    * @param name   Train name 
387    * @param cls    Class name 
388    * @param opts   Comma seperated list of options
389    * @param asProg Run as program 
390    * @param spawn  Spawn ROOT shell after execution 
391    * 
392    * @return true on success
393    */
394   static Bool_t Main(const TString& name, const TString& cls, 
395                      const TCollection* opts, 
396                      Bool_t asProg=true,
397                      Bool_t spawn=false)
398   {
399     Bool_t ret = false;
400     try {
401       if (cls.IsNull()) 
402         throw TString("No class name specified");
403       if (name.IsNull()) 
404         throw TString("No train name specified");
405
406       gROOT->ProcessLine("gSystem->RedirectOutput(\"build.log\",\"w\");");
407       Int_t error = 0;
408       Int_t r1 = gROOT->LoadMacro(Form("%s.C+g", cls.Data()), &error);
409       gROOT->ProcessLine("gSystem->RedirectOutput(0);");
410       if (r1 < 0 || error) 
411         throw TString::Format("Failed to load setup %s: %d - see build.log", 
412                               cls.Data(), error);
413
414       // Make our object using the interpreter 
415       TString create = TString::Format("new %s(\"%s\")", 
416                                        cls.Data(), name.Data());
417       gROOT->ProcessLine("gSystem->RedirectOutput(\"build.log\",\"a\");");
418       Long_t retP = gROOT->ProcessLine(create, &error);
419       gROOT->ProcessLine("gSystem->RedirectOutput(0);");
420       if (!retP || error) 
421         throw TString::Format("Failed to make object of class %s "
422                               "(see build.log): 0x%08lx/%d\n\t%s", 
423                               cls.Data(), retP, error, create.Data());
424
425       TrainSetup* train = reinterpret_cast<TrainSetup*>(retP);
426     
427       // Now parse the options 
428       if (!train->Options().Parse(opts)) 
429         throw TString("Failed to parse options");
430
431       // Info("", "URL=%s", train->Options().Get("url").Data());
432
433       // Check if we got a help request
434       if (train->Options().Has("help")) { 
435         train->Help(std::cout, asProg);
436         ret = true;
437         throw TString("");
438       }
439
440       // return train->Init();
441       ret = train->Run();
442     }
443     catch (TString& e) { 
444       if (!e.IsNull()) Error("Main", "%s", e.Data());
445     }
446     // Info("Main", "End of main loop (app=%p, asProg=%s, spawn=%s)",
447     //   gApplication, asProg ? "true" : "false", spawn ? "true" : "false");
448     if (gApplication && asProg) {
449       if (!spawn) {
450         gSystem->Sleep(3);
451         gApplication->Terminate(ret ? 0 : 1);
452       }
453     }
454     return ret;
455   }
456 protected:
457   //__________________________________________________________________
458   /** 
459    * @{ 
460    * @name Overloadable behaviour 
461    */
462   //------------------------------------------------------------------
463   /** 
464    * Create the analysis manager 
465    * 
466    * @param name Name of the analysis 
467    * 
468    * @return Created analysis manager 
469    */
470   virtual AliAnalysisManager* CreateAnalysisManager(const char* name)
471   {
472     return new AliAnalysisManager(name,"Analysis Train");
473   }
474   //------------------------------------------------------------------
475   /** 
476    * Create input handler 
477    * 
478    * @param type 
479    * 
480    * @return 
481    */
482   virtual AliVEventHandler* CreateInputHandler(UShort_t type)
483   {
484     switch (type) {
485     case Railway::kESD:  return new AliESDInputHandler(); 
486     case Railway::kAOD:  return new AliAODInputHandler(); 
487     case Railway::kUser: return 0;
488     }
489     return 0;
490   }
491   //------------------------------------------------------------------
492   /** 
493    * Create MC input handler 
494    * 
495    * @param mc    Assume monte-carlo input 
496    * 
497    * @return 
498    */
499   virtual AliVEventHandler* CreateMCHandler(UShort_t /*type*/, bool mc)
500   {
501     if (!mc) return 0;
502     AliMCEventHandler* mcHandler = new AliMCEventHandler();
503     mcHandler->SetReadTR(true); 
504     return mcHandler;
505   }
506   //------------------------------------------------------------------
507   /** 
508    * Create output event handler 
509    * 
510    * @param type 
511    * 
512    * @return 
513    */
514   virtual AliVEventHandler* CreateOutputHandler(UShort_t type)
515   {
516     AliAODHandler* ret = new AliAODHandler();
517     switch (type) { 
518     case Railway::kESD: 
519       ret->SetOutputFileName("AliAOD.root");
520       break;
521     case Railway::kAOD: 
522       ret->SetOutputFileName("AliAOD.pass2.root");
523       break;
524     case Railway::kUser: 
525       break;
526     }
527     
528     return ret;
529   }
530   //------------------------------------------------------------------
531   /** 
532    * Create physics selection, and add to manager
533    * 
534    * @param mc Whether this is for MC 
535    * @param mgr Manager
536    */
537   virtual void CreatePhysicsSelection(Bool_t mc, AliAnalysisManager* mgr)
538   {
539     if (fOptions.Has("bare-ps")) {
540       AliInputEventHandler* input = 
541         dynamic_cast<AliInputEventHandler*> (mgr->GetInputEventHandler());
542       if (!input) return;
543
544       AliPhysicsSelection* ps = new AliPhysicsSelection();
545       if (mc) ps->SetAnalyzeMC();
546
547       input->SetEventSelection(ps);
548
549       return;
550     }
551     CoupleCar("AddTaskPhysicsSelection.C", Form("%d", mc));
552     mgr->RegisterExtraFile("event_stat.root");
553     mgr->AddStatisticsTask(AliVEvent::kAny);
554   }
555   //------------------------------------------------------------------
556   /** 
557    * Create centrality selection, and add to manager
558    * 
559    * @param mc Whether this is for MC 
560    * @param mgr Manager
561    */
562   virtual void CreateCentralitySelection(Bool_t mc)
563   {
564     AliAnalysisTask* task = CoupleCar("AddTaskCentrality.C","true");
565     AliCentralitySelectionTask* ctask = 
566       dynamic_cast<AliCentralitySelectionTask*>(task);
567     if (!ctask) return;
568     if (mc) ctask->SetMCInput();
569   }
570   //------------------------------------------------------------------
571   /** 
572    * Create analysis tasks.  Must be overloaded by sub-class
573    * 
574    * @param mgr  Manager
575    */
576   virtual void CreateTasks(AliAnalysisManager* mgr)=0;
577   /** 
578    * Add a task using a script and possibly some arguments 
579    * 
580    * @param macro Script to execute 
581    * @param args  Optional arguments to the script 
582    * 
583    * @return Created task or null
584    */
585   virtual AliAnalysisTask* CoupleCar(const TString& macro, 
586                                      const TString& args)
587   {
588     TString p = gSystem->Which(gROOT->GetMacroPath(), macro.Data());
589     if (p.IsNull()) { 
590       Error("CoupleCar", "Macro %s not found", macro.Data());
591       return 0;
592     }
593     TString cmd(p);
594     if (!args.IsNull()) 
595       cmd.Append(TString::Format("(%s)", args.Data()));
596     
597     Int_t err;
598     Long_t ret = gROOT->Macro(cmd.Data(), &err, false);
599     if (!ret) { 
600       Error("CoupleCar", "Failed to execute %s (%ld)", cmd.Data(), ret);
601       return 0;
602     }
603     return reinterpret_cast<AliAnalysisTask*>(ret);
604   }
605   /** 
606    * Add a task to the train with no arguments passed to the script 
607    * 
608    * @param macro The <b>AddTask</b> macro. 
609    * 
610    * @return The added task, if any
611    */
612   virtual AliAnalysisTask* CoupleCar(const TString& macro)
613   {
614     TString args;
615     return CoupleCar(macro, args);
616   }
617   /** 
618    * Add a single event analysis task to the train, passing the
619    * specified arguments to the macro.
620    * 
621    * @param macro The <b>AddTask</b> macro 
622    * @param args  Arguments to pass the macro 
623    * 
624    * @return The added task, if any 
625    */
626   virtual AliAnalysisTaskSE* CoupleSECar(const TString& macro, 
627                                        const TString& args)
628   {
629     return dynamic_cast<AliAnalysisTaskSE*>(CoupleCar(macro, args));
630   }
631   /** 
632    * Add a single event task to the train with no arguments passed to
633    * the script
634    * 
635    * @param macro The <b>AddTask</b> macro. 
636    * 
637    * @return The added task, if any
638    */
639   virtual AliAnalysisTaskSE* CoupleSECar(const TString& macro)
640   {
641     TString args;
642     return CoupleSECar(macro, args);
643   }
644   /** 
645    * Find an already added task 
646    * 
647    * @param name    Name of the task 
648    * @param verbose If true, 
649    * 
650    * @return 
651    */
652   virtual AliAnalysisTask* FindCar(const TString& name, 
653                                     Bool_t verbose=true) const
654   {
655     AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
656     if (!mgr) {
657       ::Warning("FindCar", "No manager defined");
658       return 0;
659     }
660     AliAnalysisTask* task = mgr->GetTask(name);
661     if (!task && verbose)
662       ::Warning("FindCar", "Task \"%s\" not found in train", 
663                 name.Data());
664     return task;
665   }
666   /** 
667    * Check if we have an MC handler attached 
668    * 
669    * @return True if MC handler is found in a valid manager.  False if
670    * manager is not defined, or has no MC handler.
671    */
672   virtual Bool_t HasMCHandler() const 
673   {
674     AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
675     if (!mgr) return false;
676     return mgr->GetMCtruthEventHandler() != 0;
677   }
678   /** 
679    * Set the name of the train - should be name of the class.  Must be
680    * overloaded.
681    * 
682    * @return Class name as a constant C string 
683    */
684   virtual const Char_t* ClassName() const = 0;
685   /* @} */
686   //__________________________________________________________________
687   virtual void AddMonitor(const TString& name)
688   {
689     if (!fMonitored.IsNull()) fMonitored.Append(":");
690     fMonitored.Append(name);
691   }
692   virtual void CreateMonitors() 
693   {
694     if (fMonitored.IsNull()) return;
695     if (fRailway->Mode() != Railway::kProof) return;
696
697     TObjArray* tokens = fMonitored.Tokenize(":");
698     TObject*   token  = 0;
699     TIter      next(tokens);
700     while ((token = next())) {
701       gROOT->ProcessLine(Form("gProof->AddFeedback(\"%s\");", 
702                               token->GetName()));
703       
704     }
705     tokens->Delete();
706   }
707   //__________________________________________________________________
708   /** 
709    * @{ 
710    * @name Utility functions 
711    */
712   /** 
713    * Escape bad elements of the name 
714    * 
715    * @param name      Name to escape 
716    * @param datimeStr Date and Time string 
717    * 
718    * @return escaped name 
719    */  
720   static TString EscapeName(const char* name, TString& datimeStr)
721   {
722     TString escaped = name;
723     char  c[] = { ' ', '/', '@', 0 };
724     char* p   = c;
725     while (*p) { 
726       char tmp[] = { *p, '\0' };
727       escaped.ReplaceAll(tmp, "_");
728       p++;
729     }
730     if (!datimeStr.IsNull() && 
731         !datimeStr.EqualTo("none", TString::kIgnoreCase)) {
732       TDatime datime;
733       if (datimeStr.EqualTo("now", TString::kIgnoreCase)) 
734         datime.Set();
735       else {
736         // Try various formats 
737         struct tm t;
738         const char* formats[] = { "%Ec", // Locale 
739                                   "%c", // Locale 
740                                   "%Ex EX", // Locale 
741                                   "%x %X", // Locale 
742                                   "%Y%m%d_%H%M", // YYYYMMDD_HHMM
743                                   "%F %R", // ISO standard, no seconds 
744                                   0 };
745         const char** f = formats;
746         Bool_t found = false;
747         while (*f && !found) { 
748           // Reset needed fields 
749           t.tm_year  = 0;
750           t.tm_mon   = 0;
751           t.tm_mday  = 0;
752           t.tm_hour  = 0;
753           t.tm_min   = 0;
754           // Stop processing on first match 
755           if (strptime(datimeStr.Data(), *f, &t) != 0) found = true;
756           f++;
757         }
758         if (found) {
759           t.tm_mon += 1; // Return 0-based month
760           datime.Set(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, 0); 
761         }
762       }
763       if (datime.GetYear() <= 1995 ||
764           datime.GetMonth() == 0 || 
765           datime.GetDay() == 0) return escaped;
766       datimeStr = Form("%04d%02d%02d_%02d%02d", 
767                        datime.GetYear(), 
768                        datime.GetMonth(), 
769                        datime.GetDay(), 
770                        datime.GetHour(), 
771                        datime.GetMinute());
772       escaped.Append(Form("_%s", datimeStr.Data()));
773     }
774     return escaped;
775   }    
776   /** 
777    * Make our working directory if so requested 
778    * 
779    * @return true on success
780    */
781   Bool_t SetupWorkingDirectory()
782   {
783     // Get the name of the target directory 
784     TString& nam = fEscapedName;
785
786     // Check if the directory exists already 
787     Bool_t exists = gSystem->AccessPathName(nam.Data()) == 0;
788     if (fRailway->Operation() == Railway::kTerminate && !exists) {
789       Error("SetupWorkingDirectory", "File/directory %s does not exists", 
790             nam.Data());
791       return false;
792     }
793
794     Bool_t overwrite = fOptions.Has("overwrite");
795     // If we're not allowed to overwrite, then complain
796     if (!overwrite && exists) {
797       Error("SetupWorkingDirectory", "File/directory %s already exists", 
798             nam.Data());
799       return false;
800     }
801
802     // Make the target directory if it doesn't exists 
803     if (!exists) {
804       if (gSystem->MakeDirectory(nam.Data())) {
805         Error("SetupWorkingDirectory", "Failed to make directory '%s'", 
806               nam.Data());
807         return false;
808       }
809     }
810       
811     // Change directory to target directory 
812     if (!gSystem->ChangeDirectory(nam.Data())) { 
813       Error("SetupWorkingDirectory", "Failed to change directory to %s", 
814             nam.Data());
815       return false;
816     }
817     // Info("SetupWorkingDirectory", "Made subdirectory %s, and cd'ed there", 
818     //      nam.Data());
819     return true;
820   }
821   /** 
822    * Save the setup as a ROOT script and possibly also a shell script
823    * 
824    * @param asShellScript If true, also save as shell script
825    */
826   virtual void SaveSetup(Bool_t asShellScript)
827   {
828     OptionList tmp(fOptions);
829     const OptionList* uopts = (fRailway ? &fRailway->Options() : 0);
830     if (tmp.Find("overwrite")) tmp.Set("overwrite");
831     if (tmp.Find("date") && fEscapedName.Length() > fName.Length()+1) {
832       Int_t n = fName.Length()+1;
833       tmp.Set("date", fEscapedName(n, fEscapedName.Length()-n));
834     }
835     if (asShellScript) 
836       SaveSetupShell("rerun", ClassName(), fName, tmp, uopts);
837     SaveSetupROOT("ReRun", ClassName(), fName, tmp, uopts);
838     if (fRailway) fRailway->AuxSave(fEscapedName, asShellScript);
839     SavePostShellScript();
840   }
841   /** 
842    * Save a setup as a shell script 
843    * 
844    * @param out   Output name of shell script 
845    * @param cls   Class of the train 
846    * @param name  Name of the train
847    * @param opts  Option list
848    * @param uopts Url options 
849    */
850   static void SaveSetupShell(const TString& out, const TString& cls,
851                              const TString& name, const OptionList& opts,
852                              const OptionList* uopts)
853   {
854     std::ofstream o(Form("%s.sh", out.Data()));
855     o << "#!/bin/bash\n\n"
856       << "class=\"" << cls << "\"\n"
857       << "name=\"" << name << "\"\n\n"
858       << "# Available options\n"
859       << "# \n";
860     opts.Help(o, "#    --");
861     if (uopts) {
862       o << "#\n"
863         << "# Available URI options\n"
864         << "# \n";
865       uopts->Help(o, "#      ");
866     }
867     o << "#\n"
868       << "opts=(--class=$class \\\n"
869       << "  --name=$name";
870     opts.Store(o, " \\\n  --", "", true);
871     o << ")\n\n"
872       << "echo \"Running runTrain ${opts[@]} $@\"\n"
873       << "runTrain \"${opts[@]}\" $@\n\n"
874       << "# EOF" << std::endl;
875     o.close();
876     gSystem->Exec(Form("chmod a+x %s.sh", out.Data()));
877   }
878   /** 
879    * Save a setup as a ROOT script 
880    * 
881    * @param out   Output name of shell script 
882    * @param cls   Class of the train 
883    * @param name  Name of the train
884    * @param opts  Option list
885    * @param uopts Url options 
886    */
887   static void SaveSetupROOT(const TString& out, const TString& cls,
888                             const TString& name, const OptionList& opts,
889                             const OptionList* uopts)
890   {
891     OptionList tmp(opts);
892     tmp.Remove("url");
893
894     std::ofstream o(Form("%s.C", out.Data()));
895     o << "// Available options:\n"
896       << "// \n";
897     tmp.Help(o, "//     ");
898     if (uopts) {
899       o << "// \n"
900         << "// Available URI options\n";
901       uopts->Help(o, "//      ");
902     }
903     o << "//\n"
904       << "Bool_t " << out << "()\n" 
905       << "{\n"
906       << "  TString name(\"" << name << "\");\n"
907       << "  TString cls(\"" << cls << "\");\n"
908       << "  TUrl    uri(\"" << opts.Get("url") << "\");\n"
909       << "  \n"
910       << "  TString opts(";
911     tmp.Store(o, "\"", ",\"\n               ", false);
912     o << ");\n\n"
913       << "  TString path(";
914     TString     path(gROOT->GetMacroPath());
915     TObjArray*  elements = path.Tokenize(":");
916     TObjString* element = 0;
917     TIter       next(elements);
918     while ((element = static_cast<TObjString*>(next()))) {
919       if (element->String().IsNull()) continue;
920       o << "\n               \"" << element->GetName() << ":\"";
921     }
922     elements->Delete();
923     o << ");\n"
924       << "  path.Append(\"$ALICE_ROOT/PWGLF/FORWARD/trains\");\n"
925       << "  gROOT->SetMacroPath(path);\n\n"
926       << "  gROOT->LoadMacro(\"RunTrain.C\");\n\n"
927       << "  return RunTrain(name, cls, uri, opts);\n"
928       << "}\n" 
929       << "//\n"
930       << "// EOF\n" 
931       << "//" << std::endl;
932     o.close();
933   }    
934   /** 
935    * Write shell code to do post processing after terminate.  This
936    * code should deal with a single run (or run range).  The following
937    * shell variables are available to the code:
938    *
939    * - @c $prefix  Relative path to job directory or empty 
940    * - @c $dest    Destination for output to be stored
941    * 
942    * Note, the code is injected into a shell function, and should
943    * therefor not define new functions or the like.
944    * 
945    * @param o The output stream.  
946    */
947   virtual void PostShellCode(std::ostream& o)
948   {
949     o << "  echo \"Nothing to do for " << ClassName() 
950       << " train\"" << std::endl;
951   }
952   /** 
953    * Save a script to do post processing after terminate on each run
954    * or run-range. 
955    * 
956    * The shell script will execute the train defined code (in
957    * PostShellCode) for each run or run-range.  The train defined code
958    * and call drawing and summarizing macros or the like.
959    *
960    * In case of Grid analysis, this script will download and extract
961    * the appropriate ZIP files to separate directories, and then
962    * change directory to these directories and execute the train
963    * defined shell code there.  In this case, the script defines the
964    * shell variable @c $prefix as the relative path to the job
965    * directory.
966    * 
967    */
968   void SavePostShellScript()
969   {
970     std::ofstream f("post.sh");
971     if (!f) { 
972       Error("SavePostAll", "Failed to open post.sh script");
973       return;
974     }
975     f << "#!/bin/bash\n"
976       << "# Generated by " << ClassName() << "\n"
977       << "set -e\n"
978       << "\n"
979       << "dest=$1\n"
980       << "prefix=\n"
981       << "\n"
982       << "doall() {"
983       << std::endl;
984     PostShellCode(f);
985     f << "}\n"
986       << "\n"
987       << "if test ! -f Download.C ;then\n"
988       << "  doall\n"
989       << "  exit\n"
990       << "fi\n"
991       << "\n"
992       << "if test ! -f .download ; then\n"
993       << "  aliroot -l -b -q Download.C\\(1\\)\n"
994       << "  touch .download\n"
995       << "fi\n"
996       << "prefix=../\n"
997       << "\n"
998       << "for i in root_archive_*.zip ; do\n"
999       << "  d=`basename $i .zip` \n"
1000       << "  if test ! -d $d ; then\n"
1001       << "    echo \"Directory $d missing\"\n"
1002       << "    continue\n"
1003       << "  fi\n"
1004       << "  \n"
1005       << "  (cd $d && doall)\n"
1006       << "done\n"
1007       << "# EOF"
1008       << std::endl;
1009     f.close();
1010     gSystem->Exec("chmod a+x post.sh");
1011   }
1012     
1013   /* @} */
1014   TString      fName;
1015   TString      fEscapedName;
1016   TString      fDatimeString;
1017   OptionList   fOptions;
1018   Railway*      fRailway;
1019   TString      fMonitored;
1020 };
1021 #endif