diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/DEVELOPERS b/PhysicsAnalysis/TauID/TauDiscriminant/DEVELOPERS
new file mode 100644
index 0000000000000000000000000000000000000000..5db36bd3a0ccfe204f1e78ba91ce89d98f260973
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/DEVELOPERS
@@ -0,0 +1,13 @@
+A Message to Developers
+-----------------------
+ 
+
+
+
+* Your Athena-based algorithms MUST inherit from
+TauDiscriToolBase and implement the interface there.
+
+* If you want your id method to be useable by
+TauIDReader on ROOT ntuples, your underlying id
+class MUST inherit from MethodBase (and implement
+the interface) and MUST NOT depend on Athena-based headers.
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/README b/PhysicsAnalysis/TauID/TauDiscriminant/README
new file mode 120000
index 0000000000000000000000000000000000000000..92cacd285355271487b7e379dba6ca60f9a554a4
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/README
@@ -0,0 +1 @@
+README.rst
\ No newline at end of file
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/README.rst b/PhysicsAnalysis/TauID/TauDiscriminant/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e1dd49788755e9f8be8febef1a31bd135a821878
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/README.rst
@@ -0,0 +1,80 @@
+.. -*- mode: rst -*-
+
+Compilation within Athena
+=========================
+
+To compile TauDiscriminant for use in Athena::
+
+   cd cmt
+   make
+   cd ..
+
+
+Standalone Compilation
+======================
+
+Note: this does not require Athena in any way (setup or installed)
+Note: that means **DO NOT SETUP ATHENA BEFORE DOING THE FOLLOWING**
+(See "Known Problems" below)
+
+To compile a standalone library for use with ROOT and Python::
+
+   cd cmt
+   make -f Makefile.Standalone
+   cd ..
+
+This produces the shared object lib/libTauDiscriminant.so
+Put this shared object in your ``LD_LIBRARY_PATH`` by executing::
+
+   source setup.sh
+
+The library may now be loaded in CINT like this::
+
+   gSystem->Load("libTauDiscriminant.so");
+
+or in Python like this::
+
+   import ROOT
+   ROOT.gSystem.Load("libTauDiscriminant.so")
+
+Now you may follow the same procedure as in run/tauid-redo::
+    
+   import ROOT
+   ROOT.gSystem.Load("libTauDiscriminant.so")
+   from ROOT import TauID
+   reader = TauID.TauIDReader(True)
+   # etc.
+
+
+Recalculate Tau ID on D3PD (ROOT ntuples)
+=========================================
+
+Compile TauDiscriminant by one of the two methods above and source setup.sh
+Then execute tauid-redo::
+
+   tauid-redo *.root
+
+This will create new D3PDs with a suffix appended to the filenames
+which contain branches for the newly calculated discriminants.
+For more information execute::
+
+   tauid-redo --help
+
+
+Known Problems
+==============
+
+When running tauid-redo after a standalone compilation it might crash with::
+
+    AttributeError: type object 'TauID' has no attribute 'Types'
+
+This is most likely because you have Athena setup. Don't setup Athena.
+Just use the default Python/ROOT installation on your system.
+
+
+Additional Tools
+================
+
+Tools used to train BDTs, apply MVAs to ROOT ntuples, make performance plots,
+convert TMVA BDT xml or MethodCuts files into TauDiscriminant format, etc.
+are in the "taumva" package. taumva is now available from TauDiscriminantTools.
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/BoostedDecisionTree.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/BoostedDecisionTree.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b14d54b1c35e85e54061856c047ee175801fb85c
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/BoostedDecisionTree.cxx
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: BoostedDecisionTree.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/BoostedDecisionTree.h"
+
+float BoostedDecisionTree::response() const {
+
+    float sum = 0.;
+    float norm = 0.;
+    Node* currentNode = 0;
+    DecisionNode* decision = 0;
+    LeafNode<float>* leafNode = 0;
+    vector<pair<Node*,float> >::const_iterator tree = this->trees.begin();
+    while(tree != this->trees.end()) {
+        currentNode = (*tree).first;
+        while (!currentNode->isLeaf()) {
+            decision = static_cast<DecisionNode*>(currentNode);
+            if (decision->goRight()) {
+                currentNode = decision->getRightChild();
+            } else {
+                currentNode = decision->getLeftChild();
+            }
+            if (!currentNode) return -200.;
+        }
+        leafNode = static_cast<LeafNode<float>*>(currentNode);
+        sum += ((*tree).second)*leafNode->getValue();
+        norm += (*tree).second;
+        ++tree;
+    }
+    return norm > 0. ? sum/norm : -100.;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/CommonLikelihood.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/CommonLikelihood.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0c990748003183184ce5add3e5ce9d93afc5f7cf
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/CommonLikelihood.cxx
@@ -0,0 +1,626 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: CommonLikelihood.cxx
+ *
+ * Author: Martin Flechl (mflechl@cern.ch)
+ *         Marcin Wolter (marcin.wolter@cern.ch)
+ *             updated 16 May 2011
+ */
+
+#include "TauDiscriminant/CommonLikelihood.h"
+#include <cmath>
+
+
+TString m="";
+const double SMALL = 1.e-6;
+
+bool CommonLikelihood::build(const string& filename) {
+
+  if (filename != ""){ //otherwise try defaults from constructor
+    vector<string> substrings;
+    this->splitString(filename,substrings,",");
+    if (substrings.size()==3){
+      m_tauFilename=substrings[0];
+      m_jetFilename=substrings[1];
+      m_cutFilename=substrings[2];
+    } else{
+      //to do: otherwise, try defaults.....
+      print("Exactly 3 files required, e.g. \"pdf_taus.root,pdf_jet.root,LMTCutsLLH.root\".",0);
+    }
+    m="PDF files: tau="+m_tauFilename+", jet="+m_jetFilename+", cuts="+m_cutFilename;
+    print(m,0);
+  }
+
+  return (this->readPDFHistograms() && this->readLMTCuts() && this->smoothPDFHistograms());
+}
+
+int CommonLikelihood::response(int ilmt, int /*option*/) const {
+  if(ilmt < 0 || ilmt > 2)
+    return 0;
+  std::vector<TGraph*> cuts;
+  int prongcat = m_prongindex;
+  if(prongcat==0)
+    cuts=m_vLMTCuts1P;
+  else if(prongcat==1)
+    cuts=m_vLMTCuts3P;
+  else
+    return 0;
+  if(cuts.size()!=3)
+    return 0;
+  double cut = cuts.at(ilmt)->Eval(m_et/1000.);
+  if (  m_llh>cut ) return 1;
+  else return 0;
+}
+
+bool CommonLikelihood::calcLLHValue(const map<string,const float*>* floatVariables,
+				    const map<string,const int*>* intVariables, int option=0){
+  //option 0: safe llh; 1: default llh; 2: use whatever vars given in maps above
+
+  m="In calcLLHValue, option="; m+=option;
+  print(m,2);
+
+  m_llh=-999;
+
+  map<string,const float*>::const_iterator it_floats;
+  map<string,const int*>::const_iterator it_ints;
+
+  map<string,float> allVariables;
+  map<string,float>::const_iterator it_all;
+
+  int author;
+  it_ints = intVariables->find("AUTHOR");
+  if (it_ints == intVariables->end()) {
+    it_floats = floatVariables->find("AUTHOR");
+    if (it_floats == floatVariables->end()) {
+      print("Cannot find AUTHOR in booked float or int variables!",0);
+      return false;
+    } else{
+      author=(int)*(it_floats->second);
+    }
+  } else{
+    author=*(it_ints->second);
+  }
+  if ( author==2 ){
+    print("Author==2, assigning llh value -1111!",1);
+    m_llh=-1111; return true;
+  } //as tauRec does
+
+  it_floats = floatVariables->find("PT");
+  if (it_floats == floatVariables->end()) {
+    print("Cannot find PT in booked float variables!",0);
+    return false;
+  }
+  float et=*(it_floats->second);
+
+
+  it_floats = floatVariables->find("ETA");
+  if (it_floats == floatVariables->end()) {
+    print("Cannot find ETA in booked float variables!",0);
+    return false;
+  }
+  float eta=*(it_floats->second);
+  if ( fabs(eta)>2.5 ){ print("Not within eta range",2); m_llh=-96; return true;} //as tauRec does
+
+  it_ints = intVariables->find("NUMTRACK");
+  if (it_ints == intVariables->end()) {
+    print("Cannot find NUMTRACK in booked int variables!",0);
+    return false;
+  }
+  int numtrack=*(it_ints->second);
+  if ( numtrack<1 ) { print("0 tracks",2); m_llh=-97; return true;} //as tauRec does
+  m_prongindex=0; //1pr
+  if (numtrack>=2) m_prongindex=1; //3pr
+  m_ntau[m_prongindex]++;
+/*
+  it_ints = intVariables->find("NUMPI0");
+  if (it_ints == intVariables->end()) {
+    it_ints = intVariables->find("NPI0");
+    if (it_ints == intVariables->end()) {
+      print("Cannot find NUMPI0 in booked int variables!",0);
+      return false;
+    }
+  }
+  int numpi0=*(it_ints->second);
+*/
+  int nvtx=1;
+  it_ints = intVariables->find("NUM_PILEUP_AND_PRIMARY_VERTICES");
+  if (it_ints == intVariables->end()) {
+    print("Cannot find NUM_PILEUP_AND_PRIMARY_VERTICES in booked int variables!",0);
+    print("Setting value to 1 and will try running, but the output will not be valid.",0);
+  } else nvtx=*(it_ints->second);
+  m_nvtx=nvtx;
+
+  m="author: "; m+=author; m+=", et:"; m+=et; m+=", eta:"; m+=eta;
+  m+=", numtrack:"; m+=numtrack; //m+=", numpi0:"; m+=numpi0;
+  print(m,1);
+
+
+  allVariables = getVariables(floatVariables, intVariables, numtrack, author, option);
+
+
+
+  m="nInts: "; m+=intVariables->size(); m+=", nFloats: "; m+=floatVariables->size();
+  m+=", nAll: "; m+=allVariables.size();
+  m+=", numtrack="; m+=numtrack;
+  print(m,1);
+  //print(m,0);
+
+  //check if all vars are there
+  if (option==0){ //safeLLH
+    if (    ( numtrack==1 && (allVariables.size()!=NVAR_SAFE_1P) ) || ( numtrack>=2 && (allVariables.size()!=NVAR_SAFE_3P) )     ){
+      print("Not all safe variables found... giving up! Use the llhall option to run over a specific set of variables.",0);
+      return false;
+    }
+  }
+
+
+  double calcLH=0;
+
+  print("Start filling vars",2);
+
+  for(it_all=allVariables.begin(); it_all!=allVariables.end(); ++it_all){
+    m="Var: "+it_all->first;
+    int ivar=this->varNameToNumber(it_all->first);
+    if (ivar<0){ print("ERROR: Unknown Variable "+it_all->first,0+(m_ntau[m_prongindex]>1)); continue;  }
+    m+=", var #"; m+=ivar; m+=", value="; m+=it_all->second;
+    print(m,1);
+
+    calcLH+=getSingleLLHRatio(ivar, numtrack, author, et, nvtx, (float) it_all->second);
+  }
+
+  m_et = et;
+  m_llh=calcLH;
+  return true;
+}
+
+
+map<string,float>  CommonLikelihood::getVariables(const map<string,const float*>* floatVariables, const map<string,const int*>* intVariables, const int numtrack,const int author, const int option) const{
+
+
+
+  const string use_safe[2][NVAR_SAFE_3P]={
+    {"CORRCENTFRAC","CORRFTRK","TRKAVGDIST","NUMWIDETRACK","IPSIGLEADTRK",""},  //1PR
+    {"CORRCENTFRAC","CORRFTRK","TRKAVGDIST","DRMAX","MASSTRKSYS","TRFLIGHTPATHSIG"}}; //3PR
+
+  const string use_def[2][NVAR_DEF]={
+    {"TRKAVGDIST","CORRFTRK","IPSIGLEADTRK","DRMAX","MASSTRKSYS","TRFLIGHTPATHSIG","NUMWIDETRACK","CORRCENTFRAC"},
+    {"TRKAVGDIST","CORRFTRK","IPSIGLEADTRK","DRMAX","MASSTRKSYS","TRFLIGHTPATHSIG","NUMWIDETRACK","CORRCENTFRAC"}
+  };
+/*    {"EMRADIUS","ISOLFRAC","STRIPWIDTH2","NSTRIP","ETHAD2ETTRACKS","ETEM2ETTRACKS","ETTRACKS2ET","",     "",     "",         "",
+     "NISOLTRK","MVISEFLOW","IPZ0SINTHETASIGLEADTRK","IPSIGLEADLOOSETRK",""},
+    {"EMRADIUS","ISOLFRAC","STRIPWIDTH2","NSTRIP","ETHAD2ETTRACKS","ETEM2ETTRACKS","ETTRACKS2ET","DRMIN","DRMAX","TRKWIDTH2","MASSTRKSYS",
+     "NISOLTRK","MVISEFLOW","",                      "IPSIGLEADLOOSETRK","TRFLIGHTPATHSIG"}};
+*/
+  const string use_author3[NVAR_DEF] = {"","","","","","","","","","TRKWIDTH2","MASSTRKSYS","NISOLTRK","MVISEFLOW","IPZ0SINTHETASIGLEADTRK","","TRFLIGHTPATHSIG"};
+  const string use_3trk[NVAR_DEF] = {"","","","","","","","","","TRKWIDTH2","MASSTRKSYS","","","","",""};
+
+
+  map<string,const float*>::const_iterator it_floats;
+  map<string,const int*>::const_iterator it_ints;
+
+  int prong=m_prongindex; //1pr
+
+  map<string, float> allVariables;
+
+
+  float et = 1.;
+  it_floats = floatVariables->find("ET");
+  if (it_floats != floatVariables->end()) {
+    et=*(it_floats->second);
+  }
+  else et=99.;
+
+
+  switch (option){
+  case 0: //safe
+    for(unsigned int i=0; i<NVAR_SAFE_3P; i++) {
+      if (use_safe[prong][i] != "") {
+	it_floats = floatVariables->find(use_safe[prong][i]);
+	if (it_floats != floatVariables->end()) {
+	  if ( it_floats->first=="ETTRACKS2ET" ){
+	    print("Modified etTracks2Et",2);
+	    if (fabs(et)<SMALL)	allVariables[it_floats->first]= *(it_floats->second)/et;
+	    else  allVariables[it_floats->first] = 999.;
+	  }
+	  else allVariables[it_floats->first]= *(it_floats->second);
+	}
+
+	it_ints = intVariables->find(use_safe[prong][i]);
+	if (it_ints != intVariables->end()) {
+	  allVariables[it_ints->first]= *(it_ints->second);
+	}
+
+      }
+    }
+    break;
+  case 1: //def
+    if ( author==1 ){ //no track-seed
+      for(unsigned int i=0; i<NVAR_DEF; i++) {
+	if (use_author3[i] != "") {
+	  it_floats = floatVariables->find(use_author3[i]);
+	  if (it_floats != floatVariables->end()) {
+	    allVariables[it_floats->first]= *(it_floats->second);
+	  }
+
+	  it_ints = intVariables->find(use_author3[i]);
+	  if (it_ints != intVariables->end()) {
+	    allVariables[it_ints->first]= *(it_ints->second);
+	  }
+	}
+      }
+    }
+    else if ( numtrack!=3 ){ //not exactly 3 tracks
+      for(unsigned int i=0; i<NVAR_DEF; i++) {
+	if (use_3trk[i] != "") {
+	  it_floats = floatVariables->find(use_3trk[i]);
+	  if (it_floats != floatVariables->end()) {
+	    allVariables[it_floats->first]= *(it_floats->second);
+	  }
+
+	  it_ints = intVariables->find(use_3trk[i]);
+	  if (it_ints != intVariables->end()) {
+	    allVariables[it_ints->first]= *(it_ints->second);
+	  }
+	}
+      }
+    }
+    else {
+      for(unsigned int i=0; i<NVAR_DEF; i++) {
+	if (use_def[prong][i] != "") {
+	  it_floats = floatVariables->find(use_def[prong][i]);
+	  if (it_floats != floatVariables->end()) {
+	    allVariables[it_floats->first]= *(it_floats->second);
+	  }
+
+	  it_ints = intVariables->find(use_def[prong][i]);
+	  if (it_ints != intVariables->end()) {
+	    allVariables[it_ints->first]= *(it_ints->second);
+	  }
+	}
+      }
+    }
+    break;
+  default:
+    print("ERROR: getVariables, option > 1",0);
+    break;;//take any var
+  }
+
+  return allVariables;
+
+}
+
+
+
+
+
+float CommonLikelihood::getSingleLLHRatio(const int ivar, const int numtrack, const int author, const float et, const int nvtx, const float value){
+
+  float LLHR = this->getSimpleSingleLLHRatio( ivar, numtrack, author, et, nvtx, value );
+
+  //for now hard-coding the pt-bin boundaries (45,100), should be automatized...
+  float border[]={45.,100.};
+  for(int b=0;b<2;b++){
+    float closetoborder=et/1000.-(border[b]);
+	
+	double uppBorder = 10.0;
+	double lowBorder = 10.0;
+
+	if( border[b] >= 100. && numtrack == 1 && !(m_doTrigger) ) {
+		uppBorder = 60.0;
+		lowBorder = 30.0;
+	}
+	
+	if( closetoborder >= 0 && fabs(closetoborder) < uppBorder ) {
+		float nextbin = (border[b]-lowBorder/2.0)*1000.0;
+		float LLHRnext = this->getSimpleSingleLLHRatio( ivar, numtrack, author, nextbin, nvtx, value );
+		LLHR = LLHR*((uppBorder+fabs(closetoborder))/(uppBorder*2.0)) + LLHRnext*((uppBorder-fabs(closetoborder))/(uppBorder*2.0));
+	}
+
+
+	if( closetoborder < 0 && fabs(closetoborder) < lowBorder ) {
+		float nextbin = (border[b]+uppBorder/2.0)*1000.0;
+		float LLHRnext = this->getSimpleSingleLLHRatio( ivar, numtrack, author, nextbin, nvtx, value );
+		LLHR = LLHR*((lowBorder+fabs(closetoborder))/(lowBorder*2.0)) + LLHRnext*((lowBorder-fabs(closetoborder))/(lowBorder*2.0));
+	}
+  }
+  return LLHR;
+}
+
+
+
+float CommonLikelihood::getSimpleSingleLLHRatio(const int ivar, const int numtrack, const int author, const float et, const int nvtx, const float value){
+
+  const std::string prongs[]={"","1prong","","3prong"};
+  const std::string classes[]={"both","calo"};
+
+  int prongcat=1; int authorcat=0;
+  if(numtrack >= 2){ prongcat=3; authorcat=0; }
+  if(author==2) return -1111;
+  std::string prongType=prongs[prongcat];
+  std::string authorType=classes[authorcat];
+
+  char text[100];
+  sprintf(text,"_v%d",ivar);
+  std::string hNameLong = "hpdf";
+  hNameLong+=+"_"+prongType;
+  hNameLong+=+"_"+authorType;
+  hNameLong.append(text);
+  TH3F *hratio = m_pdfHistogramsRatio[hNameLong];
+
+  m="tau nentries: "; m+=hratio->GetEntries(); m+=", nbins: "; m+=hratio->GetNbinsX();
+  print(m,2);
+  TAxis *xaxis = hratio->GetXaxis();
+  TAxis *yaxis = hratio->GetYaxis();
+  TAxis *zaxis = hratio->GetZaxis();
+
+  float xMin = xaxis->GetXmin();
+  float xMax = xaxis->GetXmax(); //
+
+  m=""; m+=xMin; m+=" -  "; m+=xMax;
+  print(m,2);
+  float etMax = yaxis->GetXmax();
+  float vtxMax = zaxis->GetXmax();
+
+  int p_nvtx=nvtx;
+  float temp_et=et;
+  if ( nvtx>vtxMax ) p_nvtx=(int) vtxMax;
+  if ( temp_et/1000.0>etMax ) temp_et=(etMax*1000)-1;
+  if (value<xMin) return 0;
+
+  float varLL = this->Interpolate(hratio,value,temp_et/1000.0,p_nvtx);
+
+  m="llhratio value of this var: "; m+=varLL;
+  print(m,1);
+
+  return varLL;
+}
+
+
+
+bool CommonLikelihood::readPDFHistograms() {
+  print("in readPDFHistograms()",2);
+
+  m_tauFile = new TFile(m_tauFilename.c_str());
+  m_jetFile = new TFile(m_jetFilename.c_str());
+
+  const int nProngdirs=2;
+  const int nClassdirs=1;
+  const std::string prongs[nProngdirs]={"1prong","3prong"};
+  const std::string classes[nClassdirs]={"both"/*,"calo"*/};
+
+  for (int iProng=0; iProng<nProngdirs; iProng++) {
+    for (int iClass=0; iClass<nClassdirs; iClass++) {
+      std::string prongDir = prongs[iProng];
+      std::string classDir = classes[iClass];
+      for (int ivar=0; ivar<=NVAR; ivar++) {
+
+	char text[100];
+	sprintf(text,"_v%d",ivar);
+	std::string hNameLong = "hpdf";
+	hNameLong+=+"_"+prongDir;
+	hNameLong+=+"_"+classDir;
+	hNameLong.append(text);
+	std::string hname = prongDir+"/"+classDir+"/"+hNameLong;
+
+	TH3F* hTmp_tau = (TH3F*)m_tauFile->Get(hname.c_str());
+	TH3F* hTmp_jet = (TH3F*)m_jetFile->Get(hname.c_str());
+
+	m_pdfHistogramsTau[hNameLong] = (TH3F*)hTmp_tau;
+	m_pdfHistogramsJet[hNameLong] = (TH3F*)hTmp_jet;
+
+      }
+    }
+  }
+
+  return true;
+}
+
+bool CommonLikelihood::readLMTCuts() {
+  print("in readLMTCuts()",2);
+
+  m_cutFile = new TFile(m_cutFilename.c_str());
+  if(m_cutFile==0) return false; //assert(m_cutFile!=0);
+  tg_loose_cuts_1P = (TGraph*) m_cutFile->Get("1prong/loose");
+  tg_medium_cuts_1P = (TGraph*) m_cutFile->Get("1prong/medium");
+  tg_tight_cuts_1P = (TGraph*) m_cutFile->Get("1prong/tight");
+
+  if(tg_loose_cuts_1P==0 || tg_medium_cuts_1P==0 || tg_tight_cuts_1P==0)
+    return false;
+  m_vLMTCuts1P.push_back(tg_loose_cuts_1P);
+  m_vLMTCuts1P.push_back(tg_medium_cuts_1P);
+  m_vLMTCuts1P.push_back(tg_tight_cuts_1P);
+  tg_loose_cuts_3P = (TGraph*) m_cutFile->Get("3prong/loose");
+  tg_medium_cuts_3P = (TGraph*) m_cutFile->Get("3prong/medium");
+  tg_tight_cuts_3P = (TGraph*) m_cutFile->Get("3prong/tight");
+
+  if(tg_loose_cuts_3P==0 || tg_medium_cuts_3P==0 || tg_tight_cuts_3P==0)
+    return false;
+  m_vLMTCuts3P.push_back(tg_loose_cuts_3P);
+  m_vLMTCuts3P.push_back(tg_medium_cuts_3P);
+  m_vLMTCuts3P.push_back(tg_tight_cuts_3P);
+
+  return true;
+}
+
+
+bool CommonLikelihood::smoothPDFHistograms() {
+  print("in smoothPDFHistograms()",2);
+
+  const int nProngdirs=2;
+  const int nClassdirs=1;
+  const std::string prongs[nProngdirs]={"1prong","3prong"};
+  const std::string classes[nClassdirs]={"both"/*,"calo"*/};
+
+  for (int iProng=0; iProng<nProngdirs; iProng++) {
+    for (int iClass=0; iClass<nClassdirs; iClass++) {
+      std::string prongDir = prongs[iProng];
+      std::string classDir = classes[iClass];
+      for (int ivar=0; ivar<=NVAR; ivar++) {
+	char text[100];
+	sprintf(text,"_v%d",ivar);
+	std::string hNameLong = "hpdf";
+	hNameLong+=+"_"+prongDir;
+	hNameLong+=+"_"+classDir;
+	hNameLong.append(text);
+	if (m_pdfHistogramsTau[hNameLong] && m_pdfHistogramsJet[hNameLong]){
+	  if (m_smooth) {
+	    this->smooth3D(m_pdfHistogramsTau[hNameLong]);
+	    this->smooth3D(m_pdfHistogramsJet[hNameLong]);
+	  }
+	  m_pdfHistogramsRatio[hNameLong] = this->divideLog(m_pdfHistogramsTau[hNameLong],m_pdfHistogramsJet[hNameLong]);
+	}
+      }
+    }
+  }
+  return true;
+}
+
+void CommonLikelihood::smooth3D(TH3F* hTmp){
+
+  for (int i=1;i<=hTmp->GetNbinsY();i++){
+    for (int j=1;j<=hTmp->GetNbinsZ();j++){
+      TH1D* tmp = hTmp->ProjectionX("projX",i,i,j,j,"");
+      tmp->Smooth(5);
+      for (int k=1;k<=tmp->GetNbinsX();k++)
+	hTmp->SetBinContent(k,i,j,tmp->GetBinContent(k));
+      delete tmp; tmp=0;
+    }
+  }
+}
+
+TH3F* CommonLikelihood::divideLog(TH3F* hTmpTau, TH3F* hTmpJet){
+  if (
+      hTmpTau->GetNbinsX() != hTmpJet->GetNbinsX() ||
+      hTmpTau->GetNbinsY() != hTmpJet->GetNbinsY() ||
+      hTmpTau->GetNbinsZ() != hTmpJet->GetNbinsZ()
+      ) return 0;
+  TH3F* hTmpRatio = new TH3F(*hTmpTau);
+  hTmpRatio->Reset();
+  for (int i=0;i<=hTmpTau->GetNbinsY()+1;i++){
+    for (int j=0;j<=hTmpTau->GetNbinsZ()+1;j++){
+      for (int k=0;k<=hTmpTau->GetNbinsX()+1;k++) {
+	float tauLL = hTmpTau->GetBinContent(k,i,j);
+	float jetLL = hTmpJet->GetBinContent(k,i,j);
+	if(fabs(jetLL)<SMALL) jetLL = SMALL;
+	if(!tauLL) tauLL = SMALL;
+	float varLL=TMath::Log(tauLL/jetLL);
+	hTmpRatio->SetBinContent(k,i,j,varLL);
+      }
+    }
+  }
+  return hTmpRatio;
+}
+
+Double_t CommonLikelihood::Interpolate(TH3F* hist, Double_t x, Double_t y, Double_t z)
+{
+  // Given a point x, approximates the value via linear interpolation
+  // based on the two nearest bin centers
+
+
+  Int_t ibin = hist->FindBin(x,y,z);
+  Int_t xbin, ybin, zbin;
+  hist->GetBinXYZ(ibin, xbin, ybin, zbin);
+  if (!m_smooth) return hist->GetBinContent(ibin);
+
+  Double_t x0,x1,y0,y1;
+
+  if(xbin<=1 || xbin>=hist->GetNbinsX()) {
+    return hist->GetBinContent(ibin);
+  } else {
+    if(x<=hist->GetXaxis()->GetBinCenter(xbin)) {
+      y0 = hist->GetBinContent(xbin-1, ybin, zbin);
+      x0 = hist->GetXaxis()->GetBinCenter(xbin-1);
+      y1 = hist->GetBinContent(xbin, ybin, zbin);
+      x1 = hist->GetXaxis()->GetBinCenter(xbin);
+    } else {
+      y0 = hist->GetBinContent(xbin, ybin, zbin);
+      x0 = hist->GetXaxis()->GetBinCenter(xbin);
+      y1 = hist->GetBinContent(xbin+1, ybin, zbin);
+      x1 = hist->GetXaxis()->GetBinCenter(xbin+1);
+    }
+    if (fabs(x1-x0)<SMALL && fabs(x1-x0)!=0.) return y0 + (x-x0)*((y1-y0)/(x1-x0));
+    else return 999.;
+  }
+}
+
+int CommonLikelihood::varNameToNumber(std::string varname) const {
+  if(varname=="emRadius" || varname=="EMRADIUS" ) return 0;
+  if(varname=="isolFrac" || varname=="ISOLFRAC" ) return 1;
+  if(varname=="stripWidth2" || varname=="STRIPWIDTH2" ) return 2;
+  if(varname=="nStrip" || varname=="NSTRIP" ) return 3;
+  if(varname=="etHad2etTracks" || varname=="ETHAD2ETTRACKS" ) return 4;
+  if(varname=="etEM2etTracks" || varname=="ETEM2ETTRACKS" ) return 5;
+  if(varname=="etTracks2et" || varname=="ETTRACKS2ET" ) return 6;
+  if(varname=="etEM2Et" || varname=="ETEM2ET" || varname=="EMFRACTIONCALIB" || varname=="EMFRACTIONATEMSCALE" ) return 7;
+  if(varname=="etOverPtLeadTrk" || varname=="ETOVERPTLEADTRK" ) return 8;
+  if(varname=="dRmin" || varname=="DRMIN" ) return 9;
+  if(varname=="dRmax" || varname=="DRMAX" ) return 10;
+  if(varname=="trkWidth2" || varname=="TRKWIDTH2" ) return 11;
+  if(varname=="massTrkSys" || varname=="MASSTRKSYS" ) return 12;
+  if(varname=="nIsolTrk" || varname=="NISOLTRK" ) return 13;
+  if(varname=="MVisEflow" || varname=="MVISEFLOW" ) return 14;
+  if(varname=="ipZ0SinThetaSigLeadTrk" || varname=="IPZ0SINTHETASIGLEADTRK" ) return 15;
+  if(varname=="ipSigLeadLooseTrk" || varname=="IPSIGLEADLOOSETRK" ) return 16;
+  if(varname=="trFlightPathSig" || varname=="TRFLIGHTPATHSIG" ) return 17;
+  if(varname=="centFrac" || varname=="CENTFRAC" ) return 18;
+  if(varname=="numEffClus" || varname=="NUMEFFCLUS" ) return 19;
+  if(varname=="trkAvgDist" || varname=="TRKAVGDIST" ) return 20;
+  if(varname=="topoInvMass" || varname=="TOPOINVMASS" ) return 21;
+  if(varname=="calRadius" || varname=="CALRADIUS" ) return 22;
+
+  if(varname=="ipSigLeadTrk" || varname=="IPSIGLEADTRK" ) return 23;
+  if(varname=="benchdRLeadTrkLeadCluster" || varname=="BENCHDRLEADTRKLEADCLUSTER" ) return 24;
+  if(varname=="benchdRLeadTrk2ndLeadCluster" || varname=="BENCHDRLEADTRK2NDLEADCLUSTER" ) return 25;
+  if(varname=="benchdRLeadCluster2ndLeadCluster" || varname=="BENCHDRLEADCLUSTER2NDLEADCLUSTER" ) return 26;
+  if(varname=="lead2ClusterEOverAllCusterE" || varname=="LEAD2CLUSTEREOVERALLCLUSTERE" ) return 27;
+  if(varname=="geoTrackRadius" || varname=="GEOTRACKRADIUS" ) return 28;
+  if(varname=="geoTrackMass" || varname=="GEOTRACKMASS" ) return 29;
+  if(varname=="geoCentFrac" || varname=="GEOCENTFRAC" ) return 30;
+  if(varname=="geoTopoInvMass" || varname=="GEOTOPOINVMASS" ) return 31;
+  if(varname=="geoCoreEnergyFraction" || varname=="GEOCOREENERGYFRACTION" ) return 32;
+  if(varname=="geoCoreClusterEMRadius" || varname=="GEOCORECLUSTEREMRADIUS" ) return 33;
+  if(varname=="geodRLeadTrkLeadCluster" || varname=="GEODRLEADTRKLEADCLUSTER" ) return 34;
+  if(varname=="geodRLeadTrk2ndLeadCluster" || varname=="GEODRLEADTRK2NDLEADCLUSTER" ) return 35;
+  if(varname=="geodRLeadCluster2ndLeadCluster" || varname=="GEODRLEADCLUSTER2NDLEADCLUSTER" ) return 36;
+  if(varname=="geoEClusterOverETau" || varname=="GEOECLUSTEROVERETAU" ) return 37;
+  if(varname=="NClusters" || varname=="NCLUSTERS" ) return 38;
+  if(varname=="NeffClusters" || varname=="NEFFCLUSTERS" ) return 39;
+  if(varname=="effTopoInvMass" || varname=="EFFTOPOINVMASS" ) return 40;
+  if(varname=="effdRLeadTrkLeadCluster" || varname=="EFFDRLEADTRKLEADCLUSTER" ) return 41;
+  if(varname=="effdRLeadTrk2ndLeadCluster" || varname=="EFFDRLEADTRK2NDLEADCLUSTER" ) return 42;
+  if(varname=="effdRLeadCluster2ndLeadCluster" || varname=="EFFDRLEADCLUSTER2NDLEADCLUSTER" ) return 43;
+  if(varname=="effEClusterOverETau" || varname=="EFFECLUSTEROVERETAU" ) return 44;
+  if(varname=="Eiso" || varname=="EISO" ) return 45;
+  if(varname=="ClusterEMRadius" || varname=="CLUSTEREMRADIUS" ) return 46;
+  if(varname=="ClusterCentFrac" || varname=="CLUSTERCENTFRAC" ) return 47;
+  if(varname=="effEMRadius" || varname=="EFFEMRADIUS" ) return 48;
+  if(varname=="effCentFrac" || varname=="EFFCENTFRAC" ) return 49;
+  if(varname=="numWideTrack" || varname=="NUMWIDETRACK" ) return 50;
+  if(varname=="charge" || varname=="CHARGE" ) return 51;
+  if(varname=="nPi0" || varname=="NPI0" ) return 52;
+  if(varname=="corrcentFrac" || varname=="CORRCENTFRAC" ) return 53;
+  if(varname=="corrfTrk" || varname=="CORRFTRK" ) return 56;
+
+
+  print("ERROR: Variable name not corresponding to a PDF variable: "+varname,2);
+  return -1;
+}
+
+void CommonLikelihood::splitString(const string& str, vector<string>& substrings, const string& delimiters) const{
+  // Skip delimiters at beginning.
+  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
+  // Find first "non-delimiter".
+  string::size_type pos     = str.find_first_of(delimiters, lastPos);
+
+  while (string::npos != pos || string::npos != lastPos){
+    // Found a token, add it to the vector.
+    substrings.push_back(str.substr(lastPos, pos - lastPos));
+    // Skip delimiters.  Note the "not_of"
+    lastPos = str.find_first_not_of(delimiters, pos);
+    // Find next "non-delimiter"
+    pos = str.find_first_of(delimiters, lastPos);
+  }
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/CutsDecisionTree.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/CutsDecisionTree.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fbc709b6466ab1927f9cb6174c9e130b13fb5ec8
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/CutsDecisionTree.cxx
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: Cuts.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/CutsDecisionTree.h"
+
+float CutsDecisionTree::response(unsigned int level) const {
+
+    DecisionNode* decision = 0;
+    LeafNode<float>* leafNode = 0;
+    if (level >= this->trees.size()) return false;
+    Node* currentNode = this->trees[level].first;
+    while (!currentNode->isLeaf()) {
+        decision = static_cast<DecisionNode*>(currentNode);
+        if (decision->goRight()) {
+            currentNode = decision->getRightChild();
+        } else {
+            currentNode = decision->getLeftChild();
+        }
+        if (!currentNode) return false;
+    }
+    leafNode = static_cast<LeafNode<float>*>(currentNode);
+    return leafNode->getValue();
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/EMFCluster.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/EMFCluster.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bac23ddbcc6c21ac8690f5ca5ffc5a61e14f5294
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/EMFCluster.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//*******************************************************//
+// Name: EMFCluster.cxx                                  //
+// Author: Michel Trottier-McDonald <mtm@cern.ch>        //
+// Description: A simple class to house cluster          //
+// TLorentzVectors and their associated fractions of     //
+// energy in different calorimeter layers                //
+//*******************************************************//
+
+#include "TauDiscriminant/EMFCluster.h"
+
+//---------------------------------------------------------
+// Default Constructor
+//---------------------------------------------------------
+EMFCluster::EMFCluster()
+{}
+
+
+//---------------------------------------------------------
+// Initiate TLorentzVector Contructor
+//---------------------------------------------------------
+EMFCluster::EMFCluster(double pt)
+{
+    m_cluster = TLorentzVector(pt, 0.0, 0.0, 0.0);
+    m_PSSF = 0.0;
+    m_EM2F = 0.0;
+    m_EM3F = 0.0;
+}
+
+
+
+//---------------------------------------------------------
+// Main constructor
+//---------------------------------------------------------
+EMFCluster::EMFCluster(const TLorentzVector& inCluster,
+		       double inPSSF,
+		       double inEM2F,
+		       double inEM3F)
+{
+    m_cluster = inCluster;
+
+    m_PSSF = inPSSF;
+    m_EM2F = inEM2F;
+    m_EM3F = inEM3F;
+
+    update();
+}
+
+
+//---------------------------------------------------------
+// Destructor
+//---------------------------------------------------------
+EMFCluster::~EMFCluster()
+{}
+
+
+//-----------------------------------------------------------
+// Update method
+//-----------------------------------------------------------
+void EMFCluster::update()
+{
+    m_HADF = 1 - m_PSSF - m_EM2F - m_EM3F;
+    m_pseudoHADF = 1 - m_PSSF - m_EM2F;
+
+    double clE = m_cluster.E();
+
+    m_PSSE = m_PSSF*clE;
+    m_EM2E = m_EM2F*clE;
+    m_EM3E = m_EM3F*clE;
+    m_HADE = m_HADF*clE;
+    m_pseudoHADE = m_pseudoHADF*clE;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/LinkDef.h b/PhysicsAnalysis/TauID/TauDiscriminant/Root/LinkDef.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8efc8d9debb0e33186f3c324473dcb3aeaa769e
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/LinkDef.h
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __TAUDISCRIMINANT__
+#define __TAUDISCRIMINANT__
+
+#include "TauDiscriminant/MethodBase.h"
+#include "TauDiscriminant/MethodBDT.h"
+#include "TauDiscriminant/MethodCuts.h"
+#include "TauDiscriminant/MethodDummy.h"
+#include "TauDiscriminant/MethodLLH.h"
+#include "TauDiscriminant/MethodTransform.h"
+#include "TauDiscriminant/Types.h"
+#include "TauDiscriminant/TauIDReader.h"
+#include "TauDiscriminant/TauDetailsManagerStandalone.h"
+
+#ifdef __CINT__
+
+#pragma link off all globals;
+#pragma link off all classes;
+#pragma link off all functions;
+
+#pragma link C++ namespace TauID;
+#pragma link C++ namespace TauID::Types;
+
+#pragma link C++ enum TauID::Types::MethodType;
+#pragma link C++ class TauID::TauIDReader;
+#pragma link C++ class TauID::TauDetailsManagerStandalone;
+#pragma link C++ class TauID::MethodBase;
+#pragma link C++ class TauID::MethodDummy;
+#pragma link C++ class TauID::MethodTransform;
+#pragma link C++ class TauID::MethodBDT;
+#pragma link C++ class TauID::MethodCuts;
+#pragma link C++ class TauID::MethodLLH;
+
+#endif
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodBDT.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodBDT.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..93648af524f141517a37b8cbc6a28a9f63ee1533
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodBDT.cxx
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: MethodBDT.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/MethodBDT.h"
+
+using namespace TauID;
+
+float MethodBDT::response() const
+{
+    if (!this->isBuilt) return -101.;
+    BoostedDecisionTree* bdt = getCurrentCategory();
+    return bdt ? bdt->response() : -201.;
+}
+
+bool MethodBDT::build(const string& filename, bool checkTree)
+{    
+    #ifdef __STANDALONE
+    TreeReader reader(this->verbose);
+    #else
+    TreeReader reader;
+    #endif
+    reader.setVariables(&this->floatVariables, &this->intVariables);
+    this->categoryTree = reader.build(filename,checkTree);
+    if (this->categoryTree != 0)
+    {
+        this->isBuilt = true;
+        return true;
+    }
+    return false;
+}
+
+BoostedDecisionTree* MethodBDT::getCurrentCategory() const
+{
+    PointerLeafNode<BoostedDecisionTree>* leafNode;
+    DecisionNode* decision;
+    Node* currentNode = this->categoryTree;
+    if (!currentNode) return 0;
+    while (!currentNode->isLeaf())
+    {
+        decision = static_cast<DecisionNode*>(currentNode);
+        if (decision->goRight())
+        {
+            currentNode = decision->getRightChild();
+        }
+        else
+        {
+            currentNode = decision->getLeftChild();
+        }
+        if (!currentNode) return 0;
+    }
+    leafNode = static_cast<PointerLeafNode<BoostedDecisionTree>*>(currentNode);
+    return leafNode->getValue();
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodBase.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodBase.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4e4b0cc1b0a78771596467d8d06d87f0d087f6d4
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodBase.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TauDiscriminant/MethodBase.h"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodCuts.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodCuts.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b2d260eb74d4774c8efba8a0fdaea13ea2526f42
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodCuts.cxx
@@ -0,0 +1,58 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: CutsMethod.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/MethodCuts.h"
+
+using namespace TauID;
+
+float MethodCuts::response(unsigned int level) const
+{
+    if (!this->isBuilt) return 0.;
+    CutsDecisionTree* cuts = getCurrentCategory();
+    return cuts ? cuts->response(level) : 0.;
+}
+
+bool MethodCuts::build(const string& filename, bool checkTree)
+{    
+    #ifdef __STANDALONE
+    TreeReader reader(this->verbose);
+    #else
+    TreeReader reader;
+    #endif
+    reader.setVariables(&this->floatVariables,&this->intVariables);
+    this->categoryTree = reader.build(filename,checkTree);
+    if (this->categoryTree != 0)
+    {
+        this->isBuilt = true;
+        return true;
+    }
+    return false;
+}
+
+CutsDecisionTree* MethodCuts::getCurrentCategory() const
+{
+    PointerLeafNode<CutsDecisionTree>* leafNode;
+    DecisionNode* decision;
+    Node* currentNode = this->categoryTree;
+    while (!currentNode->isLeaf())
+    {
+        decision = static_cast<DecisionNode*>(currentNode);
+        if (decision->goRight())
+        {
+            currentNode = decision->getRightChild();
+        }
+        else
+        {
+            currentNode = decision->getLeftChild();
+        }
+    }
+    leafNode = static_cast<PointerLeafNode<CutsDecisionTree>*>(currentNode);
+    return leafNode->getValue();
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodDummy.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodDummy.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d90dd811bc89b9fb3e91e25e46910731dfea2a81
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodDummy.cxx
@@ -0,0 +1,23 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: MethodDummy.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/MethodDummy.h"
+
+using namespace TauID;
+
+float MethodDummy::response() const
+{    
+    return 0.;
+}
+
+bool MethodDummy::build(const string&, bool)
+{    
+    return true;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodLLH.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodLLH.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7fbdec7a05e340e0e30d8dbdf7da443f23d062fa
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodLLH.cxx
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: MethodLLH.cxx
+ *
+ * Author: Martin Flechl (mflechl@cern.ch)
+ */
+
+#include "TauDiscriminant/MethodLLH.h"
+
+using namespace TauID;
+
+float MethodLLH::response(unsigned int level) const {
+
+    if (llh){
+        std::string m="MethodLLH: Calling calcLLHValue, level="; m+=level; m+=",name="; m+=getName();
+        print(m);
+	
+        if (level==0) {
+	
+	  llh->calcLLHValue(&this->floatVariables,&this->intVariables,m_option);
+	
+	}
+
+	float llhratio=llh->getLLHValue();
+	
+	
+        if (level==3) return llhratio;
+        else return llh->response(level,m_option);
+    } else{
+        return -99;
+    }
+}
+
+bool MethodLLH::build(const string& filename, bool check) {
+
+    print("In MethodLLH::build");
+    print("Reminder: The safe and default likelihood will only return the same values as during");
+    print("  reconstruction if all required variables are given. This is currently not checked.");
+    
+    if (this->getName()=="llhsafe") m_option=0;
+    else if (this->getName()=="llhdef") m_option=1;
+    else m_option=2;
+
+    #ifdef __STANDALONE
+    llh = new CommonLikelihood(this->verbose);
+    #else
+    llh = new CommonLikelihood();
+    #endif
+
+    check = llh->build(filename);
+    this->isBuilt=check;
+
+    return check;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodTransform.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodTransform.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1a0f0b89de7873e8a662fc6f77cffa65a26c84c2
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/MethodTransform.cxx
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: MethodTransform.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/MethodTransform.h"
+
+using namespace TauID;
+
+float MethodTransform::response() const
+{
+    if (!this->isBuilt) return -101.;
+    Transformation* trans = getCurrentCategory();
+    return trans ? trans->response() : -201.;
+}
+
+bool MethodTransform::build(const string& filename, bool checkTree)
+{    
+    #ifdef __STANDALONE
+    TreeReader reader(this->verbose);
+    #else
+    TreeReader reader;
+    #endif
+    reader.setVariables(&this->floatVariables,&this->intVariables);
+    this->categoryTree = reader.build(filename,checkTree);
+    if (this->categoryTree != 0)
+    {
+        this->isBuilt = true;
+        return true;
+    }
+    return false;
+}
+
+Transformation* MethodTransform::getCurrentCategory() const
+{
+    PointerLeafNode<Transformation>* leafNode;
+    DecisionNode* decision;
+    Node* currentNode = this->categoryTree;
+    if (!currentNode) return 0;
+    while (!currentNode->isLeaf())
+    {
+        decision = static_cast<DecisionNode*>(currentNode);
+        if (decision->goRight())
+        {
+            currentNode = decision->getRightChild();
+        }
+        else
+        {
+            currentNode = decision->getLeftChild();
+        }
+        if (!currentNode) return 0;
+    }
+    leafNode = static_cast<PointerLeafNode<Transformation>*>(currentNode);
+    return leafNode->getValue();
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/Node.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Node.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..17924b97e6ce83e0305e564cca4ecc6de0e2cf6b
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Node.cxx
@@ -0,0 +1,11 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: Node.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/Node.h"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/NoiseCorrIsol.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/NoiseCorrIsol.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..deb0c56587ab75b4311a472cadd0dabb6a7ce988
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/NoiseCorrIsol.cxx
@@ -0,0 +1,210 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//*******************************************************//
+// Name: NoiseCorrIsol.cxx                               //
+// Author: Michel Trottier-McDonald <mtm@cern.ch>        //
+// Description: A class to provide isolation based       //
+// pileup/underlying event correction to the clusters    //
+// of tau decays                                         //
+//*******************************************************//
+
+#include "TauDiscriminant/NoiseCorrIsol.h"
+#include "math.h"
+#include <iostream>
+
+//---------------------------------------------------------
+// Default Constructor
+//---------------------------------------------------------
+NoiseCorrIsol::NoiseCorrIsol()
+{}
+
+
+//---------------------------------------------------------
+// Main constructor 1
+//---------------------------------------------------------
+NoiseCorrIsol::NoiseCorrIsol(const TLorentzVector& tau,
+			     const std::vector<TLorentzVector>& clusters,
+			     double innerDR,
+			     double outerDR,
+			     bool eff)
+    : m_clusters(clusters),
+      m_tau(tau),
+      m_innerDR(innerDR),
+      m_outerDR(outerDR),
+      m_eff(eff)
+{
+    m_areaRatio = areaRatio();
+}
+
+
+//---------------------------------------------------------
+// Main constructor 2
+//---------------------------------------------------------
+NoiseCorrIsol::NoiseCorrIsol(const TLorentzVector& tau,
+			     const std::vector<EMFCluster>& clusters,
+			     double innerDR,
+			     double outerDR,
+			     bool eff)
+  : m_EMFClusters(clusters),
+    m_tau(tau),
+    m_innerDR(innerDR),
+    m_outerDR(outerDR),
+    m_eff(eff)
+{
+    m_areaRatio = areaRatio();
+}
+
+
+//---------------------------------------------------------
+// Destructor
+//---------------------------------------------------------
+NoiseCorrIsol::~NoiseCorrIsol()
+{}
+
+
+//---------------------------------------------------------
+// Ratio of areas calculation
+//---------------------------------------------------------
+double NoiseCorrIsol::areaRatio()
+{
+    double delta = m_outerDR*m_outerDR - m_innerDR*m_innerDR;
+    if(delta != 0)	
+    	return (m_innerDR*m_innerDR)/(delta);
+
+    else return 99999999.0;
+}
+
+
+//---------------------------------------------------------
+// Correcting clusters (pure TLorentzVectors)
+//---------------------------------------------------------
+std::vector<TLorentzVector> NoiseCorrIsol::correctedClustersTLV()
+{
+
+    // Calculate correction
+    std::vector<TLorentzVector> innerClusters;
+    int nCl = (int)m_clusters.size();
+    double outerPt = 0;
+
+    //Obtain isolation ring energy (outerE) and inner clusters
+    for(int i = 0; i < nCl; ++i)
+    {
+	double dR = m_tau.DeltaR(m_clusters.at(i));
+
+	if( (dR < m_outerDR)&&(dR > m_innerDR) )
+	    outerPt += m_clusters.at(i).Pt();
+
+	if( dR < m_innerDR )
+	    innerClusters.push_back(m_clusters.at(i));
+    }
+
+    int nInCl = (int)innerClusters.size();
+    
+    double correction = 0.0;
+    if(nInCl != 0) correction = (outerPt * m_areaRatio)  / nInCl;
+    std::vector<TLorentzVector> finalClusters;
+
+    //Apply correction
+    for(int i = 0; i < nInCl; ++i)
+    {
+	double correctedPt = innerClusters.at(i).Pt() - correction;
+	double Eta         = innerClusters.at(i).Eta();
+	double Phi         = innerClusters.at(i).Phi();
+
+	if(correctedPt > 0) //Reject clusters with smaller E than the correction E
+	{
+	    TLorentzVector corrCluster;
+	    corrCluster.SetPtEtaPhiM(correctedPt, Eta, Phi, 0);
+	    finalClusters.push_back(corrCluster);
+	}
+    }
+
+    return finalClusters;
+}
+
+
+
+//---------------------------------------------------------
+// Correcting clusters (EMFClusters)
+//---------------------------------------------------------
+std::vector<EMFCluster> NoiseCorrIsol::correctedClusters()
+{
+
+    // Calculate correction
+    std::vector<EMFCluster> innerClusters;
+    int nCl = (int)m_EMFClusters.size();
+    double outerPt = 0;
+
+    //Obtain isolation ring energy (outerE) and inner clusters
+    for(int i = 0; i < nCl; ++i)
+    {
+	double dR = m_tau.DeltaR(m_EMFClusters.at(i).TLV());
+
+	if( (dR < m_outerDR)&&(dR > m_innerDR) )
+	    outerPt += m_EMFClusters.at(i).TLV().Pt();
+
+	if( dR < m_innerDR )
+	    innerClusters.push_back(m_EMFClusters.at(i));
+    }
+
+    int nInCl = (int)innerClusters.size();
+    double correction = 0.0;
+    if(nInCl>0) correction = (outerPt * m_areaRatio)  / nInCl;
+    std::vector<EMFCluster> finalClusters;
+
+    //Apply correction
+    for(int i = 0; i < nInCl; ++i)
+    {
+	double correctedPt = innerClusters.at(i).TLV().Pt() - correction;
+	double Eta         = innerClusters.at(i).TLV().Eta();
+	double Phi         = innerClusters.at(i).TLV().Phi();
+
+	if(correctedPt > 0) //Reject clusters with smaller E than the correction E
+	{
+	    TLorentzVector corrCluster;
+	    corrCluster.SetPtEtaPhiM(correctedPt, Eta, Phi, 0);
+	    EMFCluster corrEMFCluster(corrCluster, innerClusters.at(i).PSSF(), innerClusters.at(i).EM2F(), innerClusters.at(i).EM3F());
+	    finalClusters.push_back(corrEMFCluster);
+	}
+    }
+
+    if(m_eff) return effClusters(finalClusters);
+    return finalClusters;
+}
+
+
+//---------------------------------------------------------
+// Calculating effective clusters (post-correction)
+//---------------------------------------------------------
+bool EComp(const EMFCluster& iCluster, const EMFCluster& jCluster)
+{ return (iCluster.TLV().E() > jCluster.TLV().E()); }
+
+std::vector<EMFCluster> NoiseCorrIsol::effClusters(const std::vector<EMFCluster>& inputClusters)
+{
+    int nInputClusters = (int)inputClusters.size();
+    std::vector<EMFCluster> inClusters = inputClusters;
+
+    double SumSquare = 0;
+    double SquareSum = 0;
+
+    for(int i = 0; i < nInputClusters; ++i)
+    {
+	double E = inClusters.at(i).TLV().E();
+	SquareSum += E;
+	SumSquare += E*E;
+    }
+
+    double NEff = 0;
+    if(SumSquare != 0.0) NEff = ceil((SquareSum*SquareSum)/SumSquare);
+
+    std::sort(inClusters.begin(), inClusters.end(), EComp);
+
+    std::vector<EMFCluster> outputClusters;
+
+    for(int i = 0; i < NEff; ++i)
+	outputClusters.push_back(inClusters.at(i));
+
+    return outputClusters;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/Pi0Finder.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Pi0Finder.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..382924c18887664a497e084dde3b0c24e1ae4a45
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Pi0Finder.cxx
@@ -0,0 +1,526 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//*******************************************************//
+// Name: Pi0Finder.cxx                                   //
+// Author: Michel Trottier-McDonald <mtm@cern.ch>        //
+// Description: Main class to find which clusters are    //
+// the pi0s in tau decays involving neutral pions        //
+//*******************************************************//
+
+#include "TauDiscriminant/Pi0Finder.h"
+#include "TauDiscriminant/NoiseCorrIsol.h"
+#include "math.h"
+#include <iostream>
+
+
+//---------------------------------------------------------
+// Default Constructor
+//---------------------------------------------------------
+Pi0Finder::Pi0Finder()
+{}
+
+
+
+//---------------------------------------------------------
+// Constructor 1
+//---------------------------------------------------------
+Pi0Finder::Pi0Finder(const std::vector<TLorentzVector>& tracks,
+		     const std::vector<EMFCluster>& clusters,
+		     bool twoPi0s,
+		     double resImportance, 
+		     double turnOnPoint,
+		     double turnOnRate,
+		     double PSSFactor,
+		     double EM2Factor,
+		     double twoPi0Strength,
+		     bool usePseudoHADF,
+		     bool effClusters  ) : m_tracks(tracks),
+    m_usePseudoHADF(usePseudoHADF),
+    m_twoPi0s(twoPi0s),
+    m_resImportance(resImportance),
+    m_turnOnPoint(turnOnPoint),
+    m_turnOnRate(turnOnRate),
+    m_PSSFactor(PSSFactor),
+    m_EM2Factor(EM2Factor),
+    m_twoPi0Strength(twoPi0Strength),
+    m_caloE(0),
+    m_caloHADE(0),
+    m_trkE(0),
+    m_trkHADF(0),
+    m_doubleCountingE(0),
+    m_applyCorrCluster1(false),
+    m_applyCorrCluster2(false),
+    m_keepCluster1(false),
+    m_keepCluster2(false),
+    m_noMatch(true),
+    m_selEMFCluster1(0.000001),
+    m_selEMFCluster2(0.000001),
+    m_selEMFClusterNotCorr1(0.000001),
+    m_selEMFClusterNotCorr2(0.000001)
+{
+    TLorentzVector trksys;
+    for(unsigned int i = 0; i < m_tracks.size(); ++i)
+	trksys += m_tracks[i];
+
+    NoiseCorrIsol NCI(trksys, clusters, 0.3, 0.4, effClusters);
+    m_EMFClusters = NCI.correctedClusters();
+
+    execute();
+}
+
+
+//---------------------------------------------------------
+// Constructor 2
+//---------------------------------------------------------
+Pi0Finder::Pi0Finder(const std::vector<TLorentzVector>& tracks,
+		     const std::vector<TLorentzVector>& clusters,
+		     const std::vector<float>& PSSFs,
+		     const std::vector<float>& EM2Fs,
+		     const std::vector<float>& EM3Fs,
+		     bool twoPi0s,
+		     double resImportance, 
+		     double turnOnPoint,
+		     double turnOnRate,
+		     double PSSFactor,
+		     double EM2Factor,
+		     double twoPi0Strength,
+		     bool usePseudoHADF,
+		     bool effClusters  ) : m_tracks(tracks),
+    m_usePseudoHADF(usePseudoHADF),
+    m_twoPi0s(twoPi0s),
+    m_resImportance(resImportance),
+    m_turnOnPoint(turnOnPoint),
+    m_turnOnRate(turnOnRate),
+    m_PSSFactor(PSSFactor),
+    m_EM2Factor(EM2Factor),
+    m_twoPi0Strength(twoPi0Strength),
+    m_caloE(0),
+    m_caloHADE(0),
+    m_trkE(0),
+    m_trkHADF(0),
+    m_doubleCountingE(0),
+    m_applyCorrCluster1(false),
+    m_applyCorrCluster2(false),
+    m_keepCluster1(false),
+    m_keepCluster2(false),
+    m_noMatch(true),
+    m_selEMFCluster1(0.000001),
+    m_selEMFCluster2(0.000001),
+    m_selEMFClusterNotCorr1(0.000001),
+    m_selEMFClusterNotCorr2(0.000001)
+{
+    std::vector<EMFCluster> tmpEMFClusters = convertToEMFClusters(clusters, PSSFs, EM2Fs, EM3Fs);
+    for(unsigned int i = 0; i < m_tracks.size(); ++i)
+	m_trkSys += m_tracks[i];
+    NoiseCorrIsol NCI(m_trkSys, tmpEMFClusters, 0.3, 0.4, effClusters);
+    m_EMFClusters = NCI.correctedClusters();
+    execute();
+}
+
+
+//---------------------------------------------------------
+// Destructor
+//---------------------------------------------------------
+Pi0Finder::~Pi0Finder()
+{}
+
+
+//---------------------------------------------------------
+// Execute the pi0 finding
+//---------------------------------------------------------
+void Pi0Finder::execute()
+{
+    preSelParameters();
+    if(m_twoPi0s) select2();
+    else select();
+
+    postSelParameters();
+
+    if(m_keepCluster1)
+    {
+	if(m_applyCorrCluster1) m_selEMFCluster1 = correct(m_selEMFClusterNotCorr1);
+	else m_selEMFCluster1 = m_selEMFClusterNotCorr1;
+    }
+
+    if(m_keepCluster2)
+    {
+	if(m_applyCorrCluster2) m_selEMFCluster2 = correct(m_selEMFClusterNotCorr2);
+	else m_selEMFCluster2 = m_selEMFClusterNotCorr2;
+    }
+}
+
+
+//---------------------------------------------------------
+// Parameters to be used in the selection
+//---------------------------------------------------------
+void Pi0Finder::preSelParameters()
+{
+    //Build the track system
+    int ntracks = (int)m_tracks.size();
+
+    m_trkE = 0;
+
+    for(int i = 0; i < ntracks; ++i)
+	m_trkE += m_tracks.at(i).E();
+
+    //Calculate calo quantities
+    int nclusters = (int)m_EMFClusters.size();
+    m_caloE = 0;
+    m_caloHADE = 0;
+
+    for(int i = 0; i < nclusters; ++i)
+    {
+	double clE    = m_EMFClusters.at(i).TLV().E();
+	double clHADE = m_EMFClusters.at(i).pseudoHADE();
+	if(!m_usePseudoHADF)
+	    clHADE = m_EMFClusters.at(i).HADE();
+
+	m_caloE    += clE;
+	m_caloHADE += clHADE;
+    }
+    if(m_trkE != 0) m_trkHADF = m_caloHADE/m_trkE;
+}
+
+
+//---------------------------------------------------------
+// Calculate scores and select cluster with highest score
+//---------------------------------------------------------
+void Pi0Finder::select()
+{
+    double pi0ScoreMin  = 0.0;
+    EMFCluster cl1;
+    int nclusters = (int)m_EMFClusters.size();
+
+    for(int i = 0; i < nclusters; ++i)
+    {
+	double clE      = m_EMFClusters.at(i).TLV().E();
+	double HADF     = m_EMFClusters.at(i).HADF();
+	double rawRecoE = m_caloE - m_trkE;
+	double rawERes  = 999999.0;
+	if(rawRecoE != 0) rawERes = sqrt(fabs(clE/rawRecoE - 1.0));
+	double PSSE     = m_EMFClusters.at(i).PSSE();
+	double pi0Score = PSSE/(HADF + m_resImportance*rawERes + 0.0000001);
+	if(pi0Score > pi0ScoreMin)
+	{
+	    pi0ScoreMin = pi0Score;
+	    cl1 = m_EMFClusters.at(i);
+	    m_noMatch = false;
+	}
+    }
+    if(!m_noMatch)
+	m_selEMFClusterNotCorr1 = cl1;
+}
+
+
+
+//---------------------------------------------------------
+// Calculate scores and select pair of clusters with
+// highest score
+//---------------------------------------------------------
+void Pi0Finder::select2()
+{
+    double pi0ScoreMin  = 0.0;
+    EMFCluster cl1;
+    EMFCluster cl2;
+
+    int nclusters = (int)m_EMFClusters.size();
+
+    for(int i = 0; i < nclusters; ++i)
+    {
+
+	double clE1      = m_EMFClusters.at(i).TLV().E();
+	double clEta1    = m_EMFClusters.at(i).TLV().Eta();
+	double HADF1     = m_EMFClusters.at(i).HADF();
+	double rawRecoE1 = m_caloE - m_trkE;
+	double rawERes1 = 9999999.0;
+	if(rawRecoE1 != 0) rawERes1  = sqrt(fabs(clE1/rawRecoE1 - 1.0));
+	double PSSE1     = m_EMFClusters.at(i).PSSE();
+
+	double pi0Score1 = PSSE1/(HADF1 + m_resImportance*rawERes1 + 0.0000001);
+
+	if((pi0Score1 > pi0ScoreMin) && (fabs(clEta1) < 5.0))
+	{
+	    pi0ScoreMin = pi0Score1;
+	    cl1 = m_EMFClusters.at(i);
+	    TLorentzVector empty;
+	    EMFCluster EMFempty(empty, 0, 0, 0);
+	    cl2 = EMFempty;
+	    m_noMatch = false;
+	    m_keepCluster1 = true;
+	    m_keepCluster2 = false;
+	}
+
+	for(int j = 0; j < nclusters; ++j)
+	{
+	    if(j > i) // Do not pair a cluster with itself
+	    {
+		double clE_i    = m_EMFClusters.at(i).TLV().E();
+		double clE_j    = m_EMFClusters.at(j).TLV().E();
+		double clEta_i  = m_EMFClusters.at(i).TLV().Eta();
+		double clEta_j  = m_EMFClusters.at(j).TLV().Eta();
+		double HADE_i   = m_EMFClusters.at(i).HADE();
+		double HADE_j   = m_EMFClusters.at(j).HADE();
+		double clE      = clE_i + clE_j;
+		double HADF     = 0.0;
+		if(clE != 0) HADF = (HADE_i + HADE_j)/clE;
+		double rawRecoE = m_caloE - m_trkE;
+		double rawERes  = 999999.0;
+		if(rawRecoE != 0) rawERes = sqrt(fabs(clE/rawRecoE - 1.0));
+		double PSSE_i   = m_EMFClusters.at(i).PSSE();
+		double PSSE_j   = m_EMFClusters.at(j).PSSE();
+		double PSSE     = PSSE_i + PSSE_j;
+		
+		double pi0Score = m_twoPi0Strength*PSSE/(HADF + m_resImportance*rawERes + 0.0000001);
+
+		if((pi0Score > pi0ScoreMin) &&(fabs(clEta_i) < 5.0 && fabs(clEta_j) < 5.0))
+		{
+		    pi0ScoreMin = pi0Score;
+		    cl1 = m_EMFClusters.at(i);
+		    cl2 = m_EMFClusters.at(j);
+		    m_noMatch = false;
+		    m_keepCluster1 = true;
+		    m_keepCluster2 = true;
+		}
+	    }
+	}
+    }
+    if(!m_noMatch)
+    {
+	m_selEMFClusterNotCorr1 = cl1;
+	m_selEMFClusterNotCorr2 = cl2;
+    }
+}
+
+
+
+
+//---------------------------------------------------------
+// Parameters to be used in the correction
+//---------------------------------------------------------
+void Pi0Finder::postSelParameters()
+{
+    double clE1 = 0.0;
+    double clEta1 = 10.0;
+    double clE2 = 0.0;
+    double clEta2 = 10.0;
+
+    if(m_keepCluster1)
+    {
+	clE1 = m_selEMFClusterNotCorr1.TLV().E();
+	clEta1 = m_selEMFClusterNotCorr1.TLV().Eta();
+    }
+
+    if(m_keepCluster2)
+    {
+	clE2 = m_selEMFClusterNotCorr2.TLV().E();
+	clEta2 = m_selEMFClusterNotCorr2.TLV().Eta();
+    }
+
+
+    double DCTwoClusters = 1.0;
+    double DCCluster1 = 1.0;
+    double DCCluster2 = 1.0;
+       
+    if(m_caloE > 0.0)
+    {
+        DCTwoClusters = (clE1 + clE2 + m_trkE)/m_caloE;
+	DCCluster1    = (clE1 + m_trkE)/m_caloE;
+	DCCluster2    = (clE2 + m_trkE)/m_caloE;
+	
+    }
+
+    bool TwoClusterCorrection = (DCTwoClusters > m_turnOnPoint);
+    bool Cluster1Correction = (DCCluster1 > m_turnOnPoint);
+    bool Cluster2Correction = (DCCluster1 > m_turnOnPoint);
+
+    if((clE1 > 0.0 && clEta1 < 5.0) && (clE2 > 0.0 && clEta2 < 5.0))
+    {
+	m_doubleCountingE = DCTwoClusters;
+	
+	m_keepCluster1 = true;
+	m_keepCluster2 = true;
+
+	if(TwoClusterCorrection)
+	{
+	    if(Cluster1Correction) m_applyCorrCluster1 = true;
+	    if(Cluster2Correction) m_applyCorrCluster2 = true;
+
+	    if(!Cluster1Correction && !Cluster2Correction)
+	    {
+		if(DCCluster1 > DCCluster2) m_applyCorrCluster1 = true;
+		else m_applyCorrCluster2 = true;
+	    }
+	}
+    }
+ 
+    else if((clE1 == 0.0 || clEta1 > 5.0) && (clE2 > 0.0 && clEta2 < 5.0))
+    {
+	m_doubleCountingE = DCCluster2;
+	m_keepCluster1 = false;
+	m_keepCluster2 = true;
+	if(Cluster2Correction) m_applyCorrCluster2 = true;
+    }
+
+    else if((clE2 == 0.0 || clEta2 > 5.0) && (clE1 > 0.0 && clEta1 < 5.0))
+    {
+	m_doubleCountingE = DCCluster1;
+	m_keepCluster1 = true;
+	m_keepCluster2 = false;
+	if(Cluster1Correction) m_applyCorrCluster1 = true;
+    }
+
+    else
+    {
+	m_doubleCountingE = 0;
+	m_applyCorrCluster1 = false;
+	m_applyCorrCluster2 = false;
+	m_keepCluster1 = false;
+	m_keepCluster2 = false;
+	m_noMatch = true;
+	
+    }
+}
+
+
+//---------------------------------------------------------
+// Contamination Correction
+//---------------------------------------------------------
+EMFCluster Pi0Finder::correct(const EMFCluster& cl)
+{
+    //Start by extracting the EMFCluster since a new one will have to be built
+
+    double E    = cl.TLV().E();
+    double Eta  = cl.TLV().Eta();
+    double Phi  = cl.TLV().Phi();
+    double M    = cl.TLV().M();
+    double PSSF = cl.PSSF();
+    double EM2F = cl.EM2F();
+    double EM3F = cl.EM3F();
+    double HADE = cl.HADE();
+//    double HADF = cl.HADF();
+
+    if(m_usePseudoHADF)
+    {
+	HADE = cl.pseudoHADE();
+//	HADF = cl.pseudoHADF();
+    }
+    double PSSE = cl.PSSE();
+    double EM2E = cl.EM2E();
+
+    double ChPiContaminationE = m_trkE;
+    if(m_trkHADF > 0.0) ChPiContaminationE = HADE/m_trkHADF;
+    double corrE  = (E - ChPiContaminationE);
+    double corrE2 = 0.0;
+    if(m_doubleCountingE > 0.0) corrE2 =  (m_PSSFactor*PSSE + m_EM2Factor*EM2E)/m_doubleCountingE;
+
+    double corrScale = -exp(m_turnOnRate*(m_turnOnPoint - m_doubleCountingE)) + 1;
+
+    //If there is substantial hadronic energy, use trkHADF
+    TLorentzVector newCl;
+    double effE = E;
+    if(m_caloHADE > 0.0) effE = (1 - corrScale)*E + corrScale*((1-HADE/m_caloHADE)*corrE2 + (HADE/m_caloHADE)*corrE);
+
+    //Correct direction
+    double corrPt = effE/cosh(Eta);
+
+    newCl.SetPtEtaPhiM(corrPt, Eta, Phi, M);
+
+    EMFCluster newEMFCl(newCl, PSSF, EM2F, EM3F);
+
+    return newEMFCl;
+}
+
+
+//---------------------------------------------------------
+// give pi0 Mass
+//---------------------------------------------------------
+EMFCluster Pi0Finder::giveMass(const EMFCluster& cl)
+{
+    double Pt  = cl.TLV().Pt();
+    double Eta  = cl.TLV().Eta();
+    double Phi  = cl.TLV().Phi();
+    double PSSF = cl.PSSF();
+    double EM2F = cl.EM2F();
+    double EM3F = cl.EM3F();
+    double mass = 135;
+
+    TLorentzVector newTLV;
+    newTLV.SetPtEtaPhiM(Pt, Eta, Phi, mass);
+    return EMFCluster(newTLV, PSSF, EM2F, EM3F);
+}
+
+
+//---------------------------------------------------------
+// Return reconstructed visible tau
+//---------------------------------------------------------
+TLorentzVector Pi0Finder::visTauTLV() const
+{
+    int ntracks = (int)m_tracks.size();
+    TLorentzVector trksys;
+
+    for(int i = 0; i < ntracks; ++i)
+    {
+	double Pt  = m_tracks.at(i).Pt();
+	double Eta = m_tracks.at(i).Eta();
+	double Phi = m_tracks.at(i).Phi();
+
+	TLorentzVector trk;
+	trk.SetPtEtaPhiM(Pt, Eta, Phi, 140);
+	trksys += trk;
+    }	    
+
+    TLorentzVector visTau = trksys;
+    if(!m_noMatch)
+    {
+	TLorentzVector pi0s;
+
+	if(m_twoPi0s)
+	{
+	    double pi0sPt =  m_selEMFCluster1.TLV().Pt() +  m_selEMFCluster2.TLV().Pt();
+	    TLorentzVector pi0sdirection = m_selEMFCluster1.TLV() +  m_selEMFCluster2.TLV();
+	    double pi0sEta = pi0sdirection.Eta();
+	    double pi0sPhi = pi0sdirection.Phi();
+
+
+	    pi0s.SetPtEtaPhiM(pi0sPt, pi0sEta, pi0sPhi, 135);
+	}
+	else
+	    pi0s.SetPtEtaPhiM(m_selEMFCluster1.TLV().Pt(), m_selEMFCluster1.TLV().Eta(), m_selEMFCluster1.TLV().Phi(), 135);
+	    
+	visTau += pi0s;
+
+    }
+
+    return visTau;
+}
+
+
+//---------------------------------------------------------
+// Converts the 4 std::vectors to 1 EMFCluster std::vector
+//---------------------------------------------------------
+
+std::vector<EMFCluster> Pi0Finder::convertToEMFClusters(const std::vector<TLorentzVector>& clusters,
+							const std::vector<float>& PSSFs,
+							const std::vector<float>& EM2Fs,
+							const std::vector<float>& EM3Fs)
+{
+    int clsize   = (int)clusters.size();
+    int pssfsize = (int)PSSFs.size();
+    int em2fsize = (int)EM2Fs.size();
+    int em3fsize = (int)EM3Fs.size();
+
+    std::vector<EMFCluster> emfclusters;
+
+    if(clsize == pssfsize && pssfsize == em2fsize && em2fsize == em3fsize)
+    {
+	for(int i = 0; i < clsize; ++i)
+	{
+	    EMFCluster emfcl(clusters.at(i), PSSFs.at(i), EM2Fs.at(i), EM3Fs.at(i));
+	    emfclusters.push_back(emfcl);
+	}
+    }
+
+    return emfclusters;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/TauDetailsManagerStandalone.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TauDetailsManagerStandalone.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f1b1c143bfd3f82ebd6fba58abb02e2329748500
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TauDetailsManagerStandalone.cxx
@@ -0,0 +1,356 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+ /**
+ * file: TauDetailsManagerStandalone.cxx
+ *
+ * Author: Pawel malecki (Pawel.Malecki@cern.ch)
+ */
+
+
+#include "TauDiscriminant/TauDetailsManagerStandalone.h"
+
+#include <utility>
+#include <iostream>
+#include <TFile.h>
+#include <math.h>
+
+// redefine the macro to print strings instead of enums as in TauDetails.h
+#undef ENUM_OR_STRING
+#define ENUM_OR_STRING( x ) #x   
+
+using namespace std;
+using namespace TauID;
+
+// Arrays of the string representations of the detail names
+
+namespace Details
+{
+
+    string IntTauDetailStringS[] =
+    {
+        INT_TAU_DETAILS
+    };
+
+    string FloatTauDetailStringS[] =
+    {
+        FLOAT_TAU_DETAILS
+    };
+
+    string IntEventDetailStringS[] =
+    {
+        INT_EVENT_DETAILS
+    };
+
+    string FloatEventDetailStringS[] =
+    {
+        FLOAT_EVENT_DETAILS
+    };
+}
+
+// The default value for all details
+const float TauDetailsManagerStandalone::LOW_NUMBER = -1111.;
+
+TauDetailsManagerStandalone::TauDetailsManagerStandalone(TTree *tree) {
+
+    
+    //hard-coded for a while
+    m_clusterCone = 0.2;
+    if(tree) m_tree = tree;
+
+    // Initialize the vector containing the tau-based variables
+    this->float_data = vector<float>(Details::__FloatTauDetail__END__+1, LOW_NUMBER);
+    this->int_data = vector<int>(Details::__IntTauDetail__END__+1, int(LOW_NUMBER));
+
+    // Initialize the vector containing the event-based variables
+    this->float_event_data = vector<float>(Details::__FloatEventDetail__END__+1, LOW_NUMBER);
+    this->int_event_data = vector<int>(Details::__IntEventDetail__END__+1, int(LOW_NUMBER));
+
+    // Maps of the string representations to the addresses of the values in the above vectors 
+    unsigned int i;
+    for (i = 0; i < this->float_data.size()-1; ++i)
+    {
+        this->float_details.insert(pair<string,float*>(Details::FloatTauDetailStringS[i],&this->float_data[i]));
+    }
+    for (i = 0; i < this->float_event_data.size()-1; ++i)
+    {
+        this->float_details.insert(pair<string,float*>(Details::FloatEventDetailStringS[i],&this->float_event_data[i]));
+    }
+    for (i = 0; i < this->int_data.size()-1; ++i)
+    {
+        this->int_details.insert(pair<string,int*>(Details::IntTauDetailStringS[i],&this->int_data[i]));
+    }
+    for (i = 0; i < this->int_event_data.size()-1; ++i)
+    {
+        this->int_details.insert(pair<string,int*>(Details::IntEventDetailStringS[i],&this->int_event_data[i]));
+    }
+
+    m_tree = 0;
+
+    
+    tau_charge = 0;
+    tau_author = 0;    
+    tau_numTrack = 0;
+    tau_seedCalo_nWideTrk = 0;
+    tau_eta = 0;
+    tau_phi = 0;
+    tau_pt = 0;
+    tau_seedCalo_trkAvgDist = 0;
+    tau_etOverPtLeadTrk = 0;
+    tau_calcVars_EMFractionAtEMScale = 0;
+    tau_TRTHTOverLT_LeadTrk = 0;
+    tau_seedCalo_dRmax = 0;
+    tau_leadTrack_eta = 0;
+    tau_calcVars_ChPiEMEOverCaloEME = 0;
+    tau_seedTrk_secMaxStripEt = 0;
+    tau_seedTrk_hadLeakEt = 0;
+    tau_seedTrk_sumEMCellEtOverLeadTrkPt = 0;
+    tau_calcVars_corrFTrk = 0;
+    tau_calcVars_corrCentFrac = 0;
+    tau_seedCalo_isolFrac = 0;
+    tau_seedCalo_hadRadius = 0;
+    tau_calcVars_PSSFraction = 0;
+    tau_seedCalo_nStrip = 0;
+    tau_massTrkSys = 0;
+    tau_ipSigLeadTrk = 0;
+    tau_trFlightPathSig = 0;
+    tau_calcVars_EMPOverTrkSysP = 0;
+    evt_calcVars_numGoodVertices = -1111;
+    tau_pi0_n = 0;
+    tau_pi0_vistau_m = 0;   
+    tau_pi0_vistau_pt = 0;   
+    tau_leadTrkPt = 0;
+    tau_seedCalo_etEMAtEMScale = 0;
+    tau_seedCalo_etHadAtEMScale = 0;
+    tau_leadTrack_phi = 0;
+
+    b_tau_charge = 0;
+    b_tau_author = 0;    
+    b_tau_numTrack = 0;
+    b_tau_seedCalo_nWideTrk = 0;
+    b_tau_eta = 0;
+    b_tau_phi = 0;
+    b_tau_pt = 0;
+    b_tau_seedCalo_trkAvgDist = 0;
+    b_tau_etOverPtLeadTrk = 0;
+    b_tau_calcVars_EMFractionAtEMScale = 0;
+    b_tau_TRTHTOverLT_LeadTrk = 0;
+    b_tau_seedCalo_dRmax = 0;
+    b_tau_leadTrack_eta = 0;
+    b_tau_calcVars_ChPiEMEOverCaloEME = 0;
+    b_tau_seedTrk_secMaxStripEt = 0;
+    b_tau_seedTrk_hadLeakEt = 0;
+    b_tau_seedTrk_sumEMCellEtOverLeadTrkPt = 0;
+    b_tau_calcVars_corrFTrk = 0;
+    b_tau_calcVars_corrCentFrac = 0;
+    b_tau_seedCalo_isolFrac = 0;
+    b_tau_seedCalo_hadRadius = 0;
+    b_tau_calcVars_PSSFraction = 0;
+    b_tau_seedCalo_nStrip = 0;
+    b_tau_massTrkSys = 0;
+    b_tau_ipSigLeadTrk = 0;
+    b_tau_trFlightPathSig = 0;
+    b_tau_calcVars_EMPOverTrkSysP = 0;
+
+    b_evt_calcVars_numGoodVertices = 0;
+    b_tau_pi0_n = 0;
+    b_tau_pi0_vistau_m = 0;   
+    b_tau_pi0_vistau_pt = 0;   
+
+    b_tau_leadTrkPt = 0;
+    b_tau_seedTrk_sumEMCellEtOverLeadTrkPt = 0;
+    b_tau_seedCalo_etEMAtEMScale = 0;
+    b_tau_seedCalo_etHadAtEMScale = 0;
+    b_tau_leadTrack_eta = 0;
+    b_tau_leadTrack_phi = 0;
+
+
+    doTrigger = false;   
+}
+bool TauDetailsManagerStandalone::initTree(TTree* tree)
+{
+    if(!tree) return false;
+    m_tree = (TTree*)tree;//->Clone("newtree");
+    m_tree->SetBranchStatus("evt_calcVars_numGoodVertices",1);
+    m_tree->SetBranchAddress("evt_calcVars_numGoodVertices",&evt_calcVars_numGoodVertices,&b_evt_calcVars_numGoodVertices);
+    m_tree->SetBranchStatus("tau_*",1);
+    m_tree->SetBranchAddress("tau_charge",&tau_charge,&b_tau_charge);
+    m_tree->SetBranchAddress("tau_author",&tau_author,&b_tau_author);    
+    m_tree->SetBranchAddress("tau_numTrack",&tau_numTrack,&b_tau_numTrack);
+    m_tree->SetBranchAddress("tau_seedCalo_nWideTrk",&tau_seedCalo_nWideTrk,&b_tau_seedCalo_nWideTrk);
+    m_tree->SetBranchAddress("tau_eta",&tau_eta,&b_tau_eta);
+    m_tree->SetBranchAddress("tau_phi",&tau_phi,&b_tau_phi);
+    m_tree->SetBranchAddress("tau_pt",&tau_pt,&b_tau_pt);
+    m_tree->SetBranchAddress("tau_seedCalo_trkAvgDist",&tau_seedCalo_trkAvgDist,&b_tau_seedCalo_trkAvgDist);
+    m_tree->SetBranchAddress("tau_etOverPtLeadTrk",&tau_etOverPtLeadTrk,&b_tau_etOverPtLeadTrk);
+    m_tree->SetBranchAddress("tau_calcVars_EMFractionAtEMScale",&tau_calcVars_EMFractionAtEMScale,&b_tau_calcVars_EMFractionAtEMScale);
+    m_tree->SetBranchAddress("tau_TRTHTOverLT_LeadTrk",&tau_TRTHTOverLT_LeadTrk,&b_tau_TRTHTOverLT_LeadTrk);
+    m_tree->SetBranchAddress("tau_seedCalo_dRmax",&tau_seedCalo_dRmax,&b_tau_seedCalo_dRmax);
+    m_tree->SetBranchAddress("tau_leadTrack_eta",&tau_leadTrack_eta,&b_tau_leadTrack_eta);
+    m_tree->SetBranchAddress("tau_calcVars_ChPiEMEOverCaloEME",&tau_calcVars_ChPiEMEOverCaloEME,&b_tau_calcVars_ChPiEMEOverCaloEME);
+    m_tree->SetBranchAddress("tau_seedTrk_secMaxStripEt",&tau_seedTrk_secMaxStripEt,&b_tau_seedTrk_secMaxStripEt);
+    m_tree->SetBranchAddress("tau_seedTrk_hadLeakEt",&tau_seedTrk_hadLeakEt,&b_tau_seedTrk_hadLeakEt);
+    m_tree->SetBranchAddress("tau_seedTrk_sumEMCellEtOverLeadTrkPt",&tau_seedTrk_sumEMCellEtOverLeadTrkPt,&b_tau_seedTrk_sumEMCellEtOverLeadTrkPt);
+    m_tree->SetBranchAddress("tau_calcVars_corrFTrk",&tau_calcVars_corrFTrk,&b_tau_calcVars_corrFTrk);
+    m_tree->SetBranchAddress("tau_calcVars_corrCentFrac",&tau_calcVars_corrCentFrac,&b_tau_calcVars_corrCentFrac);
+    m_tree->SetBranchAddress("tau_seedCalo_isolFrac",&tau_seedCalo_isolFrac,&b_tau_seedCalo_isolFrac);
+    m_tree->SetBranchAddress("tau_seedCalo_hadRadius",&tau_seedCalo_hadRadius,&b_tau_seedCalo_hadRadius);
+    m_tree->SetBranchAddress("tau_calcVars_PSSFraction",&tau_calcVars_PSSFraction,&b_tau_calcVars_PSSFraction);
+    m_tree->SetBranchAddress("tau_calcVars_EMPOverTrkSysP",&tau_calcVars_EMPOverTrkSysP,&b_tau_calcVars_EMPOverTrkSysP);
+    m_tree->SetBranchAddress("tau_seedCalo_nStrip",&tau_seedCalo_nStrip,&b_tau_seedCalo_nStrip);
+    m_tree->SetBranchAddress("tau_massTrkSys",&tau_massTrkSys,&b_tau_massTrkSys);
+    m_tree->SetBranchAddress("tau_ipSigLeadTrk",&tau_ipSigLeadTrk,&b_tau_ipSigLeadTrk);
+    m_tree->SetBranchAddress("tau_trFlightPathSig",&tau_trFlightPathSig,&b_tau_trFlightPathSig);
+    m_tree->SetBranchAddress("tau_pi0_n",&tau_pi0_n,&b_tau_pi0_n);
+    m_tree->SetBranchAddress("tau_pi0_vistau_m",&tau_pi0_vistau_m,&b_tau_pi0_vistau_m);
+    m_tree->SetBranchAddress("tau_pi0_vistau_pt",&tau_pi0_vistau_pt,&b_tau_pi0_vistau_pt);
+
+    m_tree->SetBranchAddress("tau_leadTrkPt",&tau_leadTrkPt,&b_tau_leadTrkPt);
+    m_tree->SetBranchAddress("tau_seedCalo_etEMAtEMScale",&tau_seedCalo_etEMAtEMScale,&b_tau_seedCalo_etEMAtEMScale);
+    m_tree->SetBranchAddress("tau_seedCalo_etHadAtEMScale",&tau_seedCalo_etHadAtEMScale,&b_tau_seedCalo_etHadAtEMScale);
+
+    m_tree->SetBranchAddress("tau_leadTrack_phi",&tau_leadTrack_phi,&b_tau_leadTrack_phi);
+
+
+
+    m_tree->SetMakeClass(1);
+    return true;
+  
+}
+
+
+bool TauDetailsManagerStandalone::updateEvent(int entry)
+{
+    // Reset the buffers at the beginning of each event
+    this->float_event_data.assign(this->float_event_data.size(), LOW_NUMBER);
+    this->int_event_data.assign(this->int_event_data.size(), int(LOW_NUMBER));
+    
+    if(!m_tree){
+     cout<<"ERROR: no TTree assigned!"<<endl;
+     return false;
+      
+    }
+    m_tree->GetEntry(entry);
+    
+    
+    this->int_event_data[Details::NUM_PILEUP_AND_PRIMARY_VERTICES] = evt_calcVars_numGoodVertices;
+    
+
+    return true;
+}
+
+bool TauDetailsManagerStandalone::update(unsigned int itau) {
+
+    // Reset the buffers before setting the variables of each tau
+    this->float_data.assign(this->float_data.size(), LOW_NUMBER);
+    this->int_data.assign(this->int_data.size(), int(LOW_NUMBER));
+    
+    if(!m_tree){
+     cout<<"ERROR: no TTree assigned!"<<endl;
+     return false;
+      
+    }
+    
+    
+    
+    this->float_data[Details::TRKAVGDIST] 		= tau_seedCalo_trkAvgDist->at(itau);    
+    this->int_data[Details::AUTHOR]	 		= tau_author->at(itau);    
+    this->float_data[Details::ETOVERPTLEADTRK]		= tau_etOverPtLeadTrk->at(itau);
+    this->float_data[Details::EMFRACTIONATEMSCALE] 	= tau_calcVars_EMFractionAtEMScale->at(itau);
+    this->float_data[Details::TRT_NHT_OVER_NLT]		= tau_TRTHTOverLT_LeadTrk->at(itau);
+    this->float_data[Details::DRMAX]			= tau_seedCalo_dRmax->at(itau);
+    this->float_data[Details::ABS_ETA_LEAD_TRACK]	= fabs(tau_leadTrack_eta->at(itau));
+    this->float_data[Details::CHPIEMEOVERCALOEME]	= tau_calcVars_ChPiEMEOverCaloEME->at(itau);
+    this->float_data[Details::SECMAXSTRIPET]		= tau_seedTrk_secMaxStripEt->at(itau);
+    this->float_data[Details::HADLEAKET]		= tau_seedTrk_hadLeakEt->at(itau);
+    this->float_data[Details::SUMEMCELLETOVERLEADTRKPT] = tau_seedTrk_sumEMCellEtOverLeadTrkPt->at(itau);
+    this->float_data[Details::CORRFTRK]			= tau_calcVars_corrFTrk->at(itau);
+    this->float_data[Details::CORRCENTFRAC]		= tau_calcVars_corrCentFrac->at(itau);
+    this->float_data[Details::ISOLFRAC]			= tau_seedCalo_isolFrac->at(itau);
+    this->float_data[Details::HADRADIUS]		= tau_seedCalo_hadRadius->at(itau);
+    this->float_data[Details::EMPOVERTRKSYSP]		= tau_calcVars_EMPOverTrkSysP->at(itau);
+    this->float_data[Details::PSSFRACTION]		= tau_calcVars_PSSFraction->at(itau);
+    this->int_data[Details::NSTRIP]			= tau_seedCalo_nStrip->at(itau);
+    this->int_data[Details::NUMTRACK]			= tau_numTrack->at(itau);
+    this->float_data[Details::PT]			= tau_pt->at(itau);
+    this->float_data[Details::ETA]			= tau_eta->at(itau);
+    this->int_data[Details::NUMWIDETRACK]		= tau_seedCalo_nWideTrk->at(itau);
+    this->float_data[Details::MASSTRKSYS]		= tau_massTrkSys->at(itau);
+    this->float_data[Details::IPSIGLEADTRK]		= tau_ipSigLeadTrk->at(itau);
+    this->int_data[Details::TAU_PI0_N]			= tau_pi0_n->at(itau);
+    this->float_data[Details::TAU_PTRATIO]		= tau_pi0_vistau_pt->at(itau)/tau_pt->at(itau);
+    this->float_data[Details::TAU_PI0_VISTAU_M]		= tau_pi0_vistau_m->at(itau);
+    this->float_data[Details::TRFLIGHTPATHSIG]		= tau_trFlightPathSig->at(itau);
+    
+ // solve for E3 
+    float tau_sumETCellsLAr = tau_seedTrk_sumEMCellEtOverLeadTrkPt->at(itau) * tau_leadTrkPt->at(itau);
+    float tau_sumEMCellET = tau_seedCalo_etEMAtEMScale->at(itau);
+    float tau_E3 = tau_sumETCellsLAr - tau_sumEMCellET;
+ // remove E3
+    float tau_seedCalo_etHadAtEMScale_noE3 = tau_seedCalo_etHadAtEMScale->at(itau) - tau_E3;
+            float tau_seedCalo_etEMAtEMScale_yesE3 = tau_seedCalo_etEMAtEMScale->at(itau) + tau_E3;
+
+ //calculate new EMFraction
+    this->float_data[Details::EMFRACTIONATEMSCALE_MOVEE3] = tau_seedCalo_etEMAtEMScale_yesE3/(tau_seedCalo_etEMAtEMScale_yesE3+tau_seedCalo_etHadAtEMScale_noE3);
+
+
+    this->float_data[Details::TAU_ABSDELTAETA] = fabs(tau_leadTrack_eta->at(itau)-tau_eta->at(itau));
+    this->float_data[Details::TAU_ABSDELTAPHI] = fabs(tau_leadTrack_phi->at(itau)-tau_phi->at(itau));
+    this->float_data[Details::TAU_SEEDTRK_SECMAXSTRIPETOVERPT] = (tau_leadTrkPt->at(itau)!=0?tau_seedTrk_secMaxStripEt->at(itau)/tau_leadTrkPt->at(itau):LOW_NUMBER);
+
+
+    
+    
+    
+    
+
+    return true;
+}
+
+const float* TauDetailsManagerStandalone::getFloatDetailAddress(Details::FloatTauDetail detail) const {
+
+    return &this->float_data[detail];
+}
+
+const int* TauDetailsManagerStandalone::getIntDetailAddress(Details::IntTauDetail detail) const {
+
+    return &this->int_data[detail];
+}
+
+const float* TauDetailsManagerStandalone::getFloatDetailAddress(Details::FloatEventDetail detail) const {
+
+    return &this->float_event_data[detail];
+}
+
+const int* TauDetailsManagerStandalone::getIntDetailAddress(Details::IntEventDetail detail) const {
+
+    return &this->int_event_data[detail];
+}
+
+float TauDetailsManagerStandalone::getFloatDetailValue(Details::FloatTauDetail detail) const {
+
+    return this->float_data[detail];
+}
+
+int TauDetailsManagerStandalone::getIntDetailValue(Details::IntTauDetail detail) const {
+
+    return this->int_data[detail];
+}
+
+float TauDetailsManagerStandalone::getFloatDetailValue(Details::FloatEventDetail detail) const {
+
+    return this->float_event_data[detail];
+}
+
+int TauDetailsManagerStandalone::getIntDetailValue(Details::IntEventDetail detail) const {
+
+    return this->int_event_data[detail];
+}
+ 
+int TauDetailsManagerStandalone::getNtau()
+{
+ if(tau_pt) return tau_pt->size();
+ else return 0;
+}
+
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/TauIDReader.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TauIDReader.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bc8dd60b5800cf0d37cccf369ecbea65663f0708
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TauIDReader.cxx
@@ -0,0 +1,774 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#include "TauDiscriminant/TauIDReader.h"
+#include "TROOT.h"
+#include <iomanip>
+
+using namespace TauID;
+
+void TauIDReader::print() const
+{
+    ios_base::fmtflags original_flags = cout.flags();
+    vector<string>::const_iterator name_it(this->methodNames.begin());
+    vector<float*>::const_iterator it_float(this->methodBuffer.begin());
+    cout << "Methods:" << endl;
+    for(; it_float != this->methodBuffer.end(); ++it_float)
+    {
+        cout << left << setw(40) << setfill('.') << *(name_it++) << ' ' << **it_float << endl;
+    }
+    cout << "Variables:" << endl;
+    name_it = this->floatNames.begin();
+    it_float = this->floatVariables.begin();
+    for(; it_float != this->floatVariables.end(); ++it_float)
+    {
+        cout << left << setw(40) << setfill('.') << *(name_it++) << ' ' << **it_float << endl;
+    }
+    name_it = this->vectFloatNames.begin();
+    it_float = this->vectFloatVariables.begin();
+    for(; it_float != this->vectFloatVariables.end(); ++it_float)
+    {
+        cout << left << setw(40) << setfill('.') << *(name_it++) << ' ' << **it_float << endl;
+    }
+    name_it = this->intNames.begin();
+    vector<int*>::const_iterator it_int(this->intVariables.begin());
+    for(; it_int != this->intVariables.end(); ++it_int)
+    {
+        cout << left << setw(40) << setfill('.') << *(name_it++) << ' ' << **it_int << endl;
+    }
+    name_it = this->vectIntNames.begin();
+    it_int = this->vectIntVariables.begin();
+    for(; it_int != this->vectIntVariables.end(); ++it_int)
+    {
+        cout << left << setw(40) << setfill('.') << *(name_it++) << ' ' << **it_int << endl;
+    }
+    cout.flags(original_flags);
+}
+
+bool TauIDReader::bookMethod(Types::MethodType type, const string& name, const string& filename, unsigned int numResponses) {
+
+    if (name.size() == 0)
+    {
+        cout << "You must supply a non-empty name" << endl;
+        return false;
+    }
+
+    if (numResponses == 0)
+    {
+        cout << "Number of responses must be non-zero" << endl;
+        return false;
+    }
+
+    if (find(methodNames.begin(),methodNames.end(),name) != methodNames.end())
+    {
+        cout << "Method with name " << name << " was already booked." << endl;
+        return false;
+    }
+
+    MethodBase* method;
+    if (type == Types::CUTS)
+    {
+        method = new MethodCuts(name);
+	
+    }
+    else if (type == Types::BDT)
+    {
+        method = new MethodBDT(name);
+    }
+    else if (type == Types::LLH)
+    {
+        method = new MethodLLH(name);
+    }
+    else if (type == Types::DUMMY)
+    {
+        method = new MethodDummy(name);
+    }
+    else if (type == Types::TRANSFORM)
+    {
+        method = new MethodTransform(name);
+    }
+    else
+    {
+        return false;
+    }
+
+    if (verbose) cout << "Booking method " << name << endl;
+
+    // Add variables that reference the outputs of other methods
+    vector<pair<string,string> >::const_iterator methodvar_it(this->methodVariables.begin());
+    vector<string>::const_iterator method_name_it;
+    for (; methodvar_it != this->methodVariables.end(); ++methodvar_it)
+    {
+        method_name_it = find(this->methodNames.begin(),this->methodNames.end(),methodvar_it->second);
+        method->addVariable(methodvar_it->first,this->methodBuffer[method_name_it - this->methodNames.begin()],'F');
+    }
+    
+    //vector<int*>::const_iterator it_val_int(intVariables.begin());
+    vector<string>::const_iterator it_name(intNames.begin());
+    for (; it_name != intNames.end();)
+    {
+        //method->addVariable(*(it_name++),*(it_val_int++),'I');
+        string valname = *(it_name++);
+	const map<string,int*> *dets = m_tdms->getIntDetails();
+        method->addVariable(valname,dets->at(valname),'I');
+    }
+
+    //vector<float*>::const_iterator it_val_float(floatVariables.begin());
+    it_name = floatNames.begin();
+    for (;/* it_val_float != floatVariables.end() &&*/ it_name != floatNames.end();)
+    {
+        string valname = *(it_name++);     
+	const map<string,float*> *dets = m_tdms->getFloatDetails();
+	std::cout<<valname<<std::endl;	
+        method->addVariable(valname,dets->at(valname),'F');
+    }
+
+    //it_val_int = vectIntVariables.begin();
+    it_name = vectIntNames.begin();
+    for (; /*it_val_int != vectIntVariables.end() &&*/ it_name != vectIntNames.end();)
+    {
+        string valname = *(it_name++);    
+	const map<string,int*> *dets = m_tdms->getIntDetails();
+        method->addVariable(valname,dets->at(valname),'I');
+    }
+
+    //it_val_float = vectFloatVariables.begin();
+    it_name = vectFloatNames.begin();
+    for (; /*it_val_float != vectFloatVariables.end() &&*/ it_name != vectFloatNames.end();)
+    {
+        string valname = *(it_name++);      
+	const map<string,float*> *dets = m_tdms->getFloatDetails();
+        method->addVariable(valname,dets->at(valname),'F');
+    }
+
+    if (!method->build(filename,false))
+    {
+        cout << "Initializing the method " << name << " failed." << endl;
+        delete method;
+        return false;
+    }
+
+    this->methodNames.push_back(name);
+    this->methodBuffer.push_back(new float(0.));
+    this->methods.push_back(pair<MethodBase*,vector<vector<float>* > >(method,vector<vector<float>* >(numResponses, 0)));
+    vector<vector<float>* >::iterator it = methods.back().second.begin();
+    for (; it != methods.back().second.end(); ++it)
+    {
+        *it = new vector<float>();
+    }
+    return true;
+}
+
+bool TauIDReader::addVariable(const string& name, const string& type, const string& branchName) {
+
+    if (this->verbose) cout << "Adding variable " << name << " --> " << branchName << endl;
+    if (find(this->allVariableNames.begin(), this->allVariableNames.end(), name) != this->allVariableNames.end())
+    {
+        cout << "Variable " << name << " has already been booked" << endl;
+        return false;
+    }
+//     if (find(this->allBranchNames.begin(), this->allBranchNames.end(), branchName) != this->allBranchNames.end())
+//     {
+//         cout << "A variable referring to branch " << branchName << " has already been booked" << endl;
+//         return false;
+//     }
+    vector<string>::const_iterator it(find(this->methodNames.begin(),this->methodNames.end(),branchName));
+    if (it != this->methodNames.end())
+    {
+        if (this->verbose) cout << "Branch matches name of method previously booked" << endl;
+        if (type != "F")
+        {
+            cout << "The type of all method outputs are scalar floats" << endl;
+            cout << "Variable referring to branch " << branchName << " must be of type \"F\"" << endl;
+            return false;
+        }
+        if (this->verbose) cout << "This variable will refer to the output of that method" << endl;
+        this->allVariableNames.push_back(name);
+        this->allBranchNames.push_back(branchName);
+        this->methodVariables.push_back(pair<string,string>(name,branchName));
+        return true;
+    }
+    if (type == "F")
+    {
+        this->floatNames.push_back(name);
+        this->allVariableNames.push_back(name);
+        //this->floatBranches.push_back(branchName);
+        //this->allBranchNames.push_back(branchName);
+        this->floatVariables.push_back(new float(0.));
+        return true;
+    }
+    else if (type == "I")
+    {
+        this->intNames.push_back(name);
+        this->allVariableNames.push_back(name);
+        //this->intBranches.push_back(branchName);
+        //this->allBranchNames.push_back(branchName);
+        this->intVariables.push_back(new int(0));
+        return true;
+    }
+    else if (type == "VF")
+    {
+        this->vectFloatNames.push_back(name);
+        this->allVariableNames.push_back(name);
+        //this->vectFloatBranches.push_back(branchName);
+        //this->allBranchNames.push_back(branchName);
+        this->vectFloatVariables.push_back(new float(0.));
+        this->vectorMode = true;
+        return true;
+    }
+    else if (type == "VI")
+    {
+        this->vectIntNames.push_back(name);
+        this->allVariableNames.push_back(name);
+        //this->vectIntBranches.push_back(branchName);
+        //this->allBranchNames.push_back(branchName);
+        this->vectIntVariables.push_back(new int(0));
+        this->vectorMode = true;
+        return true;
+    }
+    cout << "Unknown variable type " << type << " for variable " << name << endl;
+    return false;
+}
+
+bool TauIDReader::checkBranch(TTree* tree, const char* name, string type, bool checkType)
+{
+    if (!tree)
+    {
+        return false;
+    }
+    TBranch* branch = tree->GetBranch(name);
+    if (!branch)
+    {
+        cout << "Branch " << name << " does not exist!" << endl;
+        return false;
+    }
+    if (checkType)
+    {
+        if (strcmp(type.c_str(), branch->GetClassName()))
+        {
+            cout << "Branch " << name << " does not contain the correct type: " << branch->GetClassName() << " (expected " << type << ")" << endl;
+            return false;
+        }
+    }
+    return true;
+}
+
+bool TauIDReader::setOutput(const string& outputFileName, const string& _outputDir)
+{
+    if (this->own_output)
+    {
+        delete this->output;
+    }
+    this->own_output = true;
+    this->output = new TFile(outputFileName.c_str(),"NEW");
+    gROOT->GetListOfFiles()->Remove(this->output);
+    if (!this->output->IsOpen() || this->output->IsZombie())
+    {
+        cout << "Could not create " << outputFileName << endl;
+        return false;
+    }
+    if (!this->output->GetDirectory(_outputDir.c_str()))
+    {
+        cout << "Could not find directory " << _outputDir << " in " << outputFileName << endl;
+        return false;
+    }
+    this->output->cd(_outputDir.c_str());
+    if (this->verbose)
+    {
+        cout << "Output is now " << outputFileName;
+        if (_outputDir != "")
+        {
+            cout << "/" << _outputDir;
+        }
+        cout << endl;
+    }
+    this->outputDir = _outputDir;
+    return true;
+}
+
+bool TauIDReader::setOutput(TFile& outputFile, const string& _outputDir)
+{
+    return this->setOutput(&outputFile, _outputDir);
+}
+
+bool TauIDReader::setOutput(TFile* outputFile, const string& _outputDir)
+{
+    if (!outputFile)
+    {
+        cout << "NULL file!" << endl;
+        return false;
+    }
+    if (!outputFile->IsOpen() || outputFile->IsZombie())
+    {
+        cout << "File is not open!" << endl;
+        return false;
+    }
+    if (this->own_output)
+    {
+        delete this->output;
+    }
+    this->own_output = false;
+    this->output = outputFile;
+    if (!this->output->GetDirectory(_outputDir.c_str()))
+    {
+        cout << "Could not find directory " << _outputDir << " in " << outputFile->GetName() << endl;
+        return false;
+    }
+    this->output->cd(_outputDir.c_str());
+    if (this->verbose)
+    {
+        cout << "Output is now " << outputFile->GetName();
+        if (outputDir != "")
+        {
+            cout << "/" << _outputDir;
+        }
+        cout << endl;
+    }
+    this->outputDir = _outputDir;
+    return true;
+}
+
+int TauIDReader::classify(const string& inputTreeName, const string& outputTreeName,
+    const string& inputFileName, const string& inputDir,
+    bool makeFriend,
+    bool copyTree)
+{
+    unsigned int entries(0);
+    TFile* input = new TFile(inputFileName.c_str(), "READ");
+    if (!input->IsOpen() || input->IsZombie())
+    {
+        cout << "Could not open " << inputFileName << endl;
+        delete input;
+        return -1;
+    }
+    // Try to cd to desired directory in input ROOT file
+    if (!input->GetDirectory(inputDir.c_str()))
+    {
+        delete input;
+        cout << "Could not find directory " << inputDir << " in " << inputFileName << endl;
+        return -1;
+    }
+    input->cd(inputDir.c_str());
+    TTree* tree = (TTree*)input->Get(inputTreeName.c_str());
+    if (!tree)
+    {
+        cout << "Could not find tree " << inputTreeName << " in file " << inputFileName;
+        if (inputDir != "")
+        {
+            cout << "/" << inputDir << endl;
+        }
+        else
+        {
+            cout << endl;
+        }
+        delete input;
+        return -1;
+    }
+    entries = tree->GetEntries();
+    TTree* outputTree = this->classify(tree, outputTreeName, copyTree);
+    if (!outputTree)
+    {
+        delete tree;
+        delete input;
+        return -1;
+    }
+    if (makeFriend)
+    {
+        if (verbose)
+        {
+            cout << "Making input tree a friend of output tree" << endl;
+        }
+        if (inputDir == "")
+        {
+            outputTree->AddFriend(inputTreeName.c_str(), inputFileName.c_str());
+        }
+        else
+        {
+            string fullInputDir(inputFileName + "/" + inputDir);
+            outputTree->AddFriend(inputTreeName.c_str(), fullInputDir.c_str());
+        }
+    }
+    this->output->cd(this->outputDir.c_str());
+    outputTree->Write("",TObject::kOverwrite);
+    delete tree;
+    delete input;
+    delete outputTree;
+    return entries;
+}
+
+TTree* TauIDReader::classify(TTree& tree, const string& outputTreeName, bool copyTree)
+{
+    return this->classify(&tree, outputTreeName, copyTree);
+}
+
+TTree* TauIDReader::classify(TTree* tree, const string& outputTreeName, bool copyTree)
+{
+    if (!this->output)
+    {
+        cout << "No output file has been previously specified" << endl;
+        return 0;
+    }
+    if (!this->output->IsOpen() || this->output->IsZombie())
+    {
+        cout << "Output file is not open" << endl;
+        return 0;
+    }
+    if (!tree)
+    {
+        cout << "Input tree is NULL!" << endl;
+        return 0;
+    }
+    int lastStatus(0);
+    unsigned long entry(0);
+    unsigned long numEntries(tree->GetEntries());
+    if (numEntries == 0)
+    {
+        cout << "The input tree has no entries!" << endl;
+        return 0;
+    }
+    if (this->methods.size()==0)
+    {
+        cout << "No methods were booked successfully!" << endl;
+        return 0;
+    }
+    if (tree->GetNbranches() == 0)
+    {
+        cout << "Input tree contains no branches!" << endl;
+        return 0;
+    }
+    
+    
+    
+    m_tdms->initTree(tree);
+
+
+    vector<pair<MethodBase*,vector<vector<float>* > > >::iterator method_it;
+    vector<int*>::iterator intvalue_it;
+    vector<float*>::iterator floatvalue_it;
+    vector<string>::const_iterator intbranch_it;
+    vector<string>::const_iterator floatbranch_it;
+    vector<string>::const_iterator intname_it;
+    vector<string>::const_iterator floatname_it;
+
+    
+    TTree* outputTree(0);
+    // this has already been checked in setOutput to succeed
+    this->output->cd(this->outputDir.c_str());
+
+    // Create the output tree
+    if (copyTree)
+    {
+        // Make sure that all branches are activated
+        tree->SetBranchStatus("*",1);
+
+        // Deactivate the branches we will use for the Methods (if they already exist in the input tree)
+        for (method_it = methods.begin(); method_it != methods.end(); ++method_it)
+        {
+            for (unsigned int i(0); i<(method_it->second).size(); ++i)
+            {
+                string name = method_it->first->getName();
+                if ((method_it->second).size() > 1)
+                {
+                    name += to_string<unsigned int>(i);
+                }
+                if (tree->GetBranch(name.c_str()))
+                {
+                    tree->SetBranchStatus(name.c_str(),0);
+                }
+            }
+        }
+        if (verbose)
+        {
+            cout << "Cloning input tree... " << flush;
+        }
+        outputTree = tree->CloneTree(-1,"fast");
+        if (!outputTree)
+        {
+            if (verbose) cout << "fail" << endl;
+            cout << "Could not clone tree" << endl;
+            return 0;
+        }
+        outputTree->Write("",TObject::kOverwrite);
+        if (verbose)
+        {
+            cout << "done" << endl;
+        }
+        if (outputTree->GetNbranches() == 0)
+        {
+            cout << "The output tree contains no branches" << endl;
+            cout << "They were probably all deactivated before cloning the input tree" << endl;
+            delete outputTree;
+            return 0;
+        }
+        // Separate the cloned tree from the original
+        tree->CopyAddresses(outputTree, true);
+        //outputTree->ResetBranchAddresses();
+        //outputTree->SetBranchStatus("*",0);
+    }
+    else
+    {
+        outputTree = new TTree(outputTreeName.c_str(),outputTreeName.c_str());
+    }
+
+    // Disable all branches of the input tree
+    tree->SetBranchStatus("*",0);
+    // Only the required branches are reactivated below
+
+    m_tdms->initTree(tree);
+
+    // Scalar variables:
+
+    // Set the addresses of the int variables
+//    intvalue_it = intVariables.begin();
+//    intbranch_it = intBranches.begin();
+  
+    
+//     for ( ; intvalue_it != intVariables.end() && intbranch_it != intBranches.end(); )
+//     {
+// 
+//         if (!checkBranch(tree,intbranch_it->c_str(),string("int"),false))
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//         
+//         tree->SetBranchStatus(intbranch_it->c_str(),1);
+//         if (tree->SetBranchAddress((intbranch_it++)->c_str(),*(intvalue_it++)) != 0)
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//         
+//     }
+//     
+//     // Set the addresses of the float variables
+//     floatvalue_it = floatVariables.begin();
+//     floatbranch_it = floatBranches.begin();
+//     for (; floatvalue_it != floatVariables.end() && floatbranch_it != floatBranches.end(); )
+//     {
+//         if (!checkBranch(tree,floatbranch_it->c_str(),string("float"),false))
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//         tree->SetBranchStatus(floatbranch_it->c_str(),1);
+//         if (tree->SetBranchAddress((floatbranch_it++)->c_str(),*(floatvalue_it++)) != 0)
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//     }
+// 
+//     // Vector variables:
+// 
+//     vector<vector<int>* > intBuffer(vectIntVariables.size(),0);
+//     vector<vector<float>* > floatBuffer(vectFloatVariables.size(),0);
+// 
+//     // Set the addresses of the int vector variables
+//     vector<vector<int>* >::iterator intbuffer_it(intBuffer.begin());
+//     intbranch_it = vectIntBranches.begin();
+//     for (; intbuffer_it != intBuffer.end() && intbranch_it != vectIntBranches.end(); )
+//     {
+//         if (!checkBranch(tree,intbranch_it->c_str(),string("vector<int>")))
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//         tree->SetBranchStatus(intbranch_it->c_str(),1);
+//         if (tree->SetBranchAddress((intbranch_it++)->c_str(),&(*(intbuffer_it++))) != 0)
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//     }
+// 
+//     // Set the addresses of the float vector variables
+//     vector<vector<float>* >::iterator floatbuffer_it(floatBuffer.begin());
+//     floatbranch_it = vectFloatBranches.begin();
+//     for (; floatbuffer_it != floatBuffer.end() && floatbranch_it != vectFloatBranches.end(); )
+//     {
+//         if (!checkBranch(tree,floatbranch_it->c_str(),string("vector<float>")))
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//         tree->SetBranchStatus(floatbranch_it->c_str(),1);
+//         if (tree->SetBranchAddress((floatbranch_it++)->c_str(),&(*(floatbuffer_it++))) != 0)
+//         {
+//             delete outputTree;
+//             return 0;
+//         }
+//     }
+
+    vector<TBranch*> outputBranches;
+    
+    
+    // Initialize new branches in the output tree
+    for (method_it = methods.begin(); method_it != methods.end(); ++method_it)
+    {
+        for (unsigned int i(0); i<(method_it->second).size(); ++i)
+        {
+            string name = method_it->first->getName();
+            if ((method_it->second).size() > 1)
+            {
+                name += to_string<unsigned int>(i);
+            }
+            method_it->second[i]->clear();
+            if (this->vectorMode)
+            {
+                outputBranches.push_back(outputTree->Branch(name.c_str(),"std::vector<float>",&(method_it->second[i])));
+            }
+            else
+            {
+                method_it->second[i]->push_back(0.);
+                outputBranches.push_back(outputTree->Branch(name.c_str(),&(*(method_it->second[i]->begin())),(name+"/F").c_str()));
+            }
+        }
+    }
+
+//    void* sampleVector(0);
+//    bool sampleIsInt(false);
+    unsigned int vectorLength;
+
+//     if (this->vectorMode)
+//     {
+//         if (vectIntVariables.size() > 0)
+//         {
+//             sampleVector = intBuffer[0];
+//             sampleIsInt = true;
+//         }
+//         else
+//         {
+//             sampleVector = floatBuffer[0];
+//         }
+//     }
+
+    vector<TBranch*>::const_iterator outputBranches_it;
+    vector<TBranch*>::const_iterator outputBranches_end(outputBranches.end());
+
+    // The main event loop
+
+    for (; entry < numEntries; ++entry)
+    {
+        tree->GetEntry(entry);
+	m_tdms->updateEvent(entry);
+	
+	
+        if (verbose)
+        {
+            int newStatus = int(100*(entry+1)/numEntries);
+            if (newStatus != lastStatus || entry == 0)
+            {
+                cout << "\rClassifying " << numEntries << " entries... " << newStatus << '%' << flush;
+                lastStatus = newStatus;
+            }
+        }
+        if (this->vectorMode)
+        {
+            // Assume all vectors have the same length (as they should...)
+            // TODO: protect against vectors of differing length
+//             if (sampleIsInt)
+//                 vectorLength = static_cast<vector<int>* >(sampleVector)->size();
+//             else
+//                 vectorLength = static_cast<vector<float>* >(sampleVector)->size();
+
+	    vectorLength = m_tdms->getNtau();
+
+            for (method_it = methods.begin(); method_it != methods.end(); ++method_it)
+            {
+                for (unsigned int i(0); i<(method_it->second).size(); ++i)
+                {
+                    *(method_it->second)[i] = vector<float>(vectorLength,0.);
+                }
+            }
+	    
+            for (unsigned int j(0); j < vectorLength; ++j)
+            {
+		
+		m_tdms->update(j);
+		
+                // Copy buffers to stage
+/*                intbuffer_it = intBuffer.begin();
+                intvalue_it = this->vectIntVariables.begin();
+                for (; intbuffer_it != intBuffer.end();)
+                {
+                    **(intvalue_it++) = (**(intbuffer_it++))[j];
+                }
+                floatbuffer_it = floatBuffer.begin();
+                floatvalue_it = this->vectFloatVariables.begin();
+                for (; floatbuffer_it != floatBuffer.end();)
+                {
+                    **(floatvalue_it++) = (**(floatbuffer_it++))[j];
+                }*/
+                method_it = methods.begin();
+                unsigned int k(0);
+                // Fill the output vector containing each Method's response
+                for (; method_it != methods.end(); ++method_it)
+                {
+                    for (unsigned int i(0); i<(method_it->second).size(); ++i)
+                    {
+                        float response = method_it->first->response(i);
+                        *(this->methodBuffer[k]) = response;
+                        (*(method_it->second[i]))[j] = response;
+                    }
+                    ++k;
+                }
+            }
+        }
+        else
+        {
+            unsigned int j(0);
+            // Get the response from each Method
+            for (method_it = methods.begin(); method_it != methods.end(); ++method_it)
+            {
+                for (unsigned int i(0); i<(method_it->second).size(); ++i)
+                {
+                    float response = method_it->first->response(i);
+                    *(this->methodBuffer[j]) = response;
+                    (*((method_it->second)[i]))[0] = response;
+                }
+                ++j;
+            }
+        }
+        if (copyTree)
+        {
+            outputBranches_it = outputBranches.begin();
+            for(; outputBranches_it != outputBranches_end; ++outputBranches_it)
+            {
+                if ((*outputBranches_it)->Fill() < 1)
+                {
+                    if (verbose)
+                    {
+                        cout << endl;
+                    }
+                    cout << "There was an error when filling the output tree" << endl;
+                    delete outputTree;
+                    return 0;
+                }
+            }
+        }
+        else
+        {
+            if (outputTree->Fill() < 1)
+            {
+                if (verbose)
+                {
+                    cout << endl;
+                }
+                cout << "There was an error when filling the output tree" << endl;
+                delete outputTree;
+                return 0;
+            }
+        }
+    }
+    if (verbose)
+    {
+        cout << endl;
+    }
+    return outputTree;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/Transformation.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Transformation.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..881e588aac3cc1d64a90f80c9238d4caf6bb6caa
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Transformation.cxx
@@ -0,0 +1,32 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: Transformation.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include "TauDiscriminant/Transformation.h"
+
+float Transformation::response() const {
+
+    Node* currentNode = 0;
+    DecisionNode* decision = 0;
+    LeafNode<float>* leafNode = 0;
+    vector<pair<Node*,float> >::const_iterator tree = this->trees.begin();
+    if (tree == this->trees.end()) return -200.;
+    currentNode = (*tree).first;
+    while (!currentNode->isLeaf()) {
+        decision = static_cast<DecisionNode*>(currentNode);
+        if (decision->goRight()) {
+            currentNode = decision->getRightChild();
+        } else {
+            currentNode = decision->getLeftChild();
+        }
+        if (!currentNode) return -200.;
+    }
+    leafNode = static_cast<LeafNode<float>*>(currentNode);
+    return leafNode->getValue();
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/TreeReader.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TreeReader.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e5aa846935e3fb20185706cb07f3695a2caf6b33
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TreeReader.cxx
@@ -0,0 +1,748 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#include "TauDiscriminant/TreeReader.h"
+#include "TROOT.h"
+#include "TNamed.h"
+#include "TFile.h"
+#include "TGraph.h"
+#include "TGraph2D.h"
+#include "TF1.h"
+
+bool isGoodTree(Node* node)
+{
+    if (!node) return false;
+    LeafNode<float> dummyLeafNode;
+    if (string(typeid(dummyLeafNode).name()).compare(typeid(*node).name())==0) return true;
+    return isGoodTree(node->getLeftChild()) && isGoodTree(node->getRightChild());
+}
+
+void findBadNodes(Node* node, vector<Node*>& badNodes)
+{
+    if (!node) return;
+    LeafNode<float> dummyLeafNode;
+    if (string(typeid(dummyLeafNode).name()).compare(typeid(*node).name())==0) return;
+    UnivariateCut<float>* intNode = static_cast<UnivariateCut<float>*>(node);
+    if (!intNode->isComplete()) badNodes.push_back(intNode);
+    findBadNodes(node->getLeftChild(),badNodes);
+    findBadNodes(node->getRightChild(),badNodes);
+}
+
+string signature(Node* node, vector<Node*>* badNodes, string hash = "")
+{
+    if(!node) return hash;
+    if (badNodes)
+    {
+        if (find(badNodes->begin(),badNodes->end(),node) != badNodes->end()) hash += "[BAD]";
+    }
+    Node* child = node->getLeftChild();
+    if (child) hash = signature(child,badNodes,hash+"1")+"2";
+    child = node->getRightChild();
+    if (child) hash = signature(child,badNodes,hash+"3")+"2";
+    return hash;
+}
+
+void getLeafNodes(Node* node, vector<Node*>& leafNodes)
+{
+    if (!node) return;
+    if (node->isLeaf())
+    {
+        leafNodes.push_back(node);
+        return;
+    }
+    getLeafNodes(node->getLeftChild(),leafNodes);
+    getLeafNodes(node->getRightChild(),leafNodes);
+}
+
+void getIncompleteNodes(Node* node, vector<Node*>& incompleteNodes)
+{
+    if (!node) return;
+    if (!node->isComplete())
+    {
+        incompleteNodes.push_back(node);
+    }
+    getIncompleteNodes(node->getLeftChild(),incompleteNodes);
+    getIncompleteNodes(node->getRightChild(),incompleteNodes);
+}
+
+void attachTree(Node* tree, Node* subtree)
+{
+    if (!tree || !subtree) return;
+    vector<Node*> incompleteNodes;
+    getIncompleteNodes(tree, incompleteNodes);
+    vector<Node*>::iterator it(incompleteNodes.begin());
+    for (;it != incompleteNodes.end(); ++it)
+    {
+        if(!(*it)) return;
+        if(!(*it)->getLeftChild()) static_cast<DecisionNode*>(*it)->setLeftChild(subtree->clone());
+        if(!(*it)->getRightChild()) static_cast<DecisionNode*>(*it)->setRightChild(subtree->clone());
+    }
+}
+
+Node* createBalancedTree(const vector<DecisionNode*>& nodes)
+{
+    if (nodes.size() == 0) return 0;
+    DecisionNode* root = nodes.at(nodes.size()/2);
+    if (!root) return 0;
+    vector<DecisionNode*>::const_iterator midpoint(nodes.begin()+nodes.size()/2);
+    vector<DecisionNode*> leftTree(nodes.begin(),midpoint);
+    vector<DecisionNode*> rightTree(midpoint+1,nodes.end());
+    root->setLeftChild(createBalancedTree(leftTree));
+    root->setRightChild(createBalancedTree(rightTree));
+    return root;
+}
+
+Node* createBinningTree(const void* variable, char type, const vector<float>& binCuts)
+{
+    vector<float>::const_iterator it(binCuts.begin());
+    vector<DecisionNode*> nodes;
+    for (;it != binCuts.end(); ++it)
+    {
+        if (type == 'F')
+        {
+            nodes.push_back(new UnivariateCut<float>((float*)variable,*it));
+        }
+        else if (type == 'I')
+        {
+            nodes.push_back(new UnivariateCut<int>((int*)variable,(int)*it));
+        }
+        else
+        {
+            return 0;
+        }
+    }
+    return createBalancedTree(nodes);
+}
+
+Node* buildCategoryTree(const vector<Node*>& binningTrees)
+{
+    vector<Node*>::const_iterator it(binningTrees.begin());
+    Node* tree = *(it++);
+    if (!tree) return 0;
+    tree = tree->clone();
+    for (;it != binningTrees.end(); ++it)
+    {
+        attachTree(tree,*(it));
+    }
+    vector<Node*> incompleteNodes;
+    getIncompleteNodes(tree,incompleteNodes);
+    it = incompleteNodes.begin();
+    for (;it != incompleteNodes.end(); ++it)
+    {
+        if(!(*it)) return 0;
+        if(!(*it)->getLeftChild()) static_cast<DecisionNode*>(*it)->setLeftChild(new PointerLeafNode<TreeVector>());
+        if(!(*it)->getRightChild()) static_cast<DecisionNode*>(*it)->setRightChild(new PointerLeafNode<TreeVector>());
+    }
+    return tree;
+}
+
+bool hasEnding(const string& fullString, const string& ending)
+{
+    if (fullString.length() > ending.length())
+    {
+        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
+    }
+    else
+    {
+        return false;
+    }
+}
+
+template<typename T>
+bool read(istream& file, T& data, Format format)
+{
+    if (format == ASCII || format == ROOTFILE)
+    {
+        if ((file >> data).fail())
+        {
+            return false;
+        }
+    }
+    else
+    {
+        file.read((char*)&data,sizeof(T));
+    }
+    return true;
+}
+
+bool good_file(TFile* file)
+{
+    if (!file)
+        return false;
+    return file->IsOpen() && !file->IsZombie();
+}
+
+Node* TreeReader::readTree(
+    istream& treeFile,
+    TFile* rootFile,
+    Format format,
+    vector<string>& variableList,
+    vector<char>& variableTypeList,
+    unsigned int& numNodes)
+{
+    int variable;
+    int idata;
+    float fdata;
+    string line;
+    Node* rootNode(0);
+    Node* node(0);
+    stack<DecisionNode*> parentNodeStack;
+    DecisionNode* parent(0);
+    if (!read<int>(treeFile,variable,format))
+    {
+        print("Failed extracting variable index from the discriminant file");
+        return 0;
+    }
+    while (variable != ENDOFTREE)
+    {
+        if (variable == LEAF)
+        {  // parsing leaf node
+            if (!read<float>(treeFile,fdata,format))
+            {
+                print("Failed extracting leaf node purity");
+                return 0;
+            }
+            node = new LeafNode<float>(fdata);
+        }
+        else if (variable == POINTERLEAF)
+        {
+            node = new PointerLeafNode<TreeVector>();
+        }
+        else if(variable == GRAPH || variable == FUNC || variable == TRANS) // Now reading a UnivariateSlidingCut1D/Tranformation node
+        {
+            int numPoints;
+            string expression;
+            string graphName;
+            float lowX, highX;
+            float lowY, highY;
+            if (variable == GRAPH)
+            {
+                if (!read<int>(treeFile,numPoints,format))
+                {
+                    print("Failed extracting number of points for a UnivariateSlidingCut1D node");
+                    return 0;
+                }
+                if (numPoints < 2)
+                {
+                    print("Invalid number of points in UnivariateSlidingCut1D node");
+                    return 0;
+                }
+            }
+            else if (variable == FUNC)
+            {
+                treeFile >> expression;
+                getline(treeFile,line); // Discard \n
+            }
+            else
+            {
+                if (!rootFile)
+                {
+                    print("Requested a transformation but input file is not a ROOT file");
+                    return 0;
+                }
+                if (!good_file(rootFile))
+                {
+                    print("Input ROOT file is not open while trying to get transformation!");
+                    return 0;
+                }
+                treeFile >> graphName;
+                getline(treeFile,line); // Discard \n
+            }
+            int varX, varY;
+            if (!read<int>(treeFile,varX,format))
+            {
+                print("Failed extracting X for a UnivariateSlidingCut1D node");
+                return 0;
+            }
+            if (variable == TRANS)
+            {
+                if(!read<float>(treeFile,lowX,format) || !read<float>(treeFile,highX,format))
+                {
+                    print("Failed extracting bounds on X for transformation");
+                    return 0;
+                }
+            }
+            if (!read<int>(treeFile,varY,format))
+            {
+                print("Failed extracting Y for a UnivariateSlidingCut1D node");
+                return 0;
+            }
+            if (variable == TRANS)
+            {
+                if(!read<float>(treeFile,lowY,format) || !read<float>(treeFile,highY,format))
+                {
+                    print("Failed extracting bounds on Y for transformation");
+                    return 0;
+                }
+            }
+            if (varX < 0 || varY < 0)
+            {
+                print("Invalid X or Y for UnivariateSlidingCut1D node");
+                return 0;
+            }
+            TObject* graph;
+            if (variable == GRAPH)
+            {
+                graph = new TGraph(numPoints);
+            }
+            else if (variable == FUNC)
+            {
+                graph = new TF1(expression.c_str(),expression.c_str(),0.,1.);
+            }
+            else
+            {
+                TGraph2D* tempgraph = dynamic_cast<TGraph2D*>(rootFile->Get(graphName.c_str()));
+                if (!tempgraph)
+                {
+                    print("Could not get transformation graph from ROOT file");
+                    return 0;
+                }
+                //tempgraph = dynamic_cast<TGraph2D*>(tempgraph->Clone());
+                graph = tempgraph;
+            }
+            if (variableTypeList[varX] == 'F' && variableTypeList[varY] == 'I')
+            {
+                map<string,const float*>::const_iterator it1(floatVariables->find(variableList[varX]));
+                if (it1 == floatVariables->end())
+                {
+                    print("A Did not find variable "+variableList[varX]+" in booked float variables!");
+                    return 0;
+                }
+                map<string,const int*>::const_iterator it2(intVariables->find(variableList[varY]));
+                if (it2 == intVariables->end())
+                {
+                    print("B Did not find variable "+variableList[varY]+" in booked float variables!");
+                    return 0;
+                }
+                if (variable == GRAPH)
+                {
+                    node = new UnivariateSlidingCut1D<int,TGraph,float>(it2->second,static_cast<TGraph*>(graph),it1->second);
+                }
+                else if (variable == FUNC)
+                {
+                    node = new UnivariateSlidingCut1D<int,TF1,float>(it2->second,static_cast<TF1*>(graph),it1->second);
+                }
+                else
+                {
+                    node = new TransformationNode<float,int,TGraph2D>(it1->second,lowX,highX,it2->second,lowY,highY,static_cast<TGraph2D*>(graph));
+                }
+            }
+            else if (variableTypeList[varX] == 'I' && variableTypeList[varY] == 'F')
+            { 
+                map<string,const int*>::const_iterator it1(intVariables->find(variableList[varX]));
+                if (it1 == intVariables->end())
+                {
+                    print("Did not find variable "+variableList[varX]+" in booked int variables!");
+                    return 0;
+                }
+                map<string,const float*>::const_iterator it2(floatVariables->find(variableList[varY]));
+                if (it2 == floatVariables->end())
+                {
+                    print("Did not find variable "+variableList[varY]+" in booked int variables!");
+                    return 0;
+                }
+                if (variable == GRAPH)
+                {
+                    node = new UnivariateSlidingCut1D<float,TGraph,int>(it2->second,static_cast<TGraph*>(graph),it1->second);
+                }
+                else if (variable == FUNC)
+                {
+                    node = new UnivariateSlidingCut1D<float,TF1,int>(it2->second,static_cast<TF1*>(graph),it1->second);
+                }
+                else
+                {
+                    node = new TransformationNode<int,float,TGraph2D>(it1->second,lowX,highX,it2->second,lowY,highY,static_cast<TGraph2D*>(graph));
+                }
+            }
+            else if (variableTypeList[varX] == 'F' && variableTypeList[varY] == 'F')
+            { 
+                map<string,const float*>::const_iterator it1(floatVariables->find(variableList[varX]));
+                if (it1 == floatVariables->end())
+                {
+                    print("D Did not find variable "+variableList[varX]+" in booked float variables!");
+                    return 0;
+                }
+                map<string,const float*>::const_iterator it2(floatVariables->find(variableList[varY]));
+                if (it2 == floatVariables->end())
+                {
+                    print("E Did not find variable "+variableList[varY]+" in booked float variables!");
+                    return 0;
+                }
+                if (variable == GRAPH)
+                {
+                    node = new UnivariateSlidingCut1D<float,TGraph,float>(it2->second,static_cast<TGraph*>(graph),it1->second);
+                }
+                else if (variable == FUNC)
+                {
+                    node = new UnivariateSlidingCut1D<float,TF1,float>(it2->second,static_cast<TF1*>(graph),it1->second);
+                }
+                else
+                {
+                    node = new TransformationNode<float,float,TGraph2D>(it1->second,lowX,highX,it2->second,lowY,highY,static_cast<TGraph2D*>(graph));
+                }
+            }
+            else if (variableTypeList[varX] == 'I' && variableTypeList[varY] == 'I')
+            {
+                map<string,const int*>::const_iterator it1(intVariables->find(variableList[varX]));
+                if (it1 == intVariables->end())
+                {
+                    print("Did not find variable "+variableList[varX]+" in booked int variables!");
+                    return 0;
+                }
+                map<string,const int*>::const_iterator it2(intVariables->find(variableList[varY]));
+                if (it2 == intVariables->end())
+                {
+                    print("Did not find variable "+variableList[varY]+" in booked int variables!");
+                    return 0;
+                }
+                if (variable == GRAPH)
+                {
+                    node = new UnivariateSlidingCut1D<int,TGraph,int>(it2->second,static_cast<TGraph*>(graph),it1->second);
+                }
+                else if (variable == FUNC)
+                {
+                    node = new UnivariateSlidingCut1D<int,TF1,int>(it2->second,static_cast<TF1*>(graph),it1->second);
+                }
+                else
+                {
+                    node = new TransformationNode<int,int,TGraph2D>(it1->second,lowX,highX,it2->second,lowY,highY,static_cast<TGraph2D*>(graph));
+                }
+            }
+            else
+            {
+                print("Unsupported variable type in list found in discriminant file!");
+                return 0;
+            }
+            if (variable == GRAPH)
+            {
+                TGraph* tgraph = static_cast<TGraph*>(graph);
+                float X,Y;
+                for (int k(0); k < numPoints; ++k )
+                {
+                    if (!read<float>(treeFile,X,format) || !read<float>(treeFile,Y,format))
+                    {
+                        print("Failed extracting X,Y for a UnivariateSlidingCut1D node");
+                        delete node;
+                        delete rootNode;
+                        return 0;
+                    }
+                    tgraph->SetPoint(k,X,Y);
+                }
+            }
+        }
+        else
+        { // parsing internal node
+            if (variableTypeList[variable] == 'F')\
+            { // internal node cuts on float
+                map<string,const float*>::const_iterator it(floatVariables->find(variableList[variable]));
+                if (it == floatVariables->end())
+                {
+                    print("F Did not find variable "+variableList[variable]+" in booked float variables!");
+                    return 0;
+                }
+                if (!read<float>(treeFile,fdata,format))
+                {
+                    print("Failed extracting internal float node cut");
+                    return 0;
+                }
+                node = new UnivariateCut<float>(it->second,fdata);
+            }
+            else if (variableTypeList[variable] == 'I')
+            { // internal node cuts on int
+                map<string,const int*>::const_iterator it(intVariables->find(variableList[variable]));
+                if (it == intVariables->end())
+                {
+                    print("Did not find variable "+variableList[variable]+" in booked int variables!");
+                    return 0;
+                }
+                if (!read<int>(treeFile,idata,format))
+                {
+                    print("Failed extracting internal int node cut");
+                    return 0;
+                }
+                node = new UnivariateCut<int>(it->second,idata);
+            }
+            else
+            { // unknown node type
+                print("Unsupported variable type in list found in discriminant file!");
+                return 0;
+            }
+        }
+        ++numNodes;
+
+        if (parentNodeStack.empty())
+        {
+            if (variable == LEAF)
+            {
+                print("Corrupt tree! Adding leaf node as root node.");
+                delete node;
+                return 0;
+            }
+            rootNode = node;
+        }
+        else
+        {
+            parent = parentNodeStack.top();
+            while (parent->isComplete())
+            {
+                parentNodeStack.pop();
+                if (parentNodeStack.empty())
+                {
+                    print("Corrupt tree! Expected a parent node.");
+                    delete node;
+                    delete rootNode;
+                    return 0;
+                }
+                parent = parentNodeStack.top();
+            }
+            if (!parent->getLeftChild())
+            {
+                parent->setLeftChild(node);
+            }
+            else if (!parent->getRightChild())
+            {
+                parent->setRightChild(node);
+            }
+            else
+            {
+                print("Corrupt tree! Attempted to add a child to a complete node!");
+                delete node;
+                delete rootNode;
+                return 0;
+            }
+        }
+
+        if (variable != LEAF)
+        {
+            parentNodeStack.push(static_cast<DecisionNode*>(node));
+        }
+
+        if (!read<int>(treeFile,variable,format))
+        {
+            print("Failed extracting variable index");
+            delete rootNode;
+            return 0;
+        }
+    }
+    while (!parentNodeStack.empty()) parentNodeStack.pop();
+    return rootNode;
+}
+
+Node* TreeReader::build(
+    const string& filename,
+    bool checkTree)
+{
+
+    Format format;
+    if (hasEnding(filename, ".bin"))
+    {
+        format = BINARY;
+        if (verbose) print("Reading input as binary");
+    }
+    else if (hasEnding(filename, ".txt"))
+    {
+        format = ASCII;
+        if (verbose) print("Reading input as ASCII text");
+    }
+    else if (hasEnding(filename, ".root"))
+    {
+        format = ROOTFILE;
+        if (verbose) print("Reading input as ROOT file");
+    }
+    else
+    {
+        print("Unknown discriminant format");
+        return 0;
+    }
+
+    unsigned int numVariables;
+    Node* categoryTree;
+    Node* rootNode;
+    vector<string> tokens;
+    string line;
+    string token;
+    vector<string> variableList;
+    vector<char> variableTypeList;
+    vector<string> binningVariableList;
+    vector<char> binningVariableTypeList;
+    char type;
+    unsigned int numNodes(0);
+
+    istream* treeInfoTemp;
+    ifstream treeFile;
+    istringstream treeString;
+    TFile* file(0);
+
+    if (format == ROOTFILE)
+    {
+        // read in tree data from ROOT file and place in stringstream
+        file = new TFile(filename.c_str(), "READ");
+        if (!good_file(file))
+        {
+            print("The discriminant ROOT file "+filename+" will not open!");
+            return 0;
+        }
+        TNamed* treeInfoText = (TNamed*)file->Get("TreeInfo");
+        if (!treeInfoText)
+        {
+            print("Could not find TreeInfo in discriminant ROOT file!");
+            file->Close();
+            return 0;
+        }
+        string treeInfoTextString(treeInfoText->GetTitle());
+        treeString.str(treeInfoTextString);
+        delete treeInfoText;
+        treeInfoTemp = &treeString;
+    }
+    else
+    {
+        treeFile.open(filename.c_str(),ios::in);
+        if (!treeFile.is_open())
+        {
+            print("The discriminant file "+filename+" will not open!");
+            return 0;
+        }
+        treeInfoTemp = &treeFile;
+    }
+    istream& treeInfo(*treeInfoTemp);
+
+    unsigned int numBinningVariables = 0;
+    if ((treeInfo >> numBinningVariables).fail())
+    {
+        print("Failed extracting the number of binning variables in the discriminant file!");
+        return 0;
+    }
+    
+    binningVariableList = vector<string>(numBinningVariables,"");
+    binningVariableTypeList = vector<char>(numBinningVariables,'F');
+
+    for (unsigned int j(0); j < numBinningVariables; ++j)
+    {
+        if ((treeInfo >> binningVariableList[j]).fail())
+        {
+            print("Failed extracting a variable name from the discriminant file");
+            return 0;
+        }
+        if ((treeInfo >> type).fail())
+        {
+            print("Failed extracting a variable type from the discriminant file");
+            return 0;
+        }
+        if (type != 'F' && type != 'I')
+        {
+            print("Unsupported variable type found in the discriminant file: "+type);
+            return 0;
+        }
+        binningVariableTypeList[j] = type;
+    }
+    getline(treeFile,line); // discard \n
+
+    if (numBinningVariables > 0)
+    {
+        categoryTree = readTree(treeInfo,file,ASCII,binningVariableList,binningVariableTypeList,numNodes); 
+    }
+    else
+    {
+        categoryTree = new PointerLeafNode<TreeVector>();
+    }
+
+    if ((treeInfo >> numVariables).fail())
+    {
+        print("Failed extracting number of variables from the discriminant file!");
+        delete categoryTree;
+        return 0;
+    }
+
+    variableList = vector<string>(numVariables,"");
+    variableTypeList = vector<char>(numVariables,'F');
+
+    for (unsigned int j(0); j < numVariables; ++j)
+    {
+        if ((treeInfo >> variableList[j]).fail())
+        {
+            print("Failed extracting a variable name from the discriminant file");
+            delete categoryTree;
+            return 0;
+        }
+        if ((treeInfo >> type).fail())
+        {
+            print("Failed extracting a variable type from the discriminant file");
+            delete categoryTree;
+            return 0;
+        }
+        if (type != 'F' && type != 'I')
+        {
+            print("Unsupported variable type found in the discriminant file: "+type);
+            delete categoryTree;
+            return 0;
+        }
+        variableTypeList[j] = type;
+    }
+    getline(treeInfo,line); // discard \n
+
+    // Get vector of all leaf nodes of the category tree and initialize an iterator
+    vector<Node*> categories;
+    vector<Node*>::const_iterator category_it, category_end;
+    getLeafNodes(categoryTree, categories);
+    category_it = categories.begin();
+    category_end = categories.end();
+
+    unsigned int numTrees;
+    float weight;
+
+    // Build the (categorized) TreeVector
+    TreeVector* treeVector;
+    for (;category_it != category_end; ++category_it)
+    {
+        numNodes = 0;
+        treeVector = new TreeVector();
+        static_cast<PointerLeafNode<TreeVector>* >(*category_it)->setValue(treeVector);
+
+        if (!read<unsigned int>(treeInfo,numTrees,format)) print("Failed extracting number of trees from the discriminant file!");
+
+        for (unsigned int j(0); j < numTrees; ++j)
+        {
+            rootNode = 0;
+
+            if (!read<float>(treeInfo,weight,format))
+            {
+                print("Failed extracting tree weight from the discriminant file");
+                delete categoryTree;
+                return 0;
+            }
+            rootNode = readTree(treeInfo,file,format,variableList,variableTypeList,numNodes); 
+            if (!rootNode)
+            {
+                print("Null tree!");
+                delete categoryTree;
+                return 0;
+            }
+            if (checkTree)
+            {
+                vector<Node*> badNodes;
+                findBadNodes(rootNode,badNodes);
+                if (badNodes.size()>0)
+                {
+                    print("Tree is not well-formed!");
+                    print("Bad tree has signature: "+signature(rootNode,&badNodes));
+                    delete categoryTree;
+                    return 0;
+                }
+            }
+            treeVector->addTree(rootNode,weight);
+        }
+        if (verbose) print("Created a tree vector with "+to_string(numNodes)+" nodes");
+    }
+    treeFile.close();
+    if (file)
+    {
+        //file->Close();
+    }
+    //delete file;
+    return categoryTree;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/TreeVector.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TreeVector.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3385305b2cba89d05b7915842026ba2bfbfae5d0
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/TreeVector.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TauDiscriminant/TreeVector.h"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/Root/Types.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Types.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..42a0d57e4e6ac9b4f3e69222293b3add043d7552
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/Root/Types.cxx
@@ -0,0 +1,5 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TauDiscriminant/Types.h"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/BoostedDecisionTree.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/BoostedDecisionTree.h
new file mode 100644
index 0000000000000000000000000000000000000000..fa8200b721708b55fc4f3dc8d4dab28dfbe5889e
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/BoostedDecisionTree.h
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef BOOSTEDDECISIONTREE_H
+#define BOOSTEDDECISIONTREE_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include "TauDiscriminant/Node.h"
+#include "TauDiscriminant/TreeVector.h"
+
+using namespace std;
+
+class BoostedDecisionTree : public TreeVector {
+
+    public:
+
+        //!< Default Constructor
+        BoostedDecisionTree() {}
+
+        /**
+         * @brief Returns the @c float score for the set of variables and values in @c variableMap.
+         * @param variableMap
+         */
+        float response() const;
+
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/CommonLikelihood.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/CommonLikelihood.h
new file mode 100644
index 0000000000000000000000000000000000000000..c207b745c51f43bb12f954b85ed42a76051e8549
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/CommonLikelihood.h
@@ -0,0 +1,158 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: CommonLikelihood.h
+ *
+ * Author: Martin Flechl (mflechl@cern.ch)
+ */
+
+#ifndef COMMONLIKELIHOOD_H
+#define COMMONLIKELIHOOD_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include <assert.h>
+#ifndef __STANDALONE
+#include "GaudiKernel/MsgStream.h"
+#endif
+
+#include "TH3.h"
+#include "TFile.h"
+#include "TString.h"
+#include "TMath.h"
+#include "TGraph.h"
+using namespace std;
+
+//mw added
+//////////////////////////////////////////////////////////////////////////////////////////////
+const unsigned int NVAR_SAFE_1P=5;
+const unsigned int NVAR_SAFE_3P=6;
+const unsigned int NVAR_DEF=16;
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+
+class CommonLikelihood{
+
+    public:
+
+        //Constructor
+#ifndef __STANDALONE
+        CommonLikelihood(bool _verbose=false,MsgStream* _log = 0):log(_log),
+#else
+        CommonLikelihood(bool _verbose=false):
+#endif      
+        m_tauFile(NULL),
+        m_jetFile(NULL),
+        m_cutFile(NULL),
+        m_mu_correction_factor_1P(0.317),
+        m_mu_correction_factor_3P(0.117),
+        tg_loose_cuts_1P(NULL),
+        tg_medium_cuts_1P(NULL),
+        tg_tight_cuts_1P(NULL),
+        tg_loose_cuts_3P(NULL),
+        tg_medium_cuts_3P(NULL),
+        tg_tight_cuts_3P(NULL),
+		m_doTrigger(false)
+        {
+            if (_verbose) cout << "CommonLikelihood: Creating Instance..." << endl;
+            m_tauFilename="pdfs_tau.root";
+            m_jetFilename="pdfs_jets.root";
+            m_cutFilename="LMTCutsLLH.root";
+            m_llh=-99;
+            m_et=-9999.;
+            m_prongindex=-1;
+            m_ntau[0]=0; m_ntau[1]=0;
+            NVAR=56;
+            DEBUG=0;
+            if (_verbose) DEBUG=1;
+            this->verbose = _verbose;
+            //	  DEBUG=2;
+            m_smooth = false;
+        }
+        ~CommonLikelihood(){
+		if(m_tauFile) m_tauFile->Close();
+		delete m_tauFile;
+		if(m_jetFile) m_jetFile->Close();
+		delete m_jetFile;
+		if(m_cutFile) m_cutFile->Close();
+		delete m_cutFile;
+	}
+        bool build(const string& filename);
+        int response(int level=-1, int option=-1) const;
+        bool calcLLHValue(const map<string,const float*>* floatVariables,
+                const map<string,const int*>* intVariables, int option);
+        float getLLHValue(){ return m_llh; }
+
+		void setDoTrigger(bool trig) { m_doTrigger = trig; }
+
+    private:
+    
+        bool readPDFHistograms();
+        bool readLMTCuts();
+        bool smoothPDFHistograms();
+        void smooth3D(TH3F* hTmp);
+        TH3F* divideLog(TH3F* hTmpTau, TH3F* hTmpJet);
+        Double_t Interpolate(TH3F* hist, Double_t x, Double_t y, Double_t z);
+        int varNameToNumber(std::string varname) const;
+        float getSingleLLHRatio(const int ivar, const int numtrack, const int author, const float et, const int nvtx, const float value);
+        float getSimpleSingleLLHRatio(const int ivar, const int numtrack, const int author, const float et, const int nvtx, const float value);
+        bool skipVar(const TString varname,const int numtrack,const int author,const int option) const;
+        void splitString(const string& str, vector<string>& substrings, const string& delimiters = " ") const;
+
+        void print(TString message, int level) const {
+#ifndef __STANDALONE
+            if (log) {
+                if (level<=0) (*log) << "WARNING" << message << endreq;
+                else (*log) << "DEBUG" << message << endreq;
+            }
+            else
+#endif
+                if (level<=DEBUG) cout << message << endl;
+        }
+
+#ifndef __STANDALONE
+        MsgStream* log;
+#endif
+ 
+		std::string m_tauFilename;
+        std::string m_cutFilename;
+        std::string m_jetFilename;
+        TFile *m_tauFile;
+        TFile *m_jetFile;
+        TFile *m_cutFile;
+        float m_llh;
+        float m_et;
+        int m_prongindex;
+        int m_nvtx;
+        int m_ntau[2];
+        bool m_smooth;
+
+        double m_mu_correction_factor_1P;
+        double m_mu_correction_factor_3P;
+
+        std::vector<TGraph*> m_vLMTCuts1P;
+        std::vector<TGraph*> m_vLMTCuts3P;
+        int NVAR;
+        std::map<std::string,TH3F*> m_pdfHistogramsTau;
+        std::map<std::string,TH3F*> m_pdfHistogramsJet;
+        std::map<std::string,TH3F*> m_pdfHistogramsRatio;
+        TGraph* tg_loose_cuts_1P;
+        TGraph* tg_medium_cuts_1P;
+        TGraph* tg_tight_cuts_1P;
+        TGraph* tg_loose_cuts_3P;
+        TGraph* tg_medium_cuts_3P;
+        TGraph* tg_tight_cuts_3P;
+		
+		bool m_doTrigger;
+
+        int DEBUG;
+        bool verbose;
+
+        map<string,float> getVariables(const map<string,const float*>* floatVariables,const map<string,const int*>* intVariables, const int numtrack, const int author, const int option) const;
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/CutsDecisionTree.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/CutsDecisionTree.h
new file mode 100644
index 0000000000000000000000000000000000000000..bde85473dc6f8db6343456649fb967faff9f6fd5
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/CutsDecisionTree.h
@@ -0,0 +1,32 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef CUTSDECISIONTREE_H
+#define CUTSDECISIONTREE_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include "TauDiscriminant/Node.h"
+#include "TauDiscriminant/TreeVector.h"
+
+using namespace std;
+
+class CutsDecisionTree: public TreeVector {
+
+    public:
+
+        //!< Default Constructor
+        CutsDecisionTree() {}
+
+        float response(unsigned int level) const;
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/EMFCluster.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/EMFCluster.h
new file mode 100644
index 0000000000000000000000000000000000000000..3092b134f2da100646296585c5fa0ec93267621e
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/EMFCluster.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef EMFCluster_H
+#define EMFCluster_H
+
+//*******************************************************//
+// Name: EMFCluster.h                                    //
+// Author: Michel Trottier-McDonald <mtm@cern.ch>        //
+// Description: A simple class to house cluster          //
+// TLorentzVectors and their associated fractions of     //
+// energy in different calorimeter layers                //
+//*******************************************************//
+
+#include <TLorentzVector.h>
+
+class EMFCluster
+{
+public:
+    //Constructor, Destructor
+    EMFCluster();
+    EMFCluster(double pt);
+    EMFCluster(const TLorentzVector& inCluster,
+	       double inPSSF,
+	       double inEM2F,
+	       double inEM3F);
+    ~EMFCluster();
+
+    //Getters
+    TLorentzVector TLV() const {return m_cluster;}
+
+    double PSSF() const {return m_PSSF;}
+    double EM2F() const {return m_EM2F;}
+    double EM3F() const {return m_EM3F;}
+    double HADF() const {return m_HADF;}
+    double pseudoHADF() const {return m_pseudoHADF;}
+
+    double PSSE() const {return m_PSSE;}
+    double EM2E() const {return m_EM2E;}
+    double EM3E() const {return m_EM3E;}
+    double HADE() const {return m_HADE;}
+    double pseudoHADE() const {return m_pseudoHADE;}
+
+    //Comparison operator
+    bool operator< (const EMFCluster& rhs) const
+    { return (m_cluster.E() < rhs.TLV().E()); }
+
+private:
+    //Cluster 4-vector
+    TLorentzVector m_cluster;
+
+    //Layer related info
+    double m_PSSF; // Fraction on energy in the PreSampler and Strip layers
+    double m_EM2F; // Fraction of energy in layer 2 of the EM calorimeter
+    double m_EM3F; // Fraction of energy in layer 3 of the EM calorimeter
+    double m_HADF; // Fraction of energy in the hadronic calorimeter
+    double m_pseudoHADF; // Fraction of energy in layer 3 of the EM calorimeter and hadronic calorimeter
+
+    double m_PSSE; // Energy in the PreSampler and Strip layers
+    double m_EM2E; // Energy in layer 2 of the EM calorimeter
+    double m_EM3E; // Energy in layer 3 of the EM calorimeter
+    double m_HADE; // Energy in the hadronic calorimeter
+    double m_pseudoHADE; // Energy in layer 3 of the EM calorimeter and hadronic calorimeter
+
+    //Calculate the hadronic fractions and energies
+    void update();
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/FakeTauBits.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/FakeTauBits.h
new file mode 100644
index 0000000000000000000000000000000000000000..0d58ca62688d73f8a47c95be07d0a7adb292cbe9
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/FakeTauBits.h
@@ -0,0 +1,90 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// AUTHOR: Noel Dawe
+#ifndef FAKETAUBITS_H
+#define FAKETAUBITS_H
+
+#include <bitset>
+#include <algorithm>
+#include "xAODTau/TauJet.h"
+#include "DataModel/DataVector.h"
+#include "SGTools/CLASS_DEF.h"
+
+namespace TauBit
+{
+    enum TauBit
+    {
+        BDT_LOOSE_BKG,
+        BDT_MEDIUM_BKG,
+        BDT_TIGHT_BKG
+    };
+}
+
+class FakeTauBits
+{
+    typedef TauBit::TauBit TauBit;
+    friend class FakeTauBitsContainer;
+
+    public:
+        
+        FakeTauBits(const xAOD::TauJet* tau):
+            tau(tau)
+        {}
+
+        void setBit(TauBit bit, bool value)
+        {
+            bits[bit] = value;
+        }
+
+        bool getBit(TauBit bit) const
+        {
+            return bits[bit];
+        }
+   
+    private:
+        
+        std::bitset<32> bits;
+        const xAOD::TauJet* tau;
+};
+
+class FakeTauBitsContainer: public DataVector<FakeTauBits>
+{
+    public:
+   
+        FakeTauBitsContainer( SG::OwnershipPolicy own = SG::OWN_ELEMENTS ):
+            DataVector<FakeTauBits>( own ) {}
+
+        const FakeTauBits* getBitsAssocTo(const xAOD::TauJet* tau) const
+        {
+            if (!tau)
+                return 0;
+            FakeTauBitsContainer::const_iterator it(this->begin());
+            FakeTauBitsContainer::const_iterator it_end(this->end());
+            for(; it != it_end; ++it)
+            {
+                if (tau == (*it)->tau)
+                    return *it;
+            }
+            return 0;
+        }
+
+        const FakeTauBits* getBitsAssocTo(const xAOD::TauJet& tau) const
+        {
+            FakeTauBitsContainer::const_iterator it(this->begin());
+            FakeTauBitsContainer::const_iterator it_end(this->end());
+            for(; it != it_end; ++it)
+            {
+                if (&tau == (*it)->tau)
+                    return *it;
+            }
+            return 0;
+        }
+};
+
+SG_BASE( FakeTauBitsContainer, DataVector<FakeTauBits> );
+
+CLASS_DEF( FakeTauBitsContainer , 1316558256 , 1 )
+
+#endif // FAKETAUBITS
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/FakeTauScores.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/FakeTauScores.h
new file mode 100644
index 0000000000000000000000000000000000000000..c89bf4e1122206298573530502c0648506a58ec9
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/FakeTauScores.h
@@ -0,0 +1,93 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// AUTHOR: Noel Dawe
+#ifndef FAKETAUSCORES_H
+#define FAKETAUSCORES_H
+
+#include <algorithm>
+#include "xAODTau/TauJet.h"
+#include "DataModel/DataVector.h"
+#include "SGTools/CLASS_DEF.h"
+
+namespace TauScore
+{
+    enum TauScore
+    {
+        BDT_TRANS_BKG,
+        BDT_TRANS_SIG,
+	BDT_PI0_PRIMARY,
+	BDT_PI0_SECONDARY,
+        __END__
+    };
+}
+
+class FakeTauScores
+{
+    // DR: clang32 is confused by this
+    // typedef TauScore::TauScore TauScore;
+    friend class FakeTauScoresContainer;
+
+    public:
+        
+        FakeTauScores(const xAOD::TauJet* tau):
+            scores(TauScore::__END__,0.),
+            tau(tau)
+        {}
+
+	  void setScore(TauScore::TauScore score, float value)
+        {
+            scores[score] = value;
+        }
+
+        float getScore(TauScore::TauScore score) const
+        {
+            return scores[score];
+        }
+   
+    private:
+        
+        std::vector<float> scores;
+        const xAOD::TauJet* tau;
+};
+
+class FakeTauScoresContainer: public DataVector<FakeTauScores>
+{
+    public:
+   
+        FakeTauScoresContainer( SG::OwnershipPolicy own = SG::OWN_ELEMENTS ):
+            DataVector<FakeTauScores>( own ) {}
+
+        const FakeTauScores* getScoresAssocTo(const xAOD::TauJet* tau) const
+        {
+            if (!tau)
+                return 0;
+            FakeTauScoresContainer::const_iterator it(this->begin());
+            FakeTauScoresContainer::const_iterator it_end(this->end());
+            for(; it != it_end; ++it)
+            {
+                if (tau == (*it)->tau)
+                    return *it;
+            }
+            return 0;
+        }
+
+        const FakeTauScores* getScoresAssocTo(const xAOD::TauJet& tau) const
+        {
+            FakeTauScoresContainer::const_iterator it(this->begin());
+            FakeTauScoresContainer::const_iterator it_end(this->end());
+            for(; it != it_end; ++it)
+            {
+                if (&tau == (*it)->tau)
+                    return *it;
+            }
+            return 0;
+        }
+};
+
+SG_BASE( FakeTauScoresContainer, DataVector<FakeTauScores> );
+
+CLASS_DEF( FakeTauScoresContainer , 1249450621 , 1 )
+
+#endif // FAKETAUSCORES
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodBDT.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodBDT.h
new file mode 100644
index 0000000000000000000000000000000000000000..6097e77edf3bd5926216bd981a5a10e0efc6cbd9
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodBDT.h
@@ -0,0 +1,95 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef METHODBDT_H
+#define METHODBDT_H
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+#include <utility>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+#include <typeinfo>
+#include "TauDiscriminant/MethodBase.h"
+#include "TauDiscriminant/BoostedDecisionTree.h"
+#include "TauDiscriminant/Node.h"
+#include "TauDiscriminant/TreeReader.h"
+
+using namespace std;
+
+namespace TauID
+{
+    class MethodBDT : public MethodBase
+    {
+        public:
+
+            //!< Default constructor
+            #ifdef __STANDALONE
+            MethodBDT(const string& _name = "", bool _verbose = false):
+                MethodBase(_name,_verbose),
+                isBuilt(false),
+                categoryTree(0)
+            {}
+            #else
+            MethodBDT(const string& _name = ""):
+                MethodBase(_name),
+                isBuilt(false),
+                categoryTree(0)
+            {}
+            #endif
+
+            //!< Destructor
+            ~MethodBDT()
+            {
+                delete this->categoryTree;
+            }
+
+            bool build(const string& filename, bool checkTree = false);
+
+            float response() const;
+
+            float response(unsigned int level) const
+            {
+                if (level != 0)
+                {
+                    print("MethodBDT does not output more than one possible response.");
+                    print("Use a MethodCuts on the MethodBDT response to determine loose, medium, and tight boolean values.");
+                }
+                return response();
+            }
+
+            BoostedDecisionTree* getCurrentCategory() const;
+
+            Types::MethodType getType() const
+            {
+                return Types::BDT;
+            }
+
+            void addVariable(const string& _name, const void* _value, char type = 'F')
+            {
+                MethodBase::addVariable(_name,_value,type);
+            }
+
+            #ifndef __STANDALONE
+            void setDetails(const TauDetailsManager& manager)
+            {
+                MethodBase::setDetails(manager);
+            }
+            #endif
+
+        private:
+
+            bool isBuilt;
+            Node* categoryTree;
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodBase.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..a8227f4106891ebe2cd53ed4f4afd8e540da15ea
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodBase.h
@@ -0,0 +1,177 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef METHODBASE_H
+#define METHODBASE_H
+
+#include <string>
+#include <iostream>
+#include <map>
+#include <algorithm>
+#include "TauDiscriminant/Types.h"
+
+#ifndef __STANDALONE
+    //including the Message Stream Member
+    #include "AthenaKernel/MsgStreamMember.h"
+    #include "TauDiscriminant/TauDetailsManager.h"
+#endif
+
+using namespace std;
+
+namespace TauID
+{
+    class MethodBase
+    {
+        public:
+
+            ////////////////////////////////////////////////////////////
+            /// The name is an arbitrary name set by the user and should
+            /// have no influence on the behaviour of the method
+            ////////////////////////////////////////////////////////////
+            #ifdef __STANDALONE
+            MethodBase(const string& _name = "", bool _verbose = false):
+                name(_name),
+                verbose(_verbose)
+            {}
+            #else
+            MethodBase(const string& _name = ""):
+                name(_name)
+            {}
+            #endif
+
+            virtual ~MethodBase() {}
+
+            ///////////////////////////////////////////////////////////
+            /// Return the value of a continuous discriminant
+            ///////////////////////////////////////////////////////////
+            virtual float response() const =0;
+
+            ///////////////////////////////////////////////////////////
+            /// This method should only be used for cut-based
+            /// methods for responses at different levels of
+            /// "tightness." For continuous discriminants, this
+            /// method should print a warning message and
+            /// return the value of response() above.
+            ///////////////////////////////////////////////////////////
+            virtual float response(unsigned int level) const =0;
+
+            string getName() const
+            {
+                return name;
+            }
+
+            ///////////////////////////////////////////////////////////
+            /// Add a variable. You should not need to
+            /// override this method.
+            ///////////////////////////////////////////////////////////
+            void addVariable(const string& _name, const void* value, char type = 'F')
+            {
+                if (!value)
+                {
+                    print("Variable pointer is NULL!");
+                    return;
+                }
+                string localname = _name;
+                // Convert to uppercase:
+                std::transform(localname.begin(), localname.end(), localname.begin(), &upper);
+                if (type == 'F')
+                {
+                    this->floatVariables[localname] = (const float*)value;
+                }
+                else if (type == 'I')
+                {
+                    this->intVariables[localname] = (const int*)value;
+                }
+                else
+                {
+                    print("Unsupported variable type!");
+                }
+            }
+
+            #ifndef __STANDALONE
+            //////////////////////////////////////////////////////////
+            /// This method is used in Athena to set the
+            /// variables instead of the addVariable method
+            //////////////////////////////////////////////////////////
+            void setDetails(const TauDetailsManager& manager)
+            {
+                const map<string,float*>* floatDetails = manager.getFloatDetails();
+                map<string,float*>::const_iterator it1(floatDetails->begin());
+                for (; it1 != floatDetails->end(); ++it1 )
+                {
+                    this->addVariable(it1->first,it1->second,'F');
+                }
+                const map<string,int*>* intDetails = manager.getIntDetails();
+                map<string,int*>::const_iterator it2(intDetails->begin());
+                for (; it2 != intDetails->end(); ++it2 )
+                {
+                    this->addVariable(it2->first,it2->second,'I');
+                }
+            }
+            #endif
+
+            void print(string message) const
+            {
+                #ifdef __STANDALONE
+                if (this->verbose)
+                {
+                    cout << message << endl;
+                }
+                #else
+                if (msgLvl(MSG::VERBOSE))
+                {
+                    msg(MSG::VERBOSE) << message << endreq;
+                }
+                #endif
+            }
+
+            ////////////////////////////////////////////////////////////
+            /// Build the discriminant from an input file
+            /// The first parameter is a filename. Your method
+            /// should be saved in only one file.
+            /// Specifying a list of files here separated
+            /// by commas, for example, is not acceptable.
+            /// The boolean parameter is optional and may be
+            /// used to optionally validate your method after
+            /// building from your input file.
+            ////////////////////////////////////////////////////////////
+            virtual bool build(const string&, bool = false) =0;
+
+            virtual Types::MethodType getType() const =0;
+
+            #ifndef __STANDALONE
+            //Declaring the Message method for further use
+            MsgStream& msg( MSG::Level lvl ) const { return m_msg << lvl ; }
+
+            //Declaring the Method providing Verbosity Level
+            bool msgLvl( MSG::Level lvl ) const { return m_msg.get().level() <= lvl ; }
+            #endif
+
+        private:
+
+            static int upper(int c)
+            {
+                return std::toupper((unsigned char)c);
+            }
+
+            string name;
+
+        protected:
+
+            #ifdef __STANDALONE
+            bool verbose;
+            #else
+            //Declaring private message stream member.
+            mutable Athena::MsgStreamMember m_msg ;
+            #endif
+
+            map<string,const float*> floatVariables;
+            map<string,const int*> intVariables;
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodCuts.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodCuts.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f9a72f6bcee64d9abca6c64803f05f1b4dd3f31
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodCuts.h
@@ -0,0 +1,98 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef METHODCUTS_H
+#define METHODCUTS_H
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+#include <utility>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+#include <typeinfo>
+#include "TauDiscriminant/MethodBase.h"
+#include "TauDiscriminant/CutsDecisionTree.h"
+#include "TauDiscriminant/Node.h"
+#include "TauDiscriminant/TreeReader.h"
+
+using namespace std;
+
+namespace TauID
+{
+    class MethodCuts : public MethodBase
+    {
+        public:
+
+            //!< Default constructor
+            #ifdef __STANDALONE
+            MethodCuts(const string& _name = "", bool _verbose = false):
+                MethodBase(_name,_verbose),
+                isBuilt(false),
+                nLevels(0),
+                categoryTree(0)
+            {}
+            #else
+            MethodCuts(const string& _name = ""):
+                MethodBase(_name),
+                isBuilt(false),
+                nLevels(0),
+                categoryTree(0)
+            {}
+            #endif
+
+            //!< Destructor
+            ~MethodCuts()
+            {
+                delete this->categoryTree;
+            }
+
+            bool build(const string& filename, bool checkTree = false);
+
+            float response() const
+            {
+                return response(0);
+            }
+
+            float response(unsigned int level) const;
+
+            unsigned int numLevels() const
+            {
+                return this->nLevels;
+            }
+
+            CutsDecisionTree* getCurrentCategory() const;
+
+            Types::MethodType getType() const
+            {
+                return Types::CUTS;
+            }
+
+            void addVariable(const string& _name, const void* _value, char type = 'F')
+            {
+                MethodBase::addVariable(_name,_value,type);
+            }
+
+            #ifndef __STANDALONE
+            void setDetails(const TauDetailsManager& manager)
+            {
+                MethodBase::setDetails(manager);
+            }
+            #endif
+
+        private:
+
+            bool isBuilt;
+            unsigned int nLevels;
+            Node* categoryTree;
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodDummy.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodDummy.h
new file mode 100644
index 0000000000000000000000000000000000000000..23943206f2b7bef4553b85a1ddc898d1922c025d
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodDummy.h
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * MethodDummy is used for speed benchmarking the other Methods.
+ * A better approximation of a Method's processing time per classification instance
+ * is after subtraction of the processing time per entry of MethodDummy to remove
+ * data I/O time and other constant overhead.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef METHODDUMMY_H
+#define METHODDUMMY_H
+
+#include "TauDiscriminant/MethodBase.h"
+
+using namespace std;
+
+namespace TauID
+{
+    class MethodDummy : public MethodBase
+    {
+        public:
+
+            //!< Default constructor
+            #ifdef __STANDALONE
+            MethodDummy(const string& _name = "", bool _verbose = false):
+                MethodBase(_name,_verbose)
+            {}
+            #else
+            MethodDummy(const string& _name = ""):
+                MethodBase(_name)
+            {}
+            #endif
+
+            //!< Destructor
+            ~MethodDummy()
+            {}
+
+            bool build(const string& filename, bool checkTree = false);
+
+            float response() const;
+
+            float response(unsigned int level) const
+            {
+                if (level != 0)
+                {
+                    print("MethodDummy does not output more than one possible response.");
+                }
+                return response();
+            }
+
+            Types::MethodType getType() const
+            {
+                return Types::DUMMY;
+            }
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodLLH.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodLLH.h
new file mode 100644
index 0000000000000000000000000000000000000000..5879fb0290ea8ff8f11f08ba93cacd7d152bb004
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodLLH.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: MethodLLH.h
+ *
+ * Author: Martin Flechl (mflechl@cern.ch)
+ */
+
+#ifndef METHODLLH_H
+#define METHODLLH_H
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+#include <utility>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+#include <typeinfo>
+#include "TauDiscriminant/MethodBase.h"
+#include "TauDiscriminant/CommonLikelihood.h"
+
+using namespace std;
+
+namespace TauID
+{
+    class MethodLLH : public MethodBase {
+
+        public:
+
+            #ifdef __STANDALONE
+            MethodLLH(const string& _name = "", bool _verbose = false):
+                MethodBase(_name,_verbose),
+                isBuilt(false),
+                nLevels(0),
+                llh(NULL)
+            {}
+            #else
+            MethodLLH(const string& _name = ""):
+                MethodBase(_name),
+                isBuilt(false),
+                nLevels(0),
+                llh(NULL)
+            {}
+            #endif
+
+            ~MethodLLH() {
+		delete llh;
+	     }
+
+            bool build(const string& filename, bool check=false);
+
+            float response() const { return response(0); }
+
+            float response(unsigned int level) const;
+
+            unsigned int numLevels() const { return this->nLevels; }
+
+            Types::MethodType getType() const{ return Types::LLH; }
+
+            void addVariable(const string& _name, const void* _value, char type = 'F') { MethodBase::addVariable(_name,_value,type); }
+
+            #ifndef __STANDALONE
+            void setDetails(const TauDetailsManager& manager){ MethodBase::setDetails(manager); }
+			void setDoTrigger(const TauDetailsManager& manager) { llh->setDoTrigger(manager.getDoTrigger()); }
+            #endif
+
+        private:
+
+            bool isBuilt;
+            unsigned int nLevels;
+            CommonLikelihood* llh;
+            int m_option;
+
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodTransform.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodTransform.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e9a5f48d5d4ecbc2ac243d82d426555f984ff0f
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/MethodTransform.h
@@ -0,0 +1,94 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef METHODTRANSFORM_H
+#define METHODTRANSFORM_H
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+#include <utility>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+#include <typeinfo>
+#include "TauDiscriminant/MethodBase.h"
+#include "TauDiscriminant/TreeReader.h"
+#include "TauDiscriminant/Node.h"
+#include "TauDiscriminant/Transformation.h"
+
+using namespace std;
+
+namespace TauID
+{
+    class MethodTransform : public MethodBase
+    {
+        public:
+
+            //!< Default constructor
+            #ifdef __STANDALONE
+            MethodTransform(const string& _name = "", bool _verbose = false):
+                MethodBase(_name,_verbose),
+                isBuilt(false),
+                categoryTree(0)
+            {}
+            #else
+            MethodTransform(const string& _name = ""):
+                MethodBase(_name),
+                isBuilt(false),
+                categoryTree(0)
+            {}
+            #endif
+
+            //!< Destructor
+            ~MethodTransform()
+            {
+                delete this->categoryTree;
+            }
+
+            bool build(const string& filename, bool check = false);
+
+            float response() const;
+
+            float response(unsigned int level) const
+            {
+                if (level != 0)
+                {
+                    print("MethodTransform does not output more than one possible response.");
+                }
+                return response();
+            }
+
+            Transformation* getCurrentCategory() const;
+
+            Types::MethodType getType() const
+            {
+                return Types::TRANSFORM;
+            }
+
+            void addVariable(const string& _name, const void* _value, char type = 'F')
+            {
+                MethodBase::addVariable(_name,_value,type);
+            }
+
+            #ifndef __STANDALONE
+            void setDetails(const TauDetailsManager& manager)
+            {
+                MethodBase::setDetails(manager);
+            }
+            #endif
+
+        private:
+
+            bool isBuilt;
+            Node* categoryTree;
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Node.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Node.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cbcffa8ebb5ea38b03ddc82ae5ef761548cf066
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Node.h
@@ -0,0 +1,371 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//! This is a node class.
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#include <iostream>
+using namespace std;
+
+#ifndef NODE_H
+#define NODE_H
+
+//!< Abstract base class
+class Node
+{
+    public:
+
+        virtual ~Node() {}
+        virtual bool isLeaf(void) const =0;
+        virtual bool isComplete(void) const =0;
+        virtual Node* clone(void) const =0;
+        virtual Node* getLeftChild(void) const =0;
+        virtual Node* getRightChild(void) const =0;
+};
+
+class DecisionNode: public Node
+{
+    public:
+
+        /**
+         * @brief Set left child of this node.
+         * @param child a @c Node pointer to the new left child.
+         */
+        void setLeftChild(Node* child)
+        {
+            if (leftChild != 0) delete leftChild;
+            leftChild = child;
+        }
+
+        /**
+         * @brief Set right child of this node.
+         * @param child a @c Node pointer to the new right child.
+         */
+        void setRightChild(Node* child)
+        {
+            if (rightChild != 0) delete rightChild;
+            rightChild = child;
+        }
+
+        /**
+         * @brief Returns a pointer to the left child node.
+         * @return a @c Node pointer to the left child node.
+         */
+        Node* getLeftChild() const { return this->leftChild; }
+
+        /**
+         * @brief Returns a pointer to the right child node.
+         * @return a @c Node pointer to the right child node.
+         */
+        Node* getRightChild() const { return this->rightChild; }
+
+        /**
+         * @brief Returns true if node has no children and false otherwise.
+         * @return true if node has no children and false otherwise.
+         */
+        bool isLeaf() const { return !this->rightChild && !this->leftChild; }
+
+        /**
+         * @brief Returns true if both children exist.
+         * @return true if both children exist.
+         */
+        bool isComplete() const { return this->rightChild && this->leftChild; }
+
+        //virtual void setLeftChild(Node* node) =0;
+        //virtual void setRightChild(Node* node) =0;
+        virtual bool goRight(void) const =0;
+
+    protected:
+
+        Node* leftChild;
+        Node* rightChild;
+};
+
+template <class T>
+class UnivariateCut: public DecisionNode
+{
+    public:
+
+        //!< Constructor
+        UnivariateCut(const T* _feature, T _cut):
+            feature(_feature),
+            cut(_cut)
+        {
+            this->leftChild = 0;
+            this->rightChild = 0;
+        }
+
+        //!< Copy Constructor
+        UnivariateCut(const UnivariateCut<T>& other)
+        {
+            this->feature = other.feature;
+            this->cut = other.cut;
+            this->leftChild = other.leftChild ? other.leftChild->clone() : 0;
+            this->rightChild = other.rightChild ? other.rightChild->clone() : 0;
+        }
+
+        //!< Destructor
+        ~UnivariateCut()
+        {
+            delete this->rightChild;
+            delete this->leftChild;
+        }
+
+        Node* clone() const
+        {
+            UnivariateCut<T>* node = new UnivariateCut<T>(this->feature,this->cut);
+            if (this->leftChild) node->setLeftChild(this->leftChild->clone());
+            if (this->rightChild) node->setRightChild(this->rightChild->clone());
+            return node;
+        }
+
+        bool goRight() const { return this->feature ? *this->feature > this->cut : false; }
+
+        const T* getFeature() const { return this->feature; }
+
+        T getValue() const { return this->feature ? *this->feature : -9999.; }
+
+        T getCut() const { return this->cut; }
+
+    private:
+
+        const T* feature;   //!< Pointer to the variable which is cut on at this node.
+        T cut;              //!< The cut.
+};
+
+template <class T, class U, class V>
+class UnivariateSlidingCut1D: public DecisionNode
+{
+    public:
+
+        //!< Constructor
+        UnivariateSlidingCut1D(const T* _feature, U* _function, const V* _variable):
+            feature(_feature),
+            function(_function),
+            variable(_variable)
+        {
+            this->leftChild = 0;
+            this->rightChild = 0;
+        }
+
+        //!< Copy Constructor
+        UnivariateSlidingCut1D(const UnivariateSlidingCut1D<T,U,V>& other)
+        {
+            this->feature = other.feature;
+            this->function = other.function->Clone();
+            this->variable = other.variable;
+            this->leftChild = other.leftChild ? other.leftChild->clone() : 0;
+            this->rightChild = other.rightChild ? other.rightChild->clone() : 0;
+        }
+
+        //!< Destructor
+        ~UnivariateSlidingCut1D()
+        {
+            delete this->function;
+            delete this->rightChild;
+            delete this->leftChild;
+        }
+
+        Node* clone() const
+        {
+            UnivariateSlidingCut1D<T,U,V>* node = new UnivariateSlidingCut1D<T,U,V>(this->feature,(U*)this->function->Clone(),this->variable);
+            if (this->leftChild) node->setLeftChild(this->leftChild->clone());
+            if (this->rightChild) node->setRightChild(this->rightChild->clone());
+            return node;
+        }
+
+        bool goRight() const
+        {
+            return this->feature && this->variable ? *this->feature > this->function->Eval(float(*this->variable)) : false;
+        }
+
+    private:
+
+        const T* feature;
+        U* function;
+        const V* variable;
+};
+
+template <class T, class U, class V, class W>
+class MultivariateCut2D: public DecisionNode
+{
+    public:
+
+        //!< Constructor
+        MultivariateCut2D(T* _function, const U* _x, const V* _y, W _cut):
+            function(_function),
+            x(_x),
+            y(_y),
+            cut(_cut)
+        {
+            this->leftChild = 0;
+            this->rightChild = 0;
+        }
+
+        //!< Copy Constructor
+        MultivariateCut2D(const MultivariateCut2D<T,U,V,W>& other)
+        {
+            this->function = other.function->Clone();
+            this->x = other.x;
+            this->y = other.y;
+            this->cut = other.cut;
+            this->leftChild = other.leftChild ? other.leftChild->clone() : 0;
+            this->rightChild = other.rightChild ? other.rightChild->clone() : 0;
+        }
+
+        //!< Destructor
+        ~MultivariateCut2D()
+        {
+            delete this->function;
+            delete this->rightChild;
+            delete this->leftChild;
+        }
+
+        Node* clone() const
+        {
+            MultivariateCut2D<T,U,V,W>* node = new MultivariateCut2D<T,U,V,W>((T*)this->function->Clone(),this->x,this->y,this->cut);
+            if (this->leftChild) node->setLeftChild(this->leftChild->clone());
+            if (this->rightChild) node->setRightChild(this->rightChild->clone());
+            return node;
+        }
+
+        bool goRight() const
+        {
+            return this->function && this->x && this->y ? this->function->Eval(float(*this->x),float(*this->y)) > cut : false;
+        }
+
+    private:
+
+        T* function;
+        const U* x;
+        const V* y;
+        W cut;
+};
+
+
+template <class T>
+class LeafNode: public Node
+{
+    public:
+
+        LeafNode(T _value = 0):value(_value){}
+
+        LeafNode(const LeafNode<T>& other) { this->value = other.value; }
+
+        ~LeafNode(){}
+
+        Node* clone() const { return new LeafNode<T>(this->value); }
+
+        bool isLeaf() const { return true; }
+
+        Node* getLeftChild() const { return 0; }
+
+        Node* getRightChild() const { return 0; }
+
+        bool isComplete() const { return true; }
+
+        virtual T getValue() const { return this->value; }
+
+        void setValue(T _value) { this->value = _value; }
+
+    private:
+
+        T value;
+};
+
+template <class X, class Y, class G>
+class TransformationNode: public LeafNode<float>
+{
+    public:
+
+        TransformationNode(const X* _x, float _xlow, float _xhigh, const Y* _y, float _ylow, float _yhigh, G* _transform):
+        x(_x),
+        xlow(_xlow),
+        xhigh(_xhigh),
+        y(_y),
+        ylow(_ylow),
+        yhigh(_yhigh),
+        transform(_transform)
+        {}
+
+        TransformationNode(const TransformationNode<X,Y,G>& other)
+        {
+            this->x = other.x;
+            this->xlow = other.xlow;
+            this->xhigh = other.xhigh;
+            this->y = other.y;
+            this->ylow = other.ylow;
+            this->yhigh = other.yhigh;
+            this->transform = other.transform->Clone();
+        }
+
+        ~TransformationNode()
+        {
+            //delete transform;
+        }
+
+        Node* clone() const
+        {
+            return new TransformationNode<X,Y,G>(x,xlow,xhigh,y,ylow,yhigh,(G*)transform->Clone());
+        }
+
+        float getValue() const
+        {
+            float _x = float(*x);
+            float _y = float(*y);
+            if (_x < xlow)
+                _x = xlow;
+            else if (_x > xhigh)
+                _x = xhigh;
+            if (_y < ylow)
+                _y = ylow;
+            else if (_y > yhigh)
+                _y = yhigh;
+            return this->transform->Interpolate(_x, _y);
+        }
+
+    private:
+
+        const X* x;
+        float xlow;
+        float xhigh;
+        const Y* y;
+        float ylow;
+        float yhigh;
+        G* transform;
+};
+
+template <class T>
+class PointerLeafNode: public Node
+{
+    public:
+
+        PointerLeafNode(T* _value = 0):value(_value){}
+
+        PointerLeafNode(const PointerLeafNode<T>& other) { this->value = other.value; }
+
+        ~PointerLeafNode() { delete this->value; }
+
+        Node* clone() const { return new PointerLeafNode<T>(this->value); }
+
+        bool isLeaf() const { return true; }
+
+        Node* getLeftChild() const { return 0; }
+
+        Node* getRightChild() const { return 0; }
+
+        bool isComplete() const { return true; }
+
+        T* getValue() const { return this->value; }
+
+        void setValue(T* _value) { this->value = _value; }
+
+    private:
+
+        T* value;
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/NoiseCorrIsol.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/NoiseCorrIsol.h
new file mode 100644
index 0000000000000000000000000000000000000000..627d84e280d3f0a61a93321d5df0ced62dbb5e2b
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/NoiseCorrIsol.h
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef NOISECORRISOL_H
+#define NOISECORRISOL_H
+
+//*******************************************************//
+// Name: NoiseCorrIsol.h                                 //
+// Author: Michel Trottier-McDonald <mtm@cern.ch>        //
+// Description: A class to provide isolation based       //
+// pileup/underlying event correction to the clusters    //
+// of tau decays                                         //
+//*******************************************************//
+
+#include <TLorentzVector.h>
+#include "TauDiscriminant/EMFCluster.h"
+#include <vector>
+
+class NoiseCorrIsol
+{
+public:
+    // Constructors, Destructors
+    NoiseCorrIsol();
+
+    NoiseCorrIsol(const TLorentzVector& tau,
+		  const std::vector<TLorentzVector>& clusters,
+		  double innerDR=0.3,
+		  double outerDR=0.4,
+		  bool eff=false);
+
+    NoiseCorrIsol(const TLorentzVector& tau,
+		  const std::vector<EMFCluster>& clusters,
+		  double innerDR=0.3,
+		  double outerDR=0.4,
+		  bool eff=false);
+
+    ~NoiseCorrIsol();
+
+    //Getters
+    std::vector<TLorentzVector> correctedClustersTLV();
+    std::vector<EMFCluster> correctedClusters();
+
+private:
+
+    std::vector<TLorentzVector> m_clusters;
+    std::vector<EMFCluster> m_EMFClusters;
+
+    TLorentzVector m_tau;
+
+    double m_innerDR;
+    double m_outerDR;
+    double m_areaRatio;
+
+    double areaRatio();
+    std::vector<EMFCluster> effClusters(const std::vector<EMFCluster>& inputClusters);
+
+    bool m_eff;
+
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Pi0Finder.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Pi0Finder.h
new file mode 100644
index 0000000000000000000000000000000000000000..75150d100366b8623b4d6493341bf1f999bbd57b
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Pi0Finder.h
@@ -0,0 +1,123 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef PI0FINDER_H
+#define PI0FINDER_H
+
+//*******************************************************//
+// Name: Pi0Finder.h                                     //
+// Author: Michel Trottier-McDonald <mtm@cern.ch>        //
+// Description: Main class to find which clusters are    //
+// the pi0s in tau decays involving neutral pions        //
+//*******************************************************//
+
+#include <TLorentzVector.h>
+#include "TauDiscriminant/EMFCluster.h"
+#include <vector>
+
+class Pi0Finder
+{
+public:
+    // Constructors, Destructors
+    Pi0Finder();
+
+    Pi0Finder(const std::vector<TLorentzVector>& tracks,
+	      const std::vector<EMFCluster>& clusters,
+	      bool twoPi0s = true,
+	      double resImportance = 1.0,
+	      double turnOnPoint = 1.05,
+	      double turnOnRate = 9,
+	      double PSSFactor = 1.4,
+	      double EM2Factor = 0.4,
+	      double twoPi0Strength = 0.4,
+	      bool usePseudoHADF = true,
+	      bool effClusters = false);
+
+    Pi0Finder(const std::vector<TLorentzVector>& tracks,
+	      const std::vector<TLorentzVector>& clusters,
+	      const std::vector<float>& PSSFs,
+	      const std::vector<float>& EM2Fs,
+	      const std::vector<float>& EM3Fs,
+	      bool twoPi0s = true,
+	      double resImportance = 1.0,
+	      double turnOnPoint = 1.05,
+	      double turnOnRate = 9,
+	      double PSSFactor = 1.4,
+	      double EM2Factor = 0.4,
+	      double twoPi0Strength = 0.4,
+	      bool usePseudoHADF = true,
+	      bool effClusters = false);
+
+    ~Pi0Finder();
+
+    //Getters
+    TLorentzVector visTauTLV() const; //Returns the visible tau
+    TLorentzVector pi0TLV1() const {return m_selEMFCluster1.TLV();}
+    TLorentzVector pi0TLV2() const {return m_selEMFCluster2.TLV();}
+    TLorentzVector pi0NotCorrTLV1() const {return m_selEMFClusterNotCorr1.TLV();}
+    TLorentzVector pi0NotCorrTLV2() const {return m_selEMFClusterNotCorr2.TLV();}
+    EMFCluster pi0EMFCluster1() const {return m_selEMFCluster1;}
+    EMFCluster pi0EMFCluster2() const {return m_selEMFCluster2;}
+    double doubleCounting() const {return m_doubleCountingE;}
+    bool corrected1() const {return m_applyCorrCluster1;}
+    bool corrected2() const {return m_applyCorrCluster2;}
+    bool noMatch() const {return m_noMatch;}
+
+private:
+
+    //The candidate clusters (after noise correction)
+    std::vector<EMFCluster> m_EMFClusters;
+
+    //the tracks
+    std::vector<TLorentzVector> m_tracks;
+
+    //General parameters
+    bool m_usePseudoHADF;
+    bool m_twoPi0s;
+
+    //Selection parameters
+    double m_resImportance; // Importance of the resolution of the cluster energy compared to calo energy - track energy
+
+    //Contamination correction parameters
+    double m_turnOnPoint; // Value of energy double counting at which the correction turns on.
+    double m_turnOnRate;  // How quickly the correction turns on after the turn on point
+    double m_PSSFactor;   // How many times the PreSampler-Strip Energy when finding the corrected energy
+    double m_EM2Factor;   // How many times the EMLayer 2 Energy when finding the corrected energy
+    double m_twoPi0Strength; //Biases the preference for finding 1 or 2 clusters
+
+    //Internal transient parameters
+    double m_caloE;             // total calorimeter energy
+    double m_caloHADE;          // total hadronic (pseudo-hadronic) calorimeter energy
+    double m_trkE;              // total track system energy
+    double m_trkHADF;           // energy in the hadronic (pseudo-hadronic) calorimeter divided by the track energy
+    double m_doubleCountingE;   // Values above 1 roughly indicates energy double counting, used to decide to correct cluster energy or not
+    bool m_applyCorrCluster1;   // Apply correction to cluster 1
+    bool m_applyCorrCluster2;   // Apply correction to cluster 2
+    bool m_keepCluster1;        // Keep Cluster1
+    bool m_keepCluster2;        // Keep Cluster2
+    bool m_noMatch;             // True if no pi0 has been selected
+
+    TLorentzVector m_trkSys;
+
+    //Results
+    EMFCluster m_selEMFCluster1;
+    EMFCluster m_selEMFCluster2;
+    EMFCluster m_selEMFClusterNotCorr1;
+    EMFCluster m_selEMFClusterNotCorr2;
+
+    //Internal methods
+    void preSelParameters();  // Calculate pre-selection internal transient parameters
+    void postSelParameters(); // Calculate post-selection internal transient parameters
+    void select();            // Select one pi0 cluster
+    void select2();           // Select two pi0 clusters
+    EMFCluster correct(const EMFCluster& cl);   // Correct the energy of one contaminated cluster
+    EMFCluster giveMass(const EMFCluster& cl);  // Give the pi0 mass to uncorrected clusters
+    void execute();           // Execute the pi0 finding
+    std::vector<EMFCluster> convertToEMFClusters(const std::vector<TLorentzVector>& clusters,
+						 const std::vector<float>& PSSFs,
+						 const std::vector<float>& EM2Fs,
+						 const std::vector<float>& EM3Fs);
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauCuts.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauCuts.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fc682b0856865c555cc418d49f9b82687dae9ab
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauCuts.h
@@ -0,0 +1,48 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TAUCUTS_H
+#define TAUCUTS_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/MethodCuts.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+
+using namespace TauID;
+
+class TauCuts: public TauDiscriToolBase
+{
+    public:
+
+        //-----------------------------------------------------------------
+        // Contructor and destructor
+        //-----------------------------------------------------------------
+        TauCuts(const string& type,
+                const string& name,
+                const IInterface* parent):
+            TauDiscriToolBase(type, name, parent)
+        {
+            declareInterface<TauDiscriToolBase>(this);
+            declareProperty( "cuts", m_fileName);
+        }
+
+        virtual ~TauCuts() {}
+
+        //-----------------------------------------------------------------
+        // Gaudi algorithm hooks
+        //-----------------------------------------------------------------
+        virtual StatusCode prepare(const TauDetailsManager&);
+        
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+        
+        virtual StatusCode finalize();
+
+    private:
+
+        std::string         m_fileName;
+        MethodCuts*         m_cutsManager;
+};
+
+#endif // TAUCUTS_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauCutsEleVeto.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauCutsEleVeto.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3bfabb864487badc95443136aadaa7e620226c4
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauCutsEleVeto.h
@@ -0,0 +1,48 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TAUCUTSELEVETO_H
+#define TAUCUTSELEVETO_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+
+class TauCutsEleVeto: public TauDiscriToolBase
+{
+    public:
+
+        //-----------------------------------------------------------------
+        // Contructor and destructor
+        //-----------------------------------------------------------------
+        TauCutsEleVeto(const string& type,
+                const string& name,
+                const IInterface* parent):
+            TauDiscriToolBase(type, name, parent),
+            detailsManager(0),
+            useLCscale(false)
+        {
+            declareInterface<TauDiscriToolBase>(this);
+
+            declareProperty("useLCscale", this->useLCscale);
+        }
+
+        virtual ~TauCutsEleVeto() {}
+
+        //-----------------------------------------------------------------
+        // Gaudi algorithm hooks
+        //-----------------------------------------------------------------
+        virtual StatusCode prepare(const TauDetailsManager&);
+        
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+        
+        virtual StatusCode finalize();
+
+    private:
+
+        const TauDetailsManager*  detailsManager;
+        bool useLCscale;
+};
+
+#endif // TAUCUTSELEVETO_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetails.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetails.h
new file mode 100644
index 0000000000000000000000000000000000000000..052fccef3998e80fcf3e32daf3707ec1cff63793
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetails.h
@@ -0,0 +1,146 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/*
+ * Author: Noel Dawe <Noel-dot-Dawe-AT-cern-dot-ch>
+ */
+
+#ifndef TAUDETAILS_H
+#define TAUDETAILS_H
+
+#include <string>
+
+// Add new details to the proper block below (4 blocks: int/float tau details and int/float event details)
+// Use all caps, but note that in the end, detail names are case insensitive
+// since they are always compared in the uppercase state when building the discriminants at init
+// Using this "ENUM_OR_STRING" macro allows as to use the same list to init enums (below)
+// as well as arrays of strings (in the same order) (see TauDetailsManager.cxx)
+
+#define INT_TAU_DETAILS \
+    ENUM_OR_STRING( AUTHOR ), \
+    ENUM_OR_STRING( TAU_PI0_N ), \
+    ENUM_OR_STRING( CHARGE ), \
+    ENUM_OR_STRING( NUMTRACK ), \
+    ENUM_OR_STRING( NUMWIDETRACK ), \
+    ENUM_OR_STRING( NSTRIP ), \
+    ENUM_OR_STRING( NISOLLOOSETRK ), \
+    ENUM_OR_STRING( NPI0 ), \
+    ENUM_OR_STRING( NPI0CL), \
+    ENUM_OR_STRING( NISOLTRK ), \
+    ENUM_OR_STRING( NUMTOPOCLUSTERS ), \
+    ENUM_OR_STRING( __IntTauDetail__END__ )
+
+#define FLOAT_TAU_DETAILS \
+    ENUM_OR_STRING( ETA ), \
+    ENUM_OR_STRING( ABS_ETA ), \
+    ENUM_OR_STRING( ABS_ETA_LEAD_TRACK ), \
+    ENUM_OR_STRING( TAU_ABSDELTAETA ), \
+    ENUM_OR_STRING( TAU_ABSDELTAPHI ), \
+    ENUM_OR_STRING( PHI ), \
+    ENUM_OR_STRING( E ), \
+    ENUM_OR_STRING( ET ), \
+    ENUM_OR_STRING( PT ), \
+    ENUM_OR_STRING( EMFRACTIONATEMSCALE ), \
+    ENUM_OR_STRING( EMRADIUS ), \
+    ENUM_OR_STRING( HADRADIUS ), \
+    ENUM_OR_STRING( CALRADIUS ), \
+    ENUM_OR_STRING( ISOLFRAC ), \
+    ENUM_OR_STRING( CENTFRAC ), \
+    ENUM_OR_STRING( STRIPWIDTH2 ), \
+    ENUM_OR_STRING( TRFLIGHTPATHSIG ), \
+    ENUM_OR_STRING( IPSIGLEADTRK ), \
+    ENUM_OR_STRING( IPSIGLEADLOOSETRK ), \
+    ENUM_OR_STRING( ETOVERPTLEADTRK ), \
+    ENUM_OR_STRING( PTLEADTRKOVERET ), \
+    ENUM_OR_STRING( IPZ0SINTHETASIGLEADTRK ), \
+    ENUM_OR_STRING( MASSTRKSYS ), \
+    ENUM_OR_STRING( TRKWIDTH2 ), \
+    ENUM_OR_STRING( TRKAVGDIST ), \
+    ENUM_OR_STRING( ETEFLOWOVERET ), \
+    ENUM_OR_STRING( MEFLOW ), \
+    ENUM_OR_STRING( SUMPT3TRK ), \
+    ENUM_OR_STRING( SUMPT ), \
+    ENUM_OR_STRING( DRMIN ), \
+    ENUM_OR_STRING( DRMAX ), \
+    ENUM_OR_STRING( SUMPT_OVER_ET ), \
+    ENUM_OR_STRING( SUMPT3TRK_OVER_ET ), \
+    ENUM_OR_STRING( ETHAD_EM_OVER_SUMPT3TRK ), \
+    ENUM_OR_STRING( ETEM_EM_OVER_SUMPT3TRK ), \
+    ENUM_OR_STRING( ETHAD_EM_OVER_SUMPT ), \
+    ENUM_OR_STRING( ETEM_EM_OVER_SUMPT ), \
+    ENUM_OR_STRING( TRT_NHT_OVER_NLT ), \
+    ENUM_OR_STRING( M ), \
+    ENUM_OR_STRING( TOPOINVMASS ), \
+    ENUM_OR_STRING( EFFTOPOINVMASS ), \
+    ENUM_OR_STRING( JVF ), \
+    ENUM_OR_STRING( PT_PILEUP ), \
+    ENUM_OR_STRING( TRACK_ISO ), \
+    ENUM_OR_STRING( CALO_ISO ), \
+    ENUM_OR_STRING( CALO_ISO_CORRECTED ), \
+    ENUM_OR_STRING( LEAD2CLUSTEREOVERALLCLUSTERE ), \
+    ENUM_OR_STRING( LEAD3CLUSTEREOVERALLCLUSTERE ), \
+    ENUM_OR_STRING( HADLEAKET ),\
+    ENUM_OR_STRING( SUMEMCELLETOVERLEADTRKPT ),\
+    ENUM_OR_STRING( SECMAXSTRIPET ),\
+    ENUM_OR_STRING( BDTJETSCORE ),\
+    ENUM_OR_STRING( CHPIEMEOVERCALOEME ),\
+    ENUM_OR_STRING( PSSFRACTION ),\
+    ENUM_OR_STRING( EMPOVERTRKSYSP ),\
+    ENUM_OR_STRING( EMEOVERTRKSYSE ),		\
+    ENUM_OR_STRING( CORRCENTFRAC ),		\
+    ENUM_OR_STRING( CORRFTRK ),		\
+    ENUM_OR_STRING( TAU_PI0_VISTAU_M ), \
+    ENUM_OR_STRING( TAU_PTRATIO ), \
+    ENUM_OR_STRING( INTERAXIS_ETA ), \
+    ENUM_OR_STRING( INTERAXIS_PHI ), \
+    ENUM_OR_STRING( PI0CL1_PT ), \
+    ENUM_OR_STRING( PI0CL1_ETA ), \
+    ENUM_OR_STRING( PI0CL1_PHI ), \
+    ENUM_OR_STRING( PI0CL2_PT ), \
+    ENUM_OR_STRING( PI0CL2_ETA ), \
+    ENUM_OR_STRING( PI0CL2_PHI ), \
+    ENUM_OR_STRING( VISTAU_PI0CL_PT ),	\
+    ENUM_OR_STRING( VISTAU_PI0CL_ETA ), \
+    ENUM_OR_STRING( VISTAU_PI0CL_PHI ), \
+    ENUM_OR_STRING( VISTAU_PI0CL_M ), \
+    ENUM_OR_STRING( EMFRACTIONATEMSCALE_MOVEE3 ), \
+    ENUM_OR_STRING( TAU_SEEDTRK_SECMAXSTRIPETOVERPT ), \
+    ENUM_OR_STRING( __FloatTauDetail__END__ )
+    
+#define INT_EVENT_DETAILS \
+    ENUM_OR_STRING( NUMVERTICES ), \
+    ENUM_OR_STRING( NUMGOODVERTICES ),\
+    ENUM_OR_STRING( NUM_PILEUP_AND_PRIMARY_VERTICES ),\
+    ENUM_OR_STRING( __IntEventDetail__END__ )
+
+#define FLOAT_EVENT_DETAILS \
+    ENUM_OR_STRING( __FloatEventDetail__END__ )
+
+#undef ENUM_OR_STRING
+#define ENUM_OR_STRING( x ) x
+
+namespace Details
+{
+    enum IntTauDetail
+    {
+        INT_TAU_DETAILS
+    };
+    
+    enum FloatTauDetail
+    {
+        FLOAT_TAU_DETAILS
+    };
+    
+    enum IntEventDetail
+    {
+        INT_EVENT_DETAILS
+    };
+    
+    enum FloatEventDetail
+    {
+        FLOAT_EVENT_DETAILS
+    };
+}
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetailsManager.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetailsManager.h
new file mode 100644
index 0000000000000000000000000000000000000000..6845c6dda39cdc6a0678c270b20cca41f1519d18
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetailsManager.h
@@ -0,0 +1,151 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/*!
+ * file: TauDetailsManager.h
+ *
+ * This class provides a handle on the features used for tau/jet separation.
+ * 
+ * To add new details: first add the enum entry in TauDetails.h and then 
+ * implement the code in TauDetailsManager.cxx which sets the value for each tau/event
+ *
+ * Author: Noel Dawe (Noel-dot-Dawe-AT-cern-dot-ch)
+ */
+
+#ifndef TAUDETAILSMANAGER_H
+#define TAUDETAILSMANAGER_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <iostream>
+#include <iomanip>
+
+#include "TauDiscriminant/TauDetails.h"
+#include "xAODTau/TauJet.h"
+// #include "tauEvent/TauCommonDetails.h"
+#include "xAODTau/TauDefs.h"
+#include "StoreGate/StoreGateSvc.h"
+#include "AthenaKernel/MsgStreamMember.h"
+
+using namespace std;
+
+class TauDetailsManager
+{
+    public:
+
+        static const float LOW_NUMBER;
+
+        //!< Default constructor
+        TauDetailsManager(StoreGateSvc* =0, bool isTrigger=false);
+
+        //!< Destructor
+        ~TauDetailsManager() {}
+
+        const map<string,float*>* getFloatDetails() const { return &this->float_details; }
+
+        const map<string,int*>* getIntDetails() const { return &this->int_details; }
+
+        const float* getFloatDetailAddress(Details::FloatTauDetail) const;
+
+        const int* getIntDetailAddress(Details::IntTauDetail) const;
+
+        float getFloatDetailValue(Details::FloatTauDetail) const;
+
+        int getIntDetailValue(Details::IntTauDetail) const;
+        
+        const float* getFloatDetailAddress(Details::FloatEventDetail) const;
+
+        const int* getIntDetailAddress(Details::IntEventDetail) const;
+
+        float getFloatDetailValue(Details::FloatEventDetail) const;
+
+        int getIntDetailValue(Details::IntEventDetail) const;
+
+		bool getDoTrigger() const { return doTrigger; }
+
+        bool updateEvent();
+        
+        bool update(const xAOD::TauJet& tauJet); //keep for backward compatibility
+        bool update_with_edm(xAOD::TauJet& tauJet);
+
+	bool setNpi0(xAOD::TauJet& tauJet, int nPi0);
+
+        friend MsgStream& operator<<(MsgStream&, const TauDetailsManager&);
+
+        friend ostream& operator<<(ostream&, const TauDetailsManager&);
+
+        //Declaring the Message method for further use
+        MsgStream& msg( MSG::Level lvl ) const { return m_msg << lvl; }
+        
+        //Declaring the Method providing Verbosity Level
+        bool msgLvl( MSG::Level lvl ) const { return m_msg.get().level() <= lvl; }
+    
+    protected:
+
+        template <class stream>
+        void printOn(stream& o) const;
+        
+    private:
+
+        map<string,float*> float_details;
+        map<string,int*> int_details;
+        vector<float> float_data;
+        vector<int> int_data;
+        vector<float> float_event_data;
+        vector<int> int_event_data;
+        StoreGateSvc* storeGate;
+	bool doTrigger;
+        mutable Athena::MsgStreamMember m_msg;
+
+	float m_clusterCone;
+};
+
+template <class stream>
+void TauDetailsManager::printOn(stream& o) const
+{
+    ios_base::fmtflags original_flags = (ios_base::fmtflags)o.flags();
+    o << "\n\n";
+    map<string,float*>::const_iterator it_float(this->float_details.begin());
+    map<string,float*>::const_iterator it_float_end(this->float_details.end());
+    const float* float_value(0);
+    for(; it_float != it_float_end; ++it_float)
+    {
+        o << left << setw(40) << setfill('.') << (it_float->first);
+        float_value = it_float->second;
+        if (float_value)
+            o << " " << *float_value << "\n";
+        else
+            o << " NULL\n";
+    }
+    
+    map<string,int*>::const_iterator it_int(this->int_details.begin());
+    map<string,int*>::const_iterator it_int_end(this->int_details.end());
+    const int* int_value(0);
+    for(; it_int != it_int_end; ++it_int)
+    {
+        o << left << setw(40) << setfill('.') << (it_int->first);
+        int_value = it_int->second;
+        if (int_value)
+            o << " " << *int_value << "\n";
+        else
+            o << " NULL\n";
+    }
+    o.flags(original_flags);
+}
+
+inline MsgStream& operator<<(MsgStream& o, const TauDetailsManager& manager)
+{
+    manager.printOn<MsgStream>(o);
+    return o;
+}
+
+inline ostream& operator<<(ostream& o, const TauDetailsManager& manager)
+{
+    manager.printOn<ostream>(o);
+    return o;
+}
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetailsManagerStandalone.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetailsManagerStandalone.h
new file mode 100644
index 0000000000000000000000000000000000000000..634a9577d8a193c823420d1b6aa7b1c99a072680
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDetailsManagerStandalone.h
@@ -0,0 +1,176 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/*!
+ * file: TauDetailsManagerStandalone.h
+ *
+ * This class provides a handle on the features used for tau/jet separation in standalone mode.
+ * 
+ * To add new details: first add the enum entry in TauDetails.h and then 
+ * implement the code in TauDetailsManagerStandalone.cxx which sets the value for each tau/event
+ *
+ * Author: Pawel Malecki(Pawel dot Malecki at cern dot ch)
+ */
+
+
+#ifndef TAUDETAILSMANAGERSTANDALONE_H
+#define TAUDETAILSMANAGERSTANDALONE_H
+
+
+
+#include <string>
+#include <vector>
+#include <map>
+
+
+#include "TauDiscriminant/TauDetails.h"
+
+#include <TTree.h>
+
+using namespace std;
+
+namespace TauID{
+  class TauDetailsManagerStandalone
+  {
+  public:
+    
+    static const float LOW_NUMBER;
+    
+    //!< Default constructor
+    TauDetailsManagerStandalone(TTree* tree = 0);
+    
+    //!< Destructor
+    ~TauDetailsManagerStandalone() {}
+    
+    const map<string,float*>* getFloatDetails() const { return &this->float_details; }
+    
+    const map<string,int*>* getIntDetails() const { return &this->int_details; }
+    
+    const float* getFloatDetailAddress(Details::FloatTauDetail) const;
+    
+    const int* getIntDetailAddress(Details::IntTauDetail) const;
+    
+    float getFloatDetailValue(Details::FloatTauDetail) const;
+    
+    int getIntDetailValue(Details::IntTauDetail) const;
+    
+    const float* getFloatDetailAddress(Details::FloatEventDetail) const;
+    
+    const int* getIntDetailAddress(Details::IntEventDetail) const;
+    
+    float getFloatDetailValue(Details::FloatEventDetail) const;
+    
+    int getIntDetailValue(Details::IntEventDetail) const;
+    
+    bool getDoTrigger() const { return doTrigger; }
+    
+    bool updateEvent(int entry);
+    
+    bool update(unsigned int itau);
+    
+    bool initTree(TTree* tree);
+    
+    int getNtau();
+
+    
+    //Declaring the Message method for further use
+    
+    //Declaring the Method providing Verbosity Level
+    
+    
+  private:
+    
+    map<string,float*> float_details;
+    map<string,int*> int_details;
+    vector<float> float_data;
+    vector<int> int_data;
+    vector<float> float_event_data;
+    vector<int> int_event_data;
+    bool doTrigger;
+    
+    float m_clusterCone;
+    
+    TTree *m_tree;
+    
+    
+    
+     /// non-vector variables
+    int evt_calcVars_numGoodVertices;
+    
+    ///vector variables
+    vector<float>     *tau_charge;
+    vector<int>     *tau_author;    
+    vector<int>     *tau_numTrack;
+    vector<int>     *tau_seedCalo_nWideTrk;
+    vector<float>   *tau_eta;
+    vector<float>   *tau_phi;
+    vector<float>   *tau_pt;
+    vector<float>   *tau_seedCalo_trkAvgDist;
+    vector<float>   *tau_etOverPtLeadTrk;
+    vector<float>   *tau_calcVars_EMFractionAtEMScale;
+    vector<float>   *tau_TRTHTOverLT_LeadTrk;
+    vector<float>   *tau_seedCalo_dRmax;
+    vector<float>   *tau_leadTrack_eta;
+    vector<float>   *tau_calcVars_ChPiEMEOverCaloEME;
+    vector<float>   *tau_seedTrk_secMaxStripEt;
+    vector<float>   *tau_seedTrk_hadLeakEt;
+    vector<float>   *tau_seedTrk_sumEMCellEtOverLeadTrkPt;
+    vector<float>   *tau_calcVars_corrFTrk;
+    vector<float>   *tau_calcVars_corrCentFrac;
+    vector<float>   *tau_seedCalo_isolFrac;
+    vector<float>   *tau_seedCalo_hadRadius;
+    vector<float>   *tau_calcVars_PSSFraction;
+    vector<float>   *tau_calcVars_EMPOverTrkSysP;
+    vector<int>     *tau_seedCalo_nStrip;
+    vector<float>   *tau_massTrkSys;
+    vector<float>   *tau_ipSigLeadTrk;
+    vector<float>   *tau_trFlightPathSig;
+    vector<float>   *tau_pi0_vistau_m;
+    vector<float>   *tau_pi0_vistau_pt;
+    vector<int>     *tau_pi0_n;
+    vector<float>   *tau_seedCalo_etEMAtEMScale;
+    vector<float>   *tau_seedCalo_etHadAtEMScale;
+    vector<float>   *tau_leadTrkPt;
+    vector<float>   *tau_leadTrack_phi;
+
+    TBranch   *b_evt_calcVars_numGoodVertices;
+    TBranch   *b_tau_charge;
+    TBranch   *b_tau_author;    
+    TBranch   *b_tau_numTrack;
+    TBranch   *b_tau_seedCalo_nWideTrk;
+    TBranch   *b_tau_eta;
+    TBranch   *b_tau_phi;
+    TBranch   *b_tau_pt;
+    TBranch   *b_tau_seedCalo_trkAvgDist;
+    TBranch   *b_tau_etOverPtLeadTrk;
+    TBranch   *b_tau_calcVars_EMFractionAtEMScale;
+    TBranch   *b_tau_TRTHTOverLT_LeadTrk;
+    TBranch   *b_tau_seedCalo_dRmax;
+    TBranch   *b_tau_leadTrack_eta;
+    TBranch   *b_tau_calcVars_ChPiEMEOverCaloEME;
+    TBranch   *b_tau_seedTrk_secMaxStripEt;
+    TBranch   *b_tau_seedTrk_hadLeakEt;
+    TBranch   *b_tau_seedTrk_sumEMCellEtOverLeadTrkPt;
+    TBranch   *b_tau_calcVars_corrFTrk;
+    TBranch   *b_tau_calcVars_corrCentFrac;
+    TBranch   *b_tau_seedCalo_isolFrac;
+    TBranch   *b_tau_seedCalo_hadRadius;
+    TBranch   *b_tau_calcVars_PSSFraction;
+    TBranch   *b_tau_seedCalo_nStrip;
+    TBranch   *b_tau_massTrkSys;
+    TBranch   *b_tau_ipSigLeadTrk;
+    TBranch   *b_tau_trFlightPathSig;
+    TBranch   *b_tau_calcVars_EMPOverTrkSysP;
+    TBranch   *b_tau_pi0_n;
+    TBranch   *b_tau_pi0_vistau_m;
+    TBranch   *b_tau_pi0_vistau_pt;
+    TBranch   *b_tau_seedCalo_etEMAtEMScale;
+    TBranch   *b_tau_seedCalo_etHadAtEMScale;
+    TBranch   *b_tau_leadTrkPt;
+    TBranch   *b_tau_leadTrack_phi;
+
+  };
+}
+#endif
+
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriBuilder.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriBuilder.h
new file mode 100644
index 0000000000000000000000000000000000000000..a9078a95b210295733eba5868fa4ae7eddd53eb0
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriBuilder.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//-----------------------------------------------------------------------------
+// file:        TauDiscriBuilder.h
+// package:     PhysicsAnalysis/TauID/TauDiscriminant
+// authors:     M. Wolter, A. Kaczmarska
+// date:        13 March 2008
+//-----------------------------------------------------------------------------
+
+#ifndef DISCRIBUILDER_TAU_H
+#define DISCRIBUILDER_TAU_H
+
+#include "GaudiKernel/ToolHandle.h"
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+
+#include <string>
+
+class TauDiscriToolBase;
+class StoreGateSvc;
+
+class TauDiscriBuilder: public AthAlgorithm
+{
+    public:
+        //-----------------------------------------------------------------
+        // Contructor and destructor
+        //-----------------------------------------------------------------
+        TauDiscriBuilder( const std::string &name, ISvcLocator *pSvcLocator );
+        ~TauDiscriBuilder();
+
+        //-----------------------------------------------------------------
+        // Gaudi algorithm hooks
+        //-----------------------------------------------------------------
+        virtual StatusCode initialize();
+        virtual StatusCode execute();
+        virtual StatusCode finalize();
+
+    private:
+        std::string                         tauInputContainerName;
+        ToolHandleArray<TauDiscriToolBase>  tools;
+        TauDetailsManager*                  manager;
+};
+#endif // DISCRIBUILDER_TAU_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriToolBase.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriToolBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..6495243d2cae8657cb57f46f2c0d06968c97deaf
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriToolBase.h
@@ -0,0 +1,58 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//-----------------------------------------------------------------------------
+// file:        TauDiscriToolBase.h
+// package:     PhysicsAnalysis/TauID/TauDiscriminant
+// authors:     M. Wolter, A. Kaczmarska
+// date:        13 March 2008
+//-----------------------------------------------------------------------------
+
+#ifndef DISCRITOOLBASE_TAU_H
+#define DISCRITOOLBASE_TAU_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "AthenaBaseComps/AthMessaging.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include "TauDiscriminant/FakeTauBits.h"
+#include "TauDiscriminant/FakeTauScores.h"
+
+class TauDiscriToolBase: public AthAlgTool
+{
+    public:
+    
+        TauDiscriToolBase( const std::string& type,
+                           const std::string& name,
+                           const IInterface * parent):
+            AthAlgTool(type, name, parent)
+        {}
+
+        virtual ~TauDiscriToolBase(){}
+
+        //-----------------------------------------------------------------
+        //! InterfaceID implementation needed for ToolHandle
+        //-----------------------------------------------------------------
+        static const InterfaceID& interfaceID()
+        {
+            static const InterfaceID TauDiscriToolBaseID( "TauDiscriToolBase", 1, 0 );
+            return TauDiscriToolBaseID;
+        }
+
+        //-----------------------------------------------------------------
+        //! Tool initializer
+        //-----------------------------------------------------------------
+        virtual StatusCode prepare(const TauDetailsManager&) = 0;
+        
+        //-----------------------------------------------------------------
+        //! Execute - called for each tau candidate
+        //-----------------------------------------------------------------
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*) = 0;
+
+        //-----------------------------------------------------------------
+        //! Finalizer
+        //-----------------------------------------------------------------
+        virtual StatusCode finalize() = 0;
+};
+
+#endif // DISCRITOOLBASE_TAU_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriminantDict.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriminantDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..e6940e3f8d83ebe86b31a375c766d0adb6e67ec1
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauDiscriminantDict.h
@@ -0,0 +1,15 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// This file is needed to create the Reflex dictionaries.
+#ifndef TAUD_DICT_H
+#define TAUD_DICT_H
+#include "TauDiscriminant/Types.h"
+#include "TauDiscriminant/TauIDReader.h"
+#include "TauDiscriminant/MethodCuts.h"
+#include "TauDiscriminant/MethodBDT.h"
+#include "TauDiscriminant/MethodLLH.h"
+#include "TauDiscriminant/MethodDummy.h"
+#include "TauDiscriminant/MethodTransform.h"
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauEleBDT.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauEleBDT.h
new file mode 100644
index 0000000000000000000000000000000000000000..b913b8929ceea7719a8462eb1fa7b5469cffdf6e
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauEleBDT.h
@@ -0,0 +1,88 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//! This class implements an Athena algorithm for evaluating the BDT score for tau jets.
+/*!
+ * Tool for BDT analysis.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TAUELEBDT_H
+#define TAUELEBDT_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/MethodBDT.h"
+#include "TauDiscriminant/MethodCuts.h"
+#include "TauDiscriminant/MethodTransform.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+#include <PathResolver/PathResolver.h>
+#include "xAODTau/TauJet.h"
+#include "TFile.h"
+#include "TH2F.h"
+
+using namespace std;
+using namespace TauID;
+
+class TauEleBDT: public TauDiscriToolBase
+{
+    public:
+
+        //!< Constructor
+        TauEleBDT(const string& type,
+                const string& name,
+                const IInterface* parent):
+            TauDiscriToolBase(type, name, parent),
+            eleBDTFile(""),
+            eleBitsFile(""),
+            eleBitsRootFile(""),
+	    cutsFile(NULL),
+	    hloose(NULL), hmedium(NULL), htight(NULL),
+            eleBDT(NULL),
+            eleBits(NULL)
+    {
+        declareInterface<TauDiscriToolBase>(this);
+
+        declareProperty("eleBDT", this->eleBDTFile);
+        declareProperty("eleBits", this->eleBitsFile);
+        declareProperty("eleBitsRoot", this->eleBitsRootFile);
+    }
+
+        //!< Destructor
+        virtual ~TauEleBDT() {}
+
+        /**
+         * @brief The boosted decision trees are built.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode prepare(const TauDetailsManager&);
+
+        /**
+         * @brief Values of the tau parameters are extracted and a boosted decision tree score is retrieved.
+         * @param tauJet a @c xAOD::TauJet pointer to a tau jet candidate.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+
+        /**
+         * @brief Allocated memory is freed.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode finalize();
+
+    private:
+
+        float eleScore;                     //!< Holds the current electron score which is used by a MethodCuts instance to determine if it passes loose, medium, or tight.
+
+        string eleBDTFile;                  //!< The @c string name of the bdt file for electron rejection.
+        string eleBitsFile;                 //!< The @c string name of the file used to define the loose, medium, and tight cuts for electron rejection.
+        string eleBitsRootFile;             //!< The @c string name of the ROOT file used to define the loose, medium, and tight cuts for electron rejection.
+	TFile *cutsFile;
+	TH2F *hloose, *hmedium, *htight;    //!< Histograms storing eta/pt for loose/medium/tight cuts
+        MethodBDT* eleBDT;                  //!< A pointer to the @c MethodBDT used to construct and evaluate BDTs used for electron discrimination.
+        MethodCuts* eleBits;                //!< A pointer to the @c MethodCuts used to determine whether the current electron BDT score passes loose, medium, or tight cut.
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauIDReader.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauIDReader.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba41cb71dd1535744a59b14460d63cd94709e4ed
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauIDReader.h
@@ -0,0 +1,149 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TAUIDREADER_H
+#define TAUIDREADER_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <algorithm>
+
+#include "TTree.h"
+#include "TFile.h"
+#include "TauDiscriminant/MethodBase.h"
+#include "TauDiscriminant/MethodBDT.h"
+#include "TauDiscriminant/MethodCuts.h"
+#include "TauDiscriminant/MethodLLH.h"
+#include "TauDiscriminant/MethodDummy.h"
+#include "TauDiscriminant/MethodTransform.h"
+#include "TauDiscriminant/TauDetailsManagerStandalone.h"
+
+
+using namespace std;
+
+namespace TauID
+{
+    class TauIDReader
+    {
+        public:
+
+            TauIDReader(bool _verbose = false):
+                verbose(_verbose),
+                vectorMode(false),
+                output(0),
+                own_output(true)
+            {
+	      m_tdms = new TauDetailsManagerStandalone();
+
+	      
+	    }
+
+            ~TauIDReader()
+            {
+                vector<pair<MethodBase*, vector<vector<float>* > > >::iterator it(this->methods.begin());
+                for (;it != this->methods.end(); ++it)
+                {
+                    vector<vector<float>* >::iterator it2(it->second.begin());
+                    for (; it2 != it->second.end(); ++it2)
+                    {
+                        delete *it2;
+                    }
+                    delete it->first;
+                }
+                vector<float*>::iterator it_float(this->methodBuffer.begin());
+                for(; it_float != this->methodBuffer.end(); ++it_float)
+                {
+                    delete *it_float;
+                }
+                it_float = this->floatVariables.begin();
+                for(; it_float != this->floatVariables.end(); ++it_float)
+                {
+                    delete *it_float;
+                }
+                it_float = this->vectFloatVariables.begin();
+                for(; it_float != this->vectFloatVariables.end(); ++it_float)
+                {
+                    delete *it_float;
+                }
+                vector<int*>::iterator it_int(this->intVariables.begin());
+                for(; it_int != this->intVariables.end(); ++it_int)
+                {
+                    delete *it_int;
+                }
+                it_int = this->vectIntVariables.begin();
+                for(; it_int != this->vectIntVariables.end(); ++it_int)
+                {
+                    delete *it_int;
+                }
+                if (this->own_output)
+                {
+                    delete this->output;
+                }
+                delete m_tdms;
+            }
+
+            bool bookMethod(Types::MethodType type, const string& name, const string& filename, unsigned int numResponses = 1);
+
+            bool addVariable(const string& name, const string& type = "F", const string& branchName="");
+
+            bool setOutput(const string& outputFileName, const string& outputDir = "");
+
+            bool setOutput(TFile* outputFile, const string& outputDir = "");
+
+            bool setOutput(TFile& outputFile, const string& outputDir = "");
+
+            TTree* classify(TTree& tree, const string& outputTreeName, bool copyTree = false);
+
+            TTree* classify(TTree* tree, const string& outputTreeName, bool copyTree = false);
+
+            // return the number of entries in the tree or -1 if there was a problem
+            int classify(const string& inputTreeName, const string& outputTreeName,
+                const string& inputFileName, const string& inputDir = "",
+                bool makeFriend = true,
+                bool copyTree = false);
+
+        private:
+
+            bool checkBranch(TTree* tree, const char* name, string type, bool checkType = true);
+            void print() const;
+
+            bool verbose;
+            bool vectorMode;
+
+            TFile* output;
+            string outputDir;
+            bool own_output;
+
+            vector<string> methodNames;
+            vector<pair<MethodBase*, vector<vector<float>* > > > methods;
+            vector<float*> methodBuffer;
+            vector<pair<string,string> > methodVariables;
+
+            vector<int*> intVariables;
+            vector<string> intNames;
+            vector<string> intBranches;
+            vector<float*> floatVariables;
+            vector<string> floatNames;
+            vector<string> floatBranches;
+
+            vector<int*> vectIntVariables;
+            vector<string> vectIntNames;
+            vector<string> vectIntBranches;
+            vector<float*> vectFloatVariables;
+            vector<string> vectFloatNames;
+            vector<string> vectFloatBranches;
+
+            vector<string> allVariableNames;
+            vector<string> allBranchNames;
+	    
+	    TauDetailsManagerStandalone *m_tdms;
+    };
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauJetBDT.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauJetBDT.h
new file mode 100644
index 0000000000000000000000000000000000000000..fe787ae54062e9f693594473ed459796ebfcc0e9
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauJetBDT.h
@@ -0,0 +1,95 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//! This class implements an Athena algorithm for evaluating the BDT score for tau jets.
+/*!
+ * Tool for BDT analysis.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TAUJETBDT_H
+#define TAUJETBDT_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/MethodBDT.h"
+#include "TauDiscriminant/MethodCuts.h"
+#include "TauDiscriminant/MethodTransform.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+#include <PathResolver/PathResolver.h>
+#include "xAODTau/TauJet.h"
+
+using namespace std;
+using namespace TauID;
+
+class TauJetBDT: public TauDiscriToolBase
+{
+    public:
+
+        //!< Constructor
+        TauJetBDT(const string& type,
+                const string& name,
+                const IInterface* parent):
+            TauDiscriToolBase(type, name, parent),
+            jetBDTFile(""),
+            jetSigBitsFile(""),
+            jetBkgBitsFile(""),
+            jetSigTransFile(""),
+            jetBkgTransFile(""),
+            jetBDT(NULL),
+            jetSigBits(NULL),
+            jetBkgBits(NULL),
+            jetSigTrans(NULL),
+            jetBkgTrans(NULL)
+    {
+        declareInterface<TauDiscriToolBase>(this);
+
+        declareProperty("jetBDT", this->jetBDTFile);
+        declareProperty("jetSigBits",this->jetSigBitsFile);
+        declareProperty("jetBkgBits",this->jetBkgBitsFile);
+        declareProperty("jetSigTrans",this->jetSigTransFile);
+        declareProperty("jetBkgTrans",this->jetBkgTransFile);
+    }
+
+        //!< Destructor
+        virtual ~TauJetBDT() {}
+
+        /**
+         * @brief The boosted decision trees are built.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode prepare(const TauDetailsManager&);
+
+        /**
+         * @brief Values of the tau parameters are extracted and a boosted decision tree score is retrieved.
+         * @param tauJet a @c xAOD::TauJet pointer to a tau jet candidate.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+
+        /**
+         * @brief Allocated memory is freed.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode finalize();
+
+    private:
+
+        float jetScore;                     //!< Holds the current jet score which is used by a MethodCuts instance to determine if it passes loose, medium, or tight.
+
+        string jetBDTFile;                  //!< The @c string name of the bdt file for jet rejection.
+        string jetSigBitsFile;              //!< The @c string name of the file used to define the loose, medium, and tight cuts on the signal taus for jet rejection.
+        string jetBkgBitsFile;              //!< The @c string name of the file used to define the loose, medium, and tight cuts on the background jets for jet rejection.
+        string jetSigTransFile;
+        string jetBkgTransFile;
+
+        MethodBDT* jetBDT;                  //!< A pointer to the @c MethodBDT used to construct and evaluate BDTs used for jet discrimination.
+        MethodCuts* jetSigBits;             //!< A pointer to the @c MethodCuts used to determine whether the current jet BDT score passes loose, medium, or tight signal cut.
+        MethodCuts* jetBkgBits;             //!< A pointer to the @c MethodCuts used to determine whether the current jet BDT score passes loose, medium, or tight background cut.
+        MethodTransform* jetSigTrans;
+        MethodTransform* jetBkgTrans;
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauLLH.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauLLH.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d7438ff6aa1e30e2e0bc63f3b8b4f842b5f236d
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauLLH.h
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**                                       
+ * file: TauLLH.h
+ *                                        
+ * Author: Martin Flechl (mflechl@cern.ch)
+ */
+
+#ifndef TAULLHRERUN_H
+#define TAULLHRERUN_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/MethodLLH.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+
+using namespace TauID;
+
+class TauLLH: public TauDiscriToolBase
+{
+    public:
+
+        //-----------------------------------------------------------------
+        // Contructor and destructor
+        //-----------------------------------------------------------------
+        TauLLH(const string& type,
+               const string& name,
+               const IInterface* parent):
+            TauDiscriToolBase(type, name, parent)
+        {
+            declareInterface<TauDiscriToolBase>(this);
+            declareProperty( "FileNameTauPDF", m_fileNameTauPDF = "pdfs_tau.root" );
+            declareProperty( "FileNameJetPDF", m_fileNameJetPDF = "pdfs_jet.root");
+            declareProperty( "FileNameLMTCuts", m_fileNameLMTCuts = "LMTCutsLLH.root");
+        }    
+        
+        virtual ~TauLLH() {}
+
+        //-----------------------------------------------------------------
+        // Gaudi algorithm hooks
+        //-----------------------------------------------------------------
+        virtual StatusCode prepare(const TauDetailsManager&);
+        
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+        
+        virtual StatusCode finalize();
+
+    private:
+        
+        std::string m_fileNameTauPDF;
+        std::string m_fileNameJetPDF;
+        std::string m_fileNameLMTCuts;
+
+        MethodLLH* m_defllh; 
+        MethodLLH* m_safellh; 
+};
+
+#endif // TAULLHRERUN_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauMuonVeto.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauMuonVeto.h
new file mode 100644
index 0000000000000000000000000000000000000000..26a15749170476671718acc78b500b9071b7cc78
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauMuonVeto.h
@@ -0,0 +1,44 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TAUMUONVETO_H
+#define TAUMUONVETO_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+
+class TauMuonVeto: public TauDiscriToolBase
+{
+    public:
+
+        //-----------------------------------------------------------------
+        // Contructor and destructor
+        //-----------------------------------------------------------------
+        TauMuonVeto(const string& type,
+                const string& name,
+                const IInterface* parent):
+            TauDiscriToolBase(type, name, parent),
+            detailsManager(0)
+        {
+            declareInterface<TauDiscriToolBase>(this);
+        }
+
+        virtual ~TauMuonVeto() {}
+
+        //-----------------------------------------------------------------
+        // Gaudi algorithm hooks
+        //-----------------------------------------------------------------
+        virtual StatusCode prepare(const TauDetailsManager&);
+        
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+        
+        virtual StatusCode finalize();
+
+    private:
+
+        const TauDetailsManager*  detailsManager;
+};
+
+#endif // TAUMUONVETO_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauPi0BDT.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauPi0BDT.h
new file mode 100644
index 0000000000000000000000000000000000000000..95f6b90b65c2959d571fac85c1679e6442ee192c
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauPi0BDT.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//! This class implements an Athena algorithm for evaluating the BDT score for pi0s.
+/*!
+ * Tool for BDT analysis.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TAUPI0BDT_H
+#define TAUPI0BDT_H
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/MethodBDT.h"
+#include "TauDiscriminant/TauDetailsManager.h"
+#include <string>
+#include <PathResolver/PathResolver.h>
+#include "xAODTau/TauJet.h"
+
+using namespace std;
+using namespace TauID;
+
+class TauPi0BDT: public TauDiscriToolBase
+{
+    public:
+
+        //!< Constructor
+        TauPi0BDT(const string& type,
+                  const string& name,
+                  const IInterface* parent):
+            TauDiscriToolBase(type, name, parent),
+            pi0BDTPrimaryFile(""),
+            pi0BDTSecondaryFile(""),
+            pi0BDTPrimary(NULL),
+            pi0BDTSecondary(NULL)
+    {
+        declareInterface<TauDiscriToolBase>(this);
+
+        declareProperty("pi0BDTPrimary", this->pi0BDTPrimaryFile);
+        declareProperty("pi0BDTSecondary", this->pi0BDTSecondaryFile);
+    }
+
+        //!< Destructor
+        virtual ~TauPi0BDT() {}
+
+        /**
+         * @brief The boosted decision trees are built.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode prepare(const TauDetailsManager&);
+
+        /**
+         * @brief Values of the tau parameters are extracted and a boosted decision tree score is retrieved.
+         * @param tauJet a @c xAOD::TauJet pointer to a tau jet candidate.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode execute(xAOD::TauJet*, FakeTauBits*, FakeTauScores*);
+
+        /**
+         * @brief Allocated memory is freed.
+         * @return @c StatusCode StatusCode::FAILURE if there were problems and StatusCode::SUCCESS otherwise.
+         */
+        virtual StatusCode finalize();
+
+    private:
+
+        string pi0BDTPrimaryFile;           //!< The @c string name of the bdt file for establishing the presence of pi0s
+        string pi0BDTSecondaryFile;         //!< The @c string name of the bdt file for differentiating 2 from 1 pi0s
+
+        MethodBDT* pi0BDTPrimary;           //!< A pointer to the @c MethodBDT used to construct and evaluate BDTs used for establishing the presence of pi0s
+        MethodBDT* pi0BDTSecondary;         //!< A pointer to the @c MethodBDT used to construct and evaluate BDTs used for differentiating 2 from 1 pi0s
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauPi0Clusters.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauPi0Clusters.h
new file mode 100644
index 0000000000000000000000000000000000000000..3922283ac60bbfebabfb9e4b610846c4dbe37c5c
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TauPi0Clusters.h
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/*!
+ * file: TauPi0Clusters.h
+ *
+ * This class provides a handle on the Pi0Finder algorithm, and pass the resultin data to storegate
+ *
+ * Author: Michel Trottier-McDonald (mtm-AT-cern-dot-ch)
+ */
+
+#ifndef TAUPI0CLUSTERS_H
+#define TAUPI0CLUSTERS_H
+
+#include "TauDiscriminant/TauDetails.h"
+#include "xAODTau/TauJet.h"
+#include "TauDiscriminant/Pi0Finder.h"
+
+class TauPi0Clusters
+{
+public:
+
+    static const float LOW_NUMBER;
+    
+    //!< Constructor
+    TauPi0Clusters(const xAOD::TauJet& tauJet);
+    
+    //!< Destructor
+    ~TauPi0Clusters() {}
+
+    //Getters
+    float get_cl1_Pt()  const {return m_cl1_Pt;}
+    float get_cl1_Eta() const {return m_cl1_Eta;}
+    float get_cl1_Phi() const {return m_cl1_Phi;}
+
+    float get_cl2_Pt()  const {return m_cl2_Pt;}
+    float get_cl2_Eta() const {return m_cl2_Eta;}
+    float get_cl2_Phi() const {return m_cl2_Phi;}
+
+    float get_tau_vis_Pt()  const {return m_tau_vis_Pt;}
+    float get_tau_vis_Eta() const {return m_tau_vis_Eta;}
+    float get_tau_vis_Phi() const {return m_tau_vis_Phi;}
+    float get_tau_vis_M()   const {return m_tau_vis_M;}
+
+    
+private:
+    
+    // Data members to return
+
+    // Cluster 1 4-vector
+    float m_cl1_Pt;
+    float m_cl1_Eta;
+    float m_cl1_Phi;
+    
+    // Cluster 2 4-vector
+    float m_cl2_Pt;
+    float m_cl2_Eta;
+    float m_cl2_Phi;
+
+    // Tau 4-vector from pi0s and tracks
+    float m_tau_vis_Pt;
+    float m_tau_vis_Eta;
+    float m_tau_vis_Phi;
+    float m_tau_vis_M;
+
+    // Run Pi0Finder
+    void runPi0Finder(const xAOD::TauJet& tauJet);
+
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Transformation.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Transformation.h
new file mode 100644
index 0000000000000000000000000000000000000000..375c5acf40c95627610d8c7340d7c8a1d67c7f75
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Transformation.h
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TRANSFORMATION_H
+#define TRANSFORMATION_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include "TauDiscriminant/Node.h"
+#include "TauDiscriminant/TreeVector.h"
+
+using namespace std;
+
+class Transformation : public TreeVector {
+
+    public:
+
+        //!< Default Constructor
+        Transformation() {}
+
+        /**
+         * @brief Returns the @c float score for the set of variables and values in @c variableMap.
+         * @param variableMap
+         */
+        float response() const;
+
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TreeReader.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TreeReader.h
new file mode 100644
index 0000000000000000000000000000000000000000..16b2e4ea41d70ebf75b341d95f742a1a3ff927bd
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TreeReader.h
@@ -0,0 +1,118 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TREEREADER_H
+#define TREEREADER_H
+
+#include <map>
+#include <stack>
+#include <vector>
+#include <string>
+#include <utility>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+#include <typeinfo>
+#include "TreeVector.h"
+#include "Node.h"
+#include "TFile.h"
+#ifndef __STANDALONE
+    #include "GaudiKernel/MsgStream.h"
+#endif
+
+using namespace std;
+
+enum Format {
+    ASCII,
+    BINARY,
+    ROOTFILE
+};
+
+enum {
+    ENDOFTREE = -1,
+    LEAF = -2,
+    POINTERLEAF = -3,
+    GRAPH = -4,
+    FUNC = -5,
+    TRANS = -6
+};
+
+class TreeReader {
+
+    public:
+
+        #ifdef __STANDALONE
+        TreeReader(bool _verbose = false):
+            verbose(_verbose),
+            floatVariables(0),
+            intVariables(0)
+        {}
+        #else
+        TreeReader(bool _verbose = false, MsgStream* _log = 0):
+            verbose(_verbose),
+            floatVariables(0),
+            intVariables(0),
+            log(_log)
+        {}
+        #endif
+
+        ~TreeReader(){}
+
+        void setVariables(const map<string,const float*>* _floatVariables,
+                          const map<string,const int*>* _intVariables)
+        {
+            this->floatVariables = _floatVariables;
+            this->intVariables = _intVariables;
+        }
+
+        Node* build(const string& filename, bool checkTree = false);
+
+        void print(string message) const
+        {
+            #ifndef __STANDALONE
+            if (log)
+            {
+                (*log) << "DEBUG" << message << endreq;
+            }
+            else
+            #endif
+                cout << message << endl;
+        }
+
+    private:
+
+        Node* readTree(istream& treeFile,
+                       TFile* rootFile,
+                       Format format,
+                       vector<string>& variableList,
+                       vector<char>& variableTypeList,
+                       unsigned int& numNodes);
+        bool verbose;
+        const map<string,const float*>* floatVariables;
+        const map<string,const int*>* intVariables;
+        #ifndef __STANDALONE
+        MsgStream* log;
+        #endif
+};
+
+/** This templated class is used to convert a string to various data types.
+This is used when tokenizing the BDT input file to build the boosted decision trees.*/
+template <class T>
+inline bool from_string(T& t, const string& s) {
+    istringstream ss(s);
+    return !(ss >> t).fail();
+}
+
+template <class T>
+inline string to_string (const T& t) {
+    stringstream ss;
+    ss << t;
+    return ss.str();
+}
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TreeVector.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TreeVector.h
new file mode 100644
index 0000000000000000000000000000000000000000..96c3141da0df0a048127823eef69c1eb8f4ad098
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/TreeVector.h
@@ -0,0 +1,42 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#ifndef TREEVECTOR_H
+#define TREEVECTOR_H
+
+#include <vector>
+#include <utility>
+#include "TauDiscriminant/Node.h"
+
+using namespace std;
+
+class TreeVector {
+
+    public:
+        
+        //!< Destructor
+        ~TreeVector() {
+            vector<pair<Node*,float> >::iterator it(this->trees.begin());
+            while (it != this->trees.end()) delete (*it++).first;
+        }
+       
+        /**
+         * @brief Adds a decision tree rooted at the node @c root with a weight of @c weight.
+         * @param root a @c Node* pointer to the root node.
+         * @param weight the @c float weight of the decision tree.
+         */
+        void addTree(Node* root, float weight) {
+            this->trees.push_back(pair<Node*,float>(root,weight));
+        }
+
+    protected:
+
+        vector<pair<Node*,float> > trees;
+};
+
+#endif
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Types.h b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Types.h
new file mode 100644
index 0000000000000000000000000000000000000000..65f7b80751e80ae7f7986df9809fb484d73d4a3e
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/Types.h
@@ -0,0 +1,24 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// AUTHOR: Noel Dawe
+#ifndef TYPES_H
+#define TYPES_H
+
+namespace TauID
+{
+    namespace Types
+    {
+        enum MethodType
+        {
+            DUMMY,
+            CUTS,
+            BDT,
+            LLH,
+            TRANSFORM
+        };
+    }
+}
+
+#endif // TYPES_H
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/selection.xml b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..adc974cf6e611b9c0c64287982ba9ecad804adce
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/TauDiscriminant/selection.xml
@@ -0,0 +1,10 @@
+<lcgdict>
+  <enum pattern="TauID::Types::*"/>
+  <variable pattern="TauID::Types::*"/>
+  <class name="TauID::TauIDReader"/>
+  <class name="TauID::MethodCuts"/>
+  <class name="TauID::MethodBDT"/>
+  <class name="TauID::MethodLLH"/>
+  <class name="TauID::MethodDummy"/>
+  <class name="TauID::MethodTransform"/>
+</lcgdict>
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/cmt/Makefile.RootCore b/PhysicsAnalysis/TauID/TauDiscriminant/cmt/Makefile.RootCore
new file mode 100644
index 0000000000000000000000000000000000000000..90fc95518edee8e73356134e84b9eb0204fb6082
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/cmt/Makefile.RootCore
@@ -0,0 +1,5 @@
+PACKAGE          = TauDiscriminant
+PACKAGE_PRELOAD  = Tree Hist Matrix
+PACKAGE_CXXFLAGS = -D__STANDALONE
+
+include $(ROOTCOREDIR)/Makefile-common
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/cmt/Makefile.Standalone b/PhysicsAnalysis/TauID/TauDiscriminant/cmt/Makefile.Standalone
new file mode 100644
index 0000000000000000000000000000000000000000..07edc9e2b58375c128656d313f177fe9c84244dc
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/cmt/Makefile.Standalone
@@ -0,0 +1,108 @@
+# Author: Noel Dawe (Noel.Dawe@cern.ch)
+# TauDiscriminant standalone makefile
+
+# --- External configuration ----------------------------------
+ifeq ($(wildcard $(ROOTSYS)/test/Makefile.arch),)
+    include $(ROOTSYS)/etc/Makefile.arch
+else
+    include $(ROOTSYS)/test/Makefile.arch
+endif
+
+# -------------------------------------------------------------
+# General flags
+# -------------------------------------------------------------
+PACKAGE    = TauDiscriminant
+OUTPUTDIR  = ../StandAlone
+SRCDIR     = ../Root
+HDIR       = ../$(PACKAGE)
+
+# Get these from Makefile.arch above
+#CC         = g++
+#CCFLAGS    = -g -m32 -fPIC -Wall -W -Woverloaded-virtual -Wno-parentheses -Wno-unused-parameter -Wno-unused-variable
+#LDFLAGS    = -g -m32 -fPIC 
+
+MFLAGS     = -MM -Wall -W -Woverloaded-virtual
+INCLUDES   += -I${ROOTSYS}/include -I.. -I$(HDIR) -I$(SRCDIR)
+CPPFLAGS   += -D__STANDALONE
+
+# Need these to avoid loading dependent libraries when ROOT starts
+LINKLIBS = -L${ROOTSYS}/lib -lHist -lMatrix
+
+# -------------------------------------------------------------
+# ROOT Cint
+# -------------------------------------------------------------
+CINT       = taudiscriminantcint
+LDEFFILE   = $(SRCDIR)/LinkDef.h
+CINTFILE   = $(HDIR)/TauDiscriminantCint.cxx
+CINTFILEH  = $(HDIR)/TauDiscriminantCint.h
+CINTOBJ    = $(HDIR)/TauDiscriminantCint.o
+
+SRC        = Types.cxx TauIDReader.cxx TauDetailsManagerStandalone.cxx MethodBase.cxx MethodDummy.cxx MethodTransform.cxx MethodBDT.cxx MethodCuts.cxx MethodLLH.cxx TreeVector.cxx Node.cxx TreeReader.cxx BoostedDecisionTree.cxx CommonLikelihood.cxx CutsDecisionTree.cxx Transformation.cxx
+SOURCES    = $(addprefix $(SRCDIR)/,$(SRC)) $(CINTFILE)
+HDR   	   = $(patsubst %.cxx,%.h,$(SRC))
+HEADERS    = $(addprefix $(HDIR)/,$(HDR))
+OBJECTS    = $(patsubst %.cxx,%.o,$(SOURCES))
+DEPS       = $(patsubst %.h,%.d,$(HEADERS))
+
+DICTS      = $(HDIR)/$(PACKAGE)Dict.h
+SELECTION  = $(HDIR)/selection.xml
+
+# -------------------------------------------------------------
+# Libraries
+# -------------------------------------------------------------
+SHLIBFILE  = $(OUTPUTDIR)/lib$(PACKAGE).so
+
+ifeq ($(PLATFORM),macosx)
+EXTRALDFLAGS = -install_name @rpath/$(SHLIBFILE)
+endif
+
+# get libraries of ROOT
+define ldlinksuffixROOT
+   $(addsuffix $(LDLINKSUFFIX),$(Lib)) $(shell if [ "$(findstring -Ldlink2,$(OPTIONS))" ]; then echo $(addsuffix _pkgid_$(ROOTVER),$(Lib)); fi)
+endef
+
+# -------------------------------------------------------------
+# Compilation
+# -------------------------------------------------------------
+
+default: shlib
+
+# Implicit rule making all dependency Makefiles included at the end of this makefile
+%.d: %.cxx $(HEADERS)
+	@echo "Making $@"
+	@set -e; $(CC) $(MFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) $< \
+		| awk '{ sub("^$(notdir $*).o:","$*.o $@:") ; print }' > $@ ;\
+		[ -s $@ ] || rm -f $@
+
+# Implicit rule to compile all classes
+%.o : %.cxx
+	@echo "Compiling $<"
+	@$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $< -o $*.o 
+
+# Rule to make ROOTCINT output file
+$(CINTOBJ) : $(HEADERS) $(LDEFFILE)
+	@echo "Running rootcint"
+	@$(ROOTSYS)/bin/rootcint -f $(CINTFILE) -c -p  $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) $(HEADERS) $(LDEFFILE)
+	@echo "Compiling $(CINTFILE)"
+	@$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(CINTFILE) -o $@
+
+rootcint : $(HEADERS) $(LDEFFILE)
+	@echo "Running rootcint"
+	$(ROOTSYS)/bin/rootcint -f $(CINTFILE) -c -p  $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) $(HEADERS) $(LDEFFILE)
+
+# Rule to combine objects into a shared library
+$(SHLIBFILE): $(OBJECTS)
+	@echo "Linking $(SHLIBFILE)"
+	@mkdir -p $(OUTPUTDIR)
+	@rm -f $(SHLIBFILE)
+	@$(LD) $(CXXFLAGS) $(SOFLAGS) $(LINKLIBS) $(EXTRALDFLAGS) $(OBJECTS) -o $(SHLIBFILE) 
+	@rm -f $(OUTPUTDIR)/$(PACKAGE)Lib.so
+	@ln -s $(SHLIBFILE) $(OUTPUTDIR)/$(PACKAGE)Lib.so 
+
+-include $(DEPS)
+
+taudiscriminantcint: $(CINTOBJ)
+shlib: $(SHLIBFILE)
+
+clean:
+	@rm -f ../*/*.o ../*/*.d
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/cmt/requirements b/PhysicsAnalysis/TauID/TauDiscriminant/cmt/requirements
new file mode 100644
index 0000000000000000000000000000000000000000..fd32c2464cc939cf46159454494a6876a9e8f725
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/cmt/requirements
@@ -0,0 +1,54 @@
+package TauDiscriminant
+author Marcin Wolter, Noel Dawe
+
+use AtlasPolicy         AtlasPolicy-*
+use GaudiInterface      GaudiInterface-*     External
+use xAODTau		xAODTau-*	     Event/xAOD
+use SGTools             SGTools-*            Control
+use DataModel           DataModel-*          Control
+use AthenaKernel        AthenaKernel-*       Control
+use PathResolver        PathResolver-*       Tools
+use StoreGate           StoreGate-*          Control
+use AtlasROOT           AtlasROOT-*          External
+use AthenaBaseComps     AthenaBaseComps-*    Control
+apply_tag ROOTBasicLibs
+apply_tag ROOTMathLibs
+
+macro_append TauDiscriminant_shlibflags "-L$(ROOTSYS)/lib -lCore -lCint -lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix -lPhysics -lm -ldl -lpthread -rdynamic"
+
+# suggested by RootCore
+#library TauDiscriminantLib "../Root/*.cxx"
+#apply_pattern named_installed_library library=TauDiscriminantLib
+
+apply_pattern dual_use_library files="../Root/*.cxx ../src/*.cxx"
+
+apply_pattern declare_joboptions files="../share/*.py"
+
+apply_pattern declare_runtime files="../share/*.root ../share/*.dat ../share/*.txt ../share/*.bin"
+
+apply_pattern declare_python_modules files="../python/*.py"
+
+# debug
+# private
+# macro cppdebugflags '$(cppdebugflags_s)'
+# macro_remove componentshr_linkopts "-Wl,-s"
+
+private
+use AtlasCLHEP          AtlasCLHEP-*            External
+use AnalysisUtils       AnalysisUtils-*      PhysicsAnalysis/AnalysisCommon
+#use CaloEvent           CaloEvent-*          Calorimeter
+use FourMomUtils        FourMomUtils-*       Event
+#use Particle            Particle-*           Reconstruction
+use TrkEventPrimitives  TrkEventPrimitives-* Tracking/TrkEvent
+#use TrkTrackSummary     TrkTrackSummary-*    Tracking/TrkEvent
+use xAODTracking	xAODTracking-*	     Event/xAOD	
+#use VxVertex            VxVertex-*           Tracking/TrkEvent
+use AtlasReflex   AtlasReflex-*   External -no_auto_imports
+use CaloUtils               CaloUtils-*                 Calorimeter
+use xAODCaloEvent	xAODCaloEvent-*	     Event/xAOD
+use CaloGeoHelpers	CaloGeoHelpers-*	Calorimeter
+
+apply_pattern lcgdict dict=TauDiscriminant selectionfile=selection.xml headerfiles="../TauDiscriminant/TauDiscriminantDict.h"
+
+macro_append ROOT_linkopts " -lPyROOT"
+end_private
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/python/TauDiscriGetter.py b/PhysicsAnalysis/TauID/TauDiscriminant/python/TauDiscriGetter.py
new file mode 100644
index 0000000000000000000000000000000000000000..0af8405649990fb636b6b55447770a4e3159081d
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/python/TauDiscriGetter.py
@@ -0,0 +1,168 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# AUTHOR:   Marcin.Wolter@cern.ch
+# CREATED:  20 March 2008
+#
+# 23 Nov 2010: cleaning up (Noel Dawe)
+
+from AthenaCommon.Logging import logging
+from AthenaCommon.AlgSequence import AlgSequence
+#from AthenaCommon.AppMgr import ToolSvc
+
+from RecExConfig.Configured import Configured
+import traceback
+
+def singleton(cls):
+
+    log = logging.getLogger('%s::__init__'% cls.__name__)
+    instances = {}
+    def getinstance(*args, **kwargs):
+        if cls in instances:
+            log.warning("Attempting to construct more than one %s. Returning the singleton."% cls.__name__)
+            return instances[cls]
+        obj = cls(*args, **kwargs)
+        instances[cls] = obj
+        return obj
+    return getinstance
+
+# to disable singleton behaviour comment out the following line:
+@singleton
+class TauDiscriGetter(Configured):
+
+    def __init__(self, name = "TauDiscriminant",
+                       container = "TauRecContainer",
+                       sequence = None,
+                       do_upstream_algs = False,
+                       do_only_fakebits = False,
+                       do_Pi0           = True, # Temporary, turns on the dumping of pi0 scores
+                       msglevel = 3):
+
+        self.sequence = sequence
+        if sequence is None:
+            self.sequence = AlgSequence()
+        self.name = name
+        self.container = container
+        self.upstream_algs = do_upstream_algs
+        self.only_fakebits = do_only_fakebits # temporary hack
+        self.do_Pi0 = do_Pi0 # Temporary, turns on the dumping of pi0 scores
+        self.msglevel = msglevel
+        Configured.__init__(self)
+
+    def configure(self):
+
+        from AthenaCommon.AppMgr import ToolSvc
+
+        mlog = logging.getLogger('TauDiscriGetter::configure:')
+
+        try:
+            from TauDiscriminant.TauDiscriminantConf import TauDiscriBuilder
+            TauDiscriBuilder.OutputLevel = self.msglevel
+            tauDiscriBuilder = TauDiscriBuilder(
+                self.name,
+		        container = self.container)
+        except Exception:
+            mlog.error("could not find TauDiscriBuilder")
+            print traceback.format_exc()
+            return False
+
+        tools = []
+
+        if not self.only_fakebits:
+            """
+            Cut-based tau-jet identification
+            """
+#            try:
+#                from TauDiscriminant.TauDiscriminantConf import TauCuts
+#                TauCuts.OutputLevel = self.msglevel
+#                tauCuts = TauCuts(cuts = "cuts.txt")
+#                tools.append(tauCuts)
+#            except Exception:
+#                mlog.error("could not find TauCuts in TauDiscriminant")
+#                print traceback.format_exc()
+#                return False
+            """
+            Cut-based electron veto
+            """
+            try:
+                from TauDiscriminant.TauDiscriminantConf import TauCutsEleVeto
+                TauCutsEleVeto.OutputLevel = self.msglevel
+                tauCutsEleVeto = TauCutsEleVeto()
+                tools.append(tauCutsEleVeto)
+            except Exception:
+                mlog.error("could not find TauCutsEleVeto in TauDiscriminant")
+                print traceback.format_exc()
+                return False
+            """
+            Muon veto
+            """
+            try:
+                from TauDiscriminant.TauDiscriminantConf import TauMuonVeto
+                TauMuonVeto.OutputLevel = self.msglevel
+                tauMuonVeto = TauMuonVeto()
+                tools.append(tauMuonVeto)
+            except Exception:
+                mlog.error("could not find TauMuonVeto in TauDiscriminant")
+                print traceback.format_exc()
+                return False
+
+
+            try:
+                from TauDiscriminant.TauDiscriminantConf import TauPi0BDT
+                TauPi0BDT.OutputLevel = self.msglevel
+                taupi0BDT = TauPi0BDT(pi0BDTPrimary="pi0Primary.BDT.bin",
+                                      pi0BDTSecondary="pi0Secondary.BDT.bin")
+                tools.append(taupi0BDT)
+            except Exception:
+                mlog.error("could not find TauPi0BDT in TauDiscriminant")
+                print traceback.format_exc()
+                return False
+
+
+
+            """
+            Likelihood tau-jet identification
+            """
+            try:
+                from TauDiscriminant.TauDiscriminantConf import TauLLH
+                TauLLH.OutputLevel = self.msglevel
+                tauLLH = TauLLH()
+                tools.append(tauLLH)
+            except Exception:
+                mlog.error("could not find TauLLH in TauDiscriminant")
+                print traceback.format_exc()
+                return False
+            """
+            BDT tau-jet identification
+            """
+            try:
+                from TauDiscriminant.TauDiscriminantConf import TauJetBDT
+                TauJetBDT.OutputLevel = self.msglevel
+                taujetBDT = TauJetBDT(jetBDT="jet.BDT.bin",
+                                      jetSigTrans="sig.trans.jet.BDT.root",
+                                      jetBkgTrans="",
+                                      jetSigBits="sig.bits.jet.BDT.txt",
+                                      jetBkgBits="bkg.bits.jet.BDT.txt")
+                tools.append(taujetBDT)
+            except Exception:
+                mlog.error("could not find TauJetBDT in TauDiscriminant")
+                print traceback.format_exc()
+                return False
+            """
+            BDT electron veto
+            """
+            try:
+                from TauDiscriminant.TauDiscriminantConf import TauEleBDT
+                TauEleBDT.OutputLevel = self.msglevel
+                taueleBDT = TauEleBDT(eleBDT="ele.BDT.bin",
+                                      eleBits="", eleBitsRoot="cuts.eBDT.root")
+                tools.append(taueleBDT)
+            except Exception:
+                mlog.error("could not find TauEleBDT in TauDiscriminant")
+                print traceback.format_exc()
+                return False
+
+
+        tauDiscriBuilder.tools = tools
+
+        self.sequence += tauDiscriBuilder
+        return True
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/TauEnergyCalibration_jobOption.py b/PhysicsAnalysis/TauID/TauDiscriminant/run/TauEnergyCalibration_jobOption.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed4a1ba652eee55a4a897d38bd08cba69e030ae3
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/TauEnergyCalibration_jobOption.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+f = open('input.txt', 'r')
+inputFiles = f.readlines()
+f.close()
+inputFiles = [item.strip() for item in inputFiles]
+
+from AthenaCommon.AthenaCommonFlags import jobproperties as jp
+jp.AthenaCommonFlags.FilesInput = inputFiles
+
+include('AthenaPython/read_file.py')
+
+from AthenaCommon.AppMgr import theApp, ServiceMgr
+from AthenaCommon.AlgSequence import AlgSequence
+job = AlgSequence()
+
+import AthenaPoolCnvSvc.ReadAthenaPool
+theApp.EvtMax = -1
+
+if True:  # run only TauEnergyCalibration
+    from TauDiscriminant.TauDiscriminantConf import TauEnergyCalibration
+    TauEnergyCalibration.OutputLevel = DEBUG
+    tauEnergyCalibration = TauEnergyCalibration('TauEnergyCalibration')
+    tauEnergyCalibration.tauContainerKey = 'TauRecContainer'
+    tauEnergyCalibration.calibrationFile = 'EnergyCalibration.root'
+    tauEnergyCalibration.vertexContainerKey = 'VxPrimaryCandidate'
+    job += tauEnergyCalibration
+else:
+    from TauDiscriminant.TauDiscriGetter import TauDiscriGetter
+    TauDiscriGetter(do_upstream_algs = True, msglevel = DEBUG)
+
+
+
+
+
+
+
+
+
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/TauIDAnalysisExample.C b/PhysicsAnalysis/TauID/TauDiscriminant/run/TauIDAnalysisExample.C
new file mode 100644
index 0000000000000000000000000000000000000000..a9bdb038148bc2e2e6fcb240e010617d607a2d43
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/TauIDAnalysisExample.C
@@ -0,0 +1,68 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <string>
+#include "TFile.h"
+#include "TTree.h"
+
+using namespace std;
+
+void TauIDAnalysisExample(string filename)
+{
+    if (gSystem->Load("libTauDiscriminant.so") != 0)
+    {
+        cout << "Could not load libTauDiscriminant.so" << endl;
+    }
+    using namespace TauID;
+    
+    TFile* file = TFile::Open(filename.c_str());
+    if (!file)
+    {
+        cout << "Could not open " << filename << endl;
+        return;
+    }
+    TTree* tree = (TTree*)file->Get("tauPerf");
+    if (!tree)
+    {
+        cout << "Could not find tree named tauPerf" << endl;
+        return;
+    }
+    MethodBDT* bdt = new MethodBDT("MyBDT");
+    float et;
+    int numTrack;
+    float emRadius;
+    float centFrac;
+    float trkAvgDist;
+    float etOverPtLeadTrk;
+    float emFractionAtEMScale;
+    float massTrkSys;
+    float topoInvMass;
+    float trFlightPathSig;
+    int numVertices;
+
+    bdt->addVariable("numTrack",&numTrack,"I");
+    bdt->addVariable("emRadius",&emRadius,"F");
+    bdt->addVariable("centFrac",&centFrac,"F");
+    bdt->addVariable("trkAvgDist",&trkAvgDist,"F");
+    bdt->addVariable("etOverPtLeadTrk",&etOverPtLeadTrk,"F");
+    bdt->addVariable("emFractionAtEMScale",&emFractionAtEMScale,"F");
+    bdt->addVariable("massTrkSys",&massTrkSys,"F");
+    bdt->addVariable("topoInvMass",&topoInvMass,"F");
+    bdt->addVariable("trFlightPathSig",&trFlightPathSig,"F");
+    bdt->addVariable("numVertices",&numVertices,"I");
+    if (!bdt->build("../share/jet.BDT.bin"))
+    {
+        cout "Could not build BDT" << endl;
+        return;
+    }
+
+    for(unsigned int ievent(0); ievent < tree->GetEntries(); ++ievent)
+    {
+        tree->GetEntry(ievent);
+        for (unsigned int itau(0); itau < (*tree)["tau_n"]; ++itau)
+        {
+            et = (*tree)["tau_Et"][itau];
+        }
+    }
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/TauIDAnalysisExample.py b/PhysicsAnalysis/TauID/TauDiscriminant/run/TauIDAnalysisExample.py
new file mode 100644
index 0000000000000000000000000000000000000000..975bedcd81406c21c8456b523cf81d91547a3ff8
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/TauIDAnalysisExample.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+################################################################################
+#
+# A simple script demonstrating how to use MethodBDT, MethodCuts, etc. in python
+#
+################################################################################
+
+import sys
+import ROOT
+ROOT.gSystem.Load("libTauDiscriminant.so")
+from ROOT import TauID
+from array import array
+from itertools import izip
+import math
+
+ROOT.gInterpreter.GenerateDictionary("vector<vector<float> >", "vector")
+ROOT.gInterpreter.GenerateDictionary("vector<vector<int> >", "vector")
+
+dphi = lambda phi1, phi2 : abs(math.fmod((math.fmod(phi1, 2*math.pi) - math.fmod(phi2, 2*math.pi)) + 3*math.pi, 2*math.pi) - math.pi)
+
+dR = lambda eta1, phi1, eta2, phi2: math.sqrt((eta1 - eta2)**2 + dphi(phi1, phi2)**2)
+
+filename = sys.argv[1]
+
+file = ROOT.TFile.Open(filename)
+tree = file.Get("tauPerf")
+
+bdt = TauID.MethodBDT()
+bdtbits = TauID.MethodCuts()
+cuts = TauID.MethodCuts()
+
+variables = {
+    "numTrack"                        : array('i',[0]),
+    "num_pileup_and_primary_vertices" : array('i',[0]),
+    "centFrac"                        : array('f',[0]),
+    "etOverpTLeadTrk"                 : array('f',[0]),
+    "trkAvgDist"                      : array('f',[0]),
+    "effTopoInvMass"                  : array('f',[0]),
+    "ipSigLeadTrk"                    : array('f',[0]),
+    "lead3ClusterEOverAllClusterE"    : array('f',[0]),
+    "numWideTrack"                    : array('i',[0]),
+    "calRadius"                       : array('f',[0]),
+    "drMax"                           : array('f',[0]),
+    "massTrkSys"                      : array('f',[0]),
+    "trFlightPathSig"                 : array('f',[0]),
+    "pt"                              : array('f',[0]),
+    "BDT"                             : array('f',[0]),
+    "CALO_ISO_CORRECTED"              : array('f',[0])
+}
+
+for var, value in variables.items():
+    bdt.addVariable(var.upper(), value, value.typecode.upper())
+    bdtbits.addVariable(var.upper(), value, value.typecode.upper())
+    cuts.addVariable(var.upper(), value, value.typecode.upper())
+
+if not bdt.build("../share/jet.BDT.bin"):
+    sys.exit(1)
+
+if not bdtbits.build("../share/sig.bits.jet.BDT.txt"):
+    sys.exit(1)
+
+if not cuts.build("../share/cuts.txt"):
+    sys.exit(1)
+
+for entry in xrange(tree.GetEntries()):
+    tree.GetEntry(entry)
+    # update event variables
+    nvtx = len(filter(lambda v: (tree.vxp_type[v]==1 and tree.vxp_nTracks[v]>=4) or \
+                                (tree.vxp_type[v]==3 and tree.vxp_nTracks[v]>=2), range(tree.vxp_n)))
+    variables["num_pileup_and_primary_vertices"][0] = nvtx
+
+    for itau in xrange(tree.tau_n):
+        # update tau variables
+        variables["numTrack"][0] = tree.tau_seedCalo_numTrack[itau]
+        variables["centFrac"][0] = tree.tau_seedCalo_centFrac[itau]
+        variables["etOverpTLeadTrk"][0] = tree.tau_etOverPtLeadTrk[itau]
+        variables["trkAvgDist"][0] = tree.tau_seedCalo_trkAvgDist[itau]
+        variables["effTopoInvMass"][0] = tree.tau_effTopoInvMass[itau]
+        variables["ipSigLeadTrk"][0] = tree.tau_ipSigLeadTrk[itau]
+        variables["numWideTrack"][0] = tree.tau_seedCalo_wideTrk_n[itau]
+        variables["calRadius"][0] = tree.tau_calcVars_calRadius[itau]
+        variables["drMax"][0] = tree.tau_calcVars_drMax[itau]
+        variables["massTrkSys"][0] = tree.tau_massTrkSys[itau]
+        variables["trFlightPathSig"][0] = tree.tau_trFlightPathSig[itau]
+        variables["pt"][0] = tree.tau_pt[itau]
+
+        # calculate lead3ClusterEOverAllClusterE
+        clusterE = sorted(tree.tau_cluster_E[itau], reverse=True)
+        totE = sum(clusterE)
+        variables["lead3ClusterEOverAllClusterE"][0] = sum(clusterE[:3]) / totE if totE > 0 else -1111.
+
+        # calculate CALO_ISO_CORRECTED
+        jvf = tree.tau_jet_jvtxf[itau]
+        sumPtTrk = tree.tau_jet_sumPtTrk[itau]
+        pt_pileup = (1. - jvf) * sumPtTrk
+        calo_iso = 0.
+        for E, eta, phi in izip(tree.tau_cluster_E[itau], tree.tau_cluster_eta[itau], tree.tau_cluster_phi[itau]):
+            if .2 <= dR(tree.tau_seedCalo_eta[itau], tree.tau_seedCalo_phi[itau], eta, phi) < .4:
+                calo_iso += E / math.cosh(eta)
+        max_pileup_correction = 4000.
+        alpha = 1.
+        pileup_correction = alpha * pt_pileup
+        if pileup_correction > max_pileup_correction:
+            pileup_correction = max_pileup_correction
+        calo_iso_corrected = calo_iso - pileup_correction
+        variables["CALO_ISO_CORRECTED"][0] = calo_iso_corrected
+        
+        bdtScore = bdt.response()
+        variables["BDT"][0] = bdtScore
+
+        print "BDT Score: %.3f"% bdtScore
+        print "BDT Loose: %i"% bdtbits.response(0)
+        print "BDT Medium: %i"% bdtbits.response(1)
+        print "BDT Tight: %i"% bdtbits.response(2)
+        print "Cuts Loose: %i"% cuts.response(0)
+        print "Cuts Medium: %i"% cuts.response(1)
+        print "Cuts Tight: %i"% cuts.response(2)
+        print "-"*30
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/jobOptions.py b/PhysicsAnalysis/TauID/TauDiscriminant/run/jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..ffcac520e0056e7d0f674e4f140d98f120ebc272
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/jobOptions.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+"""
+These minimal job options are for testing only!!!
+"""
+
+import AthenaPoolCnvSvc.ReadAthenaPool
+#ServiceMgr.EventSelector.InputCollections = ['/tmp/simonyan/mc11_7TeV.106052.PythiaZtautau.merge.AOD.e825_s1324_s1300_r2731_r2700_tid518036_00/AOD.518036._000301.pool.root.1']
+ServiceMgr.EventSelector.InputCollections = ['inputAODFile=/afs/cern.ch/work/p/pmalecki/mc12_8TeV.147818.Pythia8_AU2CTEQ6L1_Ztautau.merge.AOD.e1176_s1479_s1470_r3553_r3549_tid779134_00/AOD.779134._000258.pool.root.1']
+
+
+
+from RecExConfig.RecFlags import rec
+
+rec.readRDO=False
+rec.readESD=False # set true for ESD reading
+rec.readAOD=True # set false for ESD reading
+rec.doWriteAOD=False
+rec.doWriteESD=False
+rec.doWriteTAG=False
+rec.doCBNT=False
+rec.doHist=False
+rec.doTruth=False
+rec.AutoConfiguration=['everything']
+#rec.doFloatingPointException=True
+
+include ("RecExCommon/RecExCommon_topOptions.py")
+
+from AthenaCommon.AppMgr import theApp
+from TauDiscriminant.TauDiscriGetter import TauDiscriGetter
+TauDiscriGetter(msglevel = VERBOSE)
+
+theApp.EvtMax = 500
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/minJobOptions.py b/PhysicsAnalysis/TauID/TauDiscriminant/run/minJobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..703ed0be5a8301de6ca807296ba0bdf2378c7ca3
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/minJobOptions.py
@@ -0,0 +1,24 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+"""
+These minimal job options are for testing only!!!
+"""
+import AthenaPoolCnvSvc.ReadAthenaPool
+
+# lxplus
+ServiceMgr.EventSelector.InputCollections = \
+    ['/scratchdisk2/end/data/valid1.105200.T1_McAtNlo_Jimmy.recon.AOD.e603_s932_r1588_tid172295_00/AOD.172295._000154.pool.root.1']
+
+# SFU T3
+#ServiceMgr.EventSelector.InputCollections = \
+#    ['/cluster/data03/endw/ntuples/AOD/valid1.105200.T1_McAtNlo_Jimmy.recon.AOD.e603_s932_r1588_tid172295_00/AOD.172295._000001.pool.root.1']
+
+from AthenaCommon.AppMgr import theApp
+from TauDiscriminant.TauDiscriGetter import TauDiscriGetter
+
+#TauDiscriGetter(do_upstream_algs = True, msglevel = VERBOSE)
+#TauDiscriGetter(do_upstream_algs = False, do_only_fakebits = True, msglevel = VERBOSE)
+
+TauDiscriGetter(msglevel=VERBOSE, do_Pi0=True)
+
+theApp.EvtMax = 10
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/tauIDReader_LLH.py b/PhysicsAnalysis/TauID/TauDiscriminant/run/tauIDReader_LLH.py
new file mode 100755
index 0000000000000000000000000000000000000000..f66260ee9c7d560dd17082b874a98cfbdcd91b51
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/tauIDReader_LLH.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+#File tauLikelihood_rerunOnDPD.py
+#by Martin.Flechl@cern.ch, Marcin.Wolter@cern.ch
+#based on Noel's example
+#
+#usage: tauIDReader_LLH.py D3PD_file_name.root
+import PyCintex
+import ROOT
+import os
+import sys
+
+#ifile="DPD.root"
+ifile=sys.argv[1];
+ofile="FOR_DPD.root"
+
+inputType=0 #0 TauDPD, 1 small TauDPD, 2 Dugan's tiny D4PD
+
+
+if (inputType==0):
+  m_treename="tauPerf"
+  m_vartypeF="VF"
+  m_vartypeI="VI"
+  m_authortype="VI"
+if (inputType==1):
+  m_treename="tauPerfSmall"
+  m_vartypeF="VF"
+  m_vartypeI="VI"
+  m_authortype="VI"
+if (inputType==2):
+  m_treename="D4PD"
+  m_vartypeF="F"
+  m_vartypeI="I"
+  m_authortype="F"
+
+print "Setting up..."
+output = ROOT.TFile.Open(ofile,"recreate")
+### mw app = ROOT.TauID.TauIDReader(output,True)
+app = ROOT.TauID.TauIDReader(True)
+app.setOutput(output,"")
+
+#Commented out variables are not needed when running the safeLLH
+#Comment in to rerun the full LLH
+variables = [
+              ("author","tau_author",m_authortype),
+              ("eta","tau_eta",m_vartypeF),
+              ("numTrack","tau_numTrack",m_vartypeI),
+              ("et","tau_Et",m_vartypeF),
+              ("nPi0","tau_nPi0",m_vartypeI),
+              ("NUM_PILEUP_AND_PRIMARY_VERTICES","evt_calcVars_numGoodVertices","I"), # number of good vertices
+              ("vxp_n","vxp_n","I"), # not quite correct, these are all vertices
+              ("vxp_nTracks","vxp_nTracks","VI"),
+#              ("emRadius","tau_seedCalo_EMRadius",m_vartypeF), #0
+#              ("isolFrac","tau_seedCalo_isolFrac",m_vartypeF), #1
+#              ("stripWidth2","tau_seedCalo_stripWidth2",m_vartypeF), #2
+#              ("nStrip","tau_seedCalo_nStrip",m_vartypeI), #3
+#              ("etHad2etTracks","tau_calcVars_etHadSumPtTracks",m_vartypeF), #4
+#              ("etEM2etTracks","tau_calcVars_etEMSumPtTracks",m_vartypeF), #5
+#              ("etTracks2et","tau_calcVars_sumTrkPt",m_vartypeF), #6, code divides the quantity by et!!
+#              ("emFractionCalib","tau_calcVars_emFracCalib",m_vartypeF), #7
+#              ("emFractionAtEMScale","tau_calcVars_emFracCalib",m_vartypeF), #7 not quite correct, but not in current D3PD
+#              ("etOverPtLeadTrk","tau_etOverPtLeadTrk",m_vartypeF), #8
+#              ("dRmin","tau_calcVars_drMin",m_vartypeF), #9
+              ("dRmax","tau_calcVars_drMax",m_vartypeF), #10
+#              ("trkWidth2","tau_trkWidth2",m_vartypeF), #11
+              ("massTrkSys","tau_massTrkSys",m_vartypeF), #12
+#              ("nIsolTrk","tau_seedTrk_nIsolTrk",m_vartypeI), #13
+#              ("MVisEflow","tau_m",m_vartypeF), #14
+#              ("ipZ0sinThetaSigLeadTrk","tau_ipZ0SinThetaSigLeadTrk",m_vartypeF), #15
+#              ("ipSigLeadLooseTrk","tau_ipSigLeadLooseTrk",m_vartypeF), #16
+              ("trFlightPathSig","tau_trFlightPathSig",m_vartypeF), #17
+              ("centFrac","tau_seedCalo_centFrac",m_vartypeF), #18
+#              ("numEffClus","tau_calcVars_numEffTopoClusters",m_vartypeF), #19
+              ("trkAvgDist","tau_seedCalo_trkAvgDist",m_vartypeF), #20
+#              ("topoInvMass","tau_topoInvMass",m_vartypeF), #21
+	      ("calRadius","tau_calcVars_calRadius",m_vartypeF), #22 
+	      
+	      ("lead2ClusterEOverAllClusterE","tau_calcVars_lead2ClusterEOverAllClusterE",m_vartypeF), #27 
+	      ("numWideTrack","tau_seedCalo_wideTrk_n",m_vartypeI), #50 missing
+	      
+	      
+            ]
+
+for varName,branchName,type in variables:
+    app.addVariable(varName,branchName,type)
+
+# "CUTS" or "BDT", name of cuts/BDT file, number of responses
+# (i.e. 3 for cuts (loose, medium, tight), 1 for BDT)
+print "Finding PDF files"
+filePDFtau = "pdfs_tau.root"
+filePDFjet = "pdfs_jet.root"
+fileLMTCuts = "LMTCutsLLH.root"
+dataPathList = os.environ['DATAPATH'].split(os.pathsep)
+dataPathList.insert(0, os.curdir)
+from AthenaCommon.Utils.unixtools import FindFile
+fileNameTau = FindFile(filePDFtau, dataPathList, os.R_OK )
+fileNameJet = FindFile(filePDFjet, dataPathList, os.R_OK )
+fileNameLMTCuts = FindFile(fileLMTCuts, dataPathList, os.R_OK )
+if (not fileNameTau):
+  print "Input PDF not found"
+print "Tau PDF file name: ", fileNameTau
+print "Jet PDF file name: ", fileNameJet
+fileNames=fileNameTau+","+fileNameJet+","+fileNameLMTCuts
+#print "String: ", fileNames
+      
+print "Booking Method: LLH"
+### mw app.bookMethod("LLH","llhsafe",fileNames,4)
+app.bookMethod(ROOT.TauID.Types.LLH,"llhsafe",fileNames,4)
+#app.bookMethod("LLH","llhdef",fileNames,4)
+#app.bookMethod("LLH","llhall",fileNames,4)
+
+print "Opening input file"
+input = ROOT.TFile.Open(ifile)
+tree = input.Get(m_treename)
+
+print "Obtaining Discriminant ",tree,m_treename," ",ifile
+# mw llhTree = app.classify(tree,"taullh","")
+llhTree = app.classify(tree,"taullh",True)
+
+llhTree.AddFriend(m_treename,ifile)
+output.cd()
+
+print "Writing output",output.GetName()
+llhTree.Write()
+
+output.Close()
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/run/tauid-redo b/PhysicsAnalysis/TauID/TauDiscriminant/run/tauid-redo
new file mode 100755
index 0000000000000000000000000000000000000000..f48f88d86ae136fdcf133bec7221e23ea15f64d1
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/run/tauid-redo
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+
+from optparse import OptionParser
+
+parser = OptionParser(usage="%prog [options] file1.root file2.root ...")
+parser.add_option("-t","--tree", action="store", type="str", dest="treename",
+                  default="tau", help="name of tree in D3PD")
+parser.add_option("-m","--methods", action="store", type="str", dest="methods",
+                  default="BDT,BDTE,PI0P,PI0S,LLH", help="list of methods to run separated by commas")
+parser.add_option("-d","--derived-methods", action="store", type="str", dest="extramethods",
+                  default="BBDTCUTS,SBDTCUTS", help="list of derived methods to run separated by commas, set to NONE if you wish to skip these")
+parser.add_option("--clone", action="store_true", dest="clonetree",
+                  default=False, help="copy all branches from each input tree into each output tree")
+parser.add_option("--friend", action="store_true", dest="friendtree",
+                  default=False, help="make each input tree a friend of each output tree")
+parser.add_option("--idir", action="store", type="str", dest="inputdir",
+                  default="", help="the directory in which the input tree is located in each ROOT file")
+parser.add_option("--odir", action="store", type="str", dest="outputdir",
+                  default="", help="the directory in which to write the output tree in each output ROOT file")
+parser.add_option("--flat", action="store_true", dest="flat",
+                  default=False, help="the ntuples are flat (D4PD)")
+parser.add_option("-v","--verbose", action="store_true", dest="verbose",
+                  default=False, help="show verbose output")
+options, args = parser.parse_args()
+
+import sys
+
+if not args:
+    print "specify at least one input ROOT file"
+    sys.exit(1)
+
+try:
+    import ROOT
+except:
+    sys.exit("Could not import ROOT module. Is PyROOT installed?")
+
+try:
+    import PyCintex
+    from ROOT import TauID
+except:
+    try:
+        ROOT.gSystem.Load("libTauDiscriminant.so")
+        from ROOT import TauID
+    except:
+        print "Could not import TauID module"
+        print "Be sure to do one of the following:"
+        print '    1) Setup Athena, compile TauDiscriminant with "make", and source setup.sh'
+        print '    2) Compile standalone TauDiscriminant with "make -f Makefile.Standalone", and source setup.sh'
+        sys.exit(1)
+
+import os
+import time
+
+def find_file(filename, search_path_var='PATH', include_working=True):
+    """
+    find filename in PATH with the option of including
+    the current working directory in the search
+    """
+    if not os.environ.has_key(search_path_var):
+        sys.exit("Environment variable $%s is not defined"% search_path_var)
+    search_path = os.environ[search_path_var]
+    paths = search_path.split(os.pathsep)
+    if include_working:
+        paths = ['.'] + paths
+    for path in paths:
+        fullpath = os.path.join(path, filename)
+        if os.path.exists(fullpath):
+            return os.path.abspath(fullpath)
+    sys.exit("Could not find %s in %s"%(filename, search_path_var))
+
+if options.flat:
+    variables = [                
+		  ("AUTHOR","I"),
+		  ("ETA","F"),
+                  ("TRKAVGDIST","F"),                
+                  ("ETOVERPTLEADTRK","F"),
+                  ("EMFRACTIONATEMSCALE","F"),
+                  ("TRT_NHT_OVER_NLT","F"),
+                  ("NUM_PILEUP_AND_PRIMARY_VERTICES","I"),
+                  ("DRMAX","F"),
+                  ("ABS_ETA_LEAD_TRACK","F"),
+                  ("CHPIEMEOVERCALOEME","F"),
+                  ("SECMAXSTRIPET","F"),
+                  ("HADLEAKET","F"),
+                  ("SUMEMCELLETOVERLEADTRKPT","F"),
+                  ("CORRFTRK","F"),
+                  ("CORRCENTFRAC","F"),
+                  ("ISOLFRAC","F"),
+                  ("HADRADIUS","F"),
+                  ("EMPOVERTRKSYSP","F"),
+                  ("PSSFRACTION","F"),
+                  ("NSTRIP","I"),
+                  ("NUMTRACK","I"),
+                  ("PT","F"),
+                  ("NUMWIDETRACK","I"),
+                  ("MASSTRKSYS","F"),
+                  ("IPSIGLEADTRK","F"),
+                  ("TRFLIGHTPATHSIG","F"),
+                  ("TAU_PTRATIO","F"),
+                  ("TAU_PI0_N","I"),
+                  ("TAU_PI0_VISTAU_M","F"),
+                  ("EMFRACTIONATEMSCALE_MOVEE3","F"),                  
+                  ("TAU_ABSDELTAETA","F"),
+                  ("TAU_SEEDTRK_SECMAXSTRIPETOVERPT","F"),
+                  ("TAU_ABSDELTAPHI","F"),
+
+                ]
+else:
+    variables = [ ("AUTHOR","VI"),
+		  ("ETA","VF"),
+                  ("TRKAVGDIST","VF"),                
+                  ("ETOVERPTLEADTRK","VF"),
+                  ("EMFRACTIONATEMSCALE","VF"),
+                  ("TRT_NHT_OVER_NLT","VF"),
+                  ("NUM_PILEUP_AND_PRIMARY_VERTICES","I"),
+                  ("DRMAX","VF"),
+                  ("ABS_ETA_LEAD_TRACK","VF"),
+                  ("CHPIEMEOVERCALOEME","VF"),
+                  ("SECMAXSTRIPET","VF"),
+                  ("HADLEAKET","VF"),
+                  ("SUMEMCELLETOVERLEADTRKPT","VF"),
+                  ("CORRFTRK","VF"),
+                  ("CORRCENTFRAC","VF"),
+                  ("ISOLFRAC","VF"),
+                  ("HADRADIUS","VF"),
+                  ("EMPOVERTRKSYSP","VF"),
+                  ("PSSFRACTION","VF"),
+                  ("NSTRIP","VI"),
+                  ("NUMTRACK","VI"),
+                  ("PT","VF"),
+                  ("NUMWIDETRACK","VI"),
+                  ("MASSTRKSYS","VF"),
+                  ("IPSIGLEADTRK","VF"),
+                  ("TRFLIGHTPATHSIG","VF"),
+                  ("TAU_PTRATIO","VF"),
+                  ("TAU_PI0_N","VI"),
+                  ("TAU_PI0_VISTAU_M","VF"),
+                  ("EMFRACTIONATEMSCALE_MOVEE3","VF"),                  
+                  ("TAU_ABSDELTAETA","VF"),
+                  ("TAU_SEEDTRK_SECMAXSTRIPETOVERPT","VF"),
+                  ("TAU_ABSDELTAPHI","VF"),
+                ]
+
+#cutsfile = find_file("cuts.txt","DATAPATH",True)
+jetBDTfile = find_file("jet.BDT.bin","DATAPATH",True)
+eleBDTfile = find_file("ele.BDT.bin","DATAPATH",True)
+pi0pBDTfile = find_file("pi0Primary.BDT.bin","DATAPATH",True)
+pi0sBDTfile = find_file("pi0Secondary.BDT.bin","DATAPATH",True)
+llh_files = find_file("pdfs_tau.root","DATAPATH",True)+","+find_file("pdfs_jet.root","DATAPATH",True)+","+find_file("LMTCutsLLH.root","DATAPATH",True)
+
+sBDTCutsfile = find_file("sig.bits.jet.BDT.txt","DATAPATH",True)
+bBDTCutsfile = find_file("bkg.bits.jet.BDT.txt","DATAPATH",True)
+
+options.methods = list(set(options.methods.split(',')))
+options.extramethods = list(set(options.extramethods.split(',')))
+
+methods = {
+#    "CUTS":     (TauID.Types.CUTS, "CUTS", cutsfile, 3),
+    "BDT":      (TauID.Types.BDT, "BDTJetScore", jetBDTfile, 1),
+    "BDTE":     (TauID.Types.BDT, "BDTEleScore", eleBDTfile, 1),
+    "PI0P":     (TauID.Types.BDT, "Pi0PrimaryScore",pi0pBDTfile,1),
+    "PI0S":     (TauID.Types.BDT, "Pi0SecondaryScore",pi0sBDTfile,1),
+    "LLH":      (TauID.Types.LLH, "llhsafe",llh_files,4),
+}
+
+extraMethods = {
+    "SBDTCUTS": (TauID.Types.CUTS, "SBDTJetScore", sBDTCutsfile, 3),
+    "BBDTCUTS": (TauID.Types.CUTS, "BBDTJetScore", bBDTCutsfile, 3),
+}
+
+methodVariables = [
+    ("BDT","F","BDTJetScore"),
+    ("BDTE","F","BDTEleScore"),
+    ("PI0P","F","Pi0PrimaryScore"),
+    ("PI0S","F","Pi0SecondaryScore"),
+    ("LLH","F","llhsafe"),
+    
+    
+]
+
+reader = TauID.TauIDReader(options.verbose)
+
+for variable,typename in variables:
+    if not reader.addVariable(variable,typename):
+        sys.exit("Abort")
+
+for method in options.methods:
+    if methods.has_key(method):      
+        if not reader.bookMethod(*methods[method]):
+            sys.exit("Aborted")
+    else:
+        sys.exit("Method %s is not defined"% method)
+
+for variable,typename,branch in methodVariables:
+    if not reader.addVariable(variable, typename, branch):
+        sys.exit("Abort")
+        
+if extraMethods:    
+    for method in options.extramethods:
+        if not method == "NONE":
+            if extraMethods.has_key(method):	  
+                if not reader.bookMethod(*extraMethods[method]):
+                    sys.exit("Aborted")
+            else:
+                sys.exit("Method %s is not defined"% method)
+
+# Suppress warnings
+ROOT.gErrorIgnoreLevel = ROOT.kError
+
+if options.verbose:
+    print "============================================="
+totalTime = 0.
+totalEntries = 0
+for filename in args:
+    print "Processing %s..."% filename
+    outputfilename = filename+".tauid-redo.root"
+    if not reader.setOutput(outputfilename, options.outputdir):
+        sys.exit("Aborted")
+    t1 = time.time() 
+    entries = reader.classify(options.treename, options.treename, filename, options.inputdir, options.friendtree, options.clonetree)
+    if entries <= 0:
+        
+        sys.exit("Aborted")
+    dt = time.time() - t1
+    totalTime += dt
+    totalEntries += entries
+    if options.verbose:
+        print "Total time: %.3f [sec]"%dt
+        print "<Time per entry>: %i [microsec]"%int(1000000*dt/entries)
+        print "Finished processing %s"% filename
+        print "============================================="
+if options.verbose:
+    print "Total time for all files: %.3f [min]"% (totalTime/60)
+    if totalEntries > 0:
+        print "<Time per entry> over all files: %i [microsec]"%int(1000000*totalTime/totalEntries)
+print "Done"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/setup.sh b/PhysicsAnalysis/TauID/TauDiscriminant/setup.sh
new file mode 100644
index 0000000000000000000000000000000000000000..304d2656725338e9fcbc8e497c185654305e7548
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/setup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+########################################################
+#
+# Update env variables for running in standalone mode
+# on D3PDs (ROOT ntuples) with tauid-redo
+# or scripts in taumva (see README)
+#
+# - Noel Dawe
+#
+########################################################
+
+# deterine path to this script
+# http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
+SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
+TD_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
+
+export LD_LIBRARY_PATH=${TD_DIR}/StandAlone${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export PATH=${TD_DIR}/run${PATH:+:$PATH}
+export DATAPATH=${TD_DIR}/share${DATAPATH:+:$DATAPATH}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/EnergyCalibration.root b/PhysicsAnalysis/TauID/TauDiscriminant/share/EnergyCalibration.root
new file mode 100644
index 0000000000000000000000000000000000000000..ce496c79bb2c8bbbd94c9ebe8a41b0e1747341c1
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/EnergyCalibration.root differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/LMTCutsLLH.root b/PhysicsAnalysis/TauID/TauDiscriminant/share/LMTCutsLLH.root
new file mode 100644
index 0000000000000000000000000000000000000000..24f4f48502f5d7b79299227c5e97c58c21de8716
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/LMTCutsLLH.root differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/TauDiscri_jobOptions.py b/PhysicsAnalysis/TauID/TauDiscriminant/share/TauDiscri_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..a18556d6509f17f3b9ab87fe5c0f6a092a08fb41
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/share/TauDiscri_jobOptions.py
@@ -0,0 +1,2 @@
+from TauDiscriminant.TauDiscriGetter import TauDiscriGetter
+TauDiscriGetter()
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/bkg.bits.jet.BDT.txt b/PhysicsAnalysis/TauID/TauDiscriminant/share/bkg.bits.jet.BDT.txt
new file mode 100644
index 0000000000000000000000000000000000000000..df7f16c5859614f2ae97ba9926c04fb961635382
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/share/bkg.bits.jet.BDT.txt
@@ -0,0 +1,179 @@
+1
+NUMTRACK I
+0	1
+-3
+-3
+-1
+2
+BDT F
+PT F
+3
+1.000000E+00
+-4	22
+1	0
+0.000000	0.518400
+15257.155700	0.518400
+15801.688600	0.517600
+16393.516200	0.519800
+17031.783300	0.516600
+17730.442600	0.521400
+18495.901500	0.523600
+19333.822700	0.523300
+20280.374400	0.522300
+21333.054500	0.529000
+22488.162300	0.528600
+23777.609800	0.535000
+25239.947200	0.539800
+26919.403000	0.543800
+28851.328600	0.546700
+31087.770400	0.556400
+33769.034400	0.559300
+37162.997000	0.581400
+41954.037500	0.596400
+50340.639900	0.612200
+72907.442500	0.668200
+100000.000000	0.668200
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	22
+1	0
+0.000000	0.618000
+15257.155700	0.618000
+15801.688600	0.609600
+16393.516200	0.618500
+17031.783300	0.615000
+17730.442600	0.620100
+18495.901500	0.634100
+19333.822700	0.618700
+20280.374400	0.628900
+21333.054500	0.633100
+22488.162300	0.640300
+23777.609800	0.643800
+25239.947200	0.647400
+26919.403000	0.654200
+28851.328600	0.661400
+31087.770400	0.672800
+33769.034400	0.681100
+37162.997000	0.717500
+41954.037500	0.731800
+50340.639900	0.743800
+72907.442500	0.793800
+100000.000000	0.793800
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	22
+1	0
+0.000000	0.773400
+15257.155700	0.773400
+15801.688600	0.777100
+16393.516200	0.781100
+17031.783300	0.776700
+17730.442600	0.788800
+18495.901500	0.790200
+19333.822700	0.787200
+20280.374400	0.798300
+21333.054500	0.816600
+22488.162300	0.779800
+23777.609800	0.803400
+25239.947200	0.835900
+26919.403000	0.818100
+28851.328600	0.854600
+31087.770400	0.834200
+33769.034400	0.846100
+37162.997000	0.872900
+41954.037500	0.876200
+50340.639900	0.876100
+72907.442500	0.902900
+100000.000000	0.902900
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+3
+1.000000E+00
+-4	22
+1	0
+0.000000	0.436700
+16000.007700	0.436700
+17911.972000	0.432700
+19717.259300	0.433700
+21470.598800	0.425500
+23177.059000	0.421200
+24925.380500	0.398400
+26680.763400	0.426800
+28449.353600	0.393500
+30257.536300	0.420400
+32147.890800	0.402200
+34196.679100	0.407800
+36422.228100	0.403200
+38834.308800	0.415600
+41599.517400	0.407200
+44913.180800	0.422300
+49074.177400	0.406700
+54502.529500	0.360100
+62009.981200	0.421800
+72562.859400	0.398200
+88765.527900	0.405200
+100000.000000	0.405200
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	22
+1	0
+0.000000	0.530700
+16000.007700	0.530700
+17911.972000	0.537900
+19717.259300	0.528000
+21470.598800	0.529600
+23177.059000	0.527900
+24925.380500	0.536600
+26680.763400	0.537500
+28449.353600	0.533700
+30257.536300	0.541100
+32147.890800	0.531500
+34196.679100	0.534900
+36422.228100	0.538100
+38834.308800	0.539900
+41599.517400	0.546400
+44913.180800	0.547100
+49074.177400	0.551700
+54502.529500	0.550100
+62009.981200	0.552800
+72562.859400	0.554200
+88765.527900	0.568800
+100000.000000	0.568800
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	22
+1	0
+0.000000	0.660700
+16000.007700	0.660700
+17911.972000	0.687400
+19717.259300	0.676800
+21470.598800	0.679300
+23177.059000	0.675100
+24925.380500	0.677000
+26680.763400	0.696700
+28449.353600	0.695900
+30257.536300	0.692100
+32147.890800	0.686300
+34196.679100	0.685800
+36422.228100	0.700000
+38834.308800	0.728600
+41599.517400	0.705300
+44913.180800	0.712500
+49074.177400	0.706500
+54502.529500	0.716500
+62009.981200	0.704800
+72562.859400	0.708300
+88765.527900	0.712800
+100000.000000	0.712800
+-2	0.000000E+00
+-2	1.000000E+00
+-1
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/cuts.eBDT.root b/PhysicsAnalysis/TauID/TauDiscriminant/share/cuts.eBDT.root
new file mode 100644
index 0000000000000000000000000000000000000000..34fc6599a980992dc6347bd03ac7c17d616d1edc
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/cuts.eBDT.root differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/cuts.txt b/PhysicsAnalysis/TauID/TauDiscriminant/share/cuts.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ba98b560c74ce887de517483e73aaac090f4b4c3
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/share/cuts.txt
@@ -0,0 +1,93 @@
+1
+NUMTRACK I
+0	1
+-3
+-3
+-1
+6
+TRKAVGDIST F
+ETOVERPTLEADTRK F
+CALO_ISO_CORRECTED F
+NUMWIDETRACK I
+PT F
+TRFLIGHTPATHSIG F
+3
+1.000000E+00
+-5	0.0667*(80000.<=x)+(417./x+0.0824-2.61e-7*x)*(x<80000.)
+4	0
+1	4.000000E+00
+2	6.000000E+03
+3	9999
+-2	1.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-1
+1.000000E+00
+-5	0.0667*(80000.<=x)+(417./x+0.0824-2.61e-7*x)*(x<80000.)
+4	0
+1	3.330000E+00
+2	4.000000E+03
+3	0
+-2	1.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-1
+1.000000E+00
+-5	0.0567*(80000.<=x)+(417./x+0.0724-2.61e-7*x)*(x<80000.)
+4	0
+1	2.500000E+00
+2	3.000000E+03
+3	0
+-2	1.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-1
+3
+1.000000E+00
+-5	0.048*(80000.<=x)+(375./x+0.0696-3.28e-7*x)*(x<80000.)
+4	0
+1	3.330000E+00
+2	7.000000E+03
+3	9999
+5	-9.999000E+03
+-2	0.000000E+00
+-2	1.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-1
+1.000000E+00
+-5	0.048*(80000.<=x)+(375./x+0.0696-3.28e-7*x)*(x<80000.)
+4	0
+1	3.330000E+00
+2	7.000000E+03
+3	0
+5	0.000000E+00
+-2	0.000000E+00
+-2	1.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-1
+1.000000E+00
+-5	0.038*(80000.<=x)+(375./x+0.0596-3.28e-7*x)*(x<80000.)
+4	0
+1	2.500000E+00
+2	4.000000E+03
+3	0
+5	5.000000E-01
+-2	0.000000E+00
+-2	1.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-2	0.000000E+00
+-1
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/pdfs_jet.root b/PhysicsAnalysis/TauID/TauDiscriminant/share/pdfs_jet.root
new file mode 100644
index 0000000000000000000000000000000000000000..0bda98ce1378f6dd7497992283ddf2190d29ac7d
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/pdfs_jet.root differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/pdfs_tau.root b/PhysicsAnalysis/TauID/TauDiscriminant/share/pdfs_tau.root
new file mode 100644
index 0000000000000000000000000000000000000000..9fc7a07067d8903a1d4d96ba7b8de855dd877206
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/pdfs_tau.root differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/pi0Primary.BDT.bin b/PhysicsAnalysis/TauID/TauDiscriminant/share/pi0Primary.BDT.bin
new file mode 100644
index 0000000000000000000000000000000000000000..711ea8de8963f0acf38f3e9da51f80c8eec2d6a1
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/pi0Primary.BDT.bin differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/pi0Secondary.BDT.bin b/PhysicsAnalysis/TauID/TauDiscriminant/share/pi0Secondary.BDT.bin
new file mode 100644
index 0000000000000000000000000000000000000000..0f76f5fb2c1d61778c365482118a7b6e50b67e28
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/pi0Secondary.BDT.bin differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/sig.bits.jet.BDT.txt b/PhysicsAnalysis/TauID/TauDiscriminant/share/sig.bits.jet.BDT.txt
new file mode 100644
index 0000000000000000000000000000000000000000..432acacd12ad71f367813444609e44bc968aa4f6
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/share/sig.bits.jet.BDT.txt
@@ -0,0 +1,359 @@
+1
+NUMTRACK I
+0	1
+-3
+-3
+-1
+2
+BDT F
+PT F
+3
+1.000000E+00
+-4	50
+1	0
+0.000000	0.500800
+16140.164100	0.500800
+16910.560500	0.504500
+17686.359400	0.511500
+18468.919900	0.515400
+19258.222700	0.522000
+20052.459000	0.526400
+20862.914100	0.525800
+21685.084000	0.532500
+22524.728500	0.535200
+23396.103500	0.536700
+24297.113300	0.540300
+25222.515600	0.540100
+26180.726600	0.543500
+27173.009800	0.547500
+28203.728500	0.548300
+29291.011700	0.546200
+30445.728500	0.554500
+31678.771500	0.552800
+33008.894500	0.558600
+34464.570300	0.561900
+36079.656200	0.561800
+37919.226600	0.566600
+40129.968800	0.572100
+43075.921900	0.573000
+48713.335900	0.578400
+57510.192600	0.586100
+67274.898000	0.581600
+77039.603500	0.584600
+86804.309000	0.577000
+96569.014500	0.588300
+106101.859400	0.587400
+115657.031200	0.590000
+126005.000000	0.583300
+137251.093800	0.589500
+149065.281200	0.590200
+161429.625000	0.591900
+174342.312500	0.591000
+187864.937500	0.590100
+202402.296900	0.588900
+218018.359400	0.590200
+235117.968800	0.590600
+254758.593800	0.591700
+277347.875000	0.593200
+303140.625000	0.588500
+333448.437500	0.591600
+372086.750000	0.591000
+426726.468800	0.596100
+544437.242200	0.595300
+800000.000000	0.595300
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	50
+1	0
+0.000000	0.545100
+16140.164100	0.545100
+16910.560500	0.550200
+17686.359400	0.556700
+18468.919900	0.560100
+19258.222700	0.567200
+20052.459000	0.572700
+20862.914100	0.572400
+21685.084000	0.582100
+22524.728500	0.583500
+23396.103500	0.585600
+24297.113300	0.591400
+25222.515600	0.595100
+26180.726600	0.597200
+27173.009800	0.600500
+28203.728500	0.603800
+29291.011700	0.602900
+30445.728500	0.609300
+31678.771500	0.610800
+33008.894500	0.616200
+34464.570300	0.619500
+36079.656200	0.621200
+37919.226600	0.628800
+40129.968800	0.632500
+43075.921900	0.636000
+48713.335900	0.642500
+57510.192600	0.650800
+67274.898000	0.646300
+77039.603500	0.649900
+86804.309000	0.637700
+96569.014500	0.655800
+106101.859400	0.657000
+115657.031200	0.658600
+126005.000000	0.656200
+137251.093800	0.660200
+149065.281200	0.661200
+161429.625000	0.664500
+174342.312500	0.662400
+187864.937500	0.660800
+202402.296900	0.660100
+218018.359400	0.662200
+235117.968800	0.663600
+254758.593800	0.663100
+277347.875000	0.665700
+303140.625000	0.660200
+333448.437500	0.664600
+372086.750000	0.664200
+426726.468800	0.664400
+544437.242200	0.666500
+800000.000000	0.666500
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	50
+1	0
+0.000000	0.629700
+16140.164100	0.629700
+16910.560500	0.636700
+17686.359400	0.641400
+18468.919900	0.645800
+19258.222700	0.653400
+20052.459000	0.658000
+20862.914100	0.658900
+21685.084000	0.664400
+22524.728500	0.668400
+23396.103500	0.669200
+24297.113300	0.676000
+25222.515600	0.678000
+26180.726600	0.681500
+27173.009800	0.683900
+28203.728500	0.687700
+29291.011700	0.688800
+30445.728500	0.694100
+31678.771500	0.693300
+33008.894500	0.699900
+34464.570300	0.702900
+36079.656200	0.703900
+37919.226600	0.710000
+40129.968800	0.713300
+43075.921900	0.713200
+48713.335900	0.719600
+57510.192600	0.731600
+67274.898000	0.730600
+77039.603500	0.733900
+86804.309000	0.712300
+96569.014500	0.728900
+106101.859400	0.730700
+115657.031200	0.732700
+126005.000000	0.731900
+137251.093800	0.733300
+149065.281200	0.733200
+161429.625000	0.734900
+174342.312500	0.733400
+187864.937500	0.731600
+202402.296900	0.731400
+218018.359400	0.731000
+235117.968800	0.731600
+254758.593800	0.729700
+277347.875000	0.729600
+303140.625000	0.727500
+333448.437500	0.726900
+372086.750000	0.725900
+426726.468800	0.723500
+544437.242200	0.721900
+800000.000000	0.721900
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+3
+1.000000E+00
+-4	54
+1	0
+0.000000	0.475900
+16737.960900	0.475900
+17778.316400	0.486800
+18761.082000	0.493800
+19706.365200	0.499200
+20633.343800	0.508300
+21536.095700	0.507600
+22419.902300	0.514500
+23295.601600	0.518000
+24168.896500	0.518200
+25048.230500	0.521700
+25926.437500	0.521800
+26810.982400	0.523700
+27718.646500	0.527400
+28641.666000	0.530700
+29588.650400	0.531400
+30562.250000	0.533300
+31560.375000	0.536000
+32591.238300	0.535000
+33675.078100	0.534600
+34845.664100	0.537900
+36110.484400	0.537600
+37502.226600	0.537400
+39085.468800	0.541400
+40938.511700	0.542400
+43325.445300	0.544200
+47172.777300	0.546200
+51147.618800	0.553600
+54163.379700	0.555200
+57179.140600	0.559500
+60194.901600	0.553300
+63210.662500	0.556300
+69002.991800	0.557000
+77571.889500	0.554300
+86140.787100	0.549600
+94709.684800	0.556900
+103278.582400	0.559200
+113960.257800	0.566600
+127432.898400	0.573900
+142193.531200	0.572500
+157442.937500	0.567700
+172714.281200	0.569600
+188320.218800	0.569400
+204392.218800	0.568100
+221300.218800	0.569500
+239779.281200	0.563800
+260790.500000	0.560600
+284605.312500	0.552500
+310978.187500	0.553500
+341455.000000	0.549900
+380790.156200	0.533900
+435189.375000	0.529400
+550179.007800	0.506700
+800000.000000	0.506700
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	54
+1	0
+0.000000	0.506300
+16737.960900	0.506300
+17778.316400	0.515900
+18761.082000	0.518400
+19706.365200	0.524600
+20633.343800	0.534000
+21536.095700	0.533500
+22419.902300	0.540100
+23295.601600	0.543000
+24168.896500	0.541400
+25048.230500	0.546000
+25926.437500	0.546800
+26810.982400	0.547600
+27718.646500	0.552600
+28641.666000	0.553300
+29588.650400	0.554800
+30562.250000	0.555200
+31560.375000	0.558800
+32591.238300	0.559000
+33675.078100	0.558100
+34845.664100	0.562000
+36110.484400	0.561300
+37502.226600	0.562300
+39085.468800	0.566600
+40938.511700	0.567200
+43325.445300	0.569100
+47172.777300	0.571100
+51147.618800	0.581800
+54163.379700	0.586600
+57179.140600	0.589000
+60194.901600	0.585000
+63210.662500	0.586900
+69002.991800	0.589400
+77571.889500	0.588300
+86140.787100	0.582900
+94709.684800	0.577500
+103278.582400	0.597000
+113960.257800	0.602500
+127432.898400	0.610700
+142193.531200	0.610700
+157442.937500	0.607800
+172714.281200	0.606200
+188320.218800	0.608500
+204392.218800	0.603200
+221300.218800	0.606800
+239779.281200	0.603400
+260790.500000	0.600900
+284605.312500	0.593700
+310978.187500	0.593500
+341455.000000	0.589100
+380790.156200	0.580900
+435189.375000	0.577000
+550179.007800	0.560000
+800000.000000	0.560000
+-2	0.000000E+00
+-2	1.000000E+00
+-1
+1.000000E+00
+-4	54
+1	0
+0.000000	0.557000
+16737.960900	0.557000
+17778.316400	0.564000
+18761.082000	0.565500
+19706.365200	0.574000
+20633.343800	0.582200
+21536.095700	0.585500
+22419.902300	0.592200
+23295.601600	0.596300
+24168.896500	0.598100
+25048.230500	0.597700
+25926.437500	0.599700
+26810.982400	0.602500
+27718.646500	0.607900
+28641.666000	0.609400
+29588.650400	0.607700
+30562.250000	0.610200
+31560.375000	0.614200
+32591.238300	0.616500
+33675.078100	0.615000
+34845.664100	0.616800
+36110.484400	0.616800
+37502.226600	0.621000
+39085.468800	0.625400
+40938.511700	0.627700
+43325.445300	0.629700
+47172.777300	0.630300
+51147.618800	0.643900
+54163.379700	0.647900
+57179.140600	0.645000
+60194.901600	0.642200
+63210.662500	0.648400
+69002.991800	0.655800
+77571.889500	0.659200
+86140.787100	0.649900
+94709.684800	0.640300
+103278.582400	0.675100
+113960.257800	0.682000
+127432.898400	0.691200
+142193.531200	0.691900
+157442.937500	0.686900
+172714.281200	0.687000
+188320.218800	0.689900
+204392.218800	0.681800
+221300.218800	0.684600
+239779.281200	0.684200
+260790.500000	0.680400
+284605.312500	0.673400
+310978.187500	0.674600
+341455.000000	0.662800
+380790.156200	0.658300
+435189.375000	0.652200
+550179.007800	0.641500
+800000.000000	0.641500
+-2	0.000000E+00
+-2	1.000000E+00
+-1
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/share/sig.trans.jet.BDT.root b/PhysicsAnalysis/TauID/TauDiscriminant/share/sig.trans.jet.BDT.root
new file mode 100644
index 0000000000000000000000000000000000000000..ba5ba57ee1a7f5b1c813237fd7d1f4b5b229d167
Binary files /dev/null and b/PhysicsAnalysis/TauID/TauDiscriminant/share/sig.trans.jet.BDT.root differ
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/FakeTauBits.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/FakeTauBits.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3f2bc8cf305d2bddeb6e5cee162157c24ec359ec
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/FakeTauBits.cxx
@@ -0,0 +1,5 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TauDiscriminant/FakeTauBits.h"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/FakeTauScores.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/FakeTauScores.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..51c598358cd77979d2d9215bf51ef2c79e3a1321
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/FakeTauScores.cxx
@@ -0,0 +1,5 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TauDiscriminant/FakeTauScores.h"
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauCuts.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauCuts.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f0aa40e04f832119c6e014bc17d66eeb0104eb27
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauCuts.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TauDiscriminant/TauCuts.h"
+#include "xAODTau/TauJetContainer.h"
+#include "xAODTau/TauJet.h"
+// #include "tauEvent/TauCommonDetails.h"
+// #include "tauEvent/TauPID.h"
+// #include "tauEvent/TauJetParameters.h"
+#include "xAODTau/TauDefs.h"
+#include <PathResolver/PathResolver.h>
+
+using namespace xAOD;
+//-----------------------------------------------------------------------------
+// Initializer
+//-----------------------------------------------------------------------------
+StatusCode TauCuts::prepare(const TauDetailsManager& manager)
+{
+    m_cutsManager = new MethodCuts("TauCuts");
+    m_cutsManager->setDetails(manager);
+
+    string cutsPath = PathResolver::find_file(m_fileName, "DATAPATH");
+
+    if ( !m_cutsManager->build(cutsPath) ) {
+        ATH_MSG_FATAL("unable to build cuts manager");
+        return StatusCode::FAILURE;
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Finalizer
+//-----------------------------------------------------------------------------
+StatusCode TauCuts::finalize()
+{
+    delete m_cutsManager;
+    return StatusCode::SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Execution
+//-----------------------------------------------------------------------------
+StatusCode TauCuts::execute(xAOD::TauJet* tau, FakeTauBits* /*bits*/, FakeTauScores* /*scores*/)
+{
+    bool loose(false), medium(false), tight(false);
+
+    if (tau->nTracks() > 0)
+    {
+        loose = m_cutsManager->response(0);
+        medium = m_cutsManager->response(1);
+        tight = m_cutsManager->response(2); 
+    }
+
+//     Analysis::TauPID *p_tauid = tau->tauID();
+
+    tau->setIsTau(TauJetParameters::TauCutLoose, loose);
+    tau->setIsTau(TauJetParameters::TauCutMedium, medium);
+    tau->setIsTau(TauJetParameters::TauCutTight, tight);
+    
+    // Verbose messaging for extra info
+    if (msgLvl(MSG::VERBOSE))
+    {
+        if (!((!loose && !medium && !tight) || (loose && !medium && !tight) || (loose && medium && !tight) || (loose && medium && tight)))
+        {
+            msg(MSG::VERBOSE) << "Bad cuts!" << endreq;
+        }
+        msg(MSG::VERBOSE) << "TauCutLoose: " << tau->isTau(TauJetParameters::TauCutLoose) << endreq;
+        msg(MSG::VERBOSE) << "TauCutMedium: " << tau->isTau(TauJetParameters::TauCutMedium) << endreq;
+        msg(MSG::VERBOSE) << "TauCutTight: " << tau->isTau(TauJetParameters::TauCutTight) << endreq;
+    }
+   
+    return StatusCode::SUCCESS;
+} 
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauCutsEleVeto.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauCutsEleVeto.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6eff019da595f64eebd9491680288ea11eff4ba0
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauCutsEleVeto.cxx
@@ -0,0 +1,206 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TauDiscriminant/TauCutsEleVeto.h"
+#include "xAODTau/TauJetContainer.h"
+#include "xAODTau/TauJet.h"
+// #include "tauEvent/TauCommonDetails.h"
+// #include "tauEvent/TauPID.h"
+// #include "tauEvent/TauJetParameters.h"
+#include "xAODTau/TauDefs.h"
+#include "TauDiscriminant/TauDetails.h"
+#include <PathResolver/PathResolver.h>
+
+using namespace xAOD;
+
+//-----------------------------------------------------------------------------
+// Initializer
+//-----------------------------------------------------------------------------
+StatusCode TauCutsEleVeto::prepare(const TauDetailsManager& manager)
+{
+    this->detailsManager = &manager;
+    return StatusCode::SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Finalizer
+//-----------------------------------------------------------------------------
+StatusCode TauCutsEleVeto::finalize()
+{
+    return StatusCode::SUCCESS;
+}
+/* Cut-based electron veto implementation 
+ *
+ *
+ * returns false if it a tau
+ *         true  if it is an electron candidate
+ * This function can also work on D3PD-level:
+ * input D3PD variables: ******************************************************
+ * eta       : tau_seedCalo_track_eta (track with highest tau_seedCalo_track_pt)
+ * TrkAvgDist: tau_seedCalo_trkAvgDist
+ * HoP       : tau_seedCalo_etHadAtEMScale/tau_leadTrkPt
+ * EoP       : tau_seedCalo_etEMAtEMScale/tau_leadTrkPt
+ * TrtRatio  : tau_calcVars_TRTHTOverLT_LeadTrk
+ * ****************************************************************************
+ * Usage:
+ * tauElectronVeto(float eta, float TrkAvgDist, float HoP,
+ *                 float EoP, float TrtRatio,   int qual)
+ * qual: loose, medium, tight or tighter
+ * returns true if the object is identified as an electron by the algorithm
+ * ATLAS tauWG supported points: loose, medium and tight, tighter is for test
+ * purposes
+ *
+ * Changes since the last release (12 May 2011)
+ *
+ * eta range changed: central taus were defined 0.0-1.7 now 0.0-2.0
+ * variables replaced: 
+ * OLD VARIABLE (ATHENA REL16)          -> NEW VARIABLE (ATHENA REL17)
+ * tau_seedTrk_secMaxStripEt            -> tau_seedCalo_trkAvgDist
+ * tau_seedTrk_hadLeakEt                -> tau_seedCalo_etHadAtEMScale/tau_leadTrkPt
+ * tau_seedTrk_sumEMCellEtOverLeadTrkPt -> tau_seedCalo_etEMAtEMScale/tau_leadTrkPt
+ * ****************************************************************************
+ * nikolaos.rompotis at cern.ch - 6 Oct 2011
+ * 
+ */
+bool tauElectronVeto(float eta, float TrkAvgDist, float HoP,
+		     float EoP, float TrtRatio, int qual) {
+  float TrkAvgDist_C0, HoP_C0, EoP_C0, TrtRatio_C0a, TrtRatio_C0b, TrtRatio_C0c;
+  float HoP_C1, EoP_C1;
+
+  // define the cut values .............
+  if (qual == 0) {
+    TrkAvgDist_C0= 0.026;
+    HoP_C0       = 0.060;
+    EoP_C0       = 0.868;
+    TrtRatio_C0a = 0.227;
+    TrtRatio_C0b = 0.108;
+    TrtRatio_C0c = 0.151;
+
+    HoP_C1       = 0.088;
+    EoP_C1       = 0.102;
+  }
+  else if (qual == 1) {
+    TrkAvgDist_C0= 0.051;
+    HoP_C0       = 0.090;
+    EoP_C0       = 0.812;
+    TrtRatio_C0a = 0.162;
+    TrtRatio_C0b = 0.069;
+    TrtRatio_C0c = 0.097;
+		   	    
+    HoP_C1       = 0.195;
+    EoP_C1       = 1.076;
+  }
+  else if (qual == 2) {
+    TrkAvgDist_C0= 0.096;
+    HoP_C0       = 0.035;
+    EoP_C0       = 0.104;
+    TrtRatio_C0a = 0.107;
+    TrtRatio_C0b = 0.254;
+    TrtRatio_C0c = 0.085;
+		   	      
+    HoP_C1       = 0.301;
+    EoP_C1       = 2.550;
+
+  }
+  else if (qual == 3) {
+    TrkAvgDist_C0= 0.096;
+    HoP_C0       = 0.066;
+    EoP_C0       = 0.103;
+    TrtRatio_C0a = 0.098;
+    TrtRatio_C0b = 0.708;
+    TrtRatio_C0c = 0.056;
+		   	 
+    HoP_C1       = 0.560;
+    EoP_C1       = 1.582;
+  }
+  else {
+    std::cout << "No such option for qual=" << qual << endl;
+    return false;
+  }
+  
+  if (qual == 1) {
+    if (tauElectronVeto(eta, TrkAvgDist, HoP, EoP, TrtRatio, 0))
+      return true;
+  }
+  else if (qual == 2) {
+    if (tauElectronVeto(eta, TrkAvgDist, HoP, EoP, TrtRatio, 1))
+      return true;
+  }
+  else if (qual == 3) { // tighter is defined also wrt to the medium
+    if (tauElectronVeto(eta, TrkAvgDist, HoP, EoP, TrtRatio, 2))
+      return true;
+  }
+  
+  // ...................................
+  if ( fabs(eta) < 2.0 ) {  // central taus
+    //
+    if (HoP > HoP_C0) { 
+      if ( TrtRatio <= TrtRatio_C0a) return false;
+      else return true;
+    }
+    else {
+      if (TrkAvgDist > TrkAvgDist_C0) {
+	if (TrtRatio <= TrtRatio_C0b) return false;
+	else return true;
+      }
+      else {
+	if (TrtRatio <= TrtRatio_C0c && EoP <= EoP_C0) return false;
+	else return true;
+      }
+    }
+  }
+  else {                     // forward taus
+    if (HoP > HoP_C1) return false;
+    else {
+      if (EoP <= EoP_C1) return false;
+      else return true;
+    }
+  }
+}
+
+
+//-----------------------------------------------------------------------------
+// Execution
+//-----------------------------------------------------------------------------
+StatusCode TauCutsEleVeto::execute(xAOD::TauJet* tau, FakeTauBits* /*bits*/, FakeTauScores* /*scores*/)
+{
+  
+    if (!detailsManager)
+    {
+        return StatusCode::FAILURE;
+    }
+    bool loose(false), medium(false), tight(false);
+    // demand exactly 1 track and calo or calo+track driven tau
+    if (tau->nTracks() == 1)
+      {
+// 	const Analysis::TauCommonDetails* commonDetails(tau->details<Analysis::TauCommonDetails>());	
+	float etHadAtEMScale;
+	tau->detail(TauJetParameters::etHadAtEMScale,etHadAtEMScale);
+	float etEMAtEMScale;
+	tau->detail(TauJetParameters::etEMAtEMScale,etEMAtEMScale);
+	float lcScale = 1;
+	if(this->useLCscale) tau->detail(TauJetParameters::LC_TES_precalib,lcScale);
+
+	float eta        = tau->track(0)->eta();//detailsManager->getFloatDetailValue(Details::ABS_ETA_LEAD_TRACK);
+	float TrkAvgDist = detailsManager->getFloatDetailValue(Details::TRKAVGDIST);
+	float leadTrkPt  = tau->track(0)->pt();
+	float HoP        = leadTrkPt!=0?etHadAtEMScale*lcScale/leadTrkPt:0;
+	float EoP        = leadTrkPt!=0?etEMAtEMScale*lcScale/leadTrkPt:0;
+	//float HoP        = detailsManager->getFloatDetailValue(Details::ETHADATEMSCALE)/leadTrkPt;
+	//float EoP        = detailsManager->getFloatDetailValue(Details::ETEMATEMSCALE)/leadTrkPt;
+	float TrtRatio   = detailsManager->getFloatDetailValue(Details::TRT_NHT_OVER_NLT);
+	
+        loose = tauElectronVeto(eta,  TrkAvgDist, HoP, EoP, TrtRatio, 0);
+        medium = tauElectronVeto(eta, TrkAvgDist, HoP, EoP, TrtRatio, 1);
+        tight = tauElectronVeto(eta,  TrkAvgDist, HoP, EoP, TrtRatio, 2);
+      }
+
+//     Analysis::TauPID *p_tauid = tau->tauID();
+
+    tau->setIsTau(TauJetParameters::ElectronVetoLoose, loose);
+    tau->setIsTau(TauJetParameters::ElectronVetoMedium, medium);
+    tau->setIsTau(TauJetParameters::ElectronVetoTight, tight);
+
+    return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDetailsManager.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDetailsManager.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6b7401df7d2897aa585fc603a250651a5b4cd52e
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDetailsManager.cxx
@@ -0,0 +1,441 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * file: TauDetailsManager.cxx
+ *
+ * Author: Noel Dawe (end1@sfu.ca)
+ */
+
+#include <iostream>
+
+#include "TauDiscriminant/TauDetailsManager.h"
+#include "TauDiscriminant/TauPi0Clusters.h"
+#include <utility>
+
+#include "xAODTau/TauJet.h"
+#include "xAODTracking/TrackParticle.h"
+// #include "TrkTrackSummary/TrackSummary.h"
+#include "xAODTracking/VertexContainer.h"
+#include "TrkEventPrimitives/VertexType.h"
+#include "xAODCaloEvent/CaloClusterContainer.h"
+#include "xAODCaloEvent/CaloCluster.h"
+#include "FourMomUtils/P4Helpers.h"
+#include "AnalysisUtils/AnalysisMisc.h"
+#include "CaloGeoHelpers/CaloSampling.h"
+#include "CaloUtils/CaloVertexedCluster.h"
+
+// redefine the macro to print strings instead of enums as in TauDetails.h
+#undef ENUM_OR_STRING
+#define ENUM_OR_STRING( x ) #x   
+
+using CLHEP::GeV;
+using namespace std;
+using namespace xAOD;
+
+// Arrays of the string representations of the detail names
+namespace Details {
+string IntTauDetailString[]     = { INT_TAU_DETAILS };
+string FloatTauDetailString[]   = { FLOAT_TAU_DETAILS };
+string IntEventDetailString[]   = { INT_EVENT_DETAILS };
+string FloatEventDetailString[] = { FLOAT_EVENT_DETAILS };
+}
+
+// The default value for all details
+const float TauDetailsManager::LOW_NUMBER = -1111.;
+
+TauDetailsManager::TauDetailsManager(StoreGateSvc* storeGateSvc, bool isTrigger)
+{
+	this->storeGate = storeGateSvc;
+	this->doTrigger = isTrigger;
+	//hard-coded for a while
+	m_clusterCone = 0.2;
+
+	// Initialize the vector containing the tau-based variables
+	this->float_data = vector<float>(Details::__FloatTauDetail__END__ + 1,LOW_NUMBER);
+	this->int_data = vector<int>(Details::__IntTauDetail__END__ + 1,int(LOW_NUMBER));
+
+	// Initialize the vector containing the event-based variables
+	this->float_event_data = vector<float>( Details::__FloatEventDetail__END__ + 1, LOW_NUMBER);
+	this->int_event_data = vector<int>(Details::__IntEventDetail__END__ + 1, int(LOW_NUMBER));
+
+	// Maps of the string representations to the addresses of the values in the above vectors
+	unsigned int i;
+	for (i = 0; i < this->float_data.size() - 1; ++i) {
+		this->float_details.insert( pair<string, float*>(Details::FloatTauDetailString[i], &this->float_data[i]));
+	}
+	for (i = 0; i < this->float_event_data.size() - 1; ++i) {
+		this->float_details.insert(pair<string, float*>(Details::FloatEventDetailString[i],&this->float_event_data[i]));
+	}
+	for (i = 0; i < this->int_data.size() - 1; ++i) {
+		this->int_details.insert( pair<string, int*>(Details::IntTauDetailString[i], &this->int_data[i]));
+	}
+	for (i = 0; i < this->int_event_data.size() - 1; ++i) {
+		this->int_details.insert( pair<string, int*>(Details::IntEventDetailString[i], &this->int_event_data[i]));
+	}
+}
+
+bool TauDetailsManager::updateEvent()
+{
+	// Reset the buffers at the beginning of each event
+	this->float_event_data.assign(this->float_event_data.size(), LOW_NUMBER);
+	this->int_event_data.assign(this->int_event_data.size(), int(LOW_NUMBER));
+
+	if (this->storeGate && this->doTrigger) {
+		//vertex information not available at EF, thus use default values to get correct bin; also retrievin VertexContainer will crash for TrigTauDiscriminant
+		this->int_event_data[Details::NUMGOODVERTICES] = 1;
+		this->int_event_data[Details::NUM_PILEUP_AND_PRIMARY_VERTICES] = 1;
+		return true;
+	} else if (this->storeGate) {
+		const xAOD::VertexContainer* priVertices(0);
+		StatusCode sc = this->storeGate->retrieve(priVertices, "PrimaryVertices");
+		if (sc.isFailure()) {
+			return false;
+		}
+		if (priVertices) {
+			this->int_event_data[Details::NUMVERTICES] = priVertices->size();
+
+			//Martin Flechl, Nov 16, 2010: calculate number of good vertices
+			int nGoodVtx(0);
+			int nPileupPrimaryVtx(0);
+			xAOD::VertexContainer::const_iterator vxIter = priVertices->begin();
+			xAOD::VertexContainer::const_iterator vxIterEnd = priVertices->end();
+			for (; vxIter != vxIterEnd; ++vxIter) {
+				if (!(*vxIter)) continue;
+				// FF, March 2014
+				// attention: the lines below may work in Athena, but not in xAOD standalone mode, which is used for end-user analysis
+				/*
+				 std::vector<Trk::VxTrackAtVertex*>* vxTrackAtVertex = (*vxIter)->vxTrackAtVertex();
+				 if (!vxTrackAtVertex) continue;
+				 if ( vxTrackAtVertex->size() >= 4) nGoodVtx++;
+
+				 if ((vxTrackAtVertex->size() >= 4 && (*vxIter)->vertexType() == xAOD::VxType::PriVtx ) || \
+                        (vxTrackAtVertex->size() >= 2 && (*vxIter)->vertexType() == xAOD::VxType::PileUp ) ) nPileupPrimaryVtx++;
+				 */
+
+				// this works also in xAOD standalone mode, however, did not checked if results are the same
+				int nTrackParticles = (*vxIter)->nTrackParticles();
+				if (nTrackParticles >= 4) nGoodVtx++;
+				if ( (nTrackParticles >= 4 && (*vxIter)->vertexType() == xAOD::VxType::PriVtx) ||
+						(nTrackParticles >= 2 && (*vxIter)->vertexType() == xAOD::VxType::PileUp)
+				)
+					nPileupPrimaryVtx++;
+
+			}
+			this->int_event_data[Details::NUMGOODVERTICES] = nGoodVtx;
+			this->int_event_data[Details::NUM_PILEUP_AND_PRIMARY_VERTICES] = nPileupPrimaryVtx;
+		}
+	}
+	return true;
+}
+
+bool TauDetailsManager::setNpi0(xAOD::TauJet& tauJet, int nPi0)
+{
+	this->int_data[Details::TAU_PI0_N] = nPi0;
+	tauJet.setDetail(TauJetParameters::nPi0Topo, static_cast<int>(nPi0));
+
+	//ATH_MSG_VERBOSE("setting Npi0 = "<<nPi0);
+	//std::cout<<"setting Npi0 = "<<nPi0<<std::endl;
+	return true;
+}
+
+// const version of TauDetailsManager::update
+// calculate variables and store them only in internal memory
+bool TauDetailsManager::update(const xAOD::TauJet& tauJet)
+{
+	// Reset the buffers before setting the variables of each tau
+	this->float_data.assign(this->float_data.size(), LOW_NUMBER);
+	this->int_data.assign(this->int_data.size(), int(LOW_NUMBER));
+
+	// there is no author flag currently
+	// so keeping for backward compatibility only
+	this->int_data[Details::AUTHOR] = 3;
+
+	unsigned int numTrack(tauJet.nTracks());
+
+	//==========================================================================================
+	// get variables from tau EDM
+	//==========================================================================================
+	this->int_data[Details::CHARGE]       = tauJet.charge();
+	this->int_data[Details::NUMTRACK]     = numTrack;
+	this->int_data[Details::NUMWIDETRACK] = tauJet.nWideTracks();
+
+	this->float_data[Details::ETA]     = tauJet.eta();
+	this->float_data[Details::ABS_ETA] = fabs(tauJet.eta());
+
+	this->float_data[Details::PHI] = tauJet.phi();
+	this->float_data[Details::E]   = tauJet.e();
+	this->float_data[Details::ET]  = tauJet.pt();
+	this->float_data[Details::PT]  = tauJet.pt();
+	this->float_data[Details::M]   = tauJet.m();
+
+	float etOverpTLeadTrk;
+	tauJet.detail(TauJetParameters::etOverPtLeadTrk, etOverpTLeadTrk);
+	this->float_data[Details::ETOVERPTLEADTRK] = etOverpTLeadTrk;
+	this->float_data[Details::PTLEADTRKOVERET] = etOverpTLeadTrk > 0 ? 1. / etOverpTLeadTrk : LOW_NUMBER;
+
+	tauJet.detail(TauJetParameters::ipZ0SinThetaSigLeadTrk, this->float_data[Details::IPZ0SINTHETASIGLEADTRK]);
+	tauJet.detail(TauJetParameters::ipSigLeadTrk,           this->float_data[Details::IPSIGLEADTRK]);
+	tauJet.detail(TauJetParameters::massTrkSys,	            this->float_data[Details::MASSTRKSYS]);
+	tauJet.detail(TauJetParameters::trkWidth2,              this->float_data[Details::TRKWIDTH2]);
+	tauJet.detail(TauJetParameters::trFlightPathSig,        this->float_data[Details::TRFLIGHTPATHSIG]);
+
+	float etEflow = 0;
+	tauJet.detail(TauJetParameters::etEflow, etEflow);
+	this->float_data[Details::ETEFLOWOVERET] =	(tauJet.pt() > 0) ? etEflow / tauJet.pt() : LOW_NUMBER;
+
+	// this variable is not filled anymore by tauRec
+	//tauJet.detail(TauJetParameters::mEflow, this->float_data[Details::MEFLOW]);
+
+	tauJet.detail(TauJetParameters::stripWidth2, this->float_data[Details::STRIPWIDTH2]);
+	tauJet.detail(TauJetParameters::EMRadius,    this->float_data[Details::EMRADIUS]);
+	tauJet.detail(TauJetParameters::hadRadius,   this->float_data[Details::HADRADIUS]);
+	tauJet.detail(TauJetParameters::isolFrac,    this->float_data[Details::ISOLFRAC]);
+	tauJet.detail(TauJetParameters::centFrac,    this->float_data[Details::CENTFRAC]);
+	tauJet.detail(TauJetParameters::nStrip,      this->int_data[Details::NSTRIP]);
+	tauJet.detail(TauJetParameters::trkAvgDist,  this->float_data[Details::TRKAVGDIST]);
+
+	float etEMScale, etEMScale1, etEMScale2;
+	tauJet.detail(TauJetParameters::etEMAtEMScale,  etEMScale1);
+	tauJet.detail(TauJetParameters::etHadAtEMScale, etEMScale2);
+	etEMScale = etEMScale1 + etEMScale2;
+	this->float_data[Details::EMFRACTIONATEMSCALE] = (etEMScale != 0) ? etEMScale1 / etEMScale : LOW_NUMBER;
+
+	float emradius, hadradius;
+	tauJet.detail(TauJetParameters::EMRadius,  emradius);
+	tauJet.detail(TauJetParameters::hadRadius, hadradius);
+	this->float_data[Details::CALRADIUS] =	(etEMScale != 0) ? (etEMScale1 * emradius + etEMScale2 * hadradius)	/ etEMScale : LOW_NUMBER;
+
+	// New cluster-based variables
+	tauJet.detail(TauJetParameters::lead2ClusterEOverAllClusterE,   this->float_data[Details::LEAD2CLUSTEREOVERALLCLUSTERE]);
+	tauJet.detail(TauJetParameters::lead3ClusterEOverAllClusterE,   this->float_data[Details::LEAD3CLUSTEREOVERALLCLUSTERE]);
+	tauJet.detail(TauJetParameters::caloIso,                        this->float_data[Details::CALO_ISO]);
+	tauJet.detail(TauJetParameters::caloIsoCorrected,               this->float_data[Details::CALO_ISO_CORRECTED]);
+
+	// Topocluster variables:
+	tauJet.detail(TauJetParameters::topoInvMass, 	this->float_data[Details::TOPOINVMASS]);
+	tauJet.detail(TauJetParameters::effTopoInvMass, this->float_data[Details::EFFTOPOINVMASS]);
+
+	tauJet.detail(TauJetParameters::PSSFraction,		this->float_data[Details::PSSFRACTION]);
+	tauJet.detail(TauJetParameters::ChPiEMEOverCaloEME,	this->float_data[Details::CHPIEMEOVERCALOEME] );
+	tauJet.detail(TauJetParameters::EMPOverTrkSysP,		this->float_data[Details::EMPOVERTRKSYSP]);
+
+	// get intermediate axis
+	TLorentzVector tauInterAxis = tauJet.p4(TauJetParameters::IntermediateAxis);
+	this->float_data[Details::INTERAXIS_ETA] = tauInterAxis.Eta();
+	this->float_data[Details::INTERAXIS_PHI] = tauInterAxis.Phi();
+
+	// this variable is special: when TauDiscriminant is called first time, this variable is empty
+	// in case TauDiscriminant::TauDetailsManager is called after a full TauDiscriminant run or on a already full processed AOD/xAOD
+	//  this variable is filled properly
+	this->float_data[Details::BDTJETSCORE] = tauJet.discriminant(TauJetParameters::BDTJetScore);
+
+
+	//==========================================================================================
+	// calculate now variables needed for TauID not calculated by tauRec
+	//==========================================================================================
+	const xAOD::Jet* pJetSeed = (*tauJet.jetLink());
+	if (!pJetSeed) {
+		//ATH_MSG_WARNING("tau does not have jet seed");
+		return StatusCode::SUCCESS;
+	}
+
+	// JVF and PT_PILEUP
+	float pt_pileup = 0.;
+	if (!this->doTrigger) {
+		// jvf and sumPtTrk are now a vector and the old run1-type jvf value is stored in the 0-th element
+		// sumPtTrk is calculated wrt Vertices
+		std::vector<float> sumPtTrkvec;
+		std::vector<float> jvfvec;
+
+		// ToDo: still need to check if the 500MeV threshold is correct
+		pJetSeed->getAttribute(xAOD::JetAttribute::SumPtTrkPt500, sumPtTrkvec);
+		pJetSeed->getAttribute(xAOD::JetAttribute::JVF, jvfvec);
+
+		float jvf = 0.0;
+		float sumPtTrk = 0.0;
+		if (!jvfvec.empty() && !sumPtTrkvec.empty()) {
+			// ToDo: need to check if first vertex is the vertex we want to use here!
+			jvf = jvfvec[0];
+			sumPtTrk = sumPtTrkvec[0];
+		} else {
+			msg(MSG::WARNING) << "jvf value vector and/or sumPtTrk vector returned from seed jet is empty!"	<< endreq;
+		}
+		pt_pileup = (1.0 - jvf) * sumPtTrk;
+		this->float_data[Details::JVF] = jvf;
+		this->float_data[Details::PT_PILEUP] = pt_pileup;
+	}
+
+	// track-based variables for pi0 counting and ele veto
+	if (numTrack > 0) {
+		// variables used by the cut-based e-veto
+		tauJet.detail(TauJetParameters::hadLeakEt,                this->float_data[Details::HADLEAKET]);
+		tauJet.detail(TauJetParameters::sumEMCellEtOverLeadTrkPt, this->float_data[Details::SUMEMCELLETOVERLEADTRKPT]);
+		tauJet.detail(TauJetParameters::secMaxStripEt,            this->float_data[Details::SECMAXSTRIPET]);
+
+		// Used by cut-based eveto:
+		if (tauJet.track(0)) {
+			this->float_data[Details::ABS_ETA_LEAD_TRACK] = fabs( tauJet.track(0)->eta() );
+			this->float_data[Details::TAU_ABSDELTAETA]    = fabs( tauJet.track(0)->eta() - tauJet.eta() );
+			this->float_data[Details::TAU_ABSDELTAPHI]    = fabs( tauJet.track(0)->phi() - tauJet.phi() );
+			this->float_data[Details::TAU_SEEDTRK_SECMAXSTRIPETOVERPT] = (tauJet.track(0)->pt() != 0) ?	this->float_data[Details::SECMAXSTRIPET] / tauJet.track(0)->pt() : LOW_NUMBER;
+			// solve for E3
+			float tau_sumETCellsLAr = this->float_data[Details::SUMEMCELLETOVERLEADTRKPT] * tauJet.track(0)->pt();
+			float tau_sumEMCellET = etEMScale1;
+			float tau_E3 = tau_sumETCellsLAr - tau_sumEMCellET;
+
+			// remove E3
+			float tau_seedCalo_etHadAtEMScale_noE3 = etEMScale2 - tau_E3;
+			float tau_seedCalo_etEMAtEMScale_yesE3 = etEMScale1 + tau_E3;
+
+			//calculate new EMFraction
+			this->float_data[Details::EMFRACTIONATEMSCALE_MOVEE3] =	tau_seedCalo_etEMAtEMScale_yesE3 / (tau_seedCalo_etEMAtEMScale_yesE3 + tau_seedCalo_etHadAtEMScale_noE3);
+		}
+
+		// for pi0 counting
+		float sumpT3Trk(0.);
+		float sumpT(0.);
+		float dRmin = -1 * LOW_NUMBER;
+		float dR;
+
+		for (unsigned int i(0); i < numTrack; ++i) {
+			if (i < 3) sumpT3Trk += tauJet.track(i)->pt();
+			sumpT += tauJet.track(i)->pt();
+			dR = tauJet.p4().DeltaR(tauJet.track(i)->p4());
+			if (dRmin > dR)	dRmin = dR;
+		}
+
+		this->float_data[Details::SUMPT3TRK] = sumpT3Trk;
+		this->float_data[Details::SUMPT]     = sumpT;
+		this->float_data[Details::DRMIN]     = dRmin;
+		tauJet.detail(TauJetParameters::dRmax,	this->float_data[Details::DRMAX]);
+
+		if (tauJet.pt() != 0) {
+			this->float_data[Details::SUMPT_OVER_ET]     = sumpT / tauJet.pt();
+			this->float_data[Details::SUMPT3TRK_OVER_ET] = sumpT3Trk / tauJet.pt();
+		}
+
+		if (sumpT3Trk != 0) {
+			this->float_data[Details::ETHAD_EM_OVER_SUMPT3TRK] = etEMScale2	/ sumpT3Trk;
+			this->float_data[Details::ETEM_EM_OVER_SUMPT3TRK]  = etEMScale1 / sumpT3Trk;
+		}
+		if (sumpT != 0) {
+			this->float_data[Details::ETHAD_EM_OVER_SUMPT] = etEMScale2 / sumpT;
+			this->float_data[Details::ETEM_EM_OVER_SUMPT]  = etEMScale1 / sumpT;
+		}
+
+		uint8_t numberOfTRTHighThresholdHits;
+		tauJet.track(0)->summaryValue(numberOfTRTHighThresholdHits,	xAOD::numberOfTRTHighThresholdHits);
+		uint8_t numberOfTRTHits;
+		tauJet.track(0)->summaryValue(numberOfTRTHits, xAOD::numberOfTRTHits);
+		uint8_t numberOfTRTHighThresholdOutliers;
+		tauJet.track(0)->summaryValue(numberOfTRTHighThresholdOutliers,	xAOD::numberOfTRTHighThresholdOutliers);
+		uint8_t numberOfTRTOutliers;
+		tauJet.track(0)->summaryValue(numberOfTRTOutliers, xAOD::numberOfTRTOutliers);
+		this->float_data[Details::TRT_NHT_OVER_NLT] =
+				(numberOfTRTHits + numberOfTRTOutliers) > 0 ?
+						float( numberOfTRTHighThresholdHits	+ numberOfTRTHighThresholdOutliers)	/ float(numberOfTRTHits + numberOfTRTOutliers) : LOW_NUMBER;
+	}
+
+	//Pi0 Cluster finding variables
+	TauPi0Clusters tpc = TauPi0Clusters(tauJet);
+
+	this->float_data[Details::PI0CL1_PT]  = tpc.get_cl1_Pt();
+	this->float_data[Details::PI0CL1_ETA] = tpc.get_cl1_Eta();
+	this->float_data[Details::PI0CL1_PHI] = tpc.get_cl1_Phi();
+
+	this->float_data[Details::PI0CL2_PT]  = tpc.get_cl2_Pt();
+	this->float_data[Details::PI0CL2_ETA] = tpc.get_cl2_Eta();
+	this->float_data[Details::PI0CL2_PHI] = tpc.get_cl2_Phi();
+
+	this->float_data[Details::VISTAU_PI0CL_PT]  = tpc.get_tau_vis_Pt();
+	this->float_data[Details::VISTAU_PI0CL_ETA] = tpc.get_tau_vis_Eta();
+	this->float_data[Details::VISTAU_PI0CL_PHI] = tpc.get_tau_vis_Phi();
+	this->float_data[Details::VISTAU_PI0CL_M]   = tpc.get_tau_vis_M();
+	this->float_data[Details::TAU_PI0_VISTAU_M] = tpc.get_tau_vis_M();
+	this->float_data[Details::TAU_PTRATIO]      = (tauJet.pt() != 0) ? tpc.get_tau_vis_Pt() / tauJet.pt() : LOW_NUMBER;
+	//	this->int_data[Details::TAU_PI0_N] 	    = 0; //this guy is set elsewhere
+
+	// TRACK_ISO
+	float track_iso(0.);
+	for (unsigned int i_track(0); i_track < tauJet.nWideTracks(); ++i_track) {
+		track_iso += tauJet.wideTrack(i_track)->pt();
+	}
+	this->float_data[Details::TRACK_ISO] = track_iso;
+
+	//Corrected CENTRALITY FRACTION and FTRK
+	float centFrac;
+	tauJet.detail(TauJetParameters::centFrac, centFrac);
+	float corrFtrk = this->float_data[Details::PTLEADTRKOVERET];
+
+	int nVtx = this->int_event_data[Details::NUM_PILEUP_AND_PRIMARY_VERTICES];
+
+	if (nVtx != int(LOW_NUMBER) && !this->doTrigger) {
+		if (tauJet.pt() < 80 * GeV)
+			centFrac = centFrac + 0.003 * nVtx;
+
+		if (corrFtrk != float(LOW_NUMBER))
+			corrFtrk = corrFtrk + 0.003 * nVtx;
+	}
+
+	this->float_data[Details::CORRCENTFRAC] = centFrac;
+	this->float_data[Details::CORRFTRK]     = corrFtrk;
+
+	return true;
+}
+
+// non-const version of update
+bool TauDetailsManager::update_with_edm(xAOD::TauJet& tauJet)
+{
+	// update first the internal storage of the DetailsManager and calculate variables
+	if (!this->update(tauJet)) return false;
+
+	// update now the tau itself
+	tauJet.setDetail(TauJetParameters::ptRatioEflowTopo,	static_cast<float>(this->float_data[Details::TAU_PTRATIO]));
+	tauJet.setDetail(TauJetParameters::mEflowTopo,			static_cast<float>(this->float_data[Details::TAU_PI0_VISTAU_M]));
+	tauJet.setDetail(TauJetParameters::etEflowTopo,			static_cast<float>(this->float_data[Details::VISTAU_PI0CL_PT]));
+	//nPi0 saved in TauDetailsManager::setPi0(int nPi0) method
+	return true;
+}
+
+// Var Getters
+const float* TauDetailsManager::getFloatDetailAddress(Details::FloatTauDetail detail) const
+{
+	return &this->float_data[detail];
+}
+
+const int* TauDetailsManager::getIntDetailAddress(Details::IntTauDetail detail) const
+{
+	return &this->int_data[detail];
+}
+
+const float* TauDetailsManager::getFloatDetailAddress(Details::FloatEventDetail detail) const
+{
+	return &this->float_event_data[detail];
+}
+
+const int* TauDetailsManager::getIntDetailAddress(Details::IntEventDetail detail) const
+{
+	return &this->int_event_data[detail];
+}
+
+float TauDetailsManager::getFloatDetailValue(Details::FloatTauDetail detail) const
+{
+	return this->float_data[detail];
+}
+
+int TauDetailsManager::getIntDetailValue(Details::IntTauDetail detail) const
+{
+	return this->int_data[detail];
+}
+
+float TauDetailsManager::getFloatDetailValue(Details::FloatEventDetail detail) const
+{
+	return this->float_event_data[detail];
+}
+
+int TauDetailsManager::getIntDetailValue(Details::IntEventDetail detail) const
+{
+	return this->int_event_data[detail];
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDiscriBuilder.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDiscriBuilder.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..01a9407dccfc756e1285c848422bfc27fb517bdb
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDiscriBuilder.cxx
@@ -0,0 +1,209 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//-----------------------------------------------------------------------------
+// file:        TauDiscriBuilder.cxx
+// package:     PhysicsAnalysis/TauID/TauDiscriminant
+// authors:     M. Wolter, A. Kaczmarska, Noel Dawe
+// date:        13 March 2008
+//-----------------------------------------------------------------------------
+
+#include "TauDiscriminant/TauDiscriBuilder.h"
+#include "TauDiscriminant/TauDiscriToolBase.h"
+#include "TauDiscriminant/FakeTauBits.h"
+#include "TauDiscriminant/FakeTauScores.h"
+
+#include "xAODTau/TauJetContainer.h"
+#include "xAODTau/TauJet.h"
+// #include "tauEvent/TauCommonDetails.h"
+#include "xAODTau/TauDefs.h"
+
+#include "AthenaBaseComps/AthMessaging.h"
+#include "GaudiKernel/ListItem.h"
+#include "StoreGate/StoreGateSvc.h"
+
+using namespace xAOD;
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+TauDiscriBuilder::TauDiscriBuilder( const std::string &name,
+		ISvcLocator * pSvcLocator ) :
+    		AthAlgorithm( name, pSvcLocator ),
+    		tauInputContainerName( "TauContainer" ),
+    		tools( this ), //make tools private
+    		manager(0)
+{
+	declareProperty( "container", tauInputContainerName );
+	declareProperty( "tools", tools, "List of TauDiscriToolBase tools" );
+}
+
+//-----------------------------------------------------------------------------
+// Destructor
+//-----------------------------------------------------------------------------
+TauDiscriBuilder::~TauDiscriBuilder()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Initializer
+//-----------------------------------------------------------------------------
+StatusCode TauDiscriBuilder::initialize()
+{
+	StatusCode sc = StatusCode::SUCCESS;
+
+	this->manager = new TauDetailsManager(&*evtStore());
+
+	//-------------------------------------------------------------------------
+	// No tools allocated!
+	//-------------------------------------------------------------------------
+	if( this->tools.size() == 0 )
+	{
+		return StatusCode::FAILURE;
+	}
+
+	//-------------------------------------------------------------------------
+	// Allocate tools
+	//-------------------------------------------------------------------------
+	ToolHandleArray<TauDiscriToolBase>::iterator tool_it(this->tools.begin());
+	ToolHandleArray<TauDiscriToolBase>::iterator tool_end(this->tools.end());
+	ATH_MSG_INFO("------------------------------------");
+	ATH_MSG_INFO("List of tools in execution sequence:");
+	unsigned int tool_count(0);
+
+	for(; tool_it != tool_end; ++tool_it )
+	{
+		if( tool_it->retrieve().isFailure() )
+		{
+			ATH_MSG_WARNING("Cannot find tool named <" << *tool_it << ">");
+		}
+		else
+		{
+			++tool_count;
+			ATH_MSG_INFO(tool_it->name());
+			if( (*tool_it)->prepare(*this->manager).isFailure() )
+			{
+				ATH_MSG_FATAL("Initialization failed in tool " << tool_it->name());
+				return sc;
+			}
+		}
+	}
+	ATH_MSG_INFO("------------------------------------");
+
+	if(tool_count == 0)
+	{
+		ATH_MSG_ERROR("Did not allocate any tool!");
+		return StatusCode::FAILURE;
+	}
+	return sc;
+}
+
+//-----------------------------------------------------------------------------
+// Finalizer
+//-----------------------------------------------------------------------------
+StatusCode TauDiscriBuilder::finalize()
+{
+	delete this->manager;
+	return StatusCode::SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Execution
+//-----------------------------------------------------------------------------
+StatusCode TauDiscriBuilder::execute()
+{
+	xAOD::TauJetContainer  *tau_container;
+	const xAOD::TauJetContainer  *const_tau_container;
+	StatusCode sc = evtStore()->retrieve(const_tau_container, this->tauInputContainerName);
+
+	tau_container = const_cast<TauJetContainer *>(const_tau_container);
+
+	if (sc.isFailure() || ! tau_container)
+	{
+		ATH_MSG_WARNING("No input tau container found!");
+		sc = StatusCode::SUCCESS;
+		return sc;
+	}
+	ATH_MSG_VERBOSE("Processing input tau Container Name = " << this->tauInputContainerName);
+
+	FakeTauBits* fakeBits(0);
+	FakeTauBitsContainer* fakeBitsContainer(new FakeTauBitsContainer());
+
+	FakeTauScores* fakeScores(0);
+	FakeTauScoresContainer* fakeScoresContainer(new FakeTauScoresContainer());
+
+	// Update event-based variables
+	if (!this->manager->updateEvent())
+	{
+		ATH_MSG_WARNING("Updating event-based variables in TauDetailsManager failed! Do not trust discriminant outputs!");
+		return StatusCode::SUCCESS;
+	}
+
+	xAOD::TauJetContainer::iterator tau_it(tau_container->begin());
+	xAOD::TauJetContainer::iterator tau_end(tau_container->end());
+
+	// Loop over tau's:
+	for (; tau_it != tau_end; ++tau_it)
+	{
+		if (!this->manager->update_with_edm(**tau_it))
+		{
+			ATH_MSG_WARNING("Updating tau-based variables in TauDetailsManager failed! Do not trust discriminant outputs!");
+			return StatusCode::SUCCESS;
+		}
+
+		ATH_MSG_VERBOSE(*this->manager);
+
+		fakeBits = new FakeTauBits(*tau_it);
+		fakeScores = new FakeTauScores(*tau_it);
+
+		//-----------------------------------------------------------------
+		// Process the candidate
+		//-----------------------------------------------------------------
+		ToolHandleArray<TauDiscriToolBase>::iterator tool_it(this->tools.begin());
+		ToolHandleArray<TauDiscriToolBase>::iterator tool_end(this->tools.end());
+
+		//-----------------------------------------------------------------
+		// Loop stops when Failure indicated by one of the tools
+		//-----------------------------------------------------------------
+		for(; tool_it != tool_end; ++tool_it )
+		{
+			ATH_MSG_VERBOSE("Invoking tool " << tool_it->name());
+			sc = (*tool_it)->execute( *tau_it, fakeBits, fakeScores);
+			if( sc.isFailure() )
+			{
+				ATH_MSG_FATAL("Execute failed in tool " << tool_it->name());
+				return sc;
+			}
+			// TEMPORARY HACK
+			ATH_MSG_VERBOSE("Tool name: "<<tool_it->name());
+			if(tool_it->name() == "TauPi0BDT")
+			{
+				ATH_MSG_VERBOSE("HACK FOR NPI0S");
+				float Primary   = fakeScores->getScore(TauScore::BDT_PI0_PRIMARY);
+				float Secondary = fakeScores->getScore(TauScore::BDT_PI0_SECONDARY);
+				int nPi0s = 0;
+				if (Primary < 0.465)	nPi0s += 1;
+				if (Secondary < 0.565)	nPi0s += 1;
+
+				this->manager->setNpi0(**tau_it,nPi0s);
+			}
+			ATH_MSG_VERBOSE(*this->manager);
+
+		}
+		fakeBitsContainer->push_back(fakeBits);
+		fakeScoresContainer->push_back(fakeScores);
+	}
+
+	sc = evtStore()->record(fakeBitsContainer, "FakeTauBitsContainer", false);
+	if (sc.isFailure())
+	{
+		ATH_MSG_WARNING("Could not record FakeTauBitsContainer in StoreGate!");
+		return StatusCode::FAILURE;
+	}
+	sc = evtStore()->record(fakeScoresContainer, "FakeTauScoresContainer", false);
+	if (sc.isFailure())
+	{
+		ATH_MSG_WARNING("Could not record FakeTauScoresContainer in StoreGate!");
+	}
+	return sc;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDiscriToolBase.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDiscriToolBase.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8c2e574a6fea8e99a4ee2895bb4735b5ee7faf60
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauDiscriToolBase.cxx
@@ -0,0 +1,14 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//-----------------------------------------------------------------------------
+// file:        TauDiscriToolBase.cxx
+// package:     TauDiscriminant
+// authors:     M. Wolter, A. Kaczmarska
+// date:        13 March 2008
+//-----------------------------------------------------------------------------
+
+#include "TauDiscriminant/TauDiscriToolBase.h"
+
+
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauEleBDT.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauEleBDT.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..cd9576f04f68126ea9dd536ae40e9f0b5abea99b
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauEleBDT.cxx
@@ -0,0 +1,209 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Tool for BDT analysis.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#include "TauDiscriminant/TauEleBDT.h"
+#include "TH2F.h"
+#include <PathResolver/PathResolver.h>
+
+using namespace xAOD;
+
+StatusCode TauEleBDT::prepare(const TauDetailsManager& manager)
+{
+    if (this->eleBDTFile != "")
+    {
+        string eleBDTPath = PathResolver::find_file(this->eleBDTFile, "DATAPATH");
+        if(eleBDTPath == "")
+        {
+            msg(MSG::FATAL) << "File: " << this->eleBDTFile << " not found! " << endreq;
+            return StatusCode::FAILURE;
+        }
+         
+        this->eleBDT = new MethodBDT("TauBDT:EleBDT");
+        this->eleBDT->setDetails(manager);
+        
+        if (!this->eleBDT->build(eleBDTPath))
+        {
+            msg(MSG::FATAL) << "Loading electron BDT file " << eleBDTPath << " failed!" << endreq;
+            return StatusCode::FAILURE;
+        }
+        
+        if (this->eleBitsFile != "")
+        {
+            string eleBitsPath = PathResolver::find_file(this->eleBitsFile, "DATAPATH");
+            if(eleBitsPath == "")
+            {
+                msg(MSG::FATAL) << "File: " << this->eleBitsFile << " not found! " << endreq;
+                return StatusCode::FAILURE;
+            }
+
+            this->eleBits = new MethodCuts("TauBDT:EleBits");
+            this->eleBits->setDetails(manager);
+            this->eleBits->addVariable("BDT",&(this->eleScore),'F');
+
+            if (!this->eleBits->build(eleBitsPath))
+            {
+                msg(MSG::FATAL) << "Loading ele bits file " << eleBitsPath << " failed!" << endreq;
+                return StatusCode::FAILURE;
+            }
+        }
+	if(this->eleBitsRootFile != ""){
+		string eleBitsRootPath = PathResolver::find_file(this->eleBitsRootFile, "DATAPATH");
+        	if(eleBitsRootPath == "")
+	        {
+        	    msg(MSG::FATAL) << "File: " << this->eleBitsRootFile << " not found! " << endreq;
+	            return StatusCode::FAILURE;
+        	}
+
+
+		this->cutsFile = new TFile(eleBitsRootPath.c_str());	
+		if(this->cutsFile){
+		        this->hloose  = (TH2F*)this->cutsFile->Get("h2_BDTEleDecision_pteta_loose");
+		       	this->hmedium = (TH2F*)this->cutsFile->Get("h2_BDTEleDecision_pteta_medium");
+               		this->htight  = (TH2F*)this->cutsFile->Get("h2_BDTEleDecision_pteta_tight");
+		}
+
+	}
+	else
+	{
+        	msg(MSG::FATAL) << "No BDT bits file was specified!" << endreq;
+	     	return StatusCode::FAILURE;
+        }
+
+	
+    }
+    else
+    {
+        msg(MSG::FATAL) << "No BDTs were initialized!" << endreq;
+        return StatusCode::FAILURE;
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode TauEleBDT::execute(xAOD::TauJet *tauJet, FakeTauBits* /*bits*/, FakeTauScores* /*scores*/)
+{
+   
+//     Analysis::TauPID* tauJetID = tauJet->tauID();
+
+    // Initialize scores
+    tauJet->setDiscriminant(TauJetParameters::BDTEleScore, 0.);
+    
+    // Initialize bits
+    tauJet->setIsTau(TauJetParameters::EleBDTLoose, 0);
+    tauJet->setIsTau(TauJetParameters::EleBDTMedium, 0);
+    tauJet->setIsTau(TauJetParameters::EleBDTTight, 0);
+
+    // do not assign a meaningful score for tau1P3P-only candidates. return.
+    
+
+    // Set the response of the electron BDT
+    if (this->eleBDT)
+    {
+        this->eleScore = this->eleBDT->response();
+        if (msgLvl(MSG::VERBOSE))
+        {
+            msg(MSG::VERBOSE) << "BDTEleScore: " << this->eleScore << endreq;
+        }
+        if (this->eleScore < 0. || this->eleScore > 1.)
+        {
+            msg(MSG::ERROR) << "Error in computing BDTElecScore!" << endreq;
+        }
+        tauJet->setDiscriminant(TauJetParameters::BDTEleScore, this->eleScore);
+    }
+
+    // if (this->eleBDT && this->eleBits) SL: comment out, set bits by hand
+    if (this->eleBDT)
+    {
+        
+        // SL: set bits by hand, do not use bits file
+        //tauJetID->setIsTau(TauJetParameters::EleBDTLoose, this->eleBits->response(0));
+        //tauJetID->setIsTau(TauJetParameters::EleBDTMedium, this->eleBits->response(1));
+        //tauJetID->setIsTau(TauJetParameters::EleBDTTight, this->eleBits->response(2));
+
+      if (tauJet->nTracks() == 1){
+	double eta = fabs(tauJet->track(0)->eta());
+	double pt = tauJet->pt();
+
+	if(!this->cutsFile) {
+		msg(MSG::ERROR)<<"Cannot open EleBDT cut file"<<endreq;
+		tauJet->setIsTau(TauJetParameters::EleBDTLoose,  0 );
+	        tauJet->setIsTau(TauJetParameters::EleBDTMedium, 0 );
+        	tauJet->setIsTau(TauJetParameters::EleBDTTight,  0 );
+
+		return StatusCode::SUCCESS;
+	}
+
+	if(!hloose || !hmedium || !htight){
+		msg(MSG::ERROR)<<"Cannot get EleBDT cut histograms"<<endreq;
+                tauJet->setIsTau(TauJetParameters::EleBDTLoose,  0 );
+                tauJet->setIsTau(TauJetParameters::EleBDTMedium, 0 );
+                tauJet->setIsTau(TauJetParameters::EleBDTTight,  0 );
+
+		
+                return StatusCode::SUCCESS;
+
+	}
+
+	if(pt/1000. > 799) pt = 799*1000.0;
+	if(eta > 2.99) eta = 2.99;
+
+	float score_loose = hloose->GetBinContent(hloose->FindBin(pt/1000.,eta));
+	bool failed_loose =  this->eleScore < score_loose;
+
+	float score_medium = hmedium->GetBinContent(hmedium->FindBin(pt/1000.,eta));
+	bool failed_medium =  this->eleScore < score_medium;
+
+	float score_tight = htight->GetBinContent(htight->FindBin(pt/1000.,eta));
+	bool failed_tight =  this->eleScore < score_tight;
+
+	tauJet->setIsTau(TauJetParameters::EleBDTLoose,  failed_loose );
+        tauJet->setIsTau(TauJetParameters::EleBDTMedium, failed_medium );
+        tauJet->setIsTau(TauJetParameters::EleBDTTight,  failed_tight );
+
+
+
+
+	
+
+      }
+      else if(tauJet->nTracks() > 1){
+	  tauJet->setIsTau(TauJetParameters::EleBDTLoose,  0 );
+          tauJet->setIsTau(TauJetParameters::EleBDTMedium, 0 );
+          tauJet->setIsTau(TauJetParameters::EleBDTTight,  0 );
+	  
+		
+
+      }
+      
+      if (msgLvl(MSG::VERBOSE))
+        {
+	  msg(MSG::VERBOSE) << "Passes ele loose: " << tauJet->isTau(TauJetParameters::EleBDTLoose) << endreq;
+	  msg(MSG::VERBOSE) << "Passes ele medium: " << tauJet->isTau(TauJetParameters::EleBDTMedium) << endreq;
+	  msg(MSG::VERBOSE) << "Passes ele tight: " << tauJet->isTau(TauJetParameters::EleBDTTight) << endreq;
+        }
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode TauEleBDT::finalize()
+{
+    if(this->cutsFile){
+	this->cutsFile->Close();
+	delete this->cutsFile;
+	//delete this->hloose;
+	//delete this->hmedium;
+	//delete this->htight;
+    }
+        
+    delete this->eleBDT;
+    delete this->eleBits;
+    return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauJetBDT.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauJetBDT.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d532cab8ed4f434ae6a36adb39ddfdf568c224ed
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauJetBDT.cxx
@@ -0,0 +1,245 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Tool for BDT analysis.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#include "TauDiscriminant/TauJetBDT.h"
+
+using namespace xAOD;
+
+StatusCode TauJetBDT::prepare(const TauDetailsManager& manager)
+{
+    if (this->jetBDTFile != "")
+    {
+        string jetBDTPath = PathResolver::find_file(this->jetBDTFile, "DATAPATH");
+    
+        if(jetBDTPath == "")
+        {
+            msg(MSG::FATAL) << "File: " << this->jetBDTFile << " not found! " << endreq;
+            return StatusCode::FAILURE;
+        }
+    
+        this->jetBDT = new MethodBDT("TauBDT:JetBDT");
+        this->jetBDT->setDetails(manager);
+    
+        if (!this->jetBDT->build(jetBDTPath))
+        {
+            msg(MSG::FATAL) << "Loading jet BDT file " << jetBDTPath << " failed!" << endreq;
+            return StatusCode::FAILURE;
+        }
+        
+        if (this->jetSigBitsFile != "")
+        {
+            string jetSigBitsPath = PathResolver::find_file(this->jetSigBitsFile, "DATAPATH");
+            if(jetSigBitsPath == "")
+            {
+                msg(MSG::FATAL) << "File: " << this->jetSigBitsFile << " not found! " << endreq;
+                return StatusCode::FAILURE;
+            }
+            
+            this->jetSigBits = new MethodCuts("TauBDT:JetSigBits");
+            this->jetSigBits->setDetails(manager);
+            this->jetSigBits->addVariable("BDT",&(this->jetScore),'F');
+
+            if (!this->jetSigBits->build(jetSigBitsPath))
+            {
+                msg(MSG::FATAL) << "Loading jet bits file " << jetSigBitsPath << " failed!" << endreq;
+                return StatusCode::FAILURE;
+            }
+        }
+
+        // Flat signal transformed jet score
+        if (this->jetSigTransFile != "")
+        {
+            string jetSigTransPath = PathResolver::find_file(this->jetSigTransFile, "DATAPATH");
+        
+            if(jetSigTransPath == "")
+            {
+                msg(MSG::FATAL) << "File: " << this->jetSigTransFile << " not found! " << endreq;
+                return StatusCode::FAILURE;
+            }
+        
+            this->jetSigTrans = new MethodTransform("TauBDT:JetBDT:SignalTranform");
+            this->jetSigTrans->setDetails(manager);
+            this->jetSigTrans->addVariable("BDT",&(this->jetScore),'F');
+        
+            if (!this->jetSigTrans->build(jetSigTransPath))
+            {
+                msg(MSG::FATAL) << "Loading jet BDT signal transformation file " << jetSigTransPath << " failed!" << endreq;
+                return StatusCode::FAILURE;
+            }
+        }
+
+        // Flat background transformed jet score
+        if (this->jetBkgTransFile != "")
+        {
+            string jetBkgTransPath = PathResolver::find_file(this->jetBkgTransFile, "DATAPATH");
+        
+            if(jetBkgTransPath == "")
+            {
+                msg(MSG::FATAL) << "File: " << this->jetBkgTransFile << " not found! " << endreq;
+                return StatusCode::FAILURE;
+            }
+        
+            this->jetBkgTrans = new MethodTransform("TauBDT:JetBDT:BackgroundTranform");
+            this->jetBkgTrans->setDetails(manager);
+            this->jetBkgTrans->addVariable("BDT",&(this->jetScore),'F');
+                    
+            if (!this->jetBkgTrans->build(jetBkgTransPath))
+            {
+                msg(MSG::FATAL) << "Loading jet BDT background transformation file " << jetBkgTransPath << " failed!" << endreq;
+                return StatusCode::FAILURE;
+            }
+        }
+        if (this->jetBkgBitsFile != "")
+        {
+            string jetBkgBitsPath = PathResolver::find_file(this->jetBkgBitsFile, "DATAPATH");
+            if(jetBkgBitsPath == "")
+            {
+                msg(MSG::FATAL) << "File: " << this->jetBkgBitsFile << " not found! " << endreq;
+                return StatusCode::FAILURE;
+            }
+            
+            this->jetBkgBits = new MethodCuts("TauBDT:JetBkgBits");
+            this->jetBkgBits->setDetails(manager);
+            this->jetBkgBits->addVariable("BDT",&(this->jetScore),'F');
+                         
+            if (!this->jetBkgBits->build(jetBkgBitsPath))
+            {
+                msg(MSG::FATAL) << "Loading jet bits file " << jetBkgBitsPath << " failed!" << endreq;
+                return StatusCode::FAILURE;
+            }
+        }
+    }
+    else
+    {
+        msg(MSG::FATAL) << "No BDTs were initialized!" << endreq;
+        return StatusCode::FAILURE;
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode TauJetBDT::execute(xAOD::TauJet *tauJet, FakeTauBits* /*bits*/, FakeTauScores* /*scores*/)
+{
+   
+//     Analysis::TauPID* tauJetID = tauJet->tauID();
+    bool loose, medium, tight;
+
+    // Initialize scores
+    tauJet->setDiscriminant(TauJetParameters::BDTJetScore, static_cast<float>(0.));
+    tauJet->setDiscriminant(TauJetParameters::BDTJetScoreSigTrans, static_cast<float>(0.));
+    tauJet->setDiscriminant(TauJetParameters::BDTJetScoreBkgTrans, static_cast<float>(0.));
+    
+    // Initialize bits
+    tauJet->setIsTau(TauJetParameters::JetBDTSigLoose, false);
+    tauJet->setIsTau(TauJetParameters::JetBDTSigMedium, false);
+    tauJet->setIsTau(TauJetParameters::JetBDTSigTight, false);
+    
+    tauJet->setIsTau(TauJetParameters::JetBDTBkgLoose, false);
+    tauJet->setIsTau(TauJetParameters::JetBDTBkgMedium, false);
+    tauJet->setIsTau(TauJetParameters::JetBDTBkgTight, false);
+
+    // do not assign a meaningful score for tau1P3P-only candidates. return.
+    
+
+    // Set the response of the jet BDT
+    if (this->jetBDT)
+    {
+
+
+        this->jetScore = this->jetBDT->response();
+        if (msgLvl(MSG::VERBOSE))
+        {
+            msg(MSG::VERBOSE) << "BDTJetScore: " << this->jetScore << endreq;
+        }
+        if (this->jetScore < 0. || this->jetScore > 1.)
+        {
+            msg(MSG::ERROR) << "Error in computing BDTJetScore!" << endreq;
+        }
+        tauJet->setDiscriminant(TauJetParameters::BDTJetScore, this->jetScore);
+    }
+    else
+    {
+        tauJet->setDiscriminant(TauJetParameters::BDTJetScore, 0.);
+    }
+
+    if (this->jetBDT && this->jetSigBits)
+    {
+        loose = this->jetSigBits->response(0);
+        medium = this->jetSigBits->response(1);
+        tight = this->jetSigBits->response(2);
+        tauJet->setIsTau(TauJetParameters::JetBDTSigLoose, loose);
+        tauJet->setIsTau(TauJetParameters::JetBDTSigMedium, medium);
+        tauJet->setIsTau(TauJetParameters::JetBDTSigTight, tight);
+        if (msgLvl(MSG::DEBUG))
+        {
+            if (!((!loose && !medium && !tight) || (loose && !medium && !tight) || (loose && medium && !tight) || (loose && medium && tight)))
+            {
+                msg(MSG::VERBOSE) << "Bad bits!" << endreq;
+            }
+            msg(MSG::DEBUG) << "ET: " << tauJet->pt() << endreq;
+            msg(MSG::DEBUG) << "jet sig loose: " << tauJet->isTau(TauJetParameters::JetBDTSigLoose) << endreq;
+            msg(MSG::DEBUG) << "jet sig medium: " << tauJet->isTau(TauJetParameters::JetBDTSigMedium) << endreq;
+            msg(MSG::DEBUG) << "jet sig tight: " << tauJet->isTau(TauJetParameters::JetBDTSigTight) << endreq;
+        }
+    }
+
+    if (this->jetSigTrans)
+    {
+        float jetSigTransScore(this->jetSigTrans->response());
+        if (msgLvl(MSG::VERBOSE))
+        {
+            msg(MSG::VERBOSE) << "Signal Transformed BDTJetScore: " << jetSigTransScore << endreq;
+        }
+        tauJet->setDiscriminant(TauJetParameters::BDTJetScoreSigTrans, jetSigTransScore);
+    }
+    
+    if (this->jetBkgTrans)
+    {
+        float jetBkgTransScore(this->jetBkgTrans->response());
+        if (msgLvl(MSG::VERBOSE))
+        {
+            msg(MSG::VERBOSE) << "Background Transformed BDTJetScore: " << jetBkgTransScore << endreq;
+        }
+        tauJet->setDiscriminant(TauJetParameters::BDTJetScoreBkgTrans, jetBkgTransScore);
+    }
+    
+    if (this->jetBDT && this->jetBkgBits)
+    {
+        loose = this->jetBkgBits->response(0);
+        medium = this->jetBkgBits->response(1);
+        tight = this->jetBkgBits->response(2);
+        tauJet->setIsTau(TauJetParameters::JetBDTBkgLoose, loose);
+        tauJet->setIsTau(TauJetParameters::JetBDTBkgMedium, medium);
+        tauJet->setIsTau(TauJetParameters::JetBDTBkgTight, tight);
+        if (msgLvl(MSG::VERBOSE))
+        {
+            if (!((!loose && !medium && !tight) || (loose && !medium && !tight) || (loose && medium && !tight) || (loose && medium && tight)))
+            {
+                msg(MSG::VERBOSE) << "Bad bits!" << endreq;
+            }
+            msg(MSG::VERBOSE) << "ET: " << tauJet->pt() << endreq;
+            msg(MSG::VERBOSE) << "jet bkg loose: " << tauJet->isTau(TauJetParameters::JetBDTBkgLoose) << endreq;
+            msg(MSG::VERBOSE) << "jet bkg medium: " << tauJet->isTau(TauJetParameters::JetBDTBkgMedium) << endreq;
+            msg(MSG::VERBOSE) << "jet bkg tight: " << tauJet->isTau(TauJetParameters::JetBDTBkgTight) << endreq;
+        }
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode TauJetBDT::finalize()
+{
+    delete this->jetBDT;
+    delete this->jetSigBits;
+    delete this->jetBkgBits;
+    delete this->jetSigTrans;
+    delete this->jetBkgTrans;
+    return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauLLH.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauLLH.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3b966a1ce7e24fa215b1db0d25ddabb87457dd18
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauLLH.cxx
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**                                       
+ * file: TauLLH.cxx                    
+ *                                        
+ * Author: Martin Flechl (mflechl@cern.ch)
+ *
+ */
+
+#include "TauDiscriminant/TauLLH.h"
+#include "xAODTau/TauJetContainer.h"
+#include "xAODTau/TauJet.h"
+// #include "tauEvent/TauCommonDetails.h"
+// #include "tauEvent/TauPID.h"
+// #include "tauEvent/TauJetParameters.h"
+#include "xAODTau/TauDefs.h"
+#include <PathResolver/PathResolver.h>
+
+using namespace xAOD;
+
+//-----------------------------------------------------------------------------
+// Initializer
+//-----------------------------------------------------------------------------
+StatusCode TauLLH::prepare(const TauDetailsManager& manager)
+{
+    //defllh cannot be used yet
+    //  m_defllh = new MethodLLH("llhdef");
+    m_safellh = new MethodLLH("llhsafe");
+    //m_safellh = new MethodLLH("llhsafe",true);
+
+    //  m_defllh->setDetails(this->manager);
+    m_safellh->setDetails(manager);
+
+    std::string jetPDFPath = PathResolver::find_file(m_fileNameJetPDF, "DATAPATH");
+    std::string tauPDFPath = PathResolver::find_file(m_fileNameTauPDF, "DATAPATH");
+    std::string LMTCutsPath = PathResolver::find_file(m_fileNameLMTCuts, "DATAPATH");
+
+
+    std::string fileNames=tauPDFPath+","+jetPDFPath+","+LMTCutsPath;
+    if ( !m_safellh->build(fileNames) ) {
+        ATH_MSG_FATAL("unable to build safe likelihood");
+        return StatusCode::FAILURE;
+    }
+
+	// set trigger flag
+	m_safellh->setDoTrigger(manager);
+    
+	return StatusCode :: SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finalizer
+//-----------------------------------------------------------------------------
+StatusCode TauLLH :: finalize()
+{
+    //  delete m_defllh;
+    delete m_safellh;
+    return StatusCode::SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// Execution
+//-----------------------------------------------------------------------------
+StatusCode TauLLH::execute(xAOD::TauJet* tau, FakeTauBits* /*bits*/, FakeTauScores* /*scores*/)
+{
+//     Analysis::TauPID *p_tauid = tau->tauID();
+
+    bool loose = m_safellh->response(0);
+    bool medium = m_safellh->response(1);
+    bool tight = m_safellh->response(2);
+    float value = m_safellh->response(3); 
+
+    //default llh value
+    tau->setIsTau(TauJetParameters::TauLlhLoose, loose);
+    tau->setIsTau(TauJetParameters::TauLlhMedium, medium);
+    tau->setIsTau(TauJetParameters::TauLlhTight, tight);
+    tau->setDiscriminant(TauJetParameters::Likelihood, value);
+    tau->setDiscriminant(TauJetParameters::SafeLikelihood, value);
+
+    return StatusCode :: SUCCESS;
+} 
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauMuonVeto.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauMuonVeto.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..845b0b4d866e1828141de98c7c0e766f609953da
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauMuonVeto.cxx
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TauDiscriminant/TauMuonVeto.h"
+#include "xAODTau/TauJetContainer.h"
+#include "xAODTau/TauJet.h"
+// #include "tauEvent/TauCommonDetails.h"
+// #include "tauEvent/TauPID.h"
+#include "xAODTau/TauDefs.h"
+#include "TauDiscriminant/TauDetails.h"
+#include <PathResolver/PathResolver.h>
+
+using namespace xAOD;
+//-----------------------------------------------------------------------------
+// Initializer
+//-----------------------------------------------------------------------------
+StatusCode TauMuonVeto::prepare(const TauDetailsManager& manager)
+{
+    this->detailsManager = &manager;
+    return StatusCode::SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Finalizer
+//-----------------------------------------------------------------------------
+StatusCode TauMuonVeto::finalize()
+{
+    return StatusCode::SUCCESS;
+}
+
+bool tauMuonVeto(int ntracks, float EMfrac, float ptEt, float eta) {
+
+    if(ntracks != 1) return false;
+    if( (eta > -0.1 && eta < 0.1) || (eta > 1.15 && eta < 1.3) ){
+      if(EMfrac < 0.15 && ptEt > 0.9) return true;
+      if(EMfrac > 0.80 && ptEt > 1.0) return true;
+    }
+    else {
+      if(EMfrac < 0.18 && ptEt > 1.9) return true;
+      if(EMfrac > 0.82 && ptEt < 0.12) return true;
+    }
+    return false;
+
+}
+
+//-----------------------------------------------------------------------------
+// Execution
+//-----------------------------------------------------------------------------
+StatusCode TauMuonVeto::execute(xAOD::TauJet* tau, FakeTauBits* /*bits*/, FakeTauScores* /*scores*/)
+{
+    if (!detailsManager)
+    {
+        return StatusCode::FAILURE;
+    }
+
+    float ptEt = detailsManager->getFloatDetailValue(Details::PTLEADTRKOVERET);
+    float EMfrac = detailsManager->getFloatDetailValue(Details::EMFRACTIONATEMSCALE);
+
+    int ntracks = tau->nTracks();
+    float eta = tau->eta();
+
+    bool muVeto = tauMuonVeto(ntracks, EMfrac, ptEt, eta);
+
+//     Analysis::TauPID *p_tauid = tau->tauID();
+
+    tau->setIsTau(TauJetParameters::MuonVeto, muVeto);
+
+    return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauPi0BDT.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauPi0BDT.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d11b75f17c040d7b4b95c1839d84fdeb80fc4523
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauPi0BDT.cxx
@@ -0,0 +1,107 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * Tool for pi0 BDT analysis.
+ *
+ * Author: Noel Dawe (Noel%dot%Dawe%at%cern%dot%ch)
+ */
+
+#include "TauDiscriminant/TauPi0BDT.h"
+
+StatusCode TauPi0BDT::prepare(const TauDetailsManager& manager)
+{
+    // Primary pi0 BDT preparation
+    if (this->pi0BDTPrimaryFile != "")
+    {
+        string pi0BDTPrimaryPath = PathResolver::find_file(this->pi0BDTPrimaryFile, "DATAPATH");
+        if(pi0BDTPrimaryPath == "")
+        {
+            msg(MSG::FATAL) << "File: " << this->pi0BDTPrimaryFile << " not found! " << endreq;
+            return StatusCode::FAILURE;
+        }
+         
+        this->pi0BDTPrimary = new MethodBDT("TauBDT:Pi0BDTPrimary");
+        this->pi0BDTPrimary->addVariable("EMPOverTrkSysP", manager.getFloatDetailAddress(Details::EMPOVERTRKSYSP),'F');
+        this->pi0BDTPrimary->addVariable("ChPiEMEOverCaloEME", manager.getFloatDetailAddress(Details::CHPIEMEOVERCALOEME),'F');
+        this->pi0BDTPrimary->addVariable("PSSFraction", manager.getFloatDetailAddress(Details::PSSFRACTION),'F');
+        this->pi0BDTPrimary->addVariable("EtOverPtLeadTrk", manager.getFloatDetailAddress(Details::ETOVERPTLEADTRK),'F');
+        //this->pi0BDTPrimary->addVariable("mEflow", manager.getFloatDetailAddress(Details::MEFLOW),'F');
+        this->pi0BDTPrimary->addVariable("nStrip", manager.getIntDetailAddress(Details::NSTRIP),'I');
+
+        
+        if (!this->pi0BDTPrimary->build(pi0BDTPrimaryPath))
+        {
+            msg(MSG::FATAL) << "Loading primary pi0 BDT file " << pi0BDTPrimaryPath << " failed!" << endreq;
+            return StatusCode::FAILURE;
+        }
+    }
+
+    // Secondary pi0 BDT preparation
+    if (this->pi0BDTSecondaryFile != "")
+    {
+        string pi0BDTSecondaryPath = PathResolver::find_file(this->pi0BDTSecondaryFile, "DATAPATH");
+        if(pi0BDTSecondaryPath == "")
+        {
+            msg(MSG::FATAL) << "File: " << this->pi0BDTSecondaryFile << " not found! " << endreq;
+            return StatusCode::FAILURE;
+        }
+         
+        this->pi0BDTSecondary = new MethodBDT("TauBDT:Pi0BDTSecondary");
+        this->pi0BDTSecondary->addVariable("EMPOverTrkSysP", manager.getFloatDetailAddress(Details::EMPOVERTRKSYSP),'F');
+        this->pi0BDTSecondary->addVariable("ChPiEMEOverCaloEME", manager.getFloatDetailAddress(Details::CHPIEMEOVERCALOEME),'F');
+        this->pi0BDTSecondary->addVariable("PSSFraction", manager.getFloatDetailAddress(Details::PSSFRACTION),'F');
+        this->pi0BDTSecondary->addVariable("EtOverPtLeadTrk", manager.getFloatDetailAddress(Details::ETOVERPTLEADTRK),'F');
+        //this->pi0BDTSecondary->addVariable("mEflow", manager.getFloatDetailAddress(Details::MEFLOW),'F');
+        this->pi0BDTSecondary->addVariable("nStrip", manager.getIntDetailAddress(Details::NSTRIP),'I');
+        
+        if (!this->pi0BDTSecondary->build(pi0BDTSecondaryPath))
+        {
+            msg(MSG::FATAL) << "Loading secondary pi0 BDT file " << pi0BDTSecondaryPath << " failed!" << endreq;
+            return StatusCode::FAILURE;
+        }
+    }
+    
+    if (!this->pi0BDTPrimary && !this->pi0BDTSecondary)
+    {
+        msg(MSG::FATAL) << "No BDTs were initialized!" << endreq;
+        return StatusCode::FAILURE;
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode TauPi0BDT::execute(xAOD::TauJet* /*tauJet*/, FakeTauBits* /*bits*/, FakeTauScores* scores)
+{
+    // Get primary pi0 BDT score
+    if (this->pi0BDTPrimary && scores)
+    {
+        float pi0BDTPrimaryScore(this->pi0BDTPrimary->response());
+        if (msgLvl(MSG::VERBOSE))
+        {
+            msg(MSG::VERBOSE) << "Primary Pi0 BDT score: " << pi0BDTPrimaryScore << endreq;
+        }
+        scores->setScore(TauScore::BDT_PI0_PRIMARY, pi0BDTPrimaryScore);
+    }
+
+    // Get secondary pi0 BDT score
+    if (this->pi0BDTSecondary && scores)
+    {
+        float pi0BDTSecondaryScore(this->pi0BDTSecondary->response());
+        if (msgLvl(MSG::VERBOSE))
+        {
+            msg(MSG::VERBOSE) << "Secondary Pi0 BDT score: " << pi0BDTSecondaryScore << endreq;
+        }
+        scores->setScore(TauScore::BDT_PI0_SECONDARY, pi0BDTSecondaryScore);
+    }
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode TauPi0BDT::finalize()
+{
+    delete this->pi0BDTPrimary;
+    delete this->pi0BDTSecondary;
+    return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/TauPi0Clusters.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauPi0Clusters.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b496a1a05877d8fa2e191eeccfdd7017a97dd32f
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/TauPi0Clusters.cxx
@@ -0,0 +1,185 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+ /**
+ * file: TauPi0Clusters.cxx
+ *
+ * Author: Michel Trottier-McDonald (mtm@cern.ch)
+ */
+
+#include "TauDiscriminant/TauPi0Clusters.h"
+#include <utility>
+
+#include "xAODTau/TauJet.h"
+#include "xAODTau/TauJetContainer.h"
+#include "xAODTracking/TrackParticle.h"
+#include "xAODCaloEvent/CaloClusterContainer.h"
+#include "xAODCaloEvent/CaloCluster.h"
+#include "AnalysisUtils/AnalysisMisc.h"
+#include "xAODTracking/VertexContainer.h"
+#include "CLHEP/Geometry/Vector3D.h"
+#include "CaloGeoHelpers/CaloSampling.h"
+#include "CaloUtils/CaloVertexedCluster.h"
+
+#include <TLorentzVector.h>
+
+#include "math.h"
+
+//---------------------------------------------------------
+// Constructor
+//---------------------------------------------------------
+TauPi0Clusters::TauPi0Clusters(const xAOD::TauJet& tauJet) : m_cl1_Pt(0.),
+    m_cl1_Eta(0.),
+    m_cl1_Phi(0.),
+    m_cl2_Pt(0.),
+    m_cl2_Eta(0.),
+    m_cl2_Phi(0.),
+    m_tau_vis_Pt(0.),
+    m_tau_vis_Eta(0.),
+    m_tau_vis_Phi(0.),
+    m_tau_vis_M(0.)
+{
+    runPi0Finder(tauJet);
+}
+
+
+
+
+//---------------------------------------------------------
+// run Pi0Finder
+//---------------------------------------------------------
+void TauPi0Clusters::runPi0Finder(const xAOD::TauJet& tauJet)
+{
+    //Get tracks
+    int nTracks = tauJet.nTracks();
+    std::vector<TLorentzVector> tracks;
+
+    for(int i = 0; i < nTracks; ++i)
+    {
+	float track_Pt  = tauJet.track(i)->pt();
+	float track_Eta = tauJet.track(i)->eta();
+	float track_Phi = tauJet.track(i)->phi();
+
+	if(track_Pt > 0.0 and track_Eta < 5.0)
+	{
+	    TLorentzVector newTrack = TLorentzVector();
+	    newTrack.SetPtEtaPhiM(track_Pt, track_Eta, track_Phi, 0.0);
+	    tracks.push_back(newTrack);
+	}
+    }
+
+
+    //Get clusters
+
+    std::vector<const xAOD::CaloCluster*> Clusters;
+
+    const xAOD::Jet* pJetSeed = (*tauJet.jetLink());
+    xAOD::JetConstituentVector jcv = pJetSeed->getConstituents();
+    
+    xAOD::JetConstituentVector::const_iterator firstcluster = jcv.begin();
+    xAOD::JetConstituentVector::const_iterator lastcluster = jcv.end();
+
+    for ( ; firstcluster != lastcluster; firstcluster++ ) {
+    	const xAOD::CaloCluster *p_cluster = dynamic_cast<const xAOD::CaloCluster*> ((*firstcluster)->rawConstituent()); //to get to cluster-specific variables
+    	if (!p_cluster) continue;
+    	Clusters.push_back(p_cluster);
+    }
+
+
+    
+    // now insert clusters into event ordered by energy
+    // this makes it much faster to recalculate cluster-based
+    // variables in macros later
+    
+    int nClusters = int(Clusters.size());
+
+    std::vector<TLorentzVector> clusters;
+    std::vector<float> PSSFs;
+    std::vector<float> EM2Fs;
+    std::vector<float> EM3Fs;
+
+    if(nClusters > 0)
+    {
+
+	AnalysisUtils::Sort::e (&Clusters);
+	
+	for(int i = 0; i < nClusters; ++i)
+	{
+	    const xAOD::CaloCluster *p_cluster = Clusters[i];
+	    // Simplified Sampling information
+	    float PreSampler = p_cluster->eSample(CaloSampling::PreSamplerB) + p_cluster->eSample(CaloSampling::PreSamplerE);
+	    float EMLayer1   = p_cluster->eSample(CaloSampling::EMB1)        + p_cluster->eSample(CaloSampling::EME1);
+	    float EMLayer2   = p_cluster->eSample(CaloSampling::EMB2)        + p_cluster->eSample(CaloSampling::EME2);
+	    float EMLayer3   = p_cluster->eSample(CaloSampling::EMB3)        + p_cluster->eSample(CaloSampling::EME3);
+	    
+	    float Energy   = p_cluster->rawE();
+	    
+	    float PSSF = (PreSampler + EMLayer1)/Energy;
+	    float EM2F = EMLayer2/Energy;
+	    float EM3F = EMLayer3/Energy;
+	    
+	    if(PSSF < 0.) PSSF = 0.;
+	    if(PSSF > 1.) PSSF = 1.;
+	    
+	    if(EM2F < 0.) EM2F = 0.;
+	    if(EM2F > 1.) EM2F = 1.;
+	    
+	    if(EM3F < 0.) EM3F = 0.;
+	    if(EM3F > 1.) EM3F = 1.;
+
+	    xAOD::CaloVertexedCluster* clusterCorr;
+	    if (tauJet.vertexLink()) {
+	    	// Corrected cluster direction information (leave as jet constituents on purpose!)
+	    	clusterCorr = new xAOD::CaloVertexedCluster(*p_cluster, (*tauJet.vertexLink())->position());
+	    }
+	    else {
+	    	//no correction
+	    	clusterCorr = new xAOD::CaloVertexedCluster(*p_cluster);
+	    }
+
+	    float cluster_Eta = clusterCorr->eta();
+	    float cluster_Pt  = clusterCorr->e()/cosh(cluster_Eta);
+	    float cluster_Phi = clusterCorr->phi();
+
+	    if(cluster_Pt > 0.0 && cluster_Eta < 5.0)
+	    {
+		TLorentzVector newCluster = TLorentzVector();
+		newCluster.SetPtEtaPhiM(cluster_Pt, cluster_Eta, cluster_Phi, 0.0);
+		clusters.push_back(newCluster);
+		PSSFs.push_back(PSSF);
+		EM2Fs.push_back(EM2F);
+		EM3Fs.push_back(EM3F);
+	    }
+	    
+	    // clean up corrected cluster
+	    if (clusterCorr) delete clusterCorr;
+
+	}
+    }
+
+    if(nTracks > 0 && nClusters > 0)
+    {
+
+	Pi0Finder pi0F = Pi0Finder(tracks, clusters, PSSFs, EM2Fs, EM3Fs);
+	TLorentzVector cl1 = pi0F.pi0TLV1();
+	TLorentzVector cl2 = pi0F.pi0TLV2();
+	TLorentzVector tau = pi0F.visTauTLV();
+
+	m_cl1_Pt  = cl1.Pt();
+	if(m_cl1_Pt > 0.0)
+	    m_cl1_Eta = cl1.Eta();
+	m_cl1_Phi = cl1.Phi();
+
+	m_cl2_Pt  = cl2.Pt();
+	if(m_cl2_Pt > 0.0)
+	    m_cl2_Eta = cl2.Eta();
+	m_cl2_Phi = cl2.Phi();
+
+	m_tau_vis_Pt  = tau.Pt();
+	if(m_tau_vis_Pt > 0.0)
+	    m_tau_vis_Eta = tau.Eta();
+	m_tau_vis_Phi = tau.Phi();
+	m_tau_vis_M   = tau.M();
+    }
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/components/TauDiscri_entries.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/components/TauDiscri_entries.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..a7d37c09dd0470c4425ee394bd30fd98bbbea1c1
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/components/TauDiscri_entries.cxx
@@ -0,0 +1,32 @@
+#include "TauDiscriminant/TauDiscriBuilder.h"
+#include "TauDiscriminant/TauCutsEleVeto.h"
+#include "TauDiscriminant/TauCuts.h"
+#include "TauDiscriminant/TauLLH.h"
+#include "TauDiscriminant/TauJetBDT.h"
+#include "TauDiscriminant/TauEleBDT.h"
+#include "TauDiscriminant/TauPi0BDT.h"
+#include "TauDiscriminant/TauMuonVeto.h"
+#include "GaudiKernel/DeclareFactoryEntries.h"
+
+DECLARE_ALGORITHM_FACTORY( TauDiscriBuilder )
+
+DECLARE_TOOL_FACTORY( TauCutsEleVeto )
+DECLARE_TOOL_FACTORY( TauCuts )
+DECLARE_TOOL_FACTORY( TauLLH ) 
+DECLARE_TOOL_FACTORY( TauJetBDT )
+DECLARE_TOOL_FACTORY( TauEleBDT )
+DECLARE_TOOL_FACTORY( TauPi0BDT )
+DECLARE_TOOL_FACTORY( TauMuonVeto )
+
+DECLARE_FACTORY_ENTRIES(TauDiscriminant)
+{
+    DECLARE_ALGORITHM( TauDiscriBuilder )
+    
+    DECLARE_TOOL( TauCutsEleVeto )
+    DECLARE_TOOL( TauCuts )
+    DECLARE_TOOL( TauLLH )
+    DECLARE_TOOL( TauJetBDT )
+    DECLARE_TOOL( TauEleBDT )
+    DECLARE_TOOL( TauPi0BDT )
+    DECLARE_TOOL( TauMuonVeto )
+}
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/src/components/TauDiscri_load.cxx b/PhysicsAnalysis/TauID/TauDiscriminant/src/components/TauDiscri_load.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..e4cf8518b62a0cf004f4cfd7fbdd8bebcec77c6b
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/src/components/TauDiscri_load.cxx
@@ -0,0 +1,3 @@
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(TauDiscriminant)
diff --git a/PhysicsAnalysis/TauID/TauDiscriminant/svn-authors b/PhysicsAnalysis/TauID/TauDiscriminant/svn-authors
new file mode 100644
index 0000000000000000000000000000000000000000..7614aaf23a6fee0db3b66a8d94b026b8d9142293
--- /dev/null
+++ b/PhysicsAnalysis/TauID/TauDiscriminant/svn-authors
@@ -0,0 +1,22 @@
+alibrari = Atlas Librarian <Atlas.Librarian@cern.ch>
+end = Noel Dawe <Noel.Dawe@cern.ch>
+felzmann = Ulrich Felzmann <ulif@unimelb.edu.au>
+jgodfrey = Jennifer Godfrey <Jennifer.Lynn.Godfrey@cern.ch>
+mflechl = Martin Flechl <Martin.Flechl@cern.ch>
+reece = Ryan Reece <Ryan.Reece@cern.ch>
+slai = Stan Lai <Stan.Lai@cern.ch>
+wolter = Marcin Wolter <Marcin.Wolter@cern.ch>
+derue = Frederic Derue <Frederic.Derue@cern.ch>
+kojin = Koji Nakamura <Koji.Nakamura@cern.ch>
+morgens = Marcus Morgenstern <marcus.morgenstern@tu-dresden.de>
+felixf = Felix Friedrich <felix.friedrich@cern.ch>
+pmalecki = Pawel Malecki <pawel.malecki@cern.ch>
+mtm = Michel Trottier-McDonald <mta58@sfu.ca>
+kongt = KG <tankg@unimelb.edu.au>
+jkeller = John Stakely Keller <john.stakely.keller@cern.ch>
+rompotis = Nikolaos Rompotis <nikolaos.rompotis@cern.ch>
+sbedikia = Susie Bedikian <susie.bedikian@cern.ch>
+simonyan = Margar Simonyan <Margar.Simonyan@cern.ch>
+apingel = Almut Pingel <almut.pingel@cern.ch>
+wahrmund = Unknown Unknown <Unknown@cern.ch> 
+droussea = David Rousseau <rousseau@lal.in2p3.fr>