diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h b/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h
index 25c75195c70fb9157d393deb7fb75614f6be096b..201383c1b63db3e6755c49134f0a25bba915c121 100644
--- a/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h
+++ b/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h
@@ -25,5 +25,7 @@
 #include "JetRecTools/CorrectPFOTool.h"
 #include "JetRecTools/PuppiWeightTool.h"
 #include "JetRecTools/ChargedHadronSubtractionTool.h"
+#include "JetRecTools/TARJetTool.h"
+#include "JetRecTools/SATScaleTool.h"
 
 #endif
diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/SATScaleTool.h b/Reconstruction/Jet/JetRecTools/JetRecTools/SATScaleTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..2253c4442a47cfc8e9cd14e622f2bc131c54363c
--- /dev/null
+++ b/Reconstruction/Jet/JetRecTools/JetRecTools/SATScaleTool.h
@@ -0,0 +1,69 @@
+/* 
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// SATScaleTool.h
+
+#ifndef SATSCALETOOL_H
+#define SATSCALETOOL_H
+
+// Jason Veatch (jason.veatch@cern.ch)
+// November 2018
+//
+// Tool to produce rescaled tracks to be used as inputs to SAT jets
+// The output is a xAOD::TrackParticle collection
+
+#include "JetInterface/IJetExecuteTool.h"
+#include "AsgTools/AsgTool.h"
+
+#include "AthContainers/ConstDataVector.h"
+
+#include "JetRecTools/TrackAssistTool.h"
+
+class SATScaleTool 
+: public IJetExecuteTool,
+  public asg::AsgTool,
+  public TrackAssistTool
+{
+  ASG_TOOL_CLASS(SATScaleTool, IJetExecuteTool)
+
+  public:
+
+    // Constructor
+    SATScaleTool(const std::string& myname = "SATScaleTool" );
+
+    // Initialize function
+    StatusCode initialize() override;
+
+    // Print all configurable parameters
+    void print() const override;
+
+    // Execute the tool
+    int execute() const override;
+
+  private:
+
+    // Produce collection of rescaled tracks
+    StatusCode makeSATTracks() const;
+
+    // Local method to retrieve the track vertex association
+    StatusCode getTrackVertexAssociation( const jet::TrackVertexAssociation *&tva ) const override;
+
+    // Local method to retrieve the primary vertex
+    StatusCode getPrimaryVertex( const xAOD::Vertex *&pvx ) const override;
+
+    // If jet merging is turned on, flag jets that meet the merging criteria
+    StatusCode flagJetsToMerge( const xAOD::JetContainer *jets ) const;
+
+    // Configurable parameters
+    std::string m_inTrackColl; 
+    std::string m_inJetColl; 
+    std::string m_outTrackColl; 
+    float m_dRmatch;
+    bool m_doMerge;
+    float m_dRmerge;
+    float m_pTdRmerge;
+
+};
+
+#endif // SATSCALETOOL_H
diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/TARJetTool.h b/Reconstruction/Jet/JetRecTools/JetRecTools/TARJetTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..d5681a3b09399b634def936505a5c47008a47cc6
--- /dev/null
+++ b/Reconstruction/Jet/JetRecTools/JetRecTools/TARJetTool.h
@@ -0,0 +1,60 @@
+/* 
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// TARJetTool.h
+
+#ifndef TARJETTOOL_H
+#define TARJETTOOL_H
+
+// Jason Veatch (jason.veatch@cern.ch)
+// November 2018
+//
+// Tool to add rescaled tracks to reclustered jets in order to add TAR jet substructure moments
+// The output is a new collection of xAOD::TrackParticle and ElementLinks for each jet
+
+#include "JetInterface/IJetModifier.h"
+#include "AsgTools/AsgTool.h"
+
+#include "AthContainers/ConstDataVector.h"
+
+#include "JetRecTools/TrackAssistTool.h"
+
+class TARJetTool 
+: public IJetModifier,
+  public asg::AsgTool,
+  public TrackAssistTool
+{
+  ASG_TOOL_CLASS(TARJetTool, IJetModifier)
+
+  public:
+
+    // Constructor
+    TARJetTool(const std::string& myname = "TARJetTool" );
+
+    // Initialize function
+    StatusCode initialize() override;
+
+    // Print all configurable parameters 
+    void print() const override;
+
+    // Modify jet collection
+    int modify( xAOD::JetContainer& inJets ) const override;
+
+  private:
+
+    // Local method to retrieve the track vertex association
+    StatusCode getTrackVertexAssociation( const jet::TrackVertexAssociation *&tva ) const override;
+
+    // Local method to retrieve the primary vertex
+    StatusCode getPrimaryVertex( const xAOD::Vertex *&pvx ) const override;
+
+    // Configurable parameters
+    std::string m_inTrackColl;
+    std::string m_outTrackColl;
+    std::string m_assocTracksOutName;
+    float m_dRmatch;
+
+};
+
+#endif // TARJETTOOL_H
diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/TrackAssistTool.h b/Reconstruction/Jet/JetRecTools/JetRecTools/TrackAssistTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..c3494727888327277018e83443a9f7616db9797c
--- /dev/null
+++ b/Reconstruction/Jet/JetRecTools/JetRecTools/TrackAssistTool.h
@@ -0,0 +1,84 @@
+/* 
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// TrackAssist.h
+
+#ifndef TRACKASSISTTOOL_H
+#define TRACKASSISTTOOL_H
+
+// Jason Veatch (jason.veatch@cern.ch)
+// November 2018
+//
+// Base class for tools that implement track-assisted jet substructure algorithms
+
+#include "AsgTools/ToolHandle.h"
+#include "AsgTools/Check.h"
+#include "AsgTools/MessageCheck.h"
+
+#include "xAODCore/ShallowCopy.h"
+
+#include "xAODJet/Jet.h"
+#include "xAODJet/JetContainer.h"
+#include "xAODJet/JetAuxContainer.h"
+#include "xAODTracking/TrackParticle.h"
+#include "xAODTracking/TrackParticleContainer.h"
+#include "xAODTracking/TrackParticleAuxContainer.h"
+
+#include "xAODTracking/VertexContainer.h"
+#include "JetInterface/IJetTrackSelector.h"
+#include "JetEDM/TrackVertexAssociation.h"
+
+#include "xAODBase/IParticleHelpers.h"
+
+using namespace asg::msgUserCode;
+
+class TrackAssistTool
+{
+
+  public:
+
+    // Destructor.
+    virtual ~TrackAssistTool() { }
+
+    // Rescale tracks using jets with pre-determined scaling weights
+    StatusCode rescaleTracks( const xAOD::JetContainer *jets, xAOD::TrackParticleContainer *tracks) const;
+
+  protected:
+
+    // Configurable parameters
+    std::string m_assocTracksInName;
+    std::string m_vertexColl;
+    std::string m_trackVtxAssoc;
+    bool m_doTrackVtxAssoc;
+    ToolHandle<IJetTrackSelector> m_trackSelTool;
+
+    // Local method to check if track passes selection and vertex association criteria
+    bool isGoodTrack( const xAOD::TrackParticle &track, const xAOD::Vertex &pvx, const jet::TrackVertexAssociation &tva ) const;
+
+    // Constructor 
+    TrackAssistTool();
+
+    // Initialize function
+    virtual StatusCode initialize();
+
+    // Print configured parameters
+    virtual void print() const;
+
+    // Local method to retrieve all vertex information
+    StatusCode getVertexInfo( const xAOD::Vertex *&pvx, const jet::TrackVertexAssociation *&tva ) const;
+
+    // Local method to retrieve the track vertex association
+    virtual StatusCode getTrackVertexAssociation( const jet::TrackVertexAssociation *&tva ) const = 0;
+
+    // Local method to retrieve the primary vertex
+    virtual StatusCode getPrimaryVertex( const xAOD::Vertex *&pvx ) const = 0;
+
+  private:
+
+    // Local method to calculate the factors to rescale each track
+    StatusCode getRescaleFactors( const xAOD::Jet &jet, xAOD::TrackParticleContainer &tracks) const;
+
+};
+
+#endif // TRACKASSISTTOOL_H
diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml b/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml
index 624f405217aba358433a07d15e055304d8550fad..dbd65294113501eefc89b64a5ede3f0588891593 100644
--- a/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml
+++ b/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml
@@ -15,5 +15,7 @@
 <class name="TrackPseudoJetGetter"/>
 <class name="TrackVertexAssociationTool"/>
 <class name="VoronoiWeightTool"/>
+<class name="TARJetTool"/>
+<class name="SATScaleTool"/>
 
 </lcgdict>
diff --git a/Reconstruction/Jet/JetRecTools/Root/SATScaleTool.cxx b/Reconstruction/Jet/JetRecTools/Root/SATScaleTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f31c142bc7396716ec2969b1e045bf328dee4d54
--- /dev/null
+++ b/Reconstruction/Jet/JetRecTools/Root/SATScaleTool.cxx
@@ -0,0 +1,318 @@
+/* 
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// SATScaleTool.cxx
+
+#include "JetRecTools/SATScaleTool.h"
+
+SATScaleTool::SATScaleTool(const std::string& myname)
+: AsgTool(myname),
+  TrackAssistTool(),
+  m_inTrackColl(""),
+  m_inJetColl(""),
+  m_outTrackColl(""),
+  m_dRmatch(0.2),
+  m_doMerge(true),
+  m_dRmerge(0.35),
+  m_pTdRmerge(0.3)
+{
+  declareProperty("InputTrackContainer", m_inTrackColl = "InDetTrackParticles");
+  declareProperty("InputJetContainer", m_inJetColl);
+  declareProperty("OutputTrackContainer", m_outTrackColl);
+  declareProperty("MatchDeltaR", m_dRmatch);
+  declareProperty("DoMergeJets", m_doMerge);
+  declareProperty("MergeDeltaR", m_dRmerge);
+  declareProperty("MergePtOverDeltaR", m_pTdRmerge);
+
+  declareProperty("InputAssociatedTracks", m_assocTracksInName = "GhostTrack");
+  declareProperty("VertexContainer", m_vertexColl = "PrimaryVertices");
+  declareProperty("TrackVertexAssociation", m_trackVtxAssoc);
+  declareProperty("DoTrackVertexAssociation", m_doTrackVtxAssoc = true);
+  declareProperty("TrackSelTool", m_trackSelTool);
+}
+
+StatusCode SATScaleTool::initialize() 
+{
+
+  ATH_MSG_INFO("Initializing SATScaleTool " << name() << ".");
+  ATH_CHECK( TrackAssistTool::initialize() );
+  print();
+
+  return StatusCode::SUCCESS;
+}
+
+int SATScaleTool::execute() const {
+
+  if( makeSATTracks().isFailure() ) return 1;
+
+  return 0;
+}
+
+StatusCode SATScaleTool::makeSATTracks() const {
+
+  std::vector<int> matchedTrackIndices; // List of ghost matched track indices
+  std::vector<int> unmatchedTrackIndices; // List of non ghost matched track indices
+  std::vector<int> allGoodTrackIndices; // List of all good track indices
+
+  std::map<int, std::vector<int>> JetTrackMap; // Map of track indices to jet indices
+  std::map<int, std::vector<int> > JetMergeMap; // Map of jet indices for merging
+
+  // Get input jets
+  const xAOD::JetContainer *inJets = nullptr;
+  ATH_CHECK( evtStore()->retrieve(inJets,m_inJetColl) );
+
+  // Get input tracks
+  const xAOD::TrackParticleContainer *inTracks = nullptr;
+  ATH_CHECK( evtStore()->retrieve(inTracks,m_inTrackColl) );
+
+  // Get vertex objects
+  const xAOD::Vertex *pvx = nullptr;
+  const jet::TrackVertexAssociation *tva = nullptr;
+  ATH_CHECK( getVertexInfo(pvx,tva) );
+
+  // Make shallow copy of tracks
+  auto trackShallowCopy = xAOD::shallowCopyContainer(*inTracks);
+  std::unique_ptr<xAOD::TrackParticleContainer> outTracks (trackShallowCopy.first);
+  std::unique_ptr<xAOD::ShallowAuxContainer> outTracksAux (trackShallowCopy.second);
+
+  // Get list of all tracks
+  for( auto track : *inTracks ) {
+    // Make sure only tracks that pass selection are used
+    if( !isGoodTrack(*track,*pvx,*tva) ) continue;
+    allGoodTrackIndices.push_back( track->index() );
+  }
+
+  // Flag jets for merging
+  ATH_CHECK( flagJetsToMerge(inJets) );
+
+  // Loop over input jets
+  for( auto jet : *inJets ) {
+
+    // Get ghost-associated tracks
+    std::vector<const xAOD::TrackParticle*> myMatchedTracks = jet->getAssociatedObjects<xAOD::TrackParticle>(m_assocTracksInName);
+
+    // Loop over ghost-associated tracks
+    for( auto track : myMatchedTracks ) {
+
+      // Make sure only tracks that pass selection are used
+      if( !isGoodTrack(*track,*pvx,*tva) ) continue;
+
+      // Add to list of matched tracks      
+      matchedTrackIndices.push_back(track->index());
+
+      // Record which jet the track belongs to
+      JetTrackMap[jet->index()].push_back(track->index());
+    }     
+
+    // Fill map of jet indices to merge
+    int mergeIdx = jet->auxdata< int >("mergeIndex");
+    if( mergeIdx >= 0 ) {
+      int jetIdx = jet->index();
+      // Loop up the chain in case a nearest neighbor is also a jet to merge
+      while(inJets->at(mergeIdx)->auxdata< int >("mergeIndex") >= 0 ) {
+        mergeIdx = inJets->at(mergeIdx)->auxdata< int >("mergeIndex");
+      } 
+      JetMergeMap[mergeIdx].push_back(jetIdx);
+    }
+  }
+
+  // Sort track indices so the difference can be taken
+  std::sort( allGoodTrackIndices.begin(), allGoodTrackIndices.end() ); // Sort matched track indices
+  std::sort( matchedTrackIndices.begin(), matchedTrackIndices.end() ); // Sort all track indices
+
+  // Get list of unmatched track indices
+  std::set_difference(allGoodTrackIndices.begin(), allGoodTrackIndices.end(), matchedTrackIndices.begin(), matchedTrackIndices.end(), std::inserter(unmatchedTrackIndices,unmatchedTrackIndices.begin()));
+
+  // dR-match all remaining tracks
+  for( auto trackIdx : unmatchedTrackIndices ) {
+   
+    // Get input track 
+    auto track = inTracks->at(trackIdx);
+
+    float dRmin = 9999999;
+    int jetIdx = -1;
+    for(auto jet : *inJets) {
+      // Find nearest jet to track
+      if(track->p4().DeltaR(jet->p4()) < dRmin) {
+        dRmin = track->p4().DeltaR(jet->p4());
+        jetIdx = jet->index();
+      }
+    }
+
+    // Check if dR matching criteria is met
+    if(dRmin < m_dRmatch) {
+      // Record which jet the track belongs to
+      JetTrackMap[jetIdx].push_back(track->index());
+    }
+  }
+
+  // Done matching tracks to small-R jets
+
+  // Container for combined jets (and jets that survive the combination)
+  auto combinedJets = std::make_unique<xAOD::JetContainer>();
+  auto combinedJetsAux = std::make_unique<xAOD::JetAuxContainer>();
+  combinedJets->setStore( combinedJetsAux.get() );
+
+  for(auto jet : *inJets) {
+
+    int mergeIndex = jet->auxdata< int >("mergeIndex");
+
+    // Make sure jet doesn't need to be merged
+    if( mergeIndex < 0 ) {
+
+      // Copy jet into combined jet container
+      xAOD::Jet* combinedJet = new xAOD::Jet();
+      combinedJets->push_back (combinedJet);
+      *combinedJet = *jet;
+
+      for( auto trackIdx : JetTrackMap[jet->index()] ) {
+        // Set jet associations for associated tracks
+        // Nominally, each track is associated to a single jet, but this construction
+        // allows each track to be associated to multiple jets with a weight for each
+        // association that is used in the rescaling
+        outTracks->at(trackIdx)->auxdata< std::vector< std::pair<int, float> > >("JetAssociations").emplace_back(combinedJet->index(),1.0);
+      }
+
+      // Check if there are any jets to merge
+      for( auto mergeIdx : JetMergeMap[jet->index()]) {
+
+        // Add jet 4-vectors together
+        combinedJet->setJetP4(combinedJet->jetP4() + inJets->at(mergeIdx)->jetP4());
+
+        for( auto trackIdx : JetTrackMap[mergeIdx] ) {
+          // Set merged jet track associations to combined jet
+          outTracks->at(trackIdx)->auxdata< std::vector< std::pair<int, float> > >("JetAssociations").emplace_back(combinedJet->index(),1.0);
+        }
+
+      }
+
+    }
+  }
+
+  // Rescale tracks
+  ATH_CHECK( rescaleTracks(inJets,outTracks.get())  );
+
+  // Create view container to store only selected tracks
+  std::unique_ptr< ConstDataVector<xAOD::TrackParticleContainer> > outSelTracks = std::make_unique< ConstDataVector< xAOD::TrackParticleContainer> >(SG::VIEW_ELEMENTS);
+
+  // Fill view container if track passes selection requirements
+  for(auto goodIdx : allGoodTrackIndices) {
+    outSelTracks->push_back(outTracks->at(goodIdx));
+  }
+  
+  ATH_MSG_DEBUG("New SATTrack container size " << outSelTracks->size());
+
+  ATH_CHECK( evtStore()->record(outSelTracks.release(),"Sel"+m_outTrackColl) );
+
+  // Record output tracks
+  ATH_CHECK( evtStore()->record(outTracks.release(),m_outTrackColl) );
+  ATH_CHECK( evtStore()->record(outTracksAux.release(),m_outTrackColl+"Aux.") );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode SATScaleTool::flagJetsToMerge( const xAOD::JetContainer *jets ) const {
+
+  ATH_MSG_DEBUG("In SATScaleTool::flagJetsToMerge");
+
+  for( auto *jet : *jets ) {
+    
+    // Don't merge if flag is set to false
+    if(!m_doMerge) {
+      jet->auxdecor< int >("mergeIndex") = -1;
+      continue;
+    }
+
+    float dRmin = 999;
+    float ptRatioDR = -1;
+    int neighborIndex = -1;
+
+    for ( auto *jet2 : *jets ) {
+
+      // Don't use jet as its own nearest neighbor
+      if( jet == jet2 ) continue;
+
+      float dR = jet->p4().DeltaR(jet2->p4());
+
+      // Find nearest neighbor jet
+      if( dR < dRmin ) {
+        dRmin = dR;
+        // Calculate merging variables
+        ptRatioDR = (jet->pt() / jet2->pt()) / dR;
+        neighborIndex = jet2->index();
+      }
+
+    }
+
+    // Store index of jet to merge into if criteria are met
+    if( dRmin < m_dRmerge && ptRatioDR < m_pTdRmerge ) jet->auxdecor< int >("mergeIndex") = neighborIndex;
+    else jet->auxdecor< int >("mergeIndex") = -1;
+
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode SATScaleTool::getTrackVertexAssociation( const jet::TrackVertexAssociation *&tva ) const {
+
+  tva = nullptr;
+
+  // Get TrackVertexAssociation
+  ATH_CHECK( evtStore()->retrieve(tva,m_trackVtxAssoc) );
+
+  // Fail if none is found
+  if ( !tva ) {
+    ANA_MSG_ERROR("Could not retrieve the TrackVertexAssociation");
+    return StatusCode::FAILURE;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode SATScaleTool::getPrimaryVertex( const xAOD::Vertex *&pvx ) const {
+
+  // Set pvx to nullptr to make sure we are actually retrieving the one we want
+  pvx = nullptr;
+
+  // Get vertex container
+  const xAOD::VertexContainer *vtxContainer = nullptr;
+  ATH_CHECK( evtStore()->retrieve(vtxContainer,m_vertexColl) );
+
+  // Fail if no vertex container or an empty vertex container is found
+  if ( vtxContainer == 0 || vtxContainer->size() == 0 ) {
+    ANA_MSG_ERROR("Failed to retrieve PrimaryVertices collection");
+    return StatusCode::FAILURE;
+  }
+
+  // Find the first primary vertex in the container
+  for ( const auto& vx : *vtxContainer ) {
+    if ( vx->vertexType() == xAOD::VxType::PriVtx ) {
+      pvx = vx;
+      break;
+    }
+  }
+
+  // Make sure the primary vertex is found
+  if( !pvx ) {
+    ANA_MSG_ERROR("No primary vertex assigned" );
+    return StatusCode::FAILURE;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+void SATScaleTool::print() const {
+  std::string smerge = m_doMerge ? "true" : "false";
+  ATH_MSG_INFO("Properties for SATScaleTool " << name());
+  ATH_MSG_INFO("     InputTrackContainer: " << m_inTrackColl);
+  ATH_MSG_INFO("       InputJetContainer: " << m_inJetColl);
+  ATH_MSG_INFO("    OutputTrackContainer: " << m_outTrackColl);
+  ATH_MSG_INFO("             MatchDeltaR: " << m_dRmatch);
+  ATH_MSG_INFO("             DoMergeJets: " << smerge);
+  ATH_MSG_INFO("             MergeDeltaR: " << m_dRmerge);
+  ATH_MSG_INFO("       MergePtOverDeltaR: " << m_pTdRmerge);
+  TrackAssistTool::print();
+  return;
+}
+
diff --git a/Reconstruction/Jet/JetRecTools/Root/TARJetTool.cxx b/Reconstruction/Jet/JetRecTools/Root/TARJetTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..37c9a03b1bd257dd30a7a5a505f9a6e5bba81234
--- /dev/null
+++ b/Reconstruction/Jet/JetRecTools/Root/TARJetTool.cxx
@@ -0,0 +1,262 @@
+/* 
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// TARJetTool.cxx
+
+#include "JetRecTools/TARJetTool.h"
+
+TARJetTool::TARJetTool(const std::string& myname)
+: AsgTool(myname),
+  TrackAssistTool(),
+  m_inTrackColl(""),
+  m_outTrackColl(""),
+  m_assocTracksOutName(""),
+  m_dRmatch(0.3)
+{
+  declareProperty("InputTrackContainer", m_inTrackColl = "InDetTrackParticles");
+  declareProperty("OutputTrackContainer", m_outTrackColl);
+  declareProperty("OutputAssociatedTracks", m_assocTracksOutName = "TARTracks");
+  declareProperty("MatchDeltaR", m_dRmatch);
+
+  declareProperty("InputAssociatedTracks", m_assocTracksInName = "GhostTrack");
+  declareProperty("VertexContainer", m_vertexColl = "PrimaryVertices");
+  declareProperty("TrackVertexAssociation", m_trackVtxAssoc);
+  declareProperty("DoTrackVertexAssociation", m_doTrackVtxAssoc = true);
+  declareProperty("TrackSelTool", m_trackSelTool);
+}
+
+StatusCode TARJetTool::initialize() 
+{
+
+  ATH_MSG_INFO("Initializing TARJetTool " << name() << ".");
+  ATH_CHECK( TrackAssistTool::initialize() );
+  print();
+
+  return StatusCode::SUCCESS;
+}
+
+int TARJetTool::modify( xAOD::JetContainer& inJets ) const {
+
+  std::vector<int> matchedTrackIndices; // List of ghost matched track indices
+  std::vector<int> unmatchedTrackIndices; // List of non ghost matched track indices
+  std::vector<int> allGoodTrackIndices; // List of all good track indices
+
+  std::map<int, std::vector<int>> JetTrackMap; // Map of track indices to RC jet indices
+  std::map<int, std::vector<int>> ConstitTrackMap; // Map of track indices to constit jet indices
+  std::map<int, int> ConstitJetMap; // Map of all constituent jets to RC jets
+
+  // Get input tracks
+  const xAOD::TrackParticleContainer *inTracks = nullptr;
+  ATH_CHECK( evtStore()->retrieve(inTracks,m_inTrackColl) );
+
+  // Get vertex objects
+  const xAOD::Vertex *pvx = nullptr;
+  const jet::TrackVertexAssociation *tva = nullptr;
+  ATH_CHECK( getVertexInfo(pvx,tva) );
+
+  // Make container for constituent jets
+  auto constitJets = std::make_unique<xAOD::JetContainer>();
+  auto constitJetsAux = std::make_unique<xAOD::JetAuxContainer>();
+  constitJets->setStore( constitJetsAux.get() ); //< Connect the two
+
+  // Make shallow copy of tracks
+  auto trackShallowCopy = xAOD::shallowCopyContainer(*inTracks);
+  std::unique_ptr<xAOD::TrackParticleContainer> outTracks (trackShallowCopy.first);
+  std::unique_ptr<xAOD::ShallowAuxContainer> outTracksAux (trackShallowCopy.second);
+
+  // Get list of all tracks
+  for( auto track : *inTracks ) {
+    // Make sure only tracks that pass selection are used
+    if( !isGoodTrack(*track,*pvx,*tva) ) continue;
+    allGoodTrackIndices.push_back( track->index() );
+  }
+
+  // Loop over RC jets
+  for(auto jet : inJets) {
+
+    // Loop over constituent jets
+    for (unsigned int iConstit = 0; iConstit < jet->numConstituents(); iConstit++){
+
+      // Get constituent jet
+      const xAOD::Jet *constit = dynamic_cast< const xAOD::Jet* >( jet->rawConstituent(iConstit) );
+
+      // Copy constituent jet into temporary small-R jet collection
+      xAOD::Jet* constitJet = new xAOD::Jet();
+      constitJets->push_back (constitJet);
+      *constitJet = *constit;
+
+      // Record which RC jet constituent jet comes from
+      ConstitJetMap[constitJet->index()] = jet->index();
+
+      // Get ghost-associated tracks
+      std::vector<const xAOD::TrackParticle*> myMatchedTracks = constitJet->getAssociatedObjects<xAOD::TrackParticle>(m_assocTracksInName);
+
+      // Loop over ghost-associated tracks
+      for( auto track : myMatchedTracks ) {
+
+        // Make sure only tracks that pass selection are used
+        if( !isGoodTrack(*track,*pvx,*tva) ) continue;
+
+        // Add to list of matched tracks
+        matchedTrackIndices.push_back(track->index());
+
+        // Record which constituent jet and RC jet the track belongs to
+        ConstitTrackMap[constitJet->index()].push_back(track->index());
+        JetTrackMap[jet->index()].push_back(track->index());
+      }
+
+    }
+  }
+
+  // Sort track indices so the difference can be taken
+  std::sort( allGoodTrackIndices.begin(), allGoodTrackIndices.end() ); // Sort matched track indices
+  std::sort( matchedTrackIndices.begin(), matchedTrackIndices.end() ); // Sort all track indices
+
+  // Get list of unmatched track indices
+  std::set_difference(allGoodTrackIndices.begin(), allGoodTrackIndices.end(), matchedTrackIndices.begin(), matchedTrackIndices.end(), std::inserter(unmatchedTrackIndices,unmatchedTrackIndices.begin()));
+
+  // Loop over all remaining tracks
+  for( auto trackIdx : unmatchedTrackIndices ) {
+
+    // Get input track
+    auto track = inTracks->at(trackIdx);
+
+    float dRmin = 9999999;
+    int jetIdx = -1;
+
+    for(auto constitJet : *constitJets) {
+      // Find nearest small-R jet to track
+      if(track->p4().DeltaR(constitJet->p4()) < dRmin) {
+        dRmin = track->p4().DeltaR(constitJet->p4());
+        jetIdx = constitJet->index();
+      }
+    }
+
+    // Check if dR matching criteria is met
+    if(dRmin < m_dRmatch) {
+      // Record which constituent jet and RC jet track belongs to
+      ConstitTrackMap[jetIdx].push_back(track->index());
+      JetTrackMap[ConstitJetMap[jetIdx]].push_back(track->index());
+    }
+  }
+
+  // Loop over all constituent jets to assign jet associations to tracks
+  // NB: This is being done in a separate loop to make future modifications easier
+  for(auto constitJet : *constitJets) {
+
+    for( auto trackIdx : ConstitTrackMap[constitJet->index()] ) {
+      // Set jet associations for associated tracks
+      // Nominally, each track is associated to a single jet, but this construction
+      // allows each track to be associated to multiple jets with a weight for each
+      // association that is used in the rescaling
+      outTracks->at(trackIdx)->auxdata< std::vector< std::pair<int, float> > >("JetAssociations").emplace_back(constitJet->index(),1.0);
+    }
+
+  }
+
+  // Rescale tracks
+  ATH_CHECK( rescaleTracks(constitJets.get(),outTracks.get())  );
+
+  // Create view container to store only selected tracks
+  std::unique_ptr< ConstDataVector<xAOD::TrackParticleContainer> > outSelTracks = std::make_unique< ConstDataVector< xAOD::TrackParticleContainer> >(SG::VIEW_ELEMENTS);
+
+  // Fill view container if track passes selection requirements
+  for(auto goodIdx : allGoodTrackIndices) {
+    outSelTracks->push_back(outTracks->at(goodIdx));
+  }
+  
+  ATH_MSG_DEBUG("New TARTrack container size " << outSelTracks->size());
+
+  ATH_CHECK( evtStore()->record(outSelTracks.release(),"Sel"+m_outTrackColl) );
+
+  // Get bare pointer for post-record operations
+  auto pOutTracks = outTracks.get();
+
+  // Record output tracks
+  ATH_CHECK( evtStore()->record(outTracks.release(), m_outTrackColl) );
+  ATH_CHECK( evtStore()->record(outTracksAux.release(), m_outTrackColl+"Aux.") );
+
+  // Loop over input jets to link tracks
+  for(auto jet : inJets) {
+
+    std::vector<const xAOD::TrackParticle*> TARTracks;
+
+    // Sum of TAR jets for mTAR
+    TLorentzVector TARJet;
+
+    // Collect all tracks associated to RC jet
+    for( auto trackIdx : JetTrackMap[jet->index()] ) {
+      TARTracks.push_back(pOutTracks->at(trackIdx));
+      TARJet += pOutTracks->at(trackIdx)->p4();
+    }
+
+    // Add associated TAR tracks
+    jet->setAssociatedObjects< xAOD::TrackParticle >(m_assocTracksOutName,TARTracks);
+
+    // Add mTAR decoration
+    jet->setAttribute("mTAR", TARJet.M());
+
+  }
+
+  return 0;
+}
+
+StatusCode TARJetTool::getTrackVertexAssociation( const jet::TrackVertexAssociation *&tva ) const {
+
+  tva = nullptr;
+
+  // Get TrackVertexAssociation
+  ATH_CHECK( evtStore()->retrieve(tva,m_trackVtxAssoc) );
+
+  // Fail if none is found
+  if ( !tva ) {
+    ANA_MSG_ERROR("Could not retrieve the TrackVertexAssociation");
+    return StatusCode::FAILURE;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TARJetTool::getPrimaryVertex( const xAOD::Vertex *&pvx ) const {
+
+  // Set pvx to nullptr to make sure we are actually retrieving the one we want
+  pvx = nullptr;
+
+  // Get vertex container
+  const xAOD::VertexContainer *vtxContainer = nullptr;
+  ATH_CHECK( evtStore()->retrieve(vtxContainer,m_vertexColl) );
+
+  // Fail if no vertex container or an empty vertex container is found
+  if ( vtxContainer == 0 || vtxContainer->size() == 0 ) {
+    ANA_MSG_ERROR("Failed to retrieve PrimaryVertices collection");
+    return StatusCode::FAILURE;
+  }
+
+  // Find the first primary vertex in the container
+  for ( const auto& vx : *vtxContainer ) {
+    if ( vx->vertexType() == xAOD::VxType::PriVtx ) {
+      pvx = vx;
+      break;
+    }
+  }
+
+  // Make sure the primary vertex is found
+  if( !pvx ) {
+    ANA_MSG_ERROR("No primary vertex assigned" );
+    return StatusCode::FAILURE;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+void TARJetTool::print() const {
+  ATH_MSG_INFO("Properties for TARJetTool " << name());
+  ATH_MSG_INFO("     InputTrackContainer: " << m_inTrackColl);
+  ATH_MSG_INFO("    OutputTrackContainer: " << m_outTrackColl);
+  ATH_MSG_INFO("  OutputAssociatedTracks: " << m_assocTracksOutName);
+  ATH_MSG_INFO("             MatchDeltaR: " << m_dRmatch);
+  TrackAssistTool::print();
+  return;
+}
+
diff --git a/Reconstruction/Jet/JetRecTools/Root/TrackAssistTool.cxx b/Reconstruction/Jet/JetRecTools/Root/TrackAssistTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..03ac8ab1f7a61ff92fef2e8356e1e9df2c04e832
--- /dev/null
+++ b/Reconstruction/Jet/JetRecTools/Root/TrackAssistTool.cxx
@@ -0,0 +1,161 @@
+/* 
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// TrackAssist.cxx
+
+#include "JetRecTools/TrackAssistTool.h"
+
+TrackAssistTool::TrackAssistTool() 
+: m_assocTracksInName(""),
+  m_vertexColl(""),
+  m_trackVtxAssoc(""),
+  m_doTrackVtxAssoc(true),
+  m_trackSelTool("")
+{
+}
+
+StatusCode TrackAssistTool::initialize()
+{
+
+  // If no VertexContainer is given, give warning but proceed without it
+  if( m_vertexColl.empty() ) {
+    ANA_MSG_WARNING("No VertexContainer defined. The TrackVertexAssociation requirement will be turned off to avoid undefined behavior.");
+    m_doTrackVtxAssoc = false;
+  }
+  
+  // If no TrackVertexAssociation is given, give warning but proceed without it
+  if( m_trackVtxAssoc.empty() ) {
+    ANA_MSG_WARNING("No TrackVertexAssociation defined. The TrackVertexAssociation requirement will be turned off to avoid undefined behavior.");
+    m_doTrackVtxAssoc = false;
+  }
+  
+  // If TrackVertexAssociation is turned off, give warning but proceed
+  if( !m_doTrackVtxAssoc ) {
+    ANA_MSG_WARNING("TrackVertexAssociation retquirement will not be applied. This is not recommended for general use.");
+  }
+  
+  // If no TrackSelector is given, give warning but proceed without it
+  if( m_trackSelTool.empty() ) {
+    ANA_MSG_WARNING("No TrackSelector defined. No track selection will be applied. This is not recommended for general use.");
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TrackAssistTool::rescaleTracks( const xAOD::JetContainer *jets, xAOD::TrackParticleContainer *tracks) const {
+
+  for(auto jet : *jets) {
+    // Get track rescale weights
+    ANA_CHECK( getRescaleFactors(*jet, *tracks) );
+  }
+
+  for(auto track : *tracks) {
+    
+    // Get the scales
+    float scale = track->auxdata< float >("TAScale");
+
+    // Include this to preserve the pT of any unmatched tracks
+    if(!scale) scale = 1.0;
+
+    // Rescale track pT
+    track->setDefiningParameters( track->d0(), track->z0(), track->phi0(), track->theta(), track->qOverP() / scale );
+
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TrackAssistTool::getRescaleFactors( const xAOD::Jet &jet, xAOD::TrackParticleContainer &tracks) const {
+
+  float trackPt = 0;
+
+  // Get weighted sum of track pTs for jet
+  for(auto track : tracks) {
+
+    // Get associations and weights
+    std::vector< std::pair<int, float> > associations = track->auxdata< std::vector< std::pair<int, float> > >("JetAssociations");
+
+    // Loop over associations
+    for(auto association : associations) {
+
+      // Skip track if it isn't associated to the jet
+      if(unsigned(association.first) != jet.index()) continue;
+
+      // Add weighted pT to total      
+      float weight = association.second;
+      trackPt += track->pt() * weight;
+
+      // Only add each track once
+      break;
+
+    }
+
+  }
+
+  // Save rescale factor for each track
+  for(auto track : tracks) {
+
+    float scale = 0.0;
+
+    // Get associations and weights
+    std::vector< std::pair<int, float> > associations = track->auxdata< std::vector< std::pair<int, float> > >("JetAssociations");
+
+    // Loop over associations
+    for(auto association : associations) {
+
+      // Skip track if it isn't associated to the jet
+      if(unsigned(association.first) != jet.index()) continue;
+
+      // Get factor to scale 
+      float weight = association.second;
+      scale = jet.pt() * weight / trackPt;
+
+      // Break loop once association to jet is found
+      break;
+
+    }
+
+    // Save the sum of scales
+    track->auxdata< float >("TAScale") += scale;
+
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+bool TrackAssistTool::isGoodTrack( const xAOD::TrackParticle &track, const xAOD::Vertex &pvx, const jet::TrackVertexAssociation &tva ) const {
+
+  // Do TrackVertexAssociation check if configured to do so
+  if ( m_doTrackVtxAssoc ) {
+    // Check if track is associated to primary vertex
+    if( &pvx != tva.associatedVertex(&track) ) return false;
+  }
+
+  // Do track selection check if configured to do so
+  if ( !m_trackSelTool.empty() ) {
+    // Check if track passes selection criteria
+    if( !m_trackSelTool->keep(track) ) return false;
+  }
+
+  return true;
+}
+
+StatusCode TrackAssistTool::getVertexInfo( const xAOD::Vertex *&pvx, const jet::TrackVertexAssociation *&tva ) const {
+  
+  // Get primary vertex and vertex association if configured to do so
+  if( m_doTrackVtxAssoc ) { 
+    ANA_CHECK( getPrimaryVertex(pvx) );
+    ANA_CHECK( getTrackVertexAssociation(tva) );
+  }
+  return StatusCode::SUCCESS;
+}
+
+void TrackAssistTool::print() const {
+  ANA_MSG_INFO("Properties inherited from TrackAssistTool");
+  ANA_MSG_INFO("   InputAssociatedTracks: " << m_assocTracksInName);
+  ANA_MSG_INFO("         VertexContainer: " << m_vertexColl);
+  ANA_MSG_INFO("  TrackVertexAssociation: " << m_trackVtxAssoc);
+  ANA_MSG_INFO("           TrackSelector: " << m_trackSelTool);
+  return;
+}
diff --git a/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx b/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx
index 217a76402e0429502aa7455a75dd9d23b1fee07a..04d1f92bc889f5510e80257f156cee7457054ec0 100755
--- a/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx
+++ b/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx
@@ -18,6 +18,8 @@
 #include "JetRecTools/CorrectPFOTool.h"
 #include "JetRecTools/ChargedHadronSubtractionTool.h"
 #include "JetRecTools/PuppiWeightTool.h"
+#include "JetRecTools/TARJetTool.h"
+#include "JetRecTools/SATScaleTool.h"
 
 DECLARE_COMPONENT( JetTrackSelectionTool )
 DECLARE_COMPONENT( SimpleJetTrackSelectionTool )
@@ -36,4 +38,6 @@ DECLARE_COMPONENT( CorrectPFOTool )
 DECLARE_COMPONENT( ChargedHadronSubtractionTool )
 DECLARE_COMPONENT( PuppiWeightTool )
 DECLARE_COMPONENT( ConstitTimeCutTool )
+DECLARE_COMPONENT( TARJetTool )
+DECLARE_COMPONENT( SATScaleTool )