diff --git a/Control/AthenaConfiguration/python/AllConfigFlags.py b/Control/AthenaConfiguration/python/AllConfigFlags.py
index 89cfa2833b86440ec8d4a6331742b92ed4c696ae..49b4594f5ee89604fd444dedb72a85bb942c2122 100644
--- a/Control/AthenaConfiguration/python/AllConfigFlags.py
+++ b/Control/AthenaConfiguration/python/AllConfigFlags.py
@@ -39,7 +39,7 @@ def _createCfgFlags():
             return []
 
         rawCollections = [type_key[1] for type_key in GetFileMD(inputFile).get("itemList",[])]
-        collections = filter(lambda col: not col.endswith('Aux.'), rawCollections)
+        collections = [col for col in rawCollections if not col.endswith('Aux.') ]
         return collections
 
     acf.addFlag('Input.Collections', lambda prevFlags : _inputCollections(prevFlags.Input.Files) )
diff --git a/Reconstruction/Jet/JetRec/JetRec/JetGroomer.h b/Reconstruction/Jet/JetRec/JetRec/JetGroomer.h
index 666bb45ff75369d989f0628a4e5e7f6959ef34ce..1804e52b4ecc0cb1ead91e584603a6190eb64fb6 100644
--- a/Reconstruction/Jet/JetRec/JetRec/JetGroomer.h
+++ b/Reconstruction/Jet/JetRec/JetRec/JetGroomer.h
@@ -29,13 +29,15 @@
 #include "xAODJet/JetContainer.h"
 #include "xAODJet/JetAuxContainer.h"
 
-class JetGroomer
-  : public asg::AsgTool,
-    virtual public JetProvider<xAOD::JetAuxContainer>
-{
-  ASG_TOOL_CLASS(JetGroomer, IJetProvider)
 
-  public:
+namespace JetGrooming {
+  class JetGroomer
+    : public asg::AsgTool,
+      virtual public JetProvider<xAOD::JetAuxContainer>
+  {
+    ASG_TOOL_CLASS(JetGroomer, IJetProvider)
+
+    public:
     using asg::AsgTool::AsgTool;
 
     virtual StatusCode initialize() override;
@@ -46,17 +48,18 @@ class JetGroomer
     // Implementation of grooming goes here
     // The jet is inserted into the output container, which is necessary for speed
     // in the xAOD container paradigm
-  virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const = 0;
+    virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const = 0;
 
   protected:
-  /// Handle Input JetContainer (this contains the parent ungroomed jets to be trimmed)
-  SG::ReadHandleKey<xAOD::JetContainer> m_inputJetContainer {this, "UngroomedJets", "ungroomedinput", "Input ungroomed jet container"};
+    /// Handle Input JetContainer (this contains the parent ungroomed jets to be trimmed)
+    SG::ReadHandleKey<xAOD::JetContainer> m_inputJetContainer {this, "UngroomedJets", "ungroomedinput", "Input ungroomed jet container"};
 
-  /// This is the input to the parent JetContainer. It is needed in order to re-assign the ghost constituents from the final groomed PJ to the xAOD::Jet
-  SG::ReadHandleKey<PseudoJetContainer> m_inputPseudoJets {this, "ParentPseudoJets", "inputpseudojet", "input constituents of parent JetContainer"};
+    /// This is the input to the parent JetContainer. It is needed in order to re-assign the ghost constituents from the final groomed PJ to the xAOD::Jet
+    SG::ReadHandleKey<PseudoJetContainer> m_inputPseudoJets {this, "ParentPseudoJets", "inputpseudojet", "input constituents of parent JetContainer"};
 
-   SG::WriteHandleKey<PseudoJetVector> m_finalPseudoJets {this, "FinalPseudoJets_DONOTSET", "", "output pseudojets -- autoconfigured name"};
+    SG::WriteHandleKey<PseudoJetVector> m_finalPseudoJets {this, "FinalPseudoJets_DONOTSET", "", "output pseudojets -- autoconfigured name"};
 
-};
+  };
 
+}
 #endif
diff --git a/Reconstruction/Jet/JetRec/Root/JetGroomer.cxx b/Reconstruction/Jet/JetRec/Root/JetGroomer.cxx
index a54de07cdb9763f74dbe82e9beb8b747ea04eb21..a88efae8bf8dcc4cfe3b90e00121ae287e20bffd 100644
--- a/Reconstruction/Jet/JetRec/Root/JetGroomer.cxx
+++ b/Reconstruction/Jet/JetRec/Root/JetGroomer.cxx
@@ -10,11 +10,14 @@
 
 using xAOD::JetContainer;
 
-StatusCode JetGroomer::initialize() {
+using namespace JetGrooming;
+
 
+StatusCode JetGroomer::initialize() {
+  
   ATH_MSG_DEBUG("Initializing...");
 
- if(m_inputJetContainer.empty()){
+  if(m_inputJetContainer.empty()){
     ATH_MSG_ERROR("Jet grooming requested with no input ungroomed jets");
     return StatusCode::FAILURE;
   } else if(m_inputPseudoJets.empty()){
diff --git a/Reconstruction/Jet/JetRec/Root/JetRecursiveSoftDrop.cxx b/Reconstruction/Jet/JetRec/Root/JetRecursiveSoftDrop.cxx
index 8cdefda0e2807bb64c9325d2a17366c27d42b129..f2b765dfd780b1a3c9889265d36aea625fada519 100644
--- a/Reconstruction/Jet/JetRec/Root/JetRecursiveSoftDrop.cxx
+++ b/Reconstruction/Jet/JetRec/Root/JetRecursiveSoftDrop.cxx
@@ -3,7 +3,6 @@
 */
 
 // JetRecursiveSoftDrop.cxx
-
 #include "JetRec/JetRecursiveSoftDrop.h"
 #include <iomanip>
 #include "fastjet/PseudoJet.hh"
diff --git a/Reconstruction/Jet/JetRec/Root/PseudoJetTranslator.cxx b/Reconstruction/Jet/JetRec/Root/PseudoJetTranslator.cxx
index d5ae49d2096e4c0845a69add3e71b138fc58114f..99e9992994ff1c01fcdf8acfe80d03e719fe34c3 100644
--- a/Reconstruction/Jet/JetRec/Root/PseudoJetTranslator.cxx
+++ b/Reconstruction/Jet/JetRec/Root/PseudoJetTranslator.cxx
@@ -18,7 +18,7 @@ xAOD::Jet& PseudoJetTranslator::translate(const fastjet::PseudoJet& pj,
 
   // Record the jet-finding momentum, i.e. the one used to find/groom the jet.
   jet.setJetP4(xAOD::JetConstitScaleMomentum, jet.jetP4());
-
+  
   // save area if needed ---------
   if( pj.has_area() ){
 
@@ -45,13 +45,16 @@ xAOD::Jet& PseudoJetTranslator::translate(const fastjet::PseudoJet& pj,
   if ( parentCont == 0 ) { return jet ;}  // can this happen? if so THIS IS an ERROR ! should do something
 
   ElementLink<xAOD::JetContainer> el(*parentCont, parent.index());
-  jet.setAttribute("Parent", el);
+  static const SG::AuxElement::Accessor<ElementLink<xAOD::JetContainer> > parentELacc("Parent"); 
+  parentELacc(jet) =el;
 
   jet.setInputType(parent.getInputType());
   jet.setAlgorithmType(parent.getAlgorithmType());
   jet.setSizeParameter(parent.getSizeParameter());
   jet.setConstituentsSignalState(parent.getConstituentsSignalState());
-  jet.setAttribute(xAOD::JetAttribute::JetGhostArea, parent.getAttribute<float>(xAOD::JetAttribute::JetGhostArea));
 
+  float area=0;
+  if(parent.getAttribute(xAOD::JetAttribute::JetGhostArea, area)) jet.setAttribute(xAOD::JetAttribute::JetGhostArea, area);
+    
   return jet;
 }
diff --git a/Reconstruction/Jet/JetRec/share/JetRecAlgTestCfg.py b/Reconstruction/Jet/JetRec/share/JetRecAlgTestCfg.py
index 13c4c42a69c36b4ae4072b962577500eb63a017f..e34159e43cdbfdd052adae4455dd5deb665ef0da 100755
--- a/Reconstruction/Jet/JetRec/share/JetRecAlgTestCfg.py
+++ b/Reconstruction/Jet/JetRec/share/JetRecAlgTestCfg.py
@@ -162,7 +162,7 @@ def JetGroomAlgCfg(ConfigFlags,buildjetsname,groomjetsname):
     groomcfg.addSequence( CompFactory.AthSequencer(sequencename) )
 
     # Create the JetGroomer, provide it with a JetTrimmer
-    jtrim = CompFactory.JetTrimming("trimSmallR2Frac5",RClus=0.2,PtFrac=0.05)
+    jtrim = CompFactory.getComp("JetGrooming::JetTrimming")("trimSmallR2Frac5",RClus=0.2,PtFrac=0.05)
     jtrim.UngroomedJets = buildjetsname
     jtrim.ParentPseudoJets = "PseudoJetMerged_"+buildjetsname
 
diff --git a/Reconstruction/Jet/JetRec/src/JetSoftDropTools.cxx b/Reconstruction/Jet/JetRec/src/JetSoftDropTools.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6f6722ef86e5973168251dfc741e998a7ad2b03d
--- /dev/null
+++ b/Reconstruction/Jet/JetRec/src/JetSoftDropTools.cxx
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "JetSoftDropTools.h"
+
+#include "fastjet/PseudoJet.hh"
+#include "fastjet/tools/Filter.hh"
+#include "fastjet/contrib/SoftDrop.hh"
+#include "fastjet/contrib/RecursiveSoftDrop.hh"
+#include "fastjet/contrib/BottomUpSoftDrop.hh"
+#include "fastjet/Selector.hh"
+
+#include "JetEDM/PseudoJetVector.h"
+
+#include "JetRec/PseudoJetTranslator.h"
+
+using namespace JetGrooming;
+
+namespace {
+  // tool implementing the operations to convert fastjet::PseudoJet -> xAOD::Jet
+  const static PseudoJetTranslator s_pjTranslator(false, false); // false => do not save jet areas
+
+
+
+  /// A common helper to perform the creation and insertion of a groomed jet given a SoftDrop algorithm
+  template<typename SDHELPER>
+  xAOD::Jet & buildAndInsertSDJet(SDHELPER& sdhelper, const xAOD::Jet& parentjet, const PseudoJetContainer& inpjcont, xAOD::JetContainer& outcont, PseudoJetVector& groomedpjvec ){
+
+    const static SG::AuxElement::Accessor<const fastjet::PseudoJet*> s_pjAcc("PseudoJet");
+    const static SG::AuxElement::ConstAccessor<const fastjet::PseudoJet*> s_pjConstAcc("PseudoJet");
+    
+    // retrieve the PseudoJet from the parent :
+    const fastjet::PseudoJet& parentPJ = *s_pjConstAcc(parentjet);
+
+    // call the sdhelper to build the groomed PSeudoJet
+    fastjet::PseudoJet groomedPJ = sdhelper(parentPJ);
+
+    // build the xAOD::Jet from the PseudoJet, and put it in the container 
+    xAOD::Jet& jet = s_pjTranslator.translate(groomedPJ, inpjcont, outcont, parentjet);
+    
+    // now assign the groomedPJ to its  vector
+    //  Note: the caller (JetGroomer) guarantees groomedpjvec has the correct size,
+    //  however, it is not 100% sure (?) that parentjet.index()==jet.index(), so we can't assign before
+    groomedpjvec[jet.index()] = groomedPJ;
+   
+    // decorate with the pointer to the PJ we keep in the evt store.
+    //  (the decoration is done in the PseudoJetTranslator but we have to redo-it here since we must point to
+    //   the instance instance inside groomedpjvec)    
+    s_pjAcc(jet) = & groomedpjvec[jet.index()];
+    
+    return jet;
+  }
+}
+
+// a shortcut to check variable range with a 1-liner (We can't use CheckedProperty...)
+#define CHECK_VAR_RANGE( name, var, expr )			       \
+  if( ! ( expr ) ) {						       \
+  ATH_MSG_ERROR("Property "<< name << " = "<< var << " out of range"); \
+  return StatusCode::SUCCESS; }
+
+//**********************************************************************
+
+StatusCode SoftDrop::initialize() {
+  ATH_CHECK( JetGroomer::initialize() );  
+
+  CHECK_VAR_RANGE("ZCut", m_zcut, (m_zcut>=0)&&(m_zcut<=10.) );
+  CHECK_VAR_RANGE("beta", m_beta, (m_beta>=0)&&(m_beta<=10.) );
+  CHECK_VAR_RANGE("R0", m_R0, (m_R0>=0)&&(m_R0<=10.) );
+
+  return StatusCode::SUCCESS;
+}
+
+
+//**********************************************************************
+StatusCode RecSoftDrop::initialize() {
+  ATH_CHECK( SoftDrop::initialize() );  
+  CHECK_VAR_RANGE("N", m_N, (m_zcut>-1) );
+  return StatusCode::SUCCESS;
+}
+
+
+void SoftDrop::insertGroomedJet(const xAOD::Jet& parentjet, const PseudoJetContainer& inpjcont, xAOD::JetContainer& outcont, PseudoJetVector& groomedpjvec) const {
+
+  //configure soft drop tool
+  // see http://fastjet.hepforge.org/svn/contrib/contribs/RecursiveTools/tags/1.0.0/SoftDrop.hh
+  fastjet::contrib::SoftDrop softdropper(m_beta, m_zcut, m_R0);
+
+  xAOD::Jet & jet = buildAndInsertSDJet(softdropper,
+					parentjet, inpjcont, outcont, groomedpjvec);
+  
+  if(m_saveSDatt) {
+    setSoftDropAttributes(jet, groomedpjvec[jet.index()].pieces().size() );
+  }
+}
+
+void SoftDrop::setSoftDropAttributes(xAOD::Jet& groomedjet, int nsdsubjets) const {
+  groomedjet.setAttribute<float>("ZCut", m_zcut);
+  groomedjet.setAttribute<float>("SoftDropBeta", m_beta);
+  groomedjet.setAttribute<float>("SoftDropR0", m_R0);
+  groomedjet.setAttribute<int>("NSoftDropSubjets", nsdsubjets);
+}
+
+
+
+
+//**********************************************************************
+
+void RecSoftDrop::insertGroomedJet(const xAOD::Jet& parentjet, const PseudoJetContainer& inpjcont, xAOD::JetContainer& outcont, PseudoJetVector& groomedpjvec) const {
+
+  //configure recursive soft drop tool
+  //https://fastjet.hepforge.org/trac/browser/contrib/contribs/RecursiveTools/tags/2.0.0-beta1
+  fastjet::contrib::RecursiveSoftDrop softdropper(m_beta, m_zcut, m_N, m_R0);
+
+  xAOD::Jet & jet = buildAndInsertSDJet(softdropper,
+					parentjet, inpjcont, outcont, groomedpjvec);
+  
+  if(m_saveSDatt) {
+    setSoftDropAttributes(jet, groomedpjvec[jet.index()].pieces().size() );
+    jet.setAttribute<int>("RSoftDropN", m_N);
+  }
+}
+
+
+
+//**********************************************************************
+void BottomUpSoftDrop::insertGroomedJet(const xAOD::Jet& parentjet, const PseudoJetContainer& inpjcont, xAOD::JetContainer& outcont, PseudoJetVector& groomedpjvec) const {
+
+  //configure recursive soft drop tool
+  //https://fastjet.hepforge.org/trac/browser/contrib/contribs/RecursiveTools/tags/2.0.0-beta1
+  fastjet::contrib::BottomUpSoftDrop softdropper(m_beta, m_zcut, m_R0);
+
+  xAOD::Jet & jet = buildAndInsertSDJet(softdropper,
+					parentjet, inpjcont, outcont, groomedpjvec);
+  
+  if(m_saveSDatt) {
+    setSoftDropAttributes(jet, groomedpjvec[jet.index()].pieces().size() );
+  }
+}
+
diff --git a/Reconstruction/Jet/JetRec/src/JetSoftDropTools.h b/Reconstruction/Jet/JetRec/src/JetSoftDropTools.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3d600e40d4471a0442515bbca8ae3ebfa5238db
--- /dev/null
+++ b/Reconstruction/Jet/JetRec/src/JetSoftDropTools.h
@@ -0,0 +1,94 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef JETREC_JETSOFDROPTOOLS_H
+#define JETREC_JETSOFDROPTOOLS_H
+///***********************************************
+///
+/// \file JetSoftDropTools
+///
+/// Define jet grooming tools implementing variants of the soft drop algorithm
+///
+/// These tools implement the IJetProvider interface via the JetGroomer base class.
+/// The JetContainer thet returns is build by
+/// running the trimming procedure on each jet of the parent JetContainer the tool is acting on.
+/// Obviously the parent JetContainer must be present in the evt store, but also the input PseudoJetContainer from which it has been built.
+
+#include "fastjet/PseudoJet.hh"
+#include "fastjet/tools/Filter.hh"
+
+#include "xAODJet/JetContainer.h"
+
+#include "JetInterface/IJetProvider.h"
+#include "JetRec/JetGroomer.h"
+#include "JetRec/PseudoJetContainer.h"
+
+namespace JetGrooming {
+
+  /// \class SoftDrop
+  ///
+  /// A grooming tool implementing the SoftDrop method by wrapping fastjet::contrib::SoftDrop
+  /// Also serves as a base class for other SoftDrop variant.
+  class SoftDrop
+    : virtual public JetGroomer {
+    ASG_TOOL_CLASS(SoftDrop, IJetProvider)
+
+    public:
+    using JetGroomer::JetGroomer;
+    
+    StatusCode initialize() override ;
+
+    virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const override ;
+
+  protected:
+
+    void setSoftDropAttributes(xAOD::Jet& groomedjet, int nsdsubjets) const ;
+    
+    Gaudi::Property<float> m_zcut  {this, "ZCut", 0.1 , "pT fraction for retaining subjets"}; 
+    Gaudi::Property<float> m_beta {this, "Beta", 0.0, "How much to consider angular dependence"};
+    Gaudi::Property<float> m_R0   {this, "R0", 1.0, "Normalization of angular distance, usually the characteristic jet radius (default R0 = 1)"};
+
+    Gaudi::Property<bool> m_saveSDatt {this, "SaveSoftDropAttributes",false, ""};
+  
+  };
+
+
+
+  /// \class RecSoftDrop
+  ///
+  /// A specialized SoftDrop tool wrapping fastjet::contrib::RecursiveSoftDrop 
+  class RecSoftDrop:   public SoftDrop {
+    ASG_TOOL_CLASS0( RecSoftDrop )    
+    public:
+    using SoftDrop::SoftDrop;//
+
+    StatusCode initialize() override final ;
+    
+    virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const override final ;
+
+  private:
+    Gaudi::Property<int> m_N {this, "N", 0.1 , "Number of layers (-1 <> infinite)"};     
+  };
+
+
+
+
+
+  /// \class BottomUpSoftDrop
+  ///
+  /// A specialized SoftDrop tool wrapping fastjet::contrib::BottomUpSoftDrop
+  class BottomUpSoftDrop: virtual public SoftDrop {
+    ASG_TOOL_CLASS0(BottomUpSoftDrop)    
+  public:
+    using SoftDrop::SoftDrop;
+    
+    virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const override final ;
+  };
+  
+
+  
+}
+
+
+#endif
diff --git a/Reconstruction/Jet/JetRec/src/JetTrimming.cxx b/Reconstruction/Jet/JetRec/src/JetTrimming.cxx
index 6379a4d8d0f6e41b49d2b966542f669eda932e13..7334813384db0aeb8d428f9b9b35868036b72a91 100644
--- a/Reconstruction/Jet/JetRec/src/JetTrimming.cxx
+++ b/Reconstruction/Jet/JetRec/src/JetTrimming.cxx
@@ -11,9 +11,12 @@
 
 #include "JetRec/PseudoJetTranslator.h"
 
+using namespace JetGrooming;
 
+namespace {
   // tool implementing the operations to convert fastjet::PseudoJet -> xAOD::Jet
-const static PseudoJetTranslator s_pjTranslator(false, false); // false => do not save jet areas
+  const static PseudoJetTranslator s_pjTranslator(false, false); // false => do not save jet areas
+}
 
 //**********************************************************************
 
@@ -49,7 +52,7 @@ StatusCode JetTrimming::initialize() {
 //**********************************************************************
 
 void JetTrimming::insertGroomedJet(const xAOD::Jet& parentjet, const PseudoJetContainer& inpjcont, xAOD::JetContainer& outcont, PseudoJetVector& trimpjvec) const {
-
+  
   const static SG::AuxElement::Accessor<const fastjet::PseudoJet*> s_pjAcc("PseudoJet");
   const static SG::AuxElement::ConstAccessor<const fastjet::PseudoJet*> s_pjConstAcc("PseudoJet");
 
@@ -69,7 +72,7 @@ void JetTrimming::insertGroomedJet(const xAOD::Jet& parentjet, const PseudoJetCo
 
   // decorate with the pointer to the PJ we keep in the evt store.
   s_pjAcc(jet) = & trimpjvec[jet.index()];
-
+  
   int nptrim = trimmedPJ.pieces().size();
   jet.setAttribute<int>(xAOD::JetAttribute::TransformType, xAOD::JetTransform::Trim);
   jet.setAttribute<int>(xAOD::JetAttribute::NTrimSubjets, nptrim);
diff --git a/Reconstruction/Jet/JetRec/src/JetTrimming.h b/Reconstruction/Jet/JetRec/src/JetTrimming.h
index 4302969abe17fc70af7f247b3efafc6493f26855..2953fdb3d861d4923340d512ee3b488fec4ece75 100644
--- a/Reconstruction/Jet/JetRec/src/JetTrimming.h
+++ b/Reconstruction/Jet/JetRec/src/JetTrimming.h
@@ -24,27 +24,28 @@
 #include "JetRec/JetGroomer.h"
 #include "JetRec/PseudoJetContainer.h"
 
-class JetTrimming
-: virtual public JetGroomer {
-  ASG_TOOL_CLASS(JetTrimming, IJetProvider)
+namespace JetGrooming {
+  class JetTrimming
+    : virtual public JetGroomer {
+    ASG_TOOL_CLASS(JetTrimming, IJetProvider)
 
-public:
+    public:
 
-  using JetGroomer::JetGroomer;
+    using JetGroomer::JetGroomer;
 
-  StatusCode initialize() override final;
+    StatusCode initialize() override final;
 
-  virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const override final;
+    virtual void insertGroomedJet(const xAOD::Jet&, const PseudoJetContainer&, xAOD::JetContainer&, PseudoJetVector&) const override final;
 
-private:
+  private:
 
-  // The filter object that will apply the grooming
-  std::unique_ptr<fastjet::Filter> m_trimmer;
+    // The filter object that will apply the grooming
+    std::unique_ptr<fastjet::Filter> m_trimmer;
     
-  // Job options.
-  Gaudi::Property<float> m_rclus        {this, "RClus", 0.3 , "R for reclustering (0 for none)"}; 
-  Gaudi::Property<float> m_ptfrac       {this, "PtFrac", 0.03, "pT fraction for retaining subjets"};
-  
-};
+    // Job options.
+    Gaudi::Property<float> m_rclus  {this, "RClus", 0.3 , "R for reclustering (0 for none)"}; 
+    Gaudi::Property<float> m_ptfrac      {this, "PtFrac", 0.03, "pT fraction for retaining subjets"};
+  };
 
+}
 #endif
diff --git a/Reconstruction/Jet/JetRec/src/components/JetRec_entries.cxx b/Reconstruction/Jet/JetRec/src/components/JetRec_entries.cxx
index ace4fc6f397d107f577d71c9fad87e3b8ee104e6..5a6d67f97048dc883d1928fba859165d9e9844d7 100644
--- a/Reconstruction/Jet/JetRec/src/components/JetRec_entries.cxx
+++ b/Reconstruction/Jet/JetRec/src/components/JetRec_entries.cxx
@@ -2,6 +2,7 @@
 #include "../JetRecAlg.h"
 #include "../JetViewAlg.h"
 #include "../JetTrimming.h"
+#include "../JetSoftDropTools.h"
 
 #include "../PseudoJetMerger.h"
 #include "../PseudoJetAlgorithm.h"
@@ -61,4 +62,7 @@ DECLARE_COMPONENT( JetViewAlg )
 DECLARE_COMPONENT( PseudoJetAlgorithm )
 DECLARE_COMPONENT( MuonSegmentPseudoJetAlgorithm )
 
-DECLARE_COMPONENT( JetTrimming )
+DECLARE_COMPONENT( JetGrooming::JetTrimming )
+DECLARE_COMPONENT( JetGrooming::SoftDrop )
+DECLARE_COMPONENT( JetGrooming::RecSoftDrop )
+DECLARE_COMPONENT( JetGrooming::BottomUpSoftDrop )
diff --git a/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py b/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py
index 51d695fa1619661287c81a3139e6b4e542a43582..b822f2737b9b2d455b18a52e31ae97fecdd64b16 100644
--- a/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py
+++ b/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py
@@ -105,6 +105,7 @@ class JetDefinition(object):
         # used internally to resolve dependencies
         self._prereqDic = {}
         self._prereqOrder = [] 
+        self._internalAtt = {} 
         self._locked = lock
 
             
@@ -147,19 +148,9 @@ class JetDefinition(object):
     @make_lproperty
     def prefix(self): pass
     
-    @prefix.lsetter
-    def prefix(self,p):
-        self._prefix = p
-        self._defineName()
-
     @make_lproperty
     def suffix(self): pass
     
-    @suffix.lsetter
-    def suffix(self,p):
-        self._suffix = p
-        self._defineName()
-
     @make_lproperty
     def basename(self): pass
 
@@ -367,11 +358,6 @@ class JetInputDef(object):
 
     # make outputname an alias of name so JetInputDef shares an interface with JetConstitSeq.
     outputname = make_alias("name")
-    # @make_lproperty
-    # def outputname(self): pass
-    # @outputname.setter
-    # def outputname(self,v):
-    #     raise Exception("Can not set the 'outputname' attribute of a JetInputDef, set its 'name' instead")
 
 
 ########################################################################    
diff --git a/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py
index b12202360b7bc2ec715635f66874f1b7303c79d9..f0b136b329ae07ec36d1ff104c267d34cf7a8321 100644
--- a/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py
+++ b/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py
@@ -1,22 +1,19 @@
 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+"""
+JetGroomConfig: A helper module for configuring jet grooming         
+Author: TJ Khoo, P-A Delsart                                                      
+"""                                                                    
 
-########################################################################
-#                                                                      #
-# JetGroomConfig: A helper module for configuring jet grooming         #
-# Author: TJ Khoo                                                      #
-#                                                                      #
 ########################################################################
 
 from AthenaCommon import Logging
 jetlog = Logging.logging.getLogger('JetGroomConfig')
 
-from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 from AthenaConfiguration.ComponentFactory import CompFactory
 
 import JetRecConfig.JetModConfig as modH
-from JetRecConfig.JetRecConfig import instantiateAliases, buildJetModifierList
+from JetRecConfig.JetRecConfig import buildJetModifierList,  addJetClusteringToComponent, JetRecCfg
 
-import six
 
 
 def instantiateGroomingAliases( groomdef ):
@@ -27,85 +24,111 @@ def instantiateGroomingAliases( groomdef ):
       * assumes jetdef is not 'locked' 
       * implies calls to recursives function constH.aliasToInputDef and modH.aliasToModDef
     """
-
-    instantiateAliases(groomdef.ungroomeddef)
     
     for mod in groomdef.modifiers:
         modInstance = modH.aliasToModDef(mod, groomdef)
         groomdef._prereqDic['mod:'+mod] = modInstance
         groomdef._prereqOrder.append('mod:'+mod)
 
-    
+
 ########################################################################
-# Get a jet groomer class given a tool name and the grooming definition object
-# The pjsin argument is for forwards compatibility when we switch to
-# using JetRecAlg
 #
-def getJetGroomer(groomdef,pjsin):
-    tooltype = groomdef.groomspec["ToolType"]
-    toolprops = {key:val for key,val in six.iteritems (groomdef.groomspec) if key not in ["groomalg","ToolType"]}
-    return tooltype(groomdef.basename,**toolprops)
+def getJetGroomAlg(groomdef,monTool=None):
+    """Returns a configured JetRecAlg set-up to perform the grooming defined by 'groomdef' 
+    ('monTool' is a temporary placeholder, it is expected to be used in the trigger in the future) 
+    """
+    jetlog.debug("Configuring grooming alg \"jetalg_{0}\"".format(groomdef.fullname()))
 
 
-########################################################################
-# Function for configuring the jet algorithm and groomers, given the
-# set of dependencies
-#
-def getJetGroomAlg(jetname,groomdef,pjsin,monTool=None):
-    jetlog.debug("Configuring JetAlgorithm \"jetalg_{0}\"".format(jetname))
+    # the grooming tool (a IJetProvider instance)
+    groomClass = CompFactory.getComp(groomdef.tooltype)
+    groomer = groomClass(groomdef.groomalg,
+                         UngroomedJets = groomdef.ungroomeddef.fullname(),
+                         ParentPseudoJets = groomdef.ungroomeddef._internalAtt['finalPJContainer'],
+                         **groomdef.properties)
+    # get JetModifier list
+    mods = buildJetModifierList(groomdef)
 
+    # put everything together in a JetRecAlg
+    jetname = groomdef.fullname()
+    jra = CompFactory.JetRecAlg(
+        "jetrecalg_"+jetname,
+        Provider = groomer,
+        Modifiers = mods,
+        OutputContainer = jetname)
+    
+    
+    from JetRecConfig.JetRecConfig import autoconfigureModifiers
+    autoconfigureModifiers(jra.Modifiers, jetname)
+    
+    return jra
+
+def getJetGroomAlg_jetAlg(groomdef,pjsin,monTool=None):
+    """Returns a configured grooming algs based on the old JetAlgorithm
+
+    We keep this function for compatibility with trigger config. When the trigger switches to JetRecAlg, this can be removed.
+    """
+    jetname = groomdef.fullname()
+    jetlog.debug("Configuring JetAlgorithm \"jetalg_{0}\"".format(jetname))
+    
     from . import JetRecConfig
     builder = JetRecConfig.getJetBuilder()
 
-    groomer = getJetGroomer(groomdef,pjsin)
-    groomer.JetBuilder = builder
+    groomClass = CompFactory.getComp(groomdef.tooltype)
+    groomer = groomClass(groomdef.groomalg,
+                         JetBuilder = builder,
+                         **groomdef.properties)
 
+    
+    
     mods = buildJetModifierList(groomdef)
-
+    
     rectool = CompFactory.JetRecTool(jetname,
                                      JetGroomer=groomer,
                                      InputContainer=groomdef.ungroomeddef.fullname(),
                                      OutputContainer=jetname,
                                      JetPseudojetRetriever=CompFactory.JetPseudojetRetriever("jpjretriever"),
                                      JetModifiers=mods)
-
+    
     if monTool: rectool.MonTool = monTool
-
+    
     jetalg = CompFactory.JetAlgorithm("jetalg_"+jetname)
     jetalg.Tools = [rectool]
-
     return jetalg
 
-########################################################################
-# Top-level function for running jet grooming
-# Will first run the jet finder, unless the input jets are
-# found in the input file
-def JetGroomCfg(groomdef, configFlags, jetnameprefix="",jetnamesuffix=""):
-    jetsfullname = jetnameprefix+groomdef.basename+jetnamesuffix+"Jets"
-    jetlog.info("Setting up to find {0}".format(jetsfullname))
-
-    sequencename = jetsfullname
-
-    components = ComponentAccumulator()
-    from AthenaCommon.AlgSequence import AthSequencer
-    components.addSequence( AthSequencer(sequencename) )
-
-    # Check if the ungroomed jets exist in the input file.
-    # If not, we need to configure their reconstruction.
-    filecontents = configFlags.Input.Collections
-    if groomdef.ungroomedname not in filecontents:
-        from . import JetRecCfg
-        components.merge(JetRecCfg(groomdef.ungroomeddef, configFlags,
-                                   jetnameoverride=groomdef.ungroomedname))
-    else:
-        # FIXME: Need to schedule rebuilding of pseudojets
-        pass
-
-    # FIXME: Add calls to JetModConfig.getFinalModifierListAndPrereqs
-    components.addEventAlgo(getJetGroomAlg(jetsfullname,groomdef,groomdef.modifiers))
+def addGroomToComponent(components,groomdef, configFlags, ):
+    """Instantiate and schedule all the algorithms needed to run the grooming alg 'groomdef' and
+    add them in the ComponentAccumulator 'components'
+
+    This function is meant to be called from the top-level JetRecConfig.JetRecCfg
+    (groomdef is expected to be non locked and will be modified).
+    """
+    sequenceName = groomdef.fullname()
+    
+    # Translate modifier aliases into JetModifier config instances.
+    #  ( This also detects input dependencies, see below)
+    instantiateGroomingAliases(groomdef)
+
+    # Transfer the input & ghost dependencies onto the parent jet alg,
+    # so they are handled when instatiating the parent jet algs
+    groomdef.ungroomeddef.ghostdefs += [ g.split(':')[1] for g in groomdef._prereqOrder if g.startswith('ghost:')]
+    groomdef.ungroomeddef.extrainputs += [ g.split(':')[1] for g in groomdef._prereqOrder if g.startswith('input:')]
+
+    jetlog.info("Scheduling parent alg {} for {} ".format(groomdef.ungroomeddef.fullname(), groomdef.fullname()))
+    # now instantiate the parent jet alg.
+    # (we always want it even if the parent jets are already in the input file because
+    #  we need to rebuild the pseudoJet)
+    addJetClusteringToComponent(components, groomdef.ungroomeddef, configFlags, sequenceName)
+    
+    groomalg = getJetGroomAlg(groomdef)
+
+    components.addEventAlgo(groomalg, sequenceName)
+
+    jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(groomdef.fullname()))
 
     return components
 
+
 if __name__=="__main__":
 
     # Setting needed for the ComponentAccumulator to do its thing
@@ -132,7 +155,7 @@ if __name__=="__main__":
     from JetGrooming import JetTrimming
     AntiKt10LCTopo.ptminfilter = 100e3
     AntiKt10LCTopoTrimmedPtFrac5SmallR20 = JetTrimming(AntiKt10LCTopo,"AntiKt10LCTopoJets",smallR=0.2,ptfrac=0.05)
-    cfg.merge(JetGroomCfg(AntiKt10LCTopoTrimmedPtFrac5SmallR20,ConfigFlags))
+    cfg.merge(JetRecCfg(AntiKt10LCTopoTrimmedPtFrac5SmallR20,ConfigFlags))
 
     cfg.printConfig(withDetails=False,summariseProps=True)
 
diff --git a/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py b/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py
index 2d5aa6bdd27811a8fd06a2edd9ca5470f32421a1..1fcf23fcc4d13b736afcf14b11a90a6a57d21b03 100644
--- a/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py
+++ b/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py
@@ -1,43 +1,45 @@
-# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
-
-########################################################################
-#                                                                      #
-# JetGrooming: A module for classes encoding definitions of objects    #
-# for configuring jet grooming                                         #
-# Author: TJ Khoo                                                      #
-#                                                                      #
-########################################################################
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+"""
+JetGrooming: A module for classes encoding definitions of objects    
+for configuring jet grooming
+Author: TJ Khoo. P-A Delsart                                         
+"""
 
 __all__ =  ["GroomingDefinition","JetTrimming","JetSoftDrop"]
 
 from AthenaCommon import Logging
-from .Utilities import make_lproperty, onlyAttributesAreProperties, clonable
+from .Utilities import make_lproperty, onlyAttributesAreProperties, clonable, ldict
 jetlog = Logging.logging.getLogger('JetGrooming')
 
 @clonable
 @onlyAttributesAreProperties
 class GroomingDefinition(object):
+    """ Base class to define grooming procedure. 
+    Concrete classes will have to define the tooltype & groomalg class members
+    and aslo a groomSpecAsStr() function.
+
+    """
+    tooltype = None
+    groomalg = None
     def __init__(self, 
                  ungroomeddef,  # Ungroomed JetDefinition
-                 #ungroomedname, # Input collection name (cannot be determined uniquely from inputdef)
-                 groomspec,     # Dict describing the grooming settings
                  modifiers=[],  # JetModifiers to run after grooming
-                 lock=False,
+                 suffix = '',   # allows to tune the full JetContainer name
+                 lock=False,    # lock the properties of this instance to avoid accidental overwrite after __init__
+                 **properties   # any other argument is expected a grooming tool property
                  ): 
 
-        #self.ungroomedname = ungroomedname
+        self._ungroomeddef = ungroomeddef.clone() # clone to avoid messing with external jetdef
 
-        # Dedicated setter/getter
-        self._ungroomeddef = ungroomeddef.clone() # to avoid messing with external jetdef
-        self._groomspec = groomspec
-
-        self._checkGroomSpec(groomspec)
-        self._groomspec = groomspec
+        if lock: properties = ldict(properties) # ldict to freeze the properties
+        self.properties = properties
 
+        self.suffix = suffix
         self._defineName()
 
-        self.modifiers = modifiers     # Tools to modify the jet
-
+        self.modifiers = modifiers 
+        
+        
         # used internally to resolve dependencies
         self._prereqDic = {}
         self._prereqOrder = [] 
@@ -64,42 +66,21 @@ class GroomingDefinition(object):
         self._defineName()
 
 
+    @make_lproperty
+    def properties(self): pass
     @make_lproperty
     def modifiers(self): pass
-        
     @make_lproperty
-    def groomspec(self):
-        return self.__groomspec
-    @groomspec.lsetter
-    def groomspec(self,groomspec):
-        self._checkGroomSpec(groomspec)
-        self._groomspec = groomspec
-        self._defineName()
-
-    # To override in derived classes
+    def suffix(self): pass
+    
+    # To be overriden in derived classes
     def groomSpecAsStr(self):
-        return "Groomed"
+        raise Exception("Can not use a GroomingDefinition instance : use a derived class")
 
     def fullname(self):
-        return self.ungroomeddef.prefix+self.basename+"Jets"
+        return self.ungroomeddef.prefix+self.basename+"Jets"+self.suffix
     
-    def _checkGroomSpec(self,groomspec):
-        # Check if options are supported (implemented)
-        groomalg = groomspec["groomalg"]
-        supportedGrooming = ["Trim","SoftDrop"]
-        if not groomspec["groomalg"] in supportedGrooming:
-            jetlog.error("Unsupported grooming algorithm specification \"{}\"! Allowable options:")
-            for groomalg in supportedGrooming:
-                jetlog.error(groomalg)
-            raise KeyError("Invalid grooming algorithm choice: {0}".format(groomalg))
         
-    # @property
-    # def inputdef(self):
-    #     return self.__inputdef
-    # @inputdef.setter
-    # def inputdef(self,inputdef):
-    #     self.__inputdef = inputdef
-    #     self._defineName()
 
 
     def _defineName(self):
@@ -114,76 +95,34 @@ class GroomingDefinition(object):
     __repr__ = __str__
 
 
-@clonable
-@onlyAttributesAreProperties
 class JetTrimming(GroomingDefinition):
-    def __init__(self, 
-                 ungroomeddef,  # Ungroomed JetDefinition
-                 #ungroomedname, # Input collection name (cannot be determined uniquely from inputdef)
-                 smallR,        # Subjet radius
-                 ptfrac,        # Minimum subjet pt fraction
-                 modifiers=[],  # JetModifiers to run after grooming
-                 lock=False,
-                 ): 
-
-        # Apart from groomalg and ToolType, these correspond to the
-        # grooming tool property values
-        #from JetRec.JetRecConf import JetTrimmer
-        from AthenaConfiguration.ComponentFactory import CompFactory
-        JetTrimmer = CompFactory.JetTrimmer
-        groomspec = {
-            # Type of groomer
-            "groomalg": "Trim",
-            # Configurable class
-            "ToolType": JetTrimmer,
-            # Tool properties to set
-            "RClus":    smallR,
-            "PtFrac":   ptfrac,
-            }
-
-        super(JetTrimming,self).__init__(ungroomeddef,groomspec,modifiers,lock=lock,finalinit=False)
+    groomalg = "Trim"
+    tooltype = "JetGrooming::JetTrimming"
 
     def groomSpecAsStr(self):
-        ptfrac = self.groomspec["PtFrac"]
-        ptfracstr = int(ptfrac*100) # Not usually smaller than %
-        smallR = self.groomspec["RClus"]
+        ptfrac = int( self.properties["PtFrac"] *100 ) # Not usually smaller than %
         from .JetDefinition import formatRvalue
-        smallRstr = formatRvalue(smallR*10)
+        smallR = formatRvalue(self.properties["RClus"]*10)
         
-        groomstr = "TrimmedPtFrac{}SmallR{}".format(ptfracstr,smallRstr)
+        groomstr = "TrimmedPtFrac{}SmallR{}".format(ptfrac,smallR)
         return groomstr
 
-@clonable
-@onlyAttributesAreProperties
+class JetTrimmingTrig(JetTrimming):
+    """Temporary class for trigger (to be removed when jet trigger use JetRecAlg)"""
+    tooltype = "JetTrimmer"
+    
+
 class JetSoftDrop(GroomingDefinition):
-    def __init__(self,
-                 ungroomeddef,  # Ungroomed JetDefinition
-                 #ungroomedname, # Input collection name (cannot be determined uniquely from inputdef)
-                 zcut,          # ZCut
-                 beta,          # Beta
-                 modifiers=[],  # JetModifiers to run after grooming
-                 lock=False):
-
-        # Apart from groomalg and ToolType, these correspond to the
-        # grooming tool property values
-        from AthenaConfiguration.ComponentFactory import CompFactory
-        JetSD = CompFactory.JetSoftDrop
-        groomspec = {
-            # Type of groomer
-            "groomalg": "SoftDrop",
-            # Configurable class
-            "ToolType": JetSD,
-            # Tool properties to set
-            "ZCut":   zcut,
-            "Beta":   beta,
-            }
-
-        super(JetSoftDrop,self).__init__(ungroomeddef,groomspec,modifiers, lock=lock, finalinit=False)
+    groomalg = "SoftDrop"
+    tooltype = "JetGrooming::SoftDrop"
 
     def groomSpecAsStr(self):
-        beta     = self.groomspec["Beta"]
-        betastr  = int(beta*100)
-        zcut     = self.groomspec["ZCut"]
-        zcutstr  = int(zcut*100)
-        groomstr = "SoftDropBeta{}Zcut{}".format(betastr,zcutstr)
+        beta     = int( self.properties["Beta"] *100)
+        zcut     = int( self.properties["ZCut"] *100)
+        groomstr = "SoftDropBeta{}Zcut{}".format(beta,zcut)
         return groomstr
+    
+class JetSoftDropTrig(JetSoftDrop):
+    """Temporary class for trigger (to be removed when jet trigger use JetRecAlg)"""
+    tooltype = "JetSoftDrop"
+    
diff --git a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py
index 29dc80eced2ed8e188ea207134ee96bae0ebc839..e71c285f072130f3f6215480118aff09814ff11e 100644
--- a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py
+++ b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py
@@ -18,6 +18,9 @@ from AthenaConfiguration.ComponentFactory import CompFactory
 import JetRecConfig.ConstModHelpers as constH
 import JetRecConfig.JetModConfig as modH
 
+from JetRecConfig.JetDefinition import JetDefinition
+from JetRecConfig.JetGrooming import GroomingDefinition
+
 
 
 
@@ -26,78 +29,96 @@ __all__ = ["JetRecCfg", "JetInputCfg"]
 
 
 ########################################################################
+
+
+def JetRecCfg(jetdef, configFlags):
+    """Top-level function for running jet finding or grooming.
     
-def JetRecCfg(jetdef0, configFlags):
-    """Top-level function for running jet finding
-    (i.e. clustering from inputs)
     This returns a ComponentAccumulator that can be merged with others
     from elsewhere in the job, but will provide everything needed to
     reconstruct one jet collection.
-    This could still be modularised further into the subcomponents of the
-    jet reconstruction job.
-    Receives the jet definition and input flags, mainly for input file
+    Receives the jet or grooming definition (jetdef) and input flags (configFlags), mainly for input file
     peeking such that we don't attempt to reproduce stuff that's already
-    in the input file
+    in the input file.
+
     """
     # we clone the jetdef, so we're sure we're not using a 'locked' one 
-    jetdef = jetdef0.clone()
+    jetdef_i = jetdef.clone()
     
-    jetsfullname = jetdef.fullname()
-    jetlog.info("Setting up to find {0}".format(jetsfullname))
-
-    sequenceName = jetsfullname
+    sequenceName = jetdef_i.fullname()
+    jetlog.info("******************")    
+    jetlog.info("Setting up to find {0}".format(sequenceName))
 
     components = ComponentAccumulator()
     from AthenaCommon.CFElements import parOR
     components.addSequence( parOR(sequenceName) )
 
-    # create proper config instances for each input and ghost aliases in this jetdef
+    # call the relevant function according to jetdef_i type 
+    if isinstance(jetdef_i, JetDefinition):
+        addJetClusteringToComponent(components, jetdef_i, configFlags)
+
+    elif isinstance(jetdef_i, GroomingDefinition):
+        from JetRecConfig.JetGroomConfig import addGroomToComponent
+        addGroomToComponent(components, jetdef_i,  configFlags)
+
+    return components
+
+def addJetClusteringToComponent(components, jetdef_i, configFlags, sequenceName=None):
+    """internal function which instantiates the JetRecAlg defined by the JetDefinition 'jetdef_i'  
+    into the ComponentAccumulator 'components' 
+    """
+    sequenceName = sequenceName or jetdef_i.fullname()
+    # create proper config instances for each input and ghost aliases in this jetdef_i
     # this implicitely calculates and adds the dependencies.
-    instantiateAliases(jetdef)
+    instantiateAliases(jetdef_i)
     
-    # check if the conditions are compatible with the inputs & modifiers of this jetdef.
+    # check if the conditions are compatible with the inputs & modifiers of this jetdef_i.
     # if in standardRecoMode we will remove whatever is incompatible and still try to run
     # if not, we raise an exception
-    removeComponentFailingConditions(jetdef, configFlags, raiseOnFailure= not jetdef.standardRecoMode)
+    removeComponentFailingConditions(jetdef_i, configFlags, raiseOnFailure= not jetdef_i.standardRecoMode)
     
     
     # Schedule the various input collections.
     # We don't have to worry about ordering, as the scheduler
     # will handle the details. Just merge the components.
-    inputcomps = JetInputCfg(jetdef, configFlags, sequenceName)
+    inputcomps = JetInputCfg(jetdef_i, configFlags, sequenceName)
     components.merge(inputcomps)
 
     # schedule the algs to create fastjet::PseudoJet objects out of the inputs
-    pjCompo, pjContainer = PseudoJetCfg(jetdef, configFlags, sequenceName)
+    pjCompo= PseudoJetCfg(jetdef_i, configFlags, sequenceName)
     components.merge(pjCompo)
     
     # Generate a JetRecAlg to run the jet finding and modifiers
-    jetrecalg = getJetRecAlg( jetdef, pjContainer)
+    jetrecalg = getJetRecAlg( jetdef_i)
     components.addEventAlgo(jetrecalg, sequenceName)
     
-    jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(jetsfullname))
+    jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(jetdef_i.fullname()))
+
     return components
 
-def PseudoJetCfg(jetdef, configFlags, sequenceName):
-    """Builds a ComponentAccumulator for creating PseudoJetContainer needed by jetdef.
-    IMPORTANT returns a tuple : (components, finalPJContainerName) """
+
+def buildPseudoJetAlgs(jetdef):
+    """ Builds the list of configured PseudoJetAlgorithm needed for this jetdef.
+    THIS updates jetdef._internalAtt['finalPJContainer'] 
+    (this function is factorized out of PseudoJetCfg so it can be used standalone in the trigger config)
+    """
     
-    components = ComponentAccumulator(sequenceName)
-    # Schedule the constituent PseudoJetAlg
     constitpjalg = getConstitPJGAlg( jetdef.inputdef )
-    components.addEventAlgo( constitpjalg, sequenceName )
     finalPJContainer = constitpjalg.OutputContainer
+    pjalglist = [constitpjalg]
     
     # Schedule the ghost PseudoJetAlgs
     ghostlist = [ key for key in jetdef._prereqOrder if key.startswith('ghost:')]
     if ghostlist != []:
+        # then we need to schedule a PseudoJetAlg for each ghost collections...
         pjContNames = [finalPJContainer]
         for ghostkey in sorted(ghostlist):
             ghostdef = jetdef._prereqDic[ghostkey]
             ghostpjalg = getGhostPJGAlg( ghostdef )
-            components.addEventAlgo( ghostpjalg, sequenceName )
+            pjalglist.append(ghostpjalg)
             pjContNames.append( ghostpjalg.OutputContainer )
 
+        # .. and merge them together with the input constituents 
         mergeId = mergedPJId( pjContNames )
         finalPJContainer = constitpjalg.OutputContainer+"_merged"+mergeId
         mergerName = "PJMerger_id"+mergeId
@@ -106,12 +127,25 @@ def PseudoJetCfg(jetdef, configFlags, sequenceName):
             InputPJContainers = pjContNames,
             OutputContainer = finalPJContainer,
         )
-        components.addEventAlgo( mergeAlg, sequenceName)
-
-    return components, finalPJContainer
+        pjalglist.append(mergeAlg)
+        
+    # set the name of the complete,merged input PseudoJets, so it can be re-used downstream
+    jetdef._internalAtt['finalPJContainer'] = finalPJContainer
+    return pjalglist
+    
+def PseudoJetCfg(jetdef, configFlags, sequenceName):
+    """Builds a ComponentAccumulator for creating PseudoJetContainer needed by jetdef.
+    THIS updates jetdef._internalAtt['finalPJContainer'] 
+    """
+    components = ComponentAccumulator(sequenceName)
+    pjalglist = buildPseudoJetAlgs( jetdef )
+    for pjalg in pjalglist:
+        components.addEventAlgo( pjalg, sequenceName )
+    return components
 
 _mergedPJContainers = dict()
 def mergedPJId(pjList):
+    """returns a simple unique ID for the list of PseudoJet container in pjList"""
     t = tuple(pjList)
     currentSize = len(_mergedPJContainers)
     return str(_mergedPJContainers.setdefault(t, currentSize))
@@ -129,12 +163,12 @@ def JetInputCfg(jetOrConstitdef, configFlags, sequenceName='AthAlgSeq'):
      * a JetConstitSource : to allow scheduling the corresponding constituents algs independently of any jet alg. 
     """
 
-    jetlog.info("Setting up jet inputs.")
     components = ComponentAccumulator(sequenceName)
 
     
     from .JetDefinition import JetConstitSource, JetDefinition
     if isinstance(jetOrConstitdef, JetConstitSource):
+        jetlog.info("Setting up jet inputs from JetConstitSource : "+jetOrConstitdef.name)
         jetdef = JetDefinition('Kt', 0., jetOrConstitdef.clone())
         instantiateAliases(jetdef)        
         removeComponentFailingConditions(jetdef, configFlags, raiseOnFailure= not jetdef.standardRecoMode)
@@ -142,7 +176,7 @@ def JetInputCfg(jetOrConstitdef, configFlags, sequenceName='AthAlgSeq'):
         jetdef = jetOrConstitdef
     
     jetlog.info("Inspecting input file contents")
-    filecontents = [coll for coll in configFlags.Input.Collections]
+    filecontents = configFlags.Input.Collections
 
     inputdeps = [ inputkey for inputkey in jetdef._prereqOrder if inputkey.startswith('input:')]
 
@@ -162,7 +196,7 @@ def JetInputCfg(jetOrConstitdef, configFlags, sequenceName='AthAlgSeq'):
                 if constitalg:
                     components.addEventAlgo(constitalg, primary=isprimary)
         else: # it must be a JetInputDef
-            cname = inputInstance.containername(jetdef,inputInstance.specs)
+            cname = inputInstance.containername(jetdef,inputInstance.specs) # (by defaults this is just inputInstance.name)
             if cname in filecontents:
                 jetlog.debug("Input container {0} for prereq {1} already in input file.".format(cname, inputInstance.name))
             else:
@@ -257,15 +291,16 @@ def getJetAlgorithm(jetname, jetdef, pjContNames, monTool = None):
 ########################################################################
 # Function that substitues JetRecTool + JetAlgorithm
 #
-def getJetRecAlg( jetdef, pjContNames):
+def getJetRecAlg( jetdef):
     """ """
+    pjContNames = jetdef._internalAtt['finalPJContainer']
     jclust = CompFactory.JetClusterer(
         "builder",
         JetAlgorithm = jetdef.algorithm,
         JetRadius = jetdef.radius,
         PtMin = jetdef.ptmin,
         InputPseudoJets = pjContNames,
-        GhostArea = 0.01, # In which cases do we not want areas?
+        GhostArea = 0.01 if (jetdef.radius < 0.6)  else 0. , 
         JetInputType = jetdef.inputdef.jetinputtype,
     )
 
@@ -389,7 +424,6 @@ def removeComponentFailingConditions(jetdef, configflags, raiseOnFailure=True):
     The compatibility is ultimately tested using the component 'filterfn' attributes.
     Internally calls the function isComponentPassingConditions() (see below) 
     """
-    jetlog.info("******************")
     jetlog.info("Standard Reco mode : filtering components in "+str(jetdef))
 
 
diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py b/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py
index c2ec26f1a0212a0d18d1363e3167c86a8e1f5fe6..8d5b473b6506259abc029bccc54abdb636fe4a2a 100644
--- a/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py
+++ b/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py
@@ -116,5 +116,40 @@ particlejetmods = {
     }
 jetmoddict.update(particlejetmods)
 
-# Todo: jet substructure moment tools
 
+# Substructure tools 
+
+substrmods = dict( 
+    nsubjettiness = JetModifier( "NSubjettinessTool", "nsubjettiness",Alpha = 1.0),
+    nsubjettinessR = JetModifier( "NSubjettinessRatiosTool", "nsubjettinessR",),
+
+    
+    ktdr       = JetModifier("KtDeltaRTool", "ktdr", JetRadius = 0.4),
+
+    ktsplitter = JetModifier( "KTSplittingScaleTool", "ktsplitter"),
+    
+    angularity = JetModifier( "AngularityTool", "angularity"),
+    
+    dipolarity = JetModifier( "DipolarityTool", "dipolarity",SubJetRadius = 0.3),
+    
+    planarflow = JetModifier( "PlanarFlowTool", "planarflow"),
+
+    ktmassdrop = JetModifier( "KtMassDropTool", "ktmassdrop"),
+
+    ecorr      = JetModifier( "EnergyCorrelatorTool", "ecorr", Beta = 1.0),
+    ecorrR      = JetModifier( "EnergyCorrelatorRatiosTool", "ecorrR", ),
+
+    ecorrgeneral = JetModifier( "EnergyCorrelatorGeneralizedTool", "ecorrgeneral"),
+
+    ecorrgeneralratios = JetModifier( "EnergyCorrelatorGeneralizedRatiosTool", "ecorrgeneralratios"),
+
+    comshapes = JetModifier( "CenterOfMassShapesTool","comshapes"),
+
+    pull      = JetModifier("JetPullTool", "pull",  UseEtaInsteadOfY = False, IncludeTensorMoments = True ),
+
+    charge    = JetModifier( "JetChargeTool", "charge", K=1.0),
+
+    qw = JetModifier( "QwTool", "qw"),
+    #showerdec = JetModifier( "  ShowerDeconstructionTool"),
+)
+jetmoddict.update(substrmods)
diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py b/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b8549d6d66d2c27a4ec51e07a3ae23a5500c141
--- /dev/null
+++ b/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py
@@ -0,0 +1,91 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+from JetRecConfig.StandardJetConstits import jetconstitdic as cst
+from .JetDefinition import  JetDefinition
+from .JetGrooming import  JetTrimming, JetSoftDrop
+
+
+
+# *********************************************************
+# Ghost-associated particles for the standard large R jets 
+# *********************************************************
+standardghosts =  ["Track","MuonSegment","Truth"]
+
+
+flavourghosts = [
+                  "WBosons", "ZBosons", "HBosons", "TQuarksFinal",
+                  "Partons",]
+
+
+
+
+
+# *********************************************************
+# Modifiers for the standard large R jets 
+# *********************************************************
+# (use tuples rather than lists to prevent accidental modification)
+standardrecomods = (
+    "Sort",
+    "Width",
+    "TrackMoments",
+)
+clustermods      = ("ECPSFrac","ClusterMoments",) 
+truthmods        =  ("PartonTruthLabel","TruthPartonDR", ) 
+pflowmods        = ()
+
+substrmods = ("nsubjettiness", "nsubjettinessR", "ktsplitter",
+              "ecorr", "ecorrR",  "qw",
+              # ... others ?
+)
+
+
+# *********************************************************
+# Standard large R jets definitions
+# *********************************************************
+
+
+AntiKt10LCTopo = JetDefinition("AntiKt",1.0,cst.LCTopoOrigin,
+                               ghostdefs = standardghosts+flavourghosts , 
+                               modifiers = ("Sort", "Filter:50000"), 
+                               standardRecoMode = True,                               
+                               lock = True
+)
+
+
+AntiKt10LCTopoTrimmed = JetTrimming(AntiKt10LCTopo,
+                                    modifiers = standardrecomods+substrmods,
+                                    PtFrac = 0.05, RClus = 0.2,                                    
+                                    )
+
+AntiKt10LCTopoSoftDrop = JetSoftDrop(AntiKt10LCTopo,
+                                     modifiers = standardrecomods+substrmods,
+                                     Beta = 1., ZCut= 0.1,
+                                     )
+
+
+
+
+AntiKt10Truth = JetDefinition("AntiKt",1.0,cst.Truth,
+                               ghostdefs = flavourghosts , 
+                               modifiers = ("Sort", "Filter:50000"), 
+                               standardRecoMode = True,                               
+                               lock = True
+)
+
+
+AntiKt10TruthTrimmed = JetTrimming(AntiKt10Truth,
+                                   modifiers = standardrecomods+substrmods+truthmods,
+                                   PtFrac = 0.05, RClus = 0.2,                                    
+                                   )
+
+AntiKt10TruthSoftDrop = JetSoftDrop(AntiKt10Truth,
+                                    modifiers = standardrecomods+substrmods+truthmods,
+                                    Beta = 1., ZCut= 0.1,
+                                    )
+
+
+
+
+
+
+        
diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py b/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py
index af23c0a965e81e4c9ad39da9700229f979028fbd..52750d138d5c5aa7a75ed376efceff5dfdb1195d 100644
--- a/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py
+++ b/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py
@@ -12,7 +12,6 @@ from xAODBase.xAODType import xAODType
 standardghosts =  ["Track","MuonSegment","Truth"]
 
 
-#flavourghosts = ["TruthLabel"+ghosttype  for ghosttype in [
 flavourghosts = [ "BHadronsInitial", "BHadronsFinal", "BQuarksFinal",
                   "CHadronsInitial", "CHadronsFinal", "CQuarksFinal",
                   "TausFinal",
@@ -47,7 +46,7 @@ pflowmods        = ()
 
 
 AntiKt4EMPFlow = JetDefinition("AntiKt",0.4,cst.EMPFlow,
-                               ghostdefs = standardghosts+flavourghosts , # not working well yet : flavourghosts,
+                               ghostdefs = standardghosts+flavourghosts , 
                                modifiers = standardrecomods+truthmods, 
                                standardRecoMode = True,                               
                                lock = True
@@ -90,6 +89,7 @@ AntiKt4TruthWZ = JetDefinition("AntiKt",0.4, cst.TruthWZ,
 
 
 def StandardSmallRJetCfg(configFlags):
+    """Top-level function to schedule the smallR jets in standard reconstruction """
     from JetRecConfig.JetRecConfig import JetRecCfg
 
     standarSmallRList = [
diff --git a/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py b/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py
index 518ea15ad8d1114ddbe2974af350051dfaf3afe8..1322fcf541350c158fd50ca8b0ad3a720714751d 100755
--- a/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py
+++ b/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py
@@ -12,6 +12,7 @@ def DefineJetCollections(configFlags):
     ########################################################################
     # import the standard definitions 
     from JetRecConfig.StandardSmallRJets import  AntiKt4EMPFlow, AntiKt4EMTopo, AntiKt4Truth, AntiKt4TruthWZ
+    from JetRecConfig.StandardLargeRJets import  AntiKt10LCTopoSoftDrop
 
     # Example for defining a custom definition
     from JetRecConfig.JetDefinition import JetConstitSeq, JetDefinition, xAODType
@@ -38,6 +39,7 @@ def DefineJetCollections(configFlags):
         AntiKt4EMPFlow.clone(prefix="New"),
         AntiKt4EMTopoCSSK,
         AntiKt4EMPFlowCSSK,
+        AntiKt10LCTopoSoftDrop,
     ]
     if configFlags.Input.isMC:
         jetdefs += [AntiKt4Truth.clone(prefix="New"),
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py
index 6c5b54bb1b81e081d4201670da7bb5f3389d4799..df72ad99fc2bf6364009904ac03bf6857f7737aa 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py
@@ -174,11 +174,15 @@ def defineReclusteredJets(jetRecoDict,smallRjets):
     return rcJetDef
 
 def defineGroomedJets(jetRecoDict,ungroomedDef):#,ungroomedJetsName):
-    from JetRecConfig.JetGrooming import JetTrimming, JetSoftDrop
+    from JetRecConfig.JetGrooming import JetTrimmingTrig, JetSoftDropTrig
     groomAlg = jetRecoDict["recoAlg"][3:] if 'sd' in jetRecoDict["recoAlg"] else jetRecoDict["recoAlg"][-1]
+    suffix = "_"+ jetRecoDict["jetCalib"]
+    if jetRecoDict["trkopt"]!="notrk":
+        suffix += "_"+jetRecoDict["trkopt"]
+    
     groomDef = {
-        "sd":JetSoftDrop(ungroomedDef,zcut=0.1,beta=1.0),
-        "t" :JetTrimming(ungroomedDef,smallR=0.2,ptfrac=0.04),
+        "sd":JetSoftDropTrig(ungroomedDef,ZCut=0.1,Beta=1.0, suffix=suffix),
+        "t" :JetTrimmingTrig(ungroomedDef,RClus=0.2,PtFrac=0.04, suffix=suffix),
     }[groomAlg]
     return groomDef
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py
index 10c99e949b1d27acdd11a21c22c4c830574eb0f7..3ff27580630c7028523bcae9c8235c71e04c3398 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py
@@ -149,7 +149,7 @@ def standardJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoDict
     # make sure all our JetModifier have their track inputs set up according to trkopt
     from JetRecConfig.JetModConfig import jetModWithAlternateTrk    
     jetModWithAlternateTrk(jetDef, jetRecoDict['trkopt'] )
-    
+
     # Generate a JetAlgorithm to run the jet finding and modifiers
     # (via a JetRecTool instance).
     jetRecAlg = JetRecConfig.getJetAlgorithm(jetsFullName, jetDef, pjs, monTool)
@@ -177,10 +177,7 @@ def groomedJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoDict
     parentpjs = getattr(ungroomedJetRecoSequence,"jetalg_{}".format(ungroomedJetsName)).Tools[0].InputPseudoJets
 
     groomDef = JetRecoConfiguration.defineGroomedJets(jetRecoDict,ungroomedDef)
-    groomedJetsFullName = groomDef.fullname()+"_"+jetRecoDict["jetCalib"]
-    if jetRecoDict["trkopt"]!="notrk":
-        groomedJetsFullName += "_"+jetRecoDict["trkopt"]
-
+    groomedJetsFullName = groomDef.fullname()
     groomDef.modifiers = JetRecoConfiguration.defineCalibFilterMods(jetRecoDict,dataSource)
     # Can add substructure mods here
 
@@ -188,9 +185,9 @@ def groomedJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoDict
     from JetRec import JetOnlineMon
     monTool = JetOnlineMon.getMonTool_TrigJetAlgorithm("HLTJets/"+groomedJetsFullName+"/")
 
-    from JetRecConfig.JetGroomConfig import getJetGroomAlg, instantiateGroomingAliases
+    from JetRecConfig.JetGroomConfig import getJetGroomAlg_jetAlg, instantiateGroomingAliases
     instantiateGroomingAliases(groomDef)
-    groomalg = getJetGroomAlg(groomedJetsFullName,groomDef,parentpjs,monTool)
+    groomalg = getJetGroomAlg_jetAlg(groomDef,parentpjs,monTool)
     recoSeq += conf2toConfigurable( groomalg )
 
     jetsOut = recordable(groomedJetsFullName)