ROOT logo
/**
 * @file   GridHelper.C
 * @author Christian Holm Christensen <cholm@master.hehi.nbi.dk>
 * @date   Tue Oct 16 19:01:27 2012
 * 
 * @brief  Grid Analysis Helper
 * 
 * @ingroup pwglf_forward_trains_helper
 * 
 */
#ifndef GRIDHELPER_C
#define GRIDHELPER_C
#include "PluginHelper.C"
#ifndef __CINT__
# include <TUrl.h>
# include <TString.h>
# include <TGrid.h>
# include <AliAnalysisManager.h>
# include <AliAnalysisAlien.h>
#else
class TUrl;
class AliAnalysisAlien;
#endif

// ===================================================================
/**
 * Handle analysis on an the Grid
 * 
 * This helper is triggered by a URL of the form 
 *
 * @code
 * alien:///<directory>[?<options>][#<pattern>]
 * @endcode 
 * where 
 * <dl>
 *   <dt>&lt;directory&gt;</dt>
 *   <dd>Grid directory that holds the data</dd>
 *   <dt>&lt;treeName&gt;</dt>
 *   <dd>Tree to loop over</dd>
 *   <dt>&lt;options&gt;</dt>
 *   <dd>List of options separated by an &amp;
 *     <dl>
 *       <dt><tt>storage=&lt;url&gt;</tt></dt>
 *       <dd>Specify a non-default storage location for special output
 *         (e.g., AOD trees).  &lt;url&gt; should be a valid XRootd 
 *         server URI accessible to the slaves - e.g., 
 *         <tt>root://lxplus.cern.ch:10930//tmp</tt>.</dd>
 *       <dt><tt>mode=[default,rec,sim,train,custom]</tt></dt>
 *       <dd>Set the AliROOT mode.  If not specified <tt>default</tt> 
 *         is assumed.  See also CreateAliROOTPar</dd>
 *       <dt><tt>par</tt></dt>
 *       <dd> Use PAR files</dd>
 *       <dt><tt>runs=[list or file]</tt></dt>
 *       <dd>Comma separated list of run numbers, or file(s) containing 
 *         run numbers</dd> 
 *       <dt><tt>oper=[FULL,TERMINATE,SUBMIT,OFFLINE,TEST]</tt></dt>
 *       <dd>How to run the analysis</dd>
 *       <dt><tt>split=&lt;N&gt;</tt></dt>
 *       <dd>Maximum number of files per split</dd>
 *       <dt><tt>merge=&lt;N&gt;</tt></dt>
 *       <dd>Maximum number of files per merger</dd>
 *       <dt><tt>mc</tt></dt>
 *       <dd>Scan also for MC files (<tt>galice.root</tt>, 
 *          <tt>Kinematics.root</tt>, and <tt>TrackRefs.root</tt>) when 
 *          scanning &lt;datadir&gt;</dd>
 *       <dt><tt>pattern=&lt;GLOB&gt;</tt></dt>
 *       <dd>Shell glob pattern that files must check when scanning 
 *         &lt;datadir&gt;</dd>
 *     </dl>
 *   </dd>
 * </dl>  
 *
 * @ingroup pwglf_forward_trains_helper
 */
struct GridHelper : public PluginHelper
{
  /** 
   * Constructor 
   * 
   * @param url  Url 
   * @param verbose Verbosity level
   */
  GridHelper(const TUrl& url, Int_t verbose)
    : PluginHelper(url, verbose), fRuns()
  {
    // Note, split, merge, and ttl are by default set to values
    // optimized for AOD production on real PbPb data.
    //
    // TTL shouldn't be much smaller than 4h10m.  Split and merge
    // shouldn't be much larger than 75, but probably not smaller than
    // 50.
    fOptions.Add("oper", "FULL|TERMINATE|SUBMIT", "Analysis operation", "FULL");
    fOptions.Add("split",  "N|max",  "Max number of files before split","50");
    fOptions.Add("merge",  "N|max",  "Max number of files for merge",   "50");
    fOptions.Add("run",    "RUNS",   "Range, list, and/or file of runs", "");
    fOptions.Add("alien",  "VERSION","Alien API version",              "V1.1x");
    fOptions.Add("ttl",    "N|max",  "Time to live",                   "6h");
    fOptions.Add("pattern","GLOB",   "File/directory name pattern", "");
    fOptions.Add("concat", "Concatenate all runs");
  }
  GridHelper(const GridHelper& o)
    : PluginHelper(o), fRuns()
  {}
  GridHelper& operator=(const GridHelper& o)
  {
    if (&o == this) return *this;
    PluginHelper::operator=(o);
    return *this;
  }
  virtual ~GridHelper() {}
  /** 
   * Get the mode identifier 
   * 
   * @return Always kProof
   */
  virtual UShort_t Mode() const { return kGrid; }
  /**
   * Get the mode string used for AliAnalysisManager::StartAnalysis
   */
  virtual const char* ModeString() const { return "grid"; }
  /** 
   * Set-up done before task set-ups 
   * 
   * @return true on success 
   */
  virtual UShort_t Operation() const 
  {
    if (!fOptions.Has("oper")) return kFull;
    const TString& oper = fOptions.Get("oper");
    if      (oper.EqualTo("FULL",      TString::kIgnoreCase)) return kFull;
    else if (oper.EqualTo("OFFLINE",   TString::kIgnoreCase)) return kOffline;
    else if (oper.EqualTo("SUBMIT",    TString::kIgnoreCase)) return kSubmit;
    else if (oper.EqualTo("TERMINATE", TString::kIgnoreCase)) return kTerminate;
    else if (oper.EqualTo("TEST",      TString::kIgnoreCase)) return kTest;
    return kFull;
  }
  void StoreRun(Int_t r)
  {
    TObject* o = new TObject;
    o->SetUniqueID(r);
    fRuns.Add(o);
  }
  /**
   * Read run numbers 
   *
   * @return Number of registered runs 
   */
  virtual Int_t RegisterRuns()
  {
    if (!fOptions.Find("run")) {
      Error("GridHelper::RegisterRuns", "No runs specified");
      return -1;
    }
    Int_t       nRuns  = 0;
    TString     runs   = fOptions.Get("run");
    TObjArray*  tokens = runs.Tokenize(",+:");
    TObjString* part   = 0;
    TIter       next(tokens);
    Bool_t      range  = false;
    Bool_t      individual = false;
    // Info("GridHelper::RegisterRuns", "Runs specified are %s", runs.Data());
    while ((part = static_cast<TObjString*>(next()))) {
      TString& s = part->String();
      if (s.Contains("-")) { // Run range 
	if (range) { 
	  Warning("GridHelper::RegisterRuns", "Run range already specified, "
		  "ignoring %s", s.Data());
	  continue;
	}
	if (individual) { 
	  Warning("GridHelper::RegisterRuns", 
		  "Run ranges and individual run specs do not mix, "
		  "ignoring %s", s.Data());
	  continue;
	}
	TObjArray* ranges = s.Tokenize("-");
	if (ranges->GetEntriesFast() > 2) { 
	  Warning("GridHelper::RegisterRuns", "Invalid run range: %s", 
		  s.Data());
	  ranges->Delete();
	  continue;
	}
	Int_t first = static_cast<TObjString*>(ranges->At(0))->String().Atoi();
	Int_t last  = static_cast<TObjString*>(ranges->At(1))->String().Atoi();
	nRuns       = last-first+1;
	// Info("GridHelper::RegisterRuns", "Run range %d -> %d", first, last);
	fHandler->SetRunRange(first, last);
	ranges->Delete();
	range = true;
	for (Int_t r = first; r <= last; r++) StoreRun(r);
	continue;
      }
      if (s.IsDigit()) { // single run
	if (range) { 
	  Warning("GridHelper::RegisterRuns", 
		  "Run ranges and individual run specs do not mix, "
		  "ignoring %s", s.Data());
	  continue;
	}
	// Info("GridHandler::RegisterRuns", "Adding run %s", s.Data());
	fHandler->AddRunNumber(s.Atoi());
	StoreRun(s.Atoi());
	nRuns++;
	individual = true;
	continue;
      }
      if (range) { 
	Warning("GridHelper::RegisterRuns", "Run ranges and list file "
		"do not mix, ignoring %s", s.Data());
	continue;
      }

      // We assume this part is a file 
      // Info("GridHelper::RegisterRuns", "Reading runs from %s", s.Data());
      std::ifstream in(s.Data());
      if (!in) { 
	s.Prepend("../");
	in.open(s.Data());
	if (!in) {
	  Warning("GridHelper::RegisterRuns", "Failed to open %s", s.Data());
	  continue;
	}
      }
      while (!in.eof()) {
	TString lne;
	lne.ReadLine(in);

	TString bare = lne.Strip(TString::kBoth);
	if (bare[0] == '#') continue;

	TObjArray* ltokens = bare.Tokenize(" \t,");
	TIter lnext(ltokens);
	TObjString* str = 0;
	while ((str = static_cast<TObjString*>(lnext()))) {
	  const TString& token = str->String();
	  if (!token.IsDigit()) continue;
	  
	  int r = token.Atoi();
	  fHandler->AddRunNumber(r);
	  StoreRun(r);
	  nRuns++;
	}
	ltokens->Delete();
      }
#if 0
      while (!in.eof()) { 
	Int_t r;
	in >> r;
	// Info("GridHelper::RegisterRuns", "Read %d, adding", r);
	fHandler->AddRunNumber(r);
	StoreRun(r);
	nRuns++;
	Char_t c;
	in >> c;
	if (in.bad()) break;
      }
#endif
      individual = true;
      in.close();
    }
    return nRuns;
  }
  /** 
   * Executed before setting up tasks 
   * 
   * @return true on success 
   */
  virtual Bool_t PreSetup() 
  {
    if (!PluginHelper::PreSetup()) return false;
    
    // --- Add system library dir to load path -----------------------
    gSystem->AddDynamicPath("/usr/lib");

    // --- Open a connection to the grid -----------------------------
    if (!TGrid::Connect(Form("%s://", fUrl.GetProtocol()))) { 
      Error("GridHelper::PreSetup", "Failed to connect to AliEN");
      return false;
    }
    if (!gGrid || !gGrid->IsConnected()) { 
      Error("GridHelper::PreSetup", "Failed to connect to AliEN");
      return false;
    }

    return true;
  }
  /** 
   * Set-up done after the task set-ups 
   *
   * @return true on success 
   */
  virtual Bool_t PostSetup() 
  {
    // Info("GridHelper::PostSetup", "Calling super.PostSetup");
    if (!PluginHelper::PostSetup()) return false;

    // --- API version -----------------------------------------------
    fHandler->SetAPIVersion(fOptions.Get("alien"));
    
    // --- Get the name ----------------------------------------------
    // Info("GridHelper", "Proceeding with plugin setup");
    AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
    TString name(mgr->GetName());

    // --- Set the operation to do (TEST, SUBMIT, TERMINATE, FULL) ---
    TString operation("FULL");
    if (fOptions.Has("oper")) operation = fOptions.Get("oper");
    fHandler->SetRunMode(operation);

    // --- Add the run numbers ---------------------------------------
    fHandler->SetRunPrefix(fOptions.Has("mc") ? "%d" : "%09d");
    Int_t nRun = RegisterRuns();

    // --- Do not test copying ---------------------------------------
    fHandler->SetCheckCopy(false);
    
    // --- Set output to be per run ----------------------------------
    fHandler->SetOutputToRunNo(true); 

    // --- Set the job tag -------------------------------------------
    fHandler->SetJobTag(name);

    // --- Set number of test files - used in test mode only ---------
    fHandler->SetNtestFiles(1);

    // --- Set the Time-To-Live --------------------------------------
    if (fOptions.Has("ttl")) { 
      TString sttl = fOptions.Get("ttl");
      if (!sttl.EqualTo("max")) {
	Int_t   ttl  = 0;
	if (sttl.IsDigit()) ttl = sttl.Atoi();
	else { 
	  // Parse string of the form <DAYS>d<HOURS>h<MINUTES>m<SECONDS>s
	  Int_t id = sttl.Index("d", 0);
	  if (id == kNPOS) id = -1;
	  else { 
	    TString sdays(sttl(0,id));
	    ttl += 24 * 60 * 60 * sdays.Atoi();
	  }
	  Int_t ih = sttl.Index("h", id+1);
	  if (ih == kNPOS) ih = id;
	  else { 
	    TString shour(sttl(id+1,ih-id-1));
	    ttl += 60 * 60 * shour.Atoi();
	  }
	  Int_t im = sttl.Index("m", ih+1);
	  if (im == kNPOS) im = ih;
	  else { 
	    TString smin(sttl(ih+1, im-ih-1));
	    ttl += 60 * smin.Atoi();
	  }
	  Int_t is = sttl.Index("s", im+1);
	  if (is != kNPOS) { 
	    TString ssec(sttl(im+1, is-im-1));
	    ttl += ssec.Atoi();
	  }
	}
	if (ttl != 0) fHandler->SetTTL(ttl);
	else 
	  Warning("", "Option ttl given but no value found");
      }
    }
    
    // --- Re-submit failed jobs as long as the ratio of failed jobs -
    // --- is this percentage.
    fHandler->SetMasterResubmitThreshold(95);

    // --- Set the input format --------------------------------------
    fHandler->SetInputFormat("xml-single");

    // --- Set names of generated files ------------------------------
    fHandler->SetAnalysisMacro(Form("%s.C", name.Data()));
    fHandler->SetJDLName(Form("%s.jdl", name.Data()));
    fHandler->SetExecutable(Form("%s.sh", name.Data()));
    
    // ---- Set the job price !? -------------------------------------
    fHandler->SetPrice(1);

    // --- Set whether to merge via JDL ------------------------------
    fHandler->SetMergeViaJDL(true);
    
    // --- Fast read otion -------------------------------------------
    fHandler->SetFastReadOption(false);

    // --- Whether to overwrite existing output ----------------------
    fHandler->SetOverwriteMode(true);

    // --- Set the executable binary name and options ----------------
    fHandler->SetExecutableCommand("aliroot -b -q -x");

    // --- Split by storage element - must be lower case! ------------
    fHandler->SetSplitMode("se");

    // --- How much to split -----------------------------------------
    if (fOptions.Has("split")) { 
      if (!fOptions.Get("split").EqualTo("max")) {
	fHandler->SetSplitMaxInputFileNumber(fOptions.AsInt("split"));
      }
    }
    // --- Merge parameters ------------------------------------------
    if (fOptions.Has("merge")) { 
      if (!fOptions.Get("merge").EqualTo("max")) { 
	fHandler->SetMaxMergeFiles(fOptions.AsInt("merge"));
      }
    }
    fHandler->SetMergeExcludes("AliAOD.root *EventStat*.root "
			       "*event_stat*.root");
    
    // --- Set number of runs per master - 1 or all ------------------
    fHandler->SetNrunsPerMaster(fOptions.Has("concat") ? nRun+1 : 1);


    // --- Enable default outputs ------------------------------------
    fHandler->SetDefaultOutputs(true);

    // --- Keep log files ------------------------------------------
    fHandler->SetKeepLogs();

    // --- Set the working directory to be the trains name (with -----
    // --- special characters replaced by '_' and the date appended),
    // --- and also set the output directory (relative to working
    // --- directory)
    fHandler->SetGridWorkingDir(name.Data());
    fHandler->SetGridOutputDir("output");
    fHandler->SetGridDataDir(fUrl.GetFile());

    // --- Get the tree name and set the file pattern ----------------
    TString pattern;
    if (fOptions.Has("pattern")) pattern = fOptions.Get("pattern");
    else {
      TString treeName(fUrl.GetAnchor());
      if (treeName.IsNull()) { 
	Warning("GridHelper::PreSetup", "No tree name specified, assuming T");
	treeName = "T";
      }
      if      (treeName.EqualTo("esdTree")) pattern = "AliESD";
      else if (treeName.EqualTo("aodTree")) pattern = "AliAOD";
    }
    fHandler->SetDataPattern(pattern);

    // --- Loop over defined containers in the analysis manager, and -
    // --- declare these as outputs
    TString listOfAODs  = "";
    TString listOfHists = "";
    TString listOfTerms = "";

    TObjArray*  outs[] = { mgr->GetOutputs(), mgr->GetParamOutputs(), 0 };
    TObjArray** out    = outs;
    while (*out) {
      AliAnalysisDataContainer* cont = 0;
      TIter nextCont(*out);
      while ((cont = static_cast<AliAnalysisDataContainer*>(nextCont()))) {
	TString outName(cont->GetFileName());
	Bool_t   term = (*out == outs[1]);
	TString& list = (outName == "default" ? listOfAODs : 
			 !term ? listOfHists : listOfTerms);
	if (outName == "default") { 
	  if (!mgr->GetOutputEventHandler()) continue; 
	  
	  outName = mgr->GetOutputEventHandler()->GetOutputFileName();
	}
	if (list.Contains(outName)) continue;
	if (!list.IsNull()) list.Append(",");
	list.Append(outName);
      }
      out++;
    }
    TString extra = mgr->GetExtraFiles();
    if (!extra.IsNull()) { 
      if (!listOfAODs.IsNull()) listOfAODs.Append("+");
      extra.ReplaceAll(" ", ",");
      listOfAODs.Append(extra);
   }

#if 0
    Int_t nReplica = 2;
    TString outArchive = Form("stderr, stdout@disk=%d", nReplica);
    if (!listOfHists.IsNull()) 
      outArchive.Append(Form(" hist_archive.zip:%s@disk=%d", 
			     listOfHists.Data(), nReplica));
    if (!listOfAODs.IsNull()) 
      outArchive.Append(Form(" aod_archive.zip:%s@disk=%d", 
			     listOfAODs.Data(), nReplica));
    // Disabled for now 
    // plugin->SetOutputArchive(outArchive);
#endif 

    if (listOfAODs.IsNull() && listOfHists.IsNull()) 
      Fatal("PostSetup", "No outputs defined");
    if (!listOfTerms.IsNull()) 
      fHandler->SetTerminateFiles(listOfTerms);
    
    return true;
  };
  /** 
   * Start the analysis 
   * 
   * @param nEvents Number of events to analyse 
   * 
   * @return The return value of AliAnalysisManager::StartAnalysis
   */
  virtual Long64_t Run(Long64_t nEvents=-1) 
  {
    AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
    if (nEvents == 0) return 0;
    Long64_t ret = mgr->StartAnalysis("grid", nEvents);

#if 1
    std::ofstream outJobs(Form("%s.jobid", mgr->GetName()));
    outJobs << fHandler->GetGridJobIDs() << std::endl;
    outJobs.close();

    std::ofstream outStages(Form("%s.stage", mgr->GetName()));
    outStages << fHandler->GetGridStages() << std::endl;
    outStages.close();
#endif
    return ret;
  }
  /** 
   * Link an auxilary file to working directory 
   * 
   * @param name Name of the file
   * @param copy  Whether to copy or not 
   * 
   * @return true on success
   */
  virtual Bool_t AuxFile(const TString& name, bool copy=false)
  {
    if (!Helper::AuxFile(name, copy)) return false;
    // We need to add this file as an additional 'library', so that the 
    // file is uploaded to the users Grid working directory. 
    fHandler->AddAdditionalLibrary(gSystem->BaseName(name.Data()));
    return true;
  }
  /** 
   * Get the output (directory)
   *
   */
  virtual TString OutputPath() const
  {
    TString ret;
    if (!fHandler) {
      Warning("GridHelper::OutputLocation", "No AliEn handler");
      return ret;
    }
    ret = fHandler->GetGridOutputDir();
    if (ret.BeginsWith("/")) return ret;

    AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
    if (!mgr) { 
      Warning("GridHelper::OutputLocation", "No analysis manager");
      return ret;
    }
    ret.Prepend(Form("%s/",  mgr->GetName()));
    if (gGrid) 
      ret.Prepend(Form("%s/", gGrid->GetHomeDirectory())); 
    
    return ret;
  }
  /** 
   * @return URL help string
   */
  virtual const Char_t* UrlHelp() const 
  {
    return "alien:///<datadir>[?<options>][#<treeName>]";
  }
  /** 
   * @return Short description
   */
  virtual const char* Desc() const { return "AliEn"; }
  /** 
   * Write auxillary ROOT (and possible shell) script for more 
   * (post-)processing e.g., terminate
   * 
   * @param escaped        Escaped name  
   */
  void AuxSave(const TString& escaped, 
	       Bool_t /*asShellScript*/) 
  {
    // Write plug-in to file 
    TFile* plug = TFile::Open(Form("%s_plugin.root", escaped.Data()), 
			      "RECREATE");
    fHandler->Write("plugin");
    plug->Close();
    
    TIter       nextLib(&fExtraLibs);
    TObjString* lib = 0;
    TString     libs;
    while ((lib = static_cast<TObjString*>(nextLib()))) {
      if (!libs.IsNull()) libs.Append(" ");
      libs.Append(lib->String());
    }
    TIter       nextPar(&fExtraPars);
    TObjString* par = 0;
    TString     pars;
    while ((par = static_cast<TObjString*>(nextPar()))) {
      if (!pars.IsNull()) pars.Append(" ");
      pars.Append(par->String());
    }
    TIter       nextSrc(&fExtraSrcs);
    TObjString* src = 0;
    TString     srcs;
    while ((src = static_cast<TObjString*>(nextSrc()))) {
      if (!srcs.IsNull()) srcs.Append(" ");
      srcs.Append(src->String());
    }
    TString macDir("$ALICE_ROOT/PWGLF/FORWARD/trains");
    std::ofstream t("Terminate.C");
    if (!t) { 
      Error("GridHelper::AuxSave", "Failed to make terminate ROOT script");
      return;
    }

    t << "// Generated by GridHelper\n"
      << "Bool_t Terminate(Bool_t localMerge=false)\n"
      << "{\n"
      << "  TString name = \"" << escaped << "\";\n"
      << "  TString libs = \"" << libs << "\";\n"
      << "  TString pars = \"" << pars << "\";\n"
      << "  TString srcs = \"" << srcs << "\";\n\n"
      << "  gSystem->Load(\"libANALYSIS\");\n"
      << "  gSystem->Load(\"libANALYSISalice\");\n"
      << "  gSystem->AddIncludePath(\"-I$ALICE_ROOT/include\");\n\n"
      << "  gROOT->LoadMacro(\"" << macDir << "/GridTerminate.C+g\");\n\n"
      << "  return GridTerminate(name,libs,pars,srcs,localMerge);\n"
      << "}\n"
      << "// EOF\n"
      << std::endl;
    t.close();

    TString runs;
    TString format(fOptions.Has("mc") ? "%d" : "%09d");
    if (fOptions.Has("concat")) {
      Int_t first = fRuns.First()->GetUniqueID();
      Int_t last  = fRuns.Last()->GetUniqueID();
      TString fmt(format); 
      fmt.Append("_"); 
      fmt.Append(format);
      if (!runs.IsNull()) runs.Append(" ");
      runs.Append(TString::Format(fmt, first, last));
    }
    else {
      TIter next(&fRuns);
      TObject* o = 0;
      while ((o = next())) { 
	if (!runs.IsNull()) runs.Append(" ");
	runs.Append(Form(format, o->GetUniqueID()));
      }
    }

    std::ofstream d("Download.C");
    if (!d) { 
      Error("GridHelper::AuxSave", "Failed to make ROOT script Download.C");
      return;
    }
    d << "// Generated by GridHelper\n"
      << "void Download(Bool_t unpack=true)\n"
      << "{\n"
      << "  TString base = \"" << fUrl.GetProtocol() << "://" 
      << OutputPath() << "\";\n"
      << "  TString runs = \"" << runs << "\";\n\n"
      << "  gROOT->LoadMacro(\"" << macDir << "/GridDownload.C\");\n\n"
      << "  GridDownload(base, runs, unpack);\n"
      << "}\n"
      << "// EOF\n"
      << std::endl;
    d.close();

    std::ofstream w("Watch.C");
    if (!w) {
      Error("GridHelper::AuxSave", "Failed to make ROOT script Watch.C");
      return;
    }
    w << "// Generated by GridHelper\n"
      << "void Watch(Bool_t batch=false, Int_t delay=5*60)\n"
      << "{\n"
      << "  TString name = \"" << escaped << "\";\n"
      << "  gROOT->LoadMacro(\"" << macDir << "/GridWatch.C+g\");\n\n"
      << "  GridWatch(name,batch,delay);\n"
      << "}\n"
      << "// EOF\n"
      << std::endl;
    w.close();

  }
  TList fRuns;
};
#endif
//
// EOF
//
 GridHelper.C:1
 GridHelper.C:2
 GridHelper.C:3
 GridHelper.C:4
 GridHelper.C:5
 GridHelper.C:6
 GridHelper.C:7
 GridHelper.C:8
 GridHelper.C:9
 GridHelper.C:10
 GridHelper.C:11
 GridHelper.C:12
 GridHelper.C:13
 GridHelper.C:14
 GridHelper.C:15
 GridHelper.C:16
 GridHelper.C:17
 GridHelper.C:18
 GridHelper.C:19
 GridHelper.C:20
 GridHelper.C:21
 GridHelper.C:22
 GridHelper.C:23
 GridHelper.C:24
 GridHelper.C:25
 GridHelper.C:26
 GridHelper.C:27
 GridHelper.C:28
 GridHelper.C:29
 GridHelper.C:30
 GridHelper.C:31
 GridHelper.C:32
 GridHelper.C:33
 GridHelper.C:34
 GridHelper.C:35
 GridHelper.C:36
 GridHelper.C:37
 GridHelper.C:38
 GridHelper.C:39
 GridHelper.C:40
 GridHelper.C:41
 GridHelper.C:42
 GridHelper.C:43
 GridHelper.C:44
 GridHelper.C:45
 GridHelper.C:46
 GridHelper.C:47
 GridHelper.C:48
 GridHelper.C:49
 GridHelper.C:50
 GridHelper.C:51
 GridHelper.C:52
 GridHelper.C:53
 GridHelper.C:54
 GridHelper.C:55
 GridHelper.C:56
 GridHelper.C:57
 GridHelper.C:58
 GridHelper.C:59
 GridHelper.C:60
 GridHelper.C:61
 GridHelper.C:62
 GridHelper.C:63
 GridHelper.C:64
 GridHelper.C:65
 GridHelper.C:66
 GridHelper.C:67
 GridHelper.C:68
 GridHelper.C:69
 GridHelper.C:70
 GridHelper.C:71
 GridHelper.C:72
 GridHelper.C:73
 GridHelper.C:74
 GridHelper.C:75
 GridHelper.C:76
 GridHelper.C:77
 GridHelper.C:78
 GridHelper.C:79
 GridHelper.C:80
 GridHelper.C:81
 GridHelper.C:82
 GridHelper.C:83
 GridHelper.C:84
 GridHelper.C:85
 GridHelper.C:86
 GridHelper.C:87
 GridHelper.C:88
 GridHelper.C:89
 GridHelper.C:90
 GridHelper.C:91
 GridHelper.C:92
 GridHelper.C:93
 GridHelper.C:94
 GridHelper.C:95
 GridHelper.C:96
 GridHelper.C:97
 GridHelper.C:98
 GridHelper.C:99
 GridHelper.C:100
 GridHelper.C:101
 GridHelper.C:102
 GridHelper.C:103
 GridHelper.C:104
 GridHelper.C:105
 GridHelper.C:106
 GridHelper.C:107
 GridHelper.C:108
 GridHelper.C:109
 GridHelper.C:110
 GridHelper.C:111
 GridHelper.C:112
 GridHelper.C:113
 GridHelper.C:114
 GridHelper.C:115
 GridHelper.C:116
 GridHelper.C:117
 GridHelper.C:118
 GridHelper.C:119
 GridHelper.C:120
 GridHelper.C:121
 GridHelper.C:122
 GridHelper.C:123
 GridHelper.C:124
 GridHelper.C:125
 GridHelper.C:126
 GridHelper.C:127
 GridHelper.C:128
 GridHelper.C:129
 GridHelper.C:130
 GridHelper.C:131
 GridHelper.C:132
 GridHelper.C:133
 GridHelper.C:134
 GridHelper.C:135
 GridHelper.C:136
 GridHelper.C:137
 GridHelper.C:138
 GridHelper.C:139
 GridHelper.C:140
 GridHelper.C:141
 GridHelper.C:142
 GridHelper.C:143
 GridHelper.C:144
 GridHelper.C:145
 GridHelper.C:146
 GridHelper.C:147
 GridHelper.C:148
 GridHelper.C:149
 GridHelper.C:150
 GridHelper.C:151
 GridHelper.C:152
 GridHelper.C:153
 GridHelper.C:154
 GridHelper.C:155
 GridHelper.C:156
 GridHelper.C:157
 GridHelper.C:158
 GridHelper.C:159
 GridHelper.C:160
 GridHelper.C:161
 GridHelper.C:162
 GridHelper.C:163
 GridHelper.C:164
 GridHelper.C:165
 GridHelper.C:166
 GridHelper.C:167
 GridHelper.C:168
 GridHelper.C:169
 GridHelper.C:170
 GridHelper.C:171
 GridHelper.C:172
 GridHelper.C:173
 GridHelper.C:174
 GridHelper.C:175
 GridHelper.C:176
 GridHelper.C:177
 GridHelper.C:178
 GridHelper.C:179
 GridHelper.C:180
 GridHelper.C:181
 GridHelper.C:182
 GridHelper.C:183
 GridHelper.C:184
 GridHelper.C:185
 GridHelper.C:186
 GridHelper.C:187
 GridHelper.C:188
 GridHelper.C:189
 GridHelper.C:190
 GridHelper.C:191
 GridHelper.C:192
 GridHelper.C:193
 GridHelper.C:194
 GridHelper.C:195
 GridHelper.C:196
 GridHelper.C:197
 GridHelper.C:198
 GridHelper.C:199
 GridHelper.C:200
 GridHelper.C:201
 GridHelper.C:202
 GridHelper.C:203
 GridHelper.C:204
 GridHelper.C:205
 GridHelper.C:206
 GridHelper.C:207
 GridHelper.C:208
 GridHelper.C:209
 GridHelper.C:210
 GridHelper.C:211
 GridHelper.C:212
 GridHelper.C:213
 GridHelper.C:214
 GridHelper.C:215
 GridHelper.C:216
 GridHelper.C:217
 GridHelper.C:218
 GridHelper.C:219
 GridHelper.C:220
 GridHelper.C:221
 GridHelper.C:222
 GridHelper.C:223
 GridHelper.C:224
 GridHelper.C:225
 GridHelper.C:226
 GridHelper.C:227
 GridHelper.C:228
 GridHelper.C:229
 GridHelper.C:230
 GridHelper.C:231
 GridHelper.C:232
 GridHelper.C:233
 GridHelper.C:234
 GridHelper.C:235
 GridHelper.C:236
 GridHelper.C:237
 GridHelper.C:238
 GridHelper.C:239
 GridHelper.C:240
 GridHelper.C:241
 GridHelper.C:242
 GridHelper.C:243
 GridHelper.C:244
 GridHelper.C:245
 GridHelper.C:246
 GridHelper.C:247
 GridHelper.C:248
 GridHelper.C:249
 GridHelper.C:250
 GridHelper.C:251
 GridHelper.C:252
 GridHelper.C:253
 GridHelper.C:254
 GridHelper.C:255
 GridHelper.C:256
 GridHelper.C:257
 GridHelper.C:258
 GridHelper.C:259
 GridHelper.C:260
 GridHelper.C:261
 GridHelper.C:262
 GridHelper.C:263
 GridHelper.C:264
 GridHelper.C:265
 GridHelper.C:266
 GridHelper.C:267
 GridHelper.C:268
 GridHelper.C:269
 GridHelper.C:270
 GridHelper.C:271
 GridHelper.C:272
 GridHelper.C:273
 GridHelper.C:274
 GridHelper.C:275
 GridHelper.C:276
 GridHelper.C:277
 GridHelper.C:278
 GridHelper.C:279
 GridHelper.C:280
 GridHelper.C:281
 GridHelper.C:282
 GridHelper.C:283
 GridHelper.C:284
 GridHelper.C:285
 GridHelper.C:286
 GridHelper.C:287
 GridHelper.C:288
 GridHelper.C:289
 GridHelper.C:290
 GridHelper.C:291
 GridHelper.C:292
 GridHelper.C:293
 GridHelper.C:294
 GridHelper.C:295
 GridHelper.C:296
 GridHelper.C:297
 GridHelper.C:298
 GridHelper.C:299
 GridHelper.C:300
 GridHelper.C:301
 GridHelper.C:302
 GridHelper.C:303
 GridHelper.C:304
 GridHelper.C:305
 GridHelper.C:306
 GridHelper.C:307
 GridHelper.C:308
 GridHelper.C:309
 GridHelper.C:310
 GridHelper.C:311
 GridHelper.C:312
 GridHelper.C:313
 GridHelper.C:314
 GridHelper.C:315
 GridHelper.C:316
 GridHelper.C:317
 GridHelper.C:318
 GridHelper.C:319
 GridHelper.C:320
 GridHelper.C:321
 GridHelper.C:322
 GridHelper.C:323
 GridHelper.C:324
 GridHelper.C:325
 GridHelper.C:326
 GridHelper.C:327
 GridHelper.C:328
 GridHelper.C:329
 GridHelper.C:330
 GridHelper.C:331
 GridHelper.C:332
 GridHelper.C:333
 GridHelper.C:334
 GridHelper.C:335
 GridHelper.C:336
 GridHelper.C:337
 GridHelper.C:338
 GridHelper.C:339
 GridHelper.C:340
 GridHelper.C:341
 GridHelper.C:342
 GridHelper.C:343
 GridHelper.C:344
 GridHelper.C:345
 GridHelper.C:346
 GridHelper.C:347
 GridHelper.C:348
 GridHelper.C:349
 GridHelper.C:350
 GridHelper.C:351
 GridHelper.C:352
 GridHelper.C:353
 GridHelper.C:354
 GridHelper.C:355
 GridHelper.C:356
 GridHelper.C:357
 GridHelper.C:358
 GridHelper.C:359
 GridHelper.C:360
 GridHelper.C:361
 GridHelper.C:362
 GridHelper.C:363
 GridHelper.C:364
 GridHelper.C:365
 GridHelper.C:366
 GridHelper.C:367
 GridHelper.C:368
 GridHelper.C:369
 GridHelper.C:370
 GridHelper.C:371
 GridHelper.C:372
 GridHelper.C:373
 GridHelper.C:374
 GridHelper.C:375
 GridHelper.C:376
 GridHelper.C:377
 GridHelper.C:378
 GridHelper.C:379
 GridHelper.C:380
 GridHelper.C:381
 GridHelper.C:382
 GridHelper.C:383
 GridHelper.C:384
 GridHelper.C:385
 GridHelper.C:386
 GridHelper.C:387
 GridHelper.C:388
 GridHelper.C:389
 GridHelper.C:390
 GridHelper.C:391
 GridHelper.C:392
 GridHelper.C:393
 GridHelper.C:394
 GridHelper.C:395
 GridHelper.C:396
 GridHelper.C:397
 GridHelper.C:398
 GridHelper.C:399
 GridHelper.C:400
 GridHelper.C:401
 GridHelper.C:402
 GridHelper.C:403
 GridHelper.C:404
 GridHelper.C:405
 GridHelper.C:406
 GridHelper.C:407
 GridHelper.C:408
 GridHelper.C:409
 GridHelper.C:410
 GridHelper.C:411
 GridHelper.C:412
 GridHelper.C:413
 GridHelper.C:414
 GridHelper.C:415
 GridHelper.C:416
 GridHelper.C:417
 GridHelper.C:418
 GridHelper.C:419
 GridHelper.C:420
 GridHelper.C:421
 GridHelper.C:422
 GridHelper.C:423
 GridHelper.C:424
 GridHelper.C:425
 GridHelper.C:426
 GridHelper.C:427
 GridHelper.C:428
 GridHelper.C:429
 GridHelper.C:430
 GridHelper.C:431
 GridHelper.C:432
 GridHelper.C:433
 GridHelper.C:434
 GridHelper.C:435
 GridHelper.C:436
 GridHelper.C:437
 GridHelper.C:438
 GridHelper.C:439
 GridHelper.C:440
 GridHelper.C:441
 GridHelper.C:442
 GridHelper.C:443
 GridHelper.C:444
 GridHelper.C:445
 GridHelper.C:446
 GridHelper.C:447
 GridHelper.C:448
 GridHelper.C:449
 GridHelper.C:450
 GridHelper.C:451
 GridHelper.C:452
 GridHelper.C:453
 GridHelper.C:454
 GridHelper.C:455
 GridHelper.C:456
 GridHelper.C:457
 GridHelper.C:458
 GridHelper.C:459
 GridHelper.C:460
 GridHelper.C:461
 GridHelper.C:462
 GridHelper.C:463
 GridHelper.C:464
 GridHelper.C:465
 GridHelper.C:466
 GridHelper.C:467
 GridHelper.C:468
 GridHelper.C:469
 GridHelper.C:470
 GridHelper.C:471
 GridHelper.C:472
 GridHelper.C:473
 GridHelper.C:474
 GridHelper.C:475
 GridHelper.C:476
 GridHelper.C:477
 GridHelper.C:478
 GridHelper.C:479
 GridHelper.C:480
 GridHelper.C:481
 GridHelper.C:482
 GridHelper.C:483
 GridHelper.C:484
 GridHelper.C:485
 GridHelper.C:486
 GridHelper.C:487
 GridHelper.C:488
 GridHelper.C:489
 GridHelper.C:490
 GridHelper.C:491
 GridHelper.C:492
 GridHelper.C:493
 GridHelper.C:494
 GridHelper.C:495
 GridHelper.C:496
 GridHelper.C:497
 GridHelper.C:498
 GridHelper.C:499
 GridHelper.C:500
 GridHelper.C:501
 GridHelper.C:502
 GridHelper.C:503
 GridHelper.C:504
 GridHelper.C:505
 GridHelper.C:506
 GridHelper.C:507
 GridHelper.C:508
 GridHelper.C:509
 GridHelper.C:510
 GridHelper.C:511
 GridHelper.C:512
 GridHelper.C:513
 GridHelper.C:514
 GridHelper.C:515
 GridHelper.C:516
 GridHelper.C:517
 GridHelper.C:518
 GridHelper.C:519
 GridHelper.C:520
 GridHelper.C:521
 GridHelper.C:522
 GridHelper.C:523
 GridHelper.C:524
 GridHelper.C:525
 GridHelper.C:526
 GridHelper.C:527
 GridHelper.C:528
 GridHelper.C:529
 GridHelper.C:530
 GridHelper.C:531
 GridHelper.C:532
 GridHelper.C:533
 GridHelper.C:534
 GridHelper.C:535
 GridHelper.C:536
 GridHelper.C:537
 GridHelper.C:538
 GridHelper.C:539
 GridHelper.C:540
 GridHelper.C:541
 GridHelper.C:542
 GridHelper.C:543
 GridHelper.C:544
 GridHelper.C:545
 GridHelper.C:546
 GridHelper.C:547
 GridHelper.C:548
 GridHelper.C:549
 GridHelper.C:550
 GridHelper.C:551
 GridHelper.C:552
 GridHelper.C:553
 GridHelper.C:554
 GridHelper.C:555
 GridHelper.C:556
 GridHelper.C:557
 GridHelper.C:558
 GridHelper.C:559
 GridHelper.C:560
 GridHelper.C:561
 GridHelper.C:562
 GridHelper.C:563
 GridHelper.C:564
 GridHelper.C:565
 GridHelper.C:566
 GridHelper.C:567
 GridHelper.C:568
 GridHelper.C:569
 GridHelper.C:570
 GridHelper.C:571
 GridHelper.C:572
 GridHelper.C:573
 GridHelper.C:574
 GridHelper.C:575
 GridHelper.C:576
 GridHelper.C:577
 GridHelper.C:578
 GridHelper.C:579
 GridHelper.C:580
 GridHelper.C:581
 GridHelper.C:582
 GridHelper.C:583
 GridHelper.C:584
 GridHelper.C:585
 GridHelper.C:586
 GridHelper.C:587
 GridHelper.C:588
 GridHelper.C:589
 GridHelper.C:590
 GridHelper.C:591
 GridHelper.C:592
 GridHelper.C:593
 GridHelper.C:594
 GridHelper.C:595
 GridHelper.C:596
 GridHelper.C:597
 GridHelper.C:598
 GridHelper.C:599
 GridHelper.C:600
 GridHelper.C:601
 GridHelper.C:602
 GridHelper.C:603
 GridHelper.C:604
 GridHelper.C:605
 GridHelper.C:606
 GridHelper.C:607
 GridHelper.C:608
 GridHelper.C:609
 GridHelper.C:610
 GridHelper.C:611
 GridHelper.C:612
 GridHelper.C:613
 GridHelper.C:614
 GridHelper.C:615
 GridHelper.C:616
 GridHelper.C:617
 GridHelper.C:618
 GridHelper.C:619
 GridHelper.C:620
 GridHelper.C:621
 GridHelper.C:622
 GridHelper.C:623
 GridHelper.C:624
 GridHelper.C:625
 GridHelper.C:626
 GridHelper.C:627
 GridHelper.C:628
 GridHelper.C:629
 GridHelper.C:630
 GridHelper.C:631
 GridHelper.C:632
 GridHelper.C:633
 GridHelper.C:634
 GridHelper.C:635
 GridHelper.C:636
 GridHelper.C:637
 GridHelper.C:638
 GridHelper.C:639
 GridHelper.C:640
 GridHelper.C:641
 GridHelper.C:642
 GridHelper.C:643
 GridHelper.C:644
 GridHelper.C:645
 GridHelper.C:646
 GridHelper.C:647
 GridHelper.C:648
 GridHelper.C:649
 GridHelper.C:650
 GridHelper.C:651
 GridHelper.C:652
 GridHelper.C:653
 GridHelper.C:654
 GridHelper.C:655
 GridHelper.C:656
 GridHelper.C:657
 GridHelper.C:658
 GridHelper.C:659
 GridHelper.C:660
 GridHelper.C:661
 GridHelper.C:662
 GridHelper.C:663
 GridHelper.C:664
 GridHelper.C:665
 GridHelper.C:666
 GridHelper.C:667
 GridHelper.C:668
 GridHelper.C:669
 GridHelper.C:670
 GridHelper.C:671
 GridHelper.C:672
 GridHelper.C:673
 GridHelper.C:674
 GridHelper.C:675
 GridHelper.C:676
 GridHelper.C:677
 GridHelper.C:678
 GridHelper.C:679
 GridHelper.C:680
 GridHelper.C:681
 GridHelper.C:682
 GridHelper.C:683
 GridHelper.C:684
 GridHelper.C:685
 GridHelper.C:686
 GridHelper.C:687
 GridHelper.C:688
 GridHelper.C:689
 GridHelper.C:690
 GridHelper.C:691
 GridHelper.C:692