]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWG/muondep/AliMuonAccEffSubmitter.cxx
Close stream when writing is over
[u/mrichter/AliRoot.git] / PWG / muondep / AliMuonAccEffSubmitter.cxx
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line Project.                                    *
5  * Contributors are mentioned in the code where appropriate.              *
6  *                                                                        *
7  * Permission to use, copy, modify and distribute this software and its   *
8  * documentation strictly for non-commercial purposes is hereby granted   *
9  * without fee, provided that the above copyright notice appears in all   *
10  * copies and that both the copyright notice and this permission notice   *
11  * appear in the supporting documentation. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15
16 //
17 // AliMuonAccEffSubmitter : a class to help submit Acc x Eff simulations
18 // anchored to real runs for J/psi, upsilon, single muons, etc...
19 //
20 // This class is dealing with 3 different directories :
21 //
22 // - template directory ($ALICE_ROOT/PWG/muondep/AccEffTemplates) containing the
23 //   basic template files to be used for a simuation. A template can contain
24 //   some variables that will be replaced during during the copy from template
25 //   to local dir
26 //
27 // - local directory, where the files from the template directory, are copied
28 //   once the class has been configured properly (i.e. using the various Set, Use,
29 //   etc... methods). Some other files (e.g. JDL ones) are generated from
30 //   scratch and also copied into this directory.
31 //   At this point one could(should) check the files, as they are the ones
32 //   to be copied to the remote directory for the production
33 //
34 // - remote directory, the alien directory where the files will be copied
35 //   (from the local directory) before the actual submission
36 //
37 // ==========================================================
38 //
39 // Basic usage
40 //
41 // AliMuonAccEffSubmitter a; // (1)
42 // a.UseOCDBSnapshots(kFALSE);
43 // a.SetRemoteDir("/alice/cern.ch/user/l/laphecet/Analysis/LHC13d/simjpsi/pp503z0");
44 // a.ShouldOverwriteFiles(true);
45 // a.MakeNofEventsPropToTriggerCount("CMUL7-B-NOPF-MUON");
46 // a.SetVar("VAR_GENLIB_PARNAME","\"pp 5.03\"");
47 // a.SetRunList(195682);
48 // a.Print();
49 // a.Run("test"); // will do everything but the submit
50 // a.Submit(false); // actual submission
51 //
52 // (1) note that for this to work in the Root prompt you need to load (for instance
53 // in your rootlogon.C) all the chain of libraries up to libPWGmuondep. As
54 // the time of this writing (March 2014), this means :
55 //
56 // gSystem->Load("libVMC");
57 // gSystem->Load("libTree");
58 // gSystem->Load("libProofPlayer");
59 // gSystem->Load("libPhysics");
60 // gSystem->Load("libMatrix");
61 // gSystem->Load("libMinuit");
62 // gSystem->Load("libXMLParser");
63 // gSystem->Load("libGui");
64 // gSystem->Load("libSTEERBase");
65 // gSystem->Load("libESD");
66 // gSystem->Load("libAOD");
67 // gSystem->Load("libANALYSIS");
68 // gSystem->Load("libRAWDatabase");
69 // gSystem->Load("libCDB");
70 // gSystem->Load("libSTEER");
71 // gSystem->Load("libANALYSISalice");
72 // gSystem->Load("libCORRFW");
73 // gSystem->Load("libPWGmuon");
74 // gSystem->Load("libMUONcore");
75 // gSystem->Load("libMUONmapping");
76 // gSystem->Load("libMUONcalib");
77 // gSystem->Load("libMUONgeometry");
78 // gSystem->Load("libMUONtrigger");
79 // gSystem->Load("libRAWDatabase");
80 // gSystem->Load("libMUONraw");
81 // gSystem->Load("libMUONbase");
82 // gSystem->Load("libMUONshuttle");
83 // gSystem->Load("libMUONrec");
84 // gSystem->Load("libPWGmuondep");
85 //
86 // author: Laurent Aphecetche (Subatech)
87 //
88
89 #include "AliMuonAccEffSubmitter.h"
90
91 #include "AliAnalysisTriggerScalers.h"
92 #include "AliLog.h"
93 #include "TFile.h"
94 #include "TGrid.h"
95 #include "TGridResult.h"
96 #include "TMap.h"
97 #include "TMath.h"
98 #include "TObjString.h"
99 #include "TROOT.h"
100 #include "TString.h"
101 #include "TSystem.h"
102 #include <vector>
103 #include <fstream>
104 using std::ifstream;
105 namespace
106 {
107   Int_t splitLevel=10;
108 }
109
110 ClassImp(AliMuonAccEffSubmitter)
111
112 //______________________________________________________________________________
113 AliMuonAccEffSubmitter::AliMuonAccEffSubmitter(const char* generator, Bool_t localOnly,
114                                                const char* generatorVersion)
115 : AliMuonGridSubmitter(AliMuonGridSubmitter::kAccEff,localOnly),
116 fRatio(-1.0),
117 fFixedNofEvents(10000),
118 fMaxEventsPerChunk(5000),
119 fOCDBPath(""),
120 fSplitMaxInputFileNumber(20),
121 fCompactMode(1),
122 fExternalConfig(""),
123 fUseOCDBSnapshots(kFALSE),
124 fSnapshotDir(""),
125 fUseAODMerging(kFALSE)
126 {
127   // ctor
128   //
129   // if generator contains "pythia8" and generatorVersion is given then
130   // the pythiaversion must represent the integer part XXX of the
131   // include directory $ALICE_ROOT/PYTHI8/pythiaXXX/include where the file
132   // Analysis.h is to be found.
133   //
134   // if generator contains "pythia6" then generatorVersion should be the
135   // X.YY part of libpythia6.X.YY.so
136   //
137   
138   AddIncludePath("-I$ALICE_ROOT/STEER/STEER -I$ALICE_ROOT/PYTHIA6 -I$ALICE_ROOT/LHAPDF -I$ALICE_ROOT/PWG/muon -I$ALICE_ROOT/PWG/muondep -I$ALICE_ROOT/MUON -I$ALICE_ROOT/include -I$ALICE_ROOT/EVGEN -I$ALICE_ROOT/FASTSIM");
139   
140   TString ocdbPath("raw://");
141   
142   if (localOnly) {
143     ocdbPath = "local://$ALICE_ROOT/OCDB";
144   }
145   
146   SetOCDBPath(ocdbPath.Data());
147   
148   SetLocalDirectory("Snapshot",LocalDir());
149   
150   SetVar("VAR_OCDB_PATH",Form("\"%s\"",ocdbPath.Data()));
151
152   SetVar("VAR_GENPARAM_INCLUDE","AliGenMUONlib.h");
153   SetVar("VAR_GENPARAM_NPART","1");
154   SetVar("VAR_GENPARAM_GENLIB_TYPE","AliGenMUONlib::kJpsi");
155   SetVar("VAR_GENPARAM_GENLIB_PARNAME","\"pPb 5.03\"");
156
157   SetVar("VAR_GENCORRHF_QUARK","5");
158   SetVar("VAR_GENCORRHF_ENERGY","5");
159
160   // some default values for J/psi
161   SetVar("VAR_GENPARAMCUSTOM_PDGPARTICLECODE","443");
162
163   // default values below are from J/psi p+Pb (from muon_calo pass)
164   SetVar("VAR_GENPARAMCUSTOM_Y_P0","4.08E5");
165   SetVar("VAR_GENPARAMCUSTOM_Y_P1","7.1E4");
166   
167   SetVar("VAR_GENPARAMCUSTOM_PT_P0","1.13E9");
168   SetVar("VAR_GENPARAMCUSTOM_PT_P1","18.05");
169   SetVar("VAR_GENPARAMCUSTOM_PT_P2","2.05");
170   SetVar("VAR_GENPARAMCUSTOM_PT_P3","3.34");
171
172   // some default values for single muons
173   SetVar("VAR_GENPARAMCUSTOMSINGLE_PTMIN","0.35");
174   
175   SetVar("VAR_GENPARAMCUSTOMSINGLE_PT_P0","4.05962");
176   SetVar("VAR_GENPARAMCUSTOMSINGLE_PT_P1","1.0");
177   SetVar("VAR_GENPARAMCUSTOMSINGLE_PT_P2","2.46187");
178   SetVar("VAR_GENPARAMCUSTOMSINGLE_PT_P3","2.08644");
179
180   SetVar("VAR_GENPARAMCUSTOMSINGLE_Y_P0","0.729545");
181   SetVar("VAR_GENPARAMCUSTOMSINGLE_Y_P1","0.53837");
182   SetVar("VAR_GENPARAMCUSTOMSINGLE_Y_P2","0.141776");
183   SetVar("VAR_GENPARAMCUSTOMSINGLE_Y_P3","0.0130173");
184
185   // some default values for GenBox
186   
187   SetVar("VAR_GENMUBOX_PTMIN","0");
188   SetVar("VAR_GENMUBOX_PTMAX","20");
189   SetVar("VAR_GENMUBOX_YMIN","-4.1");
190   SetVar("VAR_GENMUBOX_YMAX","-2.4");
191
192   SetVar("VAR_PYTHIA8_CMS_ENERGY","8000");
193   SetVar("VAR_PYTHIA6_CMS_ENERGY","8000");
194   
195   SetVar("VAR_PURELY_LOCAL",Form("%d",localOnly));
196   
197   SetVar("VAR_SIM_ALIGNDATA","\"alien://folder=/alice/simulation/2008/v4-15-Release/Ideal\"");
198   
199   SetVar("VAR_REC_ALIGNDATA","\"alien://folder=/alice/simulation/2008/v4-15-Release/Residual\"");
200   
201   SetVar("VAR_USE_ITS_RECO","0");
202   
203   UseOCDBSnapshots(fUseOCDBSnapshots);
204
205   gSystem->Load("libEVGEN");
206
207   SetVar("VAR_TRIGGER_CONFIGURATION","");
208   
209   SetVar("VAR_PYTHIA8_INCLUDES","");
210   SetVar("VAR_PYTHIA8_SETENV","");
211
212   SetVar("VAR_PYTHIA6_INCLUDES","");
213   SetVar("VAR_PYTHIA6_SETENV","");
214   
215   if ( TString(generator).Contains("pythia8",TString::kIgnoreCase) )
216   {
217     fMaxEventsPerChunk =  500; // 5000 is not reasonable with Pythia8 (and ITS+MUON...)
218     
219     fCompactMode = 2; // keep AOD as for the time being the filtering driven from AODtrain.C cannot
220     // add SPD tracklets to muon AODs.
221     
222     SetVar("VAR_USE_ITS_RECO","1");
223
224     if (gSystem->AccessPathName(gSystem->ExpandPathName(Form("$ALICE_ROOT/PYTHIA8/pythia%s",generatorVersion))))
225     {
226       AliError(Form("Directory -I$ALICE_ROOT/PYTHIA8/pythia%s/include not found",generatorVersion));
227       Invalidate();
228       return;
229     }
230     AddIncludePath(Form("-I$ALICE_ROOT/PYTHIA8 -I$ALICE_ROOT/PYTHIA8/pythia%s/include",generatorVersion));
231 //    SetVar("VAR_PYTHIA8_VERSION",Form("\"%d\"",pythia8version));
232     
233     SetVar("VAR_PYTHIA8_INCLUDES",Form("gSystem->AddIncludePath(\"-I$ALICE_ROOT/PYTHIA6 -I$ALICE_ROOT/STEER/STEER -I$ALICE_ROOT/LHAPDF -I$ALICE_ROOT/PYTHIA8 -I$ALICE_ROOT/PYTHIA8/pythia%s/include\");\n",generatorVersion));
234   
235     TString p8env;
236     
237     p8env += Form("  gSystem->Setenv(\"PYTHIA8DATA\", gSystem->ExpandPathName(\"$ALICE_ROOT/PYTHIA8/pythia%s/xmldoc\"));\n",generatorVersion);
238     
239     p8env += "  gSystem->Setenv(\"LHAPDF\",gSystem->ExpandPathName(\"$ALICE_ROOT/LHAPDF\"));\n";
240     
241     p8env +=  "  gSystem->Setenv(\"LHAPATH\",gSystem->ExpandPathName(\"$ALICE_ROOT/LHAPDF/PDFsets\"));\n";
242     
243     SetVar("VAR_PYTHIA8_SETENV",p8env.Data());
244     
245     gSystem->Load("libFASTSIM");
246     gSystem->Load("liblhapdf");      // Parton density functions
247     gSystem->Load("libEGPythia6");   // TGenerator interface
248 //    gSystem->Load("libpythia6");     // Pythia 6.2
249     gSystem->Load("libAliPythia6");  // ALICE specific implementations
250     gSystem->Load("libpythia8");
251     gSystem->Load("libAliPythia8");
252     
253     SetVar("VAR_PYTHIA8_SETUP_STRINGS","\"\"");
254     
255     SetVar("VAR_TRIGGER_CONFIGURATION","p-p");
256   }
257   
258   if ( TString(generator).Contains("pythia6",TString::kIgnoreCase) )
259   {
260     fMaxEventsPerChunk =  500; // 5000 is not reasonable with Pythia6 (and ITS+MUON...)
261
262     fCompactMode = 2; // keep AOD as for the time being the filtering driven from AODtrain.C cannot
263     // add SPD tracklets to muon AODs.
264
265     gSystem->Load("libFASTSIM");
266     gSystem->Load("liblhapdf");      // Parton density functions
267     gSystem->Load("libEGPythia6");   // TGenerator interface
268     gSystem->Load(Form("libpythia6.%s",generatorVersion));
269     gSystem->Load("libAliPythia6");
270     
271     SetVar("VAR_PYTHIA6_INCLUDES","gSystem->AddIncludePath(\"-I$ALICE_ROOT/PYTHIA6 -I$ALICE_ROOT/STEER/STEER -I$ALICE_ROOT/STEER/STEERBase -I$ALICE_ROOT/LHAPDF -I$ALICE_ROOT/FASTSIM\");");
272     
273     TString p6env;
274     
275     p6env += Form("gSystem->Load(\"libpythia6.%s\");",generatorVersion);
276     
277     SetVar("VAR_PYTHIA6_SETENV",p6env.Data());
278     
279     SetVar("VAR_USE_ITS_RECO","1");
280     
281     SetVar("VAR_TRIGGER_CONFIGURATION","p-p");
282   }
283
284   SetGenerator(generator);
285   
286   if (localOnly)
287   {
288     MakeNofEventsFixed(10);
289   }
290   else
291   {
292     MakeNofEventsPropToTriggerCount();
293   }
294   
295   AddToTemplateFileList("CheckESD.C");
296   AddToTemplateFileList("CheckAOD.C");
297   AddToTemplateFileList("AODtrain.C");
298   AddToTemplateFileList("validation.sh");
299   
300   AddToTemplateFileList("Config.C");
301   AddToTemplateFileList("rec.C");
302   AddToTemplateFileList("sim.C");
303   AddToTemplateFileList("simrun.C");
304   AddToTemplateFileList(RunJDLName().Data());
305   
306   UseExternalConfig(fExternalConfig);
307 }
308
309 //______________________________________________________________________________
310 AliMuonAccEffSubmitter::~AliMuonAccEffSubmitter()
311 {
312   // dtor
313 }
314
315 ///______________________________________________________________________________
316 Bool_t AliMuonAccEffSubmitter::Generate(const char* jdlname) const
317 {
318   if ( TString(jdlname).Contains("merge",TString::kIgnoreCase) )
319   {
320     return GenerateMergeJDL(jdlname);
321   }
322   else
323   {
324     return GenerateRunJDL(jdlname);
325   }
326 }
327
328 ///______________________________________________________________________________
329 Bool_t AliMuonAccEffSubmitter::GenerateMergeJDL(const char* name) const
330 {
331   /// Create the JDL for merging jobs
332   /// FIXME: not checked !
333   
334   AliDebug(1,"");
335
336   std::ostream* os = CreateJDLFile(name);
337   
338   if (!os)
339   {
340     return kFALSE;
341   }
342   
343   Bool_t final = TString(name).Contains("merge",TString::kIgnoreCase);
344
345   (*os) << "# Generated merging jdl (production mode)" << std::endl
346   << "# $1 = run number" << std::endl
347   << "# $2 = merging stage" << std::endl
348   << "# Stage_<n>.xml made via: find <OutputDir> *Stage<n-1>/*root_archive.zip" << std::endl;
349
350   OutputToJDL(*os,"Packages",
351          GetMapValue("AliRoot"),
352          GetMapValue("Geant3"),
353          GetMapValue("Root"),
354          GetMapValue("API"));
355   
356   OutputToJDL(*os,"Executable","AOD_merge.sh");
357   
358   OutputToJDL(*os,"Price","1");
359
360   if ( final )
361   {
362     OutputToJDL(*os,"Jobtag","comment: AliMuonAccEffSubmitter final merging");
363   }
364   else
365   {
366     OutputToJDL(*os,"Jobtag","comment: AliMuonAccEffSubmitter merging stage $2");
367   }
368   
369   OutputToJDL(*os,"Workdirectorysize","5000MB");
370   
371   OutputToJDL(*os,"Validationcommand",Form("%s/validation_merge.sh",RemoteDir().Data()));
372   
373   OutputToJDL(*os,"TTL","14400");
374   
375   OutputToJDL(*os,"OutputArchive",
376     "log_archive.zip:stderr,stdout@disk=1",
377     "root_archive.zip:AliAOD.root,AliAOD.Muons.root,AnalysisResults.root@disk=3"
378          );
379   
380   OutputToJDL(*os,"Arguments",(final ? "2":"1")); // for AOD_merge.sh, 1 means intermediate merging stage, 2 means final merging
381   
382   if ( !final )
383   {
384     OutputToJDL(*os,"InputFile",Form("LF:%s/AODtrain.C",RemoteDir().Data()));
385     OutputToJDL(*os,"OutputDir",Form("%s/$1/Stage_$2/#alien_counter_03i#",RemoteDir().Data()));
386     OutputToJDL(*os,"InputDataCollection",Form("%s/$1/Stage_$2.xml,nodownload",RemoteDir().Data()));
387     OutputToJDL(*os,"split","se");
388     OutputToJDL(*os,"SplitMaxInputFileNumber",GetSplitMaxInputFileNumber());
389     OutputToJDL(*os,"InputDataListFormat","xml-single");
390     OutputToJDL(*os,"InputDataList","wn.xml");
391   }
392   else
393   {
394     OutputToJDL(*os,"InputFile",Form("LF:%s/AODtrain.C",RemoteDir().Data()),
395            Form("LF:%s/$1/wn.xml",RemoteDir().Data()));
396     OutputToJDL(*os,"OutputDir",Form("%s/$1",RemoteDir().Data()));
397   }
398   
399   return kTRUE;
400 }
401
402 //______________________________________________________________________________
403 Bool_t AliMuonAccEffSubmitter::GenerateRunJDL(const char* name) const
404 {
405   /// Generate (locally) the JDL to perform the simulation+reco+aod filtering
406   /// (to be then copied to the grid and finally submitted)
407   
408   AliDebug(1,"");
409
410   std::ostream* os = CreateJDLFile(name);
411   
412   if (!os)
413   {
414     return kFALSE;
415   }
416   
417   OutputToJDL(*os,"Packages",
418               GetMapValue("AliRoot"),
419               GetMapValue("Geant3"),
420               GetMapValue("Root"),
421               GetMapValue("API"));
422               
423   OutputToJDL(*os,"Jobtag","comment: AliMuonAccEffSubmitter RUN $1");
424
425   OutputToJDL(*os,"split","production:1-$2");
426
427   OutputToJDL(*os,"Price","1");
428   
429   OutputToJDL(*os,"OutputDir",Form("%s/$1/#alien_counter_03i#",RemoteDir().Data()));
430
431   OutputToJDL(*os,"Executable","/alice/bin/aliroot_new");
432   
433   TObjArray files;
434   files.SetOwner(kTRUE);
435   TIter next(LocalFileList());
436   TObjString* file;
437   
438   while ( ( file = static_cast<TObjString*>(next())) )
439   {
440     if ( !file->String().Contains(".jdl",TString::kIgnoreCase) ||
441          !file->String().Contains("OCDB_") )
442     {
443       files.Add(new TObjString(Form("LF:%s/%s",RemoteDir().Data(),file->String().Data())));
444     }
445   }
446   
447   if ( fUseOCDBSnapshots )
448   {
449     files.Add(new TObjString(Form("LF:%s/OCDB/$1/OCDB_sim.root",RemoteDir().Data())));
450     files.Add(new TObjString(Form("LF:%s/OCDB/$1/OCDB_rec.root",RemoteDir().Data())));
451   }
452   
453   OutputToJDL(*os,"InputFile",files);
454   
455   if ( CompactMode() == 0 )
456   {
457     // store everything
458     OutputToJDL(*os,"OutputArchive",  "log_archive.zip:stderr,stdout,aod.log,checkaod.log,checkesd.log,rec.log,sim.log@disk=1",
459            "root_archive.zip:galice*.root,Kinematics*.root,TrackRefs*.root,AliESDs.root,AliAOD.root,AliAOD.Muons.root,Merged.QA.Data.root,Run*.root@disk=2");
460   }
461   else if ( CompactMode() == 1 )
462   {
463     // keep only muon AODs and QA
464     OutputToJDL(*os,"OutputArchive",  "log_archive.zip:stderr,stdout,*.log@disk=1",
465            "root_archive.zip:galice*.root,AliAOD.Muons.root,Merged.QA.Data.root@disk=2");
466   }
467   else if ( CompactMode() == 2 )
468   {
469     // keep only AODs and QA
470     OutputToJDL(*os,"OutputArchive",  "log_archive.zip:stderr,stdout,aod.log,checkaod.log,checkesd.log,rec.log,sim.log@disk=1",
471                 "root_archive.zip:galice*.root,AliAOD.root,Merged.QA.Data.root@disk=2");
472   }
473   else
474   {
475     AliError(Form("Unknown CompactMode %d",CompactMode()));
476     delete os;
477     return kFALSE;
478   }
479   
480   OutputToJDL(*os,"splitarguments","simrun.C --run $1 --chunk #alien_counter# --event $3");
481   
482   OutputToJDL(*os,"Workdirectorysize","5000MB");
483   
484   OutputToJDL(*os,"JDLVariables","Packages","OutputDir");
485
486   OutputToJDL(*os,"Validationcommand",Form("%s/validation.sh",RemoteDir().Data()));
487
488   if ( GetVar("VAR_GENERATOR").Contains("pythia",TString::kIgnoreCase) )
489   {
490     OutputToJDL(*os,"TTL","36000");
491   }
492   else
493   {
494     OutputToJDL(*os,"TTL","14400");
495   }
496   
497   return kTRUE;
498 }
499
500 //______________________________________________________________________________
501 Bool_t AliMuonAccEffSubmitter::MakeOCDBSnapshots()
502 {
503   /// Run sim.C and rec.C in a special mode to generate OCDB snapshots
504   /// Can only be done after the templates have been copied locally
505   
506   if (!IsValid()) return kFALSE;
507
508   if (!fUseOCDBSnapshots) return kTRUE;
509   
510   if (!NofRuns()) return kFALSE;
511   
512   AliDebug(1,"");
513
514   Bool_t ok(kTRUE);
515   
516   const std::vector<int>& runs = RunList();
517   
518   for ( std::vector<int>::size_type i = 0; i < runs.size(); ++i )
519   {
520     Int_t runNumber = runs[i];
521
522     TString ocdbSim(Form("%s/OCDB/%d/OCDB_sim.root",SnapshotDir().Data(),runNumber));
523     TString ocdbRec(Form("%s/OCDB/%d/OCDB_rec.root",SnapshotDir().Data(),runNumber));
524
525     if ( !gSystem->AccessPathName(ocdbSim.Data()) &&
526          !gSystem->AccessPathName(ocdbRec.Data()) )
527     {
528       AliWarning(Form("Local OCDB snapshots already there for run %d. Will not redo them. If you want to force them, delete them by hand !",runNumber));
529       continue;
530     }
531     else
532     {
533       gSystem->Exec(Form("aliroot -b -q -x simrun.C --run %d --snapshot",runNumber));
534     
535       if ( gSystem->AccessPathName(ocdbSim.Data()) )
536       {
537         AliError(Form("Could not create OCDB snapshot for simulation"));
538         ok = kFALSE;
539       }
540
541       if ( gSystem->AccessPathName(ocdbRec.Data()) )
542       {
543         AliError(Form("Could not create OCDB snapshot for reconstruction"));
544         ok = kFALSE;
545       }
546     }
547     
548     AddToLocalFileList(ocdbSim);
549     AddToLocalFileList(ocdbRec);
550   }
551   
552   return ok;
553 }
554
555 //______________________________________________________________________________
556 Bool_t AliMuonAccEffSubmitter::Merge(Int_t stage, Bool_t dryRun)
557 {
558   /// Submit multiple merging jobs with the format "submit AOD_merge(_final).jdl run# (stage#)".
559   /// Also produce the xml collection before sending jobs
560   /// Initial AODs will be taken from fRemoteDir/[RUNNUMBER] while the merged
561   /// ones will be put into fMergedDir/AODs/[RUNNUMBER]
562   ///
563   /// Example:
564   /// - inDir = "/alice/sim/2012/LHC12a10_bis" (where to find the data to merge)
565   ///         = 0x0 --> inDir = homeDir/outDir/resDir
566   /// - outDir = "Sim/LHC11h/embedding/AODs" (where to store merged results)
567   /// - runList.txt must contains the list of run number
568   /// - stage=0 --> final merging / stage>0 --> intermediate merging i
569   ///
570   
571   if (!RemoteDirectoryExists(MergedDir().Data())) {
572     AliError(Form("directory %s does not exist", MergedDir().Data()));
573     return kFALSE;
574   }
575   
576   gGrid->Cd(MergedDir().Data());
577   
578   TString jdl = MergeJDLName(stage==0);
579   
580   if (!RemoteFileExists(jdl.Data()))
581   {
582     AliError(Form("file %s does not exist in %s\n", jdl.Data(), RemoteDir().Data()));
583     return kFALSE;
584   }
585   
586   const std::vector<int>& runs = RunList();
587   
588   if (runs.empty())
589   {
590     AliError("No run to work with");
591     return 0;
592   }
593
594   TString currRun;
595   TString reply = "";
596   gSystem->Exec("rm -f __failed__");
597   Bool_t failedRun = kFALSE;
598   
599   for ( std::vector<int>::size_type i = 0; i < runs.size(); ++i )
600   {
601     Int_t run = runs[i];
602     AliInfo(Form("\n --- processing run %d ---\n", run));
603     
604     TString runDir = Form("%s/%d", MergedDir().Data(), run);
605     
606     if (!RemoteDirectoryExists(runDir.Data()))
607     {
608       AliInfo(Form(" - creating output directory %s\n", runDir.Data()));
609       gSystem->Exec(Form("alien_mkdir -p %s", runDir.Data()));
610     }
611     
612     if (RemoteFileExists(Form("%s/root_archive.zip", runDir.Data())))
613     {
614       AliWarning(" ! final merging already done");
615       continue;
616     }
617     
618     Int_t lastStage = GetLastStage(runDir.Data());
619     
620     if (stage > 0 && stage != lastStage+1)
621     {
622       AliError(Form(" ! lastest merging stage = %d. Next must be stage %d or final stage\n", lastStage, lastStage+1));
623       continue;
624     }
625     
626     TString wn = (stage > 0) ? Form("Stage_%d.xml", stage) : "wn.xml";
627     TString find = (lastStage == 0) ?
628     Form("alien_find -x %s %s/%d *root_archive.zip", wn.Data(), RemoteDir().Data(), run) :
629     Form("alien_find -x %s %s/%d/Stage_%d *root_archive.zip", wn.Data(), RemoteDir().Data(), run, lastStage);
630     gSystem->Exec(Form("%s 1> %s 2>/dev/null", find.Data(), wn.Data()));
631     gSystem->Exec(Form("grep -c /event %s > __nfiles__", wn.Data()));
632     ifstream f2("__nfiles__");
633     TString nFiles;
634     nFiles.ReadLine(f2,kTRUE);
635     f2.close();
636     gSystem->Exec("rm -f __nfiles__");
637     printf(" - number of files to merge = %d\n", nFiles.Atoi());
638     if (nFiles.Atoi() == 0) {
639       printf(" ! collection of files to merge is empty\n");
640       gSystem->Exec(Form("rm -f %s", wn.Data()));
641       continue;
642     } else if (stage > 0 && nFiles.Atoi() <= splitLevel && !reply.BeginsWith("y")) {
643       if (!reply.BeginsWith("n")) {
644         printf(" ! number of files to merge <= split level (%d). Continue? [Y/n] ", splitLevel);
645         fflush(stdout);
646         reply.Gets(stdin,kTRUE);
647         reply.ToLower();
648       }
649       if (reply.BeginsWith("n")) {
650         gSystem->Exec(Form("rm -f %s", wn.Data()));
651         continue;
652       } else reply = "y";
653     }
654     
655     if (!dryRun)
656     {
657       TString dirwn = Form("%s/%s", runDir.Data(), wn.Data());
658       if (RemoteFileExists(dirwn.Data())) gGrid->Rm(dirwn.Data());
659       gSystem->Exec(Form("alien_cp file:%s alien://%s", wn.Data(), dirwn.Data()));
660       gSystem->Exec(Form("rm -f %s", wn.Data()));
661     }
662     
663     TString query;
664     if (stage > 0) query = Form("submit %s %d %d", jdl.Data(), run, stage);
665     else query = Form("submit %s %d", jdl.Data(), run);
666     printf(" - %s ...", query.Data());
667     fflush(stdout);
668     
669     if (dryRun)
670     {
671       AliInfo(" dry run");
672       continue;
673     }
674     
675     Bool_t done = kFALSE;
676     TGridResult *res = gGrid->Command(query);
677     if (res)
678     {
679       TString cjobId1 = res->GetKey(0,"jobId");
680       if (!cjobId1.IsDec())
681       {
682         AliError(" FAILED");
683         gGrid->Stdout();
684         gGrid->Stderr();
685       }
686       else
687       {
688         AliInfo(Form(" DONE\n   --> the job Id is: %s \n", cjobId1.Data()));
689         done = kTRUE;
690       }
691       delete res;
692     }
693     else
694     {
695       AliError(" FAILED");
696     }
697     
698     if (!done)
699     {
700       gSystem->Exec(Form("echo %d >> __failed__", run));
701       failedRun = kTRUE;
702     }
703     
704   }
705   
706   if (failedRun)
707   {
708     AliInfo("\n--------------------\n");
709     AliInfo("list of failed runs:\n");
710     gSystem->Exec("cat __failed__");
711     gSystem->Exec("rm -f __failed__");
712     return kFALSE;
713   }
714   
715   return kTRUE;
716 }
717
718 //______________________________________________________________________________
719 void AliMuonAccEffSubmitter::Print(Option_t* opt) const
720 {
721   /// Printout
722   
723   AliMuonGridSubmitter::Print(opt);
724
725   if ( fRatio > 0 )
726   {
727     std::cout << std::endl << Form("-- For each run, will generate %5.2f times the number of real events for trigger %s",
728                       fRatio,ReferenceTrigger().Data()) << std::endl;
729   }
730   else
731   {
732     std::cout << std::endl <<  Form("-- For each run, will generate %10d events",fFixedNofEvents) << std::endl;
733   }
734   
735   std::cout << "-- MaxEventsPerChunk = " << fMaxEventsPerChunk << std::endl;
736   
737   std::cout << "-- Will" << (fUseOCDBSnapshots ? "" : " NOT") << " use OCDB snaphosts" << std::endl;
738 }
739
740 //______________________________________________________________________________
741 Bool_t AliMuonAccEffSubmitter::Run(const char* mode)
742 {
743   /// mode can be one of (case insensitive)
744   ///
745   /// LOCAL : copy the template files from the template directory to the local one
746   /// UPLOAD : copy the local files to the grid (requires LOCAL)
747   /// OCDB : make ocdb snapshots (requires LOCAL)
748   /// SUBMIT : submit the jobs (requires LOCAL + UPLOAD)
749   /// FULL : all of the above (requires all of the above)
750   ///
751   /// TEST : as SUBMIT, but in dry mode (does not actually submit the jobs)
752   ///
753   /// LOCALTEST : completely local test (including execution)
754   
755   if (!IsValid()) return kFALSE;
756   
757   TString smode(mode);
758   smode.ToUpper();
759   
760   if ( smode == "FULL")
761   {
762     return  ( Run("LOCAL") && Run("OCDB") && Run("UPLOAD") && Run("SUBMIT") );
763   }
764   
765   if ( smode == "LOCAL")
766   {
767     return CopyTemplateFilesToLocal();
768   }
769   
770   if ( smode == "UPLOAD" )
771   {
772     return (CopyLocalFilesToRemote());
773   }
774   
775   if ( smode == "OCDB" )
776   {
777     Bool_t ok = Run("LOCAL");
778     if (ok)
779     {
780       ok = MakeOCDBSnapshots();
781     }
782     return ok;
783   }
784   
785   if ( smode == "TEST" )
786   {
787     Bool_t ok = Run("LOCAL") && Run("OCDB") && Run("UPLOAD");
788     if ( ok )
789     {
790       ok = (Submit(kTRUE)>0);
791     }
792     return ok;
793   }
794   
795   if ( smode == "FULL" )
796   {
797     Bool_t ok = Run("LOCAL")  && Run("OCDB") && Run("UPLOAD");
798     if ( ok )
799     {
800       ok = (Submit(kFALSE)>0);
801     }
802     return ok;
803   }
804
805   if( smode == "SUBMIT" )
806   {
807     return (Submit(kFALSE)>0);
808   }
809   
810   if ( smode == "LOCALTEST" )
811   {
812     Bool_t ok = Run("LOCAL");
813     if ( ok )
814     {
815       ok = LocalTest();
816     }
817     return ok;
818   }
819   
820   return kFALSE;
821 }
822
823 //______________________________________________________________________________
824 Bool_t AliMuonAccEffSubmitter::SetGenerator(const char* generator)
825 {
826   // set the variable to select the generator macro in Config.C
827   
828   gSystem->Load("libEVGEN");
829   
830   Invalidate();
831   
832   TString generatorFile(Form("%s/%s.C",TemplateDir().Data(),generator));
833   
834   Int_t nofMissingVariables(0);
835   
836   // first check we indeed have such a macro
837   if (!gSystem->AccessPathName(generatorFile.Data()))
838   {
839     TObjArray* variables = GetVariables(generatorFile.Data());
840     
841     TIter next(variables);
842     TObjString* var;
843     
844     while ( ( var = static_cast<TObjString*>(next())) )
845     {
846       if ( !Vars()->GetValue(var->String()) )
847       {
848         ++nofMissingVariables;
849         AliError(Form("file %s expect the variable %s to be defined, but we've not defined it !",generatorFile.Data(),var->String().Data()));
850       }
851     }
852     
853     delete variables;
854     
855     if ( !nofMissingVariables )
856     {
857       if (CheckCompilation(generatorFile.Data()))
858       {
859         Validate();
860         SetVar("VAR_GENERATOR",Form("%s",generator));        
861         AddToTemplateFileList(Form("%s.C",generator));
862         return kTRUE;
863       }
864     }
865     else
866     {
867       return kFALSE;
868     }
869   }
870   else
871   {
872     AliError(Form("Can not work with the macro %s",generatorFile.Data()));
873   }
874   return kFALSE;
875 }
876
877 //______________________________________________________________________________
878 void AliMuonAccEffSubmitter::SetOCDBPath(const char* ocdbPath)
879 {
880   /// Sets the OCDB path to be used
881   
882   SetMapKeyValue("OCDBPath",ocdbPath);
883 }
884
885
886 //______________________________________________________________________________
887 void AliMuonAccEffSubmitter::SetOCDBSnapshotDir(const char* dir)
888 {
889   // change the directory used for snapshot
890   
891   if (gSystem->AccessPathName(Form("%s/OCDB",dir)))
892   {
893     AliError(Form("Snapshot top directory (%s) should contain an OCDB subdir with runnumbers in there",dir));
894   }
895   else
896   {
897     SetMapKeyValue("OCDBSnapshot",dir);
898   }
899 }
900
901 //______________________________________________________________________________
902 void AliMuonAccEffSubmitter::MakeNofEventsPropToTriggerCount(const char* trigger, Float_t ratio)
903 {
904   SetMapKeyValue("ReferenceTrigger",trigger);
905   fRatio = ratio;
906 }
907
908 //______________________________________________________________________________
909 void AliMuonAccEffSubmitter::MakeNofEventsFixed(Int_t nevents)
910 {
911   fFixedNofEvents = nevents;
912   fRatio=0.0;
913   SetMapKeyValue("ReferenceTrigger","");
914 }
915
916 //______________________________________________________________________________
917 Int_t AliMuonAccEffSubmitter::LocalTest()
918 {
919   /// Generate a local macro (simrun.sh) to execute locally a full scale test
920   /// Can only be used with a fixed number of events (and runnumber is fixed to zero)
921   
922   if ( fRatio > 0 )
923   {
924     AliError("Can only work in local test with a fixed number of events");
925     return 0;
926   }
927   
928   if ( fFixedNofEvents <= 0 )
929   {
930     AliError("Please fix the number of input events using MakeNofEventsFixed()");
931     return 0;
932   }
933   
934   const std::vector<int>& runs = RunList();
935
936   if ( runs.empty() )
937   {
938     AliError("No run to work with");
939     return 0;
940   }
941   
942   std::cout << "Generating script to execute : ./simrun.sh" << std::endl;
943   
944   std::ofstream out("simrun.sh");
945   
946   out << "#!/bin/bash" << std::endl;
947 //  root.exe -b -q simrun.C  --run <x> --chunk <y> --event <n>
948   out << "root.exe -b -q simrun.C --run "<< runs[0] <<" --event " << fFixedNofEvents << std::endl;
949
950   out.close();
951
952   gSystem->Exec("chmod +x simrun.sh");
953
954   std::cout << "Cleaning up left-over files from previous simulation/reconstructions" << std::endl;
955   
956   gSystem->Exec("rm -rf TrackRefs.root *.SDigits*.root Kinematics.root *.Hits.root geometry.root gphysi.dat Run*.tag.root HLT*.root *.ps *.Digits.root *.RecPoints.root galice.root *QA*.root Trigger.root *.log AliESD* AliAOD* *.d *.so *.stat");
957
958   std::cout << "Executing the script : ./simrun.sh" << std::endl;
959
960
961   gSystem->Exec("./simrun.sh");
962   
963   return 1;
964 }
965
966 namespace  {
967
968   void OutputRunList(const char* filename, const std::vector<int>& runlist)
969   {
970     /// output a runlist to ASCII file
971     
972     std::ofstream out(filename);
973
974     for ( std::vector<int>::size_type j = 0; j < runlist.size(); ++j )
975     {
976       out << runlist[j] << std::endl;
977     }
978   }
979 }
980
981 //______________________________________________________________________________
982 Int_t AliMuonAccEffSubmitter::SplitRunList(const char* inputList, int maxJobs)
983 {
984   /// In order to be able to submit, split a given runlist into chunks that will
985   /// fit within maxJobs (1500 for a typical user)
986
987   std::vector<int> runs;
988   
989   AliAnalysisTriggerScalers tmp(inputList);
990   runs = tmp.GetRunList();
991   
992   AliAnalysisTriggerScalers* ts(0x0);
993   std::vector<int> currentRunList;
994   
995   int nJobs(0);
996   int nTotalJobs(0);
997   int nEvts(0);
998   int nFiles(0);
999   
1000   for (std::vector<int>::size_type i=0; i < runs.size(); ++i)
1001   {
1002     Int_t runNumber = runs[i];
1003   
1004     Int_t nEvtRun(fFixedNofEvents);
1005     
1006     if ( fRatio > 0 )
1007     {
1008       if (!ts)
1009       {
1010         AliInfo(Form("Creating AliAnalysisTriggerScalers from OCDB=%s",OCDBPath().Data()));
1011         ts = new AliAnalysisTriggerScalers(runs,OCDBPath().Data());
1012       }
1013       
1014       AliAnalysisTriggerScalerItem* trigger = ts->GetTriggerScaler(runNumber, "L2A", ReferenceTrigger().Data());
1015       
1016       if (!trigger)
1017       {
1018         AliError(Form("Could not get trigger %s for run %09d",ReferenceTrigger().Data(),runNumber));
1019         continue;
1020       }
1021       nEvtRun = TMath::Nint(fRatio * trigger->Value());
1022     }
1023     
1024     Int_t nChunk = 1;
1025     
1026     while (nEvtRun/nChunk+0.5 > MaxEventsPerChunk())
1027     {
1028       ++nChunk;
1029     }
1030     
1031     Int_t nEvtChunk = TMath::Nint(nEvtRun/nChunk + 0.5);
1032     
1033     nJobs += nChunk;
1034     
1035     nTotalJobs += nChunk;
1036     
1037     nEvts += nChunk*nEvtChunk;
1038
1039     if ( nJobs > maxJobs )
1040     {
1041       ++nFiles;
1042       
1043       OutputRunList(Form("%s.%d",inputList,nFiles),currentRunList);
1044       nJobs = 0;
1045       currentRunList.clear();
1046     }
1047     
1048     
1049     currentRunList.push_back(runNumber);
1050     
1051   }
1052   
1053   if ( !currentRunList.empty() )
1054   {
1055     ++nFiles;
1056     OutputRunList(Form("%s.%d",inputList,nFiles),currentRunList);
1057
1058   }
1059   
1060   delete ts;
1061   
1062   std::cout << Form("input run list was split into %d files. Total number of jobs %d. Total number of events %d",
1063                     nFiles,nTotalJobs,nEvts) << std::endl;
1064   
1065   return nFiles;
1066 }
1067
1068
1069 //______________________________________________________________________________
1070 Int_t AliMuonAccEffSubmitter::Submit(Bool_t dryRun)
1071 {
1072   /// Submit multiple production jobs with the format "submit jdl 000run#.xml 000run#".
1073   ///
1074   /// Return the number of submitted (master) jobs
1075   ///
1076   /// Example:
1077   /// - outputDir = "/alice/cern.ch/user/p/ppillot/Sim/LHC10h/JPsiPbPb276/AlignRawVtxRaw/ESDs"
1078   /// - runList must contains the list of run number
1079   /// - trigger is the (fully qualified) trigger name used to compute the base number of events
1080   /// - mult is the factor to apply to the number of trigger to get the number of events to be generated
1081   ///   (# generated events = # triggers x mult
1082   
1083   if (!IsValid()) return 0;
1084   
1085   AliDebug(1,"");
1086
1087   gGrid->Cd(RemoteDir());
1088   
1089   if (!RemoteFileExists(RunJDLName()))
1090   {
1091     AliError(Form("file %s does not exist in %s", RunJDLName().Data(), RemoteDir().Data()));
1092     return 0;
1093   }
1094   
1095   if ( !NofRuns() )
1096   {
1097     AliError("No run list set. Use SetRunList");
1098     return 0;
1099   }
1100   const std::vector<int>& runs = RunList();
1101   
1102   if (runs.empty())
1103   {
1104     AliError("No run to work with");
1105     return 0;
1106   }
1107   
1108   //  cout << "total number of selected MB events = " << totEvt << endl;
1109   //  cout << "required number of generated events = " << nGenEvents << endl;
1110   //  cout << "number of generated events per MB event = " << ratio << endl;
1111   //  cout << endl;
1112   
1113   std::cout << "run\tchunks\tevents" << std::endl;
1114   std::cout << "----------------------" << std::endl;
1115   
1116   Int_t nJobs(0);
1117   Int_t nEvts(0);
1118   
1119   AliAnalysisTriggerScalers* ts(0x0);
1120   
1121   for (std::vector<int>::size_type i=0; i < runs.size(); ++i)
1122   {
1123     Int_t runNumber = runs[i];
1124     
1125     Int_t nEvtRun(fFixedNofEvents);
1126     
1127     if ( fRatio > 0 )
1128     {
1129       if (!ts)
1130       {
1131         AliInfo(Form("Creating AliAnalysisTriggerScalers from OCDB=%s",OCDBPath().Data()));
1132         ts = new AliAnalysisTriggerScalers(runs,OCDBPath().Data());
1133       }
1134       
1135       AliAnalysisTriggerScalerItem* trigger = ts->GetTriggerScaler(runNumber, "L2A", ReferenceTrigger().Data());
1136     
1137       if (!trigger)
1138       {
1139         AliError(Form("Could not get trigger %s for run %09d",ReferenceTrigger().Data(),runNumber));
1140         continue;
1141       }
1142       nEvtRun = TMath::Nint(fRatio * trigger->Value());
1143     }
1144     
1145     Int_t nChunk = 1;
1146     
1147     while (nEvtRun/nChunk+0.5 > MaxEventsPerChunk())
1148     {
1149       ++nChunk;
1150     }
1151     
1152     Int_t nEvtChunk = TMath::Nint(nEvtRun/nChunk + 0.5);
1153     
1154     nJobs += nChunk;
1155     
1156     nEvts += nChunk*nEvtChunk;
1157     
1158     std::cout << runNumber << "\t" << nChunk << "\t" << nEvtChunk << std::endl;
1159     
1160     TString query(Form("submit %s %d %d %d", RunJDLName().Data(), runNumber, nChunk, nEvtChunk));
1161     
1162     std::cout << query.Data() << " ..." << std::flush;
1163     
1164     TGridResult* res = 0x0;
1165     
1166     if (!dryRun)
1167     {
1168       res = gGrid->Command(query);
1169     }
1170     
1171     if (res)
1172     {
1173       TString cjobId1 = res->GetKey(0,"jobId");
1174       
1175       if (!cjobId1.Length())
1176       {
1177         std::cout << " FAILED" << std::endl << std::endl;
1178         gGrid->Stdout();
1179         gGrid->Stderr();
1180       }
1181       else
1182       {
1183         std::cout << "DONE" << std::endl;
1184         std::cout << Form("   --> the job Id is: %s",cjobId1.Data()) << std::endl << std::endl;
1185       }
1186     }
1187     else
1188     {
1189       std::cout << " FAILED" << std::endl << std::endl;
1190     }
1191     
1192     delete res;
1193   }
1194   
1195   std::cout << std::endl
1196   << "total number of jobs = " << nJobs << std::endl
1197   << "total number of generated events = " << nEvts << std::endl
1198   << std::endl;
1199   
1200   delete ts;
1201   
1202   return nJobs;
1203 }
1204
1205 //______________________________________________________________________________
1206 void AliMuonAccEffSubmitter::UpdateLocalFileList(Bool_t clearSnapshots)
1207 {
1208   /// Update the list of local files
1209   
1210   AliMuonGridSubmitter::UpdateLocalFileList();
1211   
1212   if (!NofRuns()) return;
1213   
1214   if ( clearSnapshots )
1215   {
1216     TIter next(LocalFileList());
1217     TObjString* file;
1218     
1219     while ( ( file = static_cast<TObjString*>(next())) )
1220     {
1221       if ( file->String().Contains("OCDB_") )
1222       {
1223         LocalFileList()->Remove(file);
1224       }
1225     }
1226     LocalFileList()->Compress();
1227   }
1228
1229   const char* type[] = { "sim","rec" };
1230   
1231   const std::vector<int>& runs = RunList();
1232   
1233   for ( std::vector<int>::size_type i = 0; i < runs.size(); ++i )
1234   {
1235     Int_t runNumber = runs[i];
1236     
1237     for ( Int_t t = 0; t < 2; ++t )
1238     {
1239       TString snapshot(Form("%s/OCDB/%d/OCDB_%s.root",SnapshotDir().Data(),runNumber,type[t]));
1240       
1241       if ( !gSystem->AccessPathName(snapshot.Data()) )
1242       {
1243         AddToLocalFileList(snapshot);
1244       }
1245     }
1246   }
1247 }
1248
1249 //______________________________________________________________________________
1250 void AliMuonAccEffSubmitter::UseOCDBSnapshots(Bool_t flag)
1251 {
1252   /// Whether or not to use OCDB snapshots
1253   /// Using OCDB snapshots will speed-up both the sim and reco initialization
1254   /// phases on each worker node, but takes time to produce...
1255   /// So using them is not always a win-win...
1256   
1257   fUseOCDBSnapshots = flag;
1258   if ( flag )
1259   {
1260     SetVar("VAR_OCDB_SNAPSHOT","kTRUE");
1261     
1262     // for some reason must include ITS objects in the snapshot
1263     // (to be able to instantiante the vertexer later on ?)
1264     
1265     SetVar("VAR_USE_ITS_RECO","1");
1266   }
1267   else
1268   {
1269     SetVar("VAR_OCDB_SNAPSHOT","kFALSE");
1270   }
1271   
1272   UpdateLocalFileList();
1273 }
1274
1275 //______________________________________________________________________________
1276 void AliMuonAccEffSubmitter::UseAODMerging(Bool_t flag)
1277 {
1278   /// whether or not we should generate JDL for merging AODs
1279   
1280   fUseAODMerging = flag;
1281   
1282   AddToTemplateFileList(MergeJDLName(kFALSE).Data());
1283   AddToTemplateFileList(MergeJDLName(kTRUE).Data());
1284   AddToTemplateFileList("AOD_merge.sh");
1285   AddToTemplateFileList("validation_merge.sh");
1286 }
1287
1288 //______________________________________________________________________________
1289 void AliMuonAccEffSubmitter::UseExternalConfig(const char* externalConfigFullFilePath)
1290 {
1291   // use an external config (or the default Config.C if externalConfigFullFilePath="")
1292   
1293   fExternalConfig = externalConfigFullFilePath;
1294   if ( fExternalConfig.Length() > 0 )
1295   {
1296     AddToTemplateFileList(fExternalConfig);
1297   }
1298   else
1299   {
1300     AddToTemplateFileList("Config.C");
1301   }
1302 }