From ea051a21c9fc8a37e5305423c7c74cb7724dbf33 Mon Sep 17 00:00:00 2001
From: Jonathan Burr <jon.burr@cern.ch>
Date: Tue, 19 Nov 2019 10:14:01 +0000
Subject: [PATCH] Revert "Merge branch 'revert-9c7cf5e1' into 'master'"

This reverts merge request !28085
---
 .../TrigEFMissingET/CMakeLists.txt            |  55 ++++-
 .../TrigEFMissingET/METComponent.h            |  78 +++++++
 .../TrigEFMissingET/SignedKinematics.h        | 127 ++++++++++++
 .../TrigEFMissingET/StatusFlags.h             |  47 +++++
 .../TrigEFMissingET/src/FexBase.cxx           | 167 +++++++++++++++
 .../TrigEFMissingET/src/FexBase.h             | 122 +++++++++++
 .../TrigEFMissingET/src/METComponent.cxx      | 121 +++++++++++
 .../TrigEFMissingET/src/MonGroupBuilder.cxx   |  33 +++
 .../TrigEFMissingET/src/MonGroupBuilder.h     |  90 ++++++++
 .../TrigEFMissingET/src/SignedKinematics.cxx  | 196 ++++++++++++++++++
 .../TrigEFMissingET/src/StatusFlags.cxx       |  66 ++++++
 .../TrigEFMissingET/src/TrkMHTFex.cxx         | 159 ++++++++++++++
 .../TrigEFMissingET/src/TrkMHTFex.h           | 108 ++++++++++
 .../components/TrigEFMissingET_entries.cxx    |   3 +-
 .../share/q221_RDOtoRDOTrig_mt1_build.ref     |   2 +
 .../TrigUpgradeTest/share/full_menu_build.ref |   2 +
 .../TrigUpgradeTest/share/slice_met.ref       |  22 +-
 .../TrigEDMConfig/python/TriggerEDMRun3.py    |   3 +
 .../MET/METChainConfiguration.py              |  12 +-
 .../HLTMenuConfig/MET/METMenuSequences.py     |  16 ++
 .../HLTMenuConfig/MET/METRecoSequences.py     |  43 +++-
 .../python/HLTMenuConfig/Menu/LS2_v1.py       |   1 +
 .../HLTMenuConfig/Menu/SignatureDicts.py      |   2 +-
 23 files changed, 1449 insertions(+), 26 deletions(-)
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/METComponent.h
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/SignedKinematics.h
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/StatusFlags.h
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.cxx
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.h
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/METComponent.cxx
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.cxx
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.h
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/SignedKinematics.cxx
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/StatusFlags.cxx
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.cxx
 create mode 100644 Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.h

diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/CMakeLists.txt b/Trigger/TrigAlgorithms/TrigEFMissingET/CMakeLists.txt
index 6c24106d7ae..ca238691323 100644
--- a/Trigger/TrigAlgorithms/TrigEFMissingET/CMakeLists.txt
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/CMakeLists.txt
@@ -34,6 +34,7 @@ atlas_depends_on_subdirs(
    Event/EventKernel
    Event/xAOD/xAODEventInfo
    Reconstruction/Jet/JetEvent
+   Reconstruction/Jet/JetEDM
    Trigger/TrigEvent/TrigMuonEvent
    Trigger/TrigEvent/TrigSteeringEvent
    Trigger/TrigT1/TrigT1Interfaces )
@@ -44,18 +45,52 @@ find_package( FastJetContrib COMPONENTS SoftKiller ConstituentSubtractor )
 find_package( ROOT COMPONENTS Core Hist Matrix )
 find_package( tdaq-common COMPONENTS eformat )
 
+atlas_add_library( TrigEFMissingETLib
+  src/METComponent.cxx
+  src/StatusFlags.cxx
+  src/SignedKinematics.cxx
+  PUBLIC_HEADERS TrigEFMissingET
+  INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+  LINK_LIBRARIES xAODTrigMissingET xAODBase
+)
+
 # Component(s) in the package:
 atlas_add_component( TrigEFMissingET
-   src/*.cxx
-   src/components/*.cxx
-   INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${TDAQ-COMMON_INCLUDE_DIRS} ${FASTJETCONTRIB_INCLUDE_DIRS} ${FASTJET_INCLUDE_DIRS}
-   LINK_LIBRARIES ${ROOT_LIBRARIES} ${FASTJET_LIBRARIES} ${TDAQ-COMMON_LIBRARIES} ${FASTJETCONTRIB_LIBRARIES}
-   AthenaBaseComps CaloDetDescrLib CaloEvent CaloGeoHelpers
-   CaloIdentifier CxxUtils EventKernel GaudiKernel Identifier
-   JetEvent LArIdentifier PathResolver
-   TrigCaloEvent TrigInterfacesLib TrigMissingEtEvent TrigMuonEvent
-   TrigParticle TrigSteeringEvent TrigT1Interfaces TrigT2CaloCommonLib
-   TrigTimeAlgsLib xAODCaloEvent xAODEventInfo xAODJet xAODTrigMissingET )
+  src/EFMissingET.cxx
+  src/EFMissingETHelper.cxx
+  src/EFMissingETBaseTool.cxx
+  src/EFMissingETComponentCopier.cxx
+  src/EFMissingETFromCells.cxx
+  src/EFMissingETFromClusters.cxx
+  src/EFMissingETFromClustersPS.cxx
+  src/EFMissingETFromClustersPUC.cxx
+  src/EFMissingETFromFEBHeader.cxx
+  src/EFMissingETFromJets.cxx
+  src/EFMissingETFromTrackAndJets.cxx
+  src/EFMissingETFromClustersTracksPUC.cxx
+  src/EFMissingETFromTrackAndClusters.cxx
+  src/EFMissingETFlags.cxx
+  src/EFMissingETFromHelper.cxx
+  src/EFMissingETAlgMT.cxx
+  src/EFMissingETFromCellsMT.cxx
+  src/EFMissingETFromClustersMT.cxx
+  src/EFMissingETFromClustersPufitMT.cxx
+  src/EFMissingETFromJetsMT.cxx
+  src/EFMissingETFlagsMT.cxx
+  src/FexBase.cxx
+  src/MonGroupBuilder.cxx
+  src/TrkMHTFex.cxx
+  src/components/TrigEFMissingET_entries.cxx
+  src/components/TrigEFMissingET_load.cxx
+  INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${TDAQ-COMMON_INCLUDE_DIRS} ${FASTJETCONTRIB_INCLUDE_DIRS} ${FASTJET_INCLUDE_DIRS}
+  LINK_LIBRARIES ${ROOT_LIBRARIES} ${FASTJET_LIBRARIES} ${TDAQ-COMMON_LIBRARIES} ${FASTJETCONTRIB_LIBRARIES}
+  TrigEFMissingETLib
+  AthenaBaseComps CaloDetDescrLib CaloEvent CaloGeoHelpers
+  CaloIdentifier CxxUtils EventKernel GaudiKernel Identifier
+  JetEvent LArIdentifier PathResolver JetEDM
+  TrigCaloEvent TrigInterfacesLib TrigMissingEtEvent TrigMuonEvent
+  TrigParticle TrigSteeringEvent TrigT1Interfaces TrigT2CaloCommonLib
+  TrigTimeAlgsLib xAODCaloEvent xAODEventInfo xAODJet xAODTrigMissingET )
 
 # Install files from the package:
 atlas_install_python_modules( python/*.py )
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/METComponent.h b/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/METComponent.h
new file mode 100644
index 00000000000..570a0f80c0b
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/METComponent.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @class   METComponent
+ *
+ * @brief Helper class to build up MET values
+ * @author Jon Burr
+ *****************************************************************************/
+
+#ifndef TRIGEFMISSINGET_METCOMPONENT_H
+#define TRIGEFMISSINGET_METCOMPONENT_H
+
+#include "xAODTrigMissingET/TrigMissingET.h"
+#include "TrigEFMissingET/SignedKinematics.h"
+#include <TLorentzVector.h>
+#include <ostream>
+
+namespace HLT { namespace MET {
+  /**
+   * @brief Helper struct to build up MET values before moving them into the EDM
+   *
+   * The reason for doing this is that the EDM doesn't allow += operators etc
+   * which makes interacting with it rather painful and (maybe?) slow.
+   */
+  struct METComponent {
+    METComponent() {}
+    /// Initialize from an xAOD::TrigMissingET object
+    METComponent(const xAOD::TrigMissingET& met);
+    /// Initialize from a *component* of an xAOD::TrigMissingET object
+    METComponent(std::size_t idx, const xAOD::TrigMissingET& met);
+    /// Momentum components
+    /// x momentum
+    float mpx{0.};
+    /// y momentum
+    float mpy{0.};
+    /// z momentum
+    float mpz{0.};
+    /// The actual met
+    float met() const;
+    /// The magnitude of the missing 3-vector
+    float magnitude() const;
+    /// The direction
+    float phi() const;
+    /// The (pseudo) eta
+    float eta() const;
+
+    /// Also store the sumE
+    float sumE{0.};
+    /// And the sumEt
+    float sumEt{0.};
+    /// The status flag
+    int status{0};
+
+    /// Add two of these things together
+    friend METComponent operator+(const METComponent& lhs, const METComponent& rhs);
+    /// Add one to us
+    METComponent& operator+=(const METComponent& other);
+    /// Add a four momentum. Use the convention used in xAOD::MissingET
+    /// where adding particle *subtracts* its four momentum. So 'adding' a
+    /// particle to the MET works as you'd expect.
+    METComponent& operator+=(const TLorentzVector& otherP4);
+    /// Add a signed object. This uses the same convention as above (so a
+    /// negative energy object increases the object
+    METComponent& operator+=(const SignedKinematics& kin);
+
+    /// Fill the main component of the MET with this
+    void fillMET(xAOD::TrigMissingET& met) const;
+    /// Fill a component of the MET with this
+    void fillMETComponent(std::size_t idx, xAOD::TrigMissingET& met) const;
+  }; //> end class METComponent
+
+  std::ostream& operator<<(std::ostream& os, const METComponent& component);
+
+} } //> end namespace HLT::MET
+#endif //> !TRIGEFMISSINGET_METCOMPONENT_H
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/SignedKinematics.h b/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/SignedKinematics.h
new file mode 100644
index 00000000000..5c764d0102c
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/SignedKinematics.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ *
+ * @brief Helper class to describe the kinematics of an object that can have
+ * negative energies.
+ * @author Jon Burr
+ *****************************************************************************/
+
+#ifndef TRIGEFMISSINGET_SIGNEDKINEMATICS_H
+#define TRIGEFMISSINGET_SIGNEDKINEMATICS_H
+
+#include "Math/Vector4D.h"
+
+// Forward declares
+class TLorentzVector;
+
+namespace xAOD {
+  class IParticle;
+}
+
+namespace HLT { namespace MET {
+
+  /**
+   * @brief Class to describe the kinematics of an object that can have negative
+   * energies.
+   *
+   * This class is used instead of TLorentzVector anywhere that there could be a
+   * negative energy. This is to get complete control of the behaviour in that
+   * case as TLorentzVector behaves oddly.
+   *
+   * Algebraic operators are provided but it's worth noting that negative-energy
+   * objects behaviour a little oddly (they are not valid four-momenta after
+   * all). This should only really affect the mass.
+   *
+   * The sign of the vector is defined as sign(E) where E is the scalar sum of
+   * all constituents energies
+   */
+  class SignedKinematics {
+    public:
+      SignedKinematics();
+      /// Constructor from px, py, pz and E
+      SignedKinematics(double px, double py, double pz, double energy);
+      /// Construct from a TLorentzVector
+      SignedKinematics(const TLorentzVector& tlv);
+      /// Construct from an IParticle
+      SignedKinematics(const xAOD::IParticle& particle);
+      /// Factory function to construct from energy, eta, phi (massless)
+      static SignedKinematics fromEnergyEtaPhi(
+          double energy, double eta, double phi);
+      /// Factory function to construct from energy eta, phi and m
+      static SignedKinematics fromEnergyEtaPhiM(
+          double energy, double eta, double phi, double mass);
+      /// Factory function to construct from et, eta, phi (massless)
+      static SignedKinematics fromEtEtaPhi(
+          double et, double eta, double phi);
+      /// Factory function to construct from et eta, phi and m
+      static SignedKinematics fromEtEtaPhiM(
+          double et, double eta, double phi, double mass);
+
+      /// The sign of the kinematics
+      int sign() const;
+
+      /// Direction
+      double eta() const;
+      double phi() const;
+      /// Provide accessors for sin and cos phi
+      double sinPhi() const;
+      double cosPhi() const;
+      /// Provide accessors for sinh and cosh eta
+      double sinhEta() const;
+      double coshEta() const;
+
+      /// Momentum values
+      /// (signed) momentum
+      double p() const;
+      /// unsigned momentum
+      double absP() const;
+      double p2() const;
+      /// (signed) pt
+      double pt() const;
+      /// unsigned pt
+      double absPt() const;
+      double pt2() const;
+      double px() const;
+      double py() const;
+      double pz() const;
+
+      /// Energy values
+      /// (signed) energy
+      double energy() const;
+      /// unsigned energy
+      double absEnergy() const;
+      double energy2() const;
+      /// (signed) et
+      double et() const;
+      /// Unsigned et
+      double absEt() const;
+      double et2() const;
+      double ex() const;
+      double ey() const;
+      double ez() const;
+
+      /// The squared mass. There is no guarantee that this will be > 0
+      double m2() const;
+
+      explicit operator ROOT::Math::PxPyPzEVector() const;
+
+      /// Add another SignedKinematics to this
+      SignedKinematics& operator+=(const SignedKinematics& other);
+      /// Subtract a SignedKinematics from this (exact opposite of the above
+      /// function.
+      SignedKinematics& operator-=(const SignedKinematics& other);
+    private:
+      /// The actual kinematics
+      ROOT::Math::PxPyPzEVector m_p4;
+  }; //> end class SignedKinematics
+  /// 'free' sum operator
+  SignedKinematics operator+(const SignedKinematics& lhs, const SignedKinematics& rhs);
+  /// 'free' difference operator
+  SignedKinematics operator-(const SignedKinematics& lhs, const SignedKinematics& rhs);
+
+} } //> end namespace HLT::MET
+#endif //> !TRIGEFMISSINGET_SIGNEDKINEMATICS_H
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/StatusFlags.h b/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/StatusFlags.h
new file mode 100644
index 00000000000..3fb5356accd
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/TrigEFMissingET/StatusFlags.h
@@ -0,0 +1,47 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/********************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * 
+ * @brief Enum for EDM status flags
+ * @author Jon Burr
+ ********************************************************************/
+
+#include <string>
+
+namespace HLT { namespace MET {
+  namespace StatusFlag {
+    // Right now just taking these from the values in the old code for
+    // consistency
+    //
+    // Use bitshift notation as these represent different bits in the status
+    // word. 1 << x is equivalent to 2^x. 
+    enum StatusFlag : int {
+      // 1 << 0 free
+      BSConvError             = 1 << 1,
+      MuonError               = 1 << 2,
+      FEBError                = 1 << 3,
+      // 1 << 4 free
+      ComponentBigMEtSEtRatio = 1 << 5,
+      BadComponentEnergy      = 1 << 6,
+      BadEnergyRatio          = 1 << 7,
+      NoisyEnergyRatio        = 1 << 8,
+      BadCellQuality          = 1 << 9,
+      BadCellEnergy           = 1 << 10,
+      BadCellTime             = 1 << 11,
+      NoMuonTrack             = 1 << 12,
+      // 1 << 13 free
+      // 1 << 14 free
+      ComponentErrors         = 1 << 15,
+      // everything until 1 << 27 free
+      GlobalNoisyEnergyRatio  = 1 << 27,
+      BadEMFraction           = 1 << 28,
+      GlobalBigMEtSEtRatio    = 1 << 29,
+      ObjectInCrack           = 1 << 30,
+      GlobalErrors            = 1 << 31
+    }; //> end enum StatusFlag
+    std::string flagIndexToString(int idx);
+  } //> end namespace StatusFlag
+} } //> end namespace HLT::MET
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.cxx
new file mode 100644
index 00000000000..d3b361a9a89
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.cxx
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @file FexBase.cxx
+ *
+ * Implementation of base Fex class
+ * @author Jon Burr
+ *****************************************************************************/
+
+#include "FexBase.h"
+#include "xAODTrigMissingET/TrigMissingETAuxContainer.h"
+#include "TrigEFMissingET/METComponent.h"
+#include "TrigEFMissingET/StatusFlags.h"
+#include "AthenaMonitoring/Monitored.h"
+#include <memory>
+#include <algorithm>
+#include <iterator>
+
+namespace {
+  // Convert from MeV to GeV if above threshold, else fallback value
+  float toLinGeV(float x, float fallback=0, float epsilon=1e-6)
+  {
+    float xGeV = x / Gaudi::Units::GeV;
+    if (std::fabs(xGeV) < epsilon)
+      return fallback;
+    return xGeV;
+  }
+  // convert from MeV to GeV and then log10, preserving the sign and the minimum
+  // dictated by monitoring histograms
+  float toLog10GeV(float x, float fallback=0, float epsilon=1e-6)
+  {
+    float absXGeV = std::fabs(x / Gaudi::Units::GeV);
+    if (absXGeV < epsilon)
+      return fallback;
+    return std::copysign(std::log10(absXGeV), x);
+  }
+} //> end anonymous namespace
+
+namespace HLT { namespace MET {
+  FexBase::FexBase(const std::string& name, ISvcLocator* pSvcLocator) :
+    AthReentrantAlgorithm(name, pSvcLocator)
+  {
+  }
+
+  StatusCode FexBase::initializeBase(
+      const std::vector<std::string>& componentNames)
+  {
+    ATH_MSG_DEBUG("Initialising FexBase base class");
+    m_baseInitialised = true;
+    m_componentNames = componentNames;
+    CHECK( m_metContainerKey.initialize() );
+    CHECK( m_monTool.retrieve() );
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode FexBase::execute( const EventContext& context ) const
+  {
+    if (!m_baseInitialised) {
+      ATH_MSG_ERROR("Base class was not initialised! This means that the "
+          << "derived class was not correctly written!");
+      return StatusCode::FAILURE;
+    }
+    ATH_MSG_DEBUG("Executing " << name() << "...");
+    Monitored::Timer totalTimer("TIME_Total");
+    // Create the output
+    auto metCont = std::make_unique<xAOD::TrigMissingETContainer>();
+    auto metContAux = std::make_unique<xAOD::TrigMissingETAuxContainer>();
+    metCont->setStore(metContAux.get() );
+
+    // Create the actual output object
+    metCont->push_back(std::make_unique<xAOD::TrigMissingET>());
+    xAOD::TrigMissingET* met = metCont->back();
+
+    // Initialise the components
+    met->defineComponents(m_componentNames);
+    // We also need to initialise all of the values. This is not done by the EDM
+    // class so you can easily get missing aux element errors if you don't do
+    // this
+    met->setEx(0);
+    met->setEy(0);
+    met->setEz(0);
+    met->setSumEt(0);
+    met->setSumE(0);
+    met->setFlag(0);
+    met->setRoiWord(0);
+
+    MonGroupBuilder monitors;
+    CHECK( fillMET(*met, context, monitors) );
+    // Add flags
+    CHECK( flagMET(*met) );
+    // Add extra monitors
+    CHECK( monitor(*met, monitors) );
+    // Create the actual group and trigger the monitoring
+    monitors.build(m_monTool);
+
+    // Output REGTEST information
+    if (msgLvl(MSG::DEBUG) ) {
+      ATH_MSG_DEBUG( "REGTEST " << METComponent(*met) );
+      ATH_MSG_DEBUG( "REGTEST flag = " << met->flag() );
+      ATH_MSG_DEBUG( "REGTEST Name, status, values: ");
+      for (std::size_t ii = 0; ii < met->getNumberOfComponents(); ++ii)
+        ATH_MSG_DEBUG( "REGTEST "
+            << met->nameOfComponent(ii) << ", "
+            << met->statusComponent(ii) << ", "
+            << METComponent(ii, *met) );
+    }
+
+    // Push this output to the store
+    auto handle = SG::makeHandle(m_metContainerKey, context);
+    CHECK( handle.record( std::move(metCont), std::move(metContAux) ) );
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode FexBase::flagMET(xAOD::TrigMissingET& met) const
+  {
+    // Start with the components
+    // Keep a flag to OR into the main value
+    int overall = 0;
+    for (std::size_t ii = 0; ii < met.getNumberOfComponents(); ++ii) {
+      METComponent component(ii, met);
+      if (component.sumEt > 0 &&
+          component.met() / component.sumEt > m_maxComponentMetSumEtRatio) {
+        component.status |= StatusFlag::ComponentBigMEtSEtRatio;
+        overall |= StatusFlag::ComponentBigMEtSEtRatio;
+      }
+      met.setStatusComponent(ii, component.status);
+    }
+    METComponent total(met);
+    total.status |= overall;
+    if (total.sumEt > 0 &&
+        total.met() / total.sumEt > m_maxGlobalMetSumEtRatio)
+      total.status |= StatusFlag::GlobalBigMEtSEtRatio;
+    met.setFlag(total.status);
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode FexBase::monitor(
+      const xAOD::TrigMissingET& met,
+      MonGroupBuilder& monitors) const
+  {
+    METComponent metComponent(met);
+    // Increase the capacity of the vector
+    monitors.increaseCapacity(16, true);
+    // Add the standard variables
+    monitors.add(Monitored::Scalar("EF_MEx_log", toLog10GeV(metComponent.mpx) ) );
+    monitors.add(Monitored::Scalar("EF_MEy_log", toLog10GeV(metComponent.mpy) ) );
+    monitors.add(Monitored::Scalar("EF_MEz_log", toLog10GeV(metComponent.mpz) ) );
+    monitors.add(Monitored::Scalar("EF_MET_log", toLog10GeV(metComponent.met() ) ) );
+    monitors.add(Monitored::Scalar("EF_ME_log", toLog10GeV(metComponent.magnitude() ) ) );
+    monitors.add(Monitored::Scalar("EF_SumEt_log", toLog10GeV(metComponent.sumEt) ) );
+    monitors.add(Monitored::Scalar("EF_SumE_log", toLog10GeV(metComponent.sumE, -9e9) ) );
+    monitors.add(Monitored::Scalar("EF_MEx_lin", toLinGeV(metComponent.mpx) ) );
+    monitors.add(Monitored::Scalar("EF_MEy_lin", toLinGeV(metComponent.mpy) ) );
+    monitors.add(Monitored::Scalar("EF_MEz_lin", toLinGeV(metComponent.mpz) ) );
+    monitors.add(Monitored::Scalar("EF_MET_lin", toLinGeV(metComponent.met() ) ) );
+    monitors.add(Monitored::Scalar("EF_ME_lin", toLinGeV(metComponent.magnitude() ) ) );
+    monitors.add(Monitored::Scalar("EF_SumEt_lin", toLinGeV(metComponent.sumEt) ) );
+    monitors.add(Monitored::Scalar("EF_SumE_lin", toLinGeV(metComponent.sumE) ) );
+    monitors.add(Monitored::Scalar(
+          "EF_XS", toLinGeV(metComponent.met() ) / toLinGeV(metComponent.sumEt, 1) ) );
+    monitors.add(Monitored::Scalar("EF_MET_phi", metComponent.phi() ) );
+    return StatusCode::SUCCESS;
+  }
+} } //> end namespace HLT::MET
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.h b/Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.h
new file mode 100644
index 00000000000..07f9c8064c3
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/FexBase.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @class   FexBase
+ *
+ * @brief Base class for MET Trigger Fexs
+ * @author Jon Burr
+ *****************************************************************************/
+
+#ifndef TRIGEFMISSINGET_FEXBASE_H
+#define TRIGEFMISSINGET_FEXBASE_H 1
+
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "AthenaMonitoring/GenericMonitoringTool.h"
+#include "GaudiKernel/Property.h"
+#include "xAODTrigMissingET/TrigMissingETContainer.h"
+#include "GaudiKernel/SystemOfUnits.h"
+#include "MonGroupBuilder.h"
+
+#include <string>
+#include <vector>
+
+/******************************************************************************
+ * @class FexBase 
+ *
+ * Base class for HLT MET Fex algorithms.
+ *
+ * Responsible for creating the MET object
+ *****************************************************************************/
+
+namespace HLT { namespace MET {
+  class FexBase : public ::AthReentrantAlgorithm {
+    public:
+      /// Constructor
+      FexBase(const std::string& name, ISvcLocator* pSvcLocator);
+
+      /// Run the algorithm
+      virtual StatusCode execute(const EventContext& context) const override;
+
+    protected:
+      /************************************************************************
+       * Data members (accessible from derived classes)
+       ***********************************************************************/
+      /// The names of the output MET components
+      std::vector<std::string> m_componentNames;
+
+      /************************************************************************
+       * Internal functions
+       ***********************************************************************/
+      /**
+       * @brief Initialize the base class
+       * @param componentNames The names of any components in the output MET
+       * object.
+       *
+       * The xAOD::TrigMissingET writes out a main 'met' value but can also
+       * write out extra MET components - either for debugging, monitoring or
+       * further use offline. The object has to be initialized with this names,
+       * they cannot be extended after this, so this information must be
+       * available for the base class to use.
+       */
+      StatusCode initializeBase(
+          const std::vector<std::string>& componentNames);
+
+      /**
+       * @brief Calculate and fill the output MET value
+       * @param met The object to fill
+       * @param context The event context
+       * @param monitors Extra variables to be monitored
+       *
+       * The builder passed in to the monitors variable can be filled with any
+       * variables that the particular algorithm wishes to monitor. Extra
+       * variables that can be calculated directly from the xAOD::TrigMissingET
+       * object can also be added using the monitor function.
+       */
+      virtual StatusCode fillMET(
+          xAOD::TrigMissingET& met,
+          const EventContext& context,
+          MonGroupBuilder& monitors) const = 0;
+
+      /**
+       * @brief Add monitor variables from an xAOD::TrigMissingET object
+       * @param met The object to fill from
+       * @param monitors Extra variables to be monitored
+       */
+      virtual StatusCode monitor(
+          const xAOD::TrigMissingET& met,
+          MonGroupBuilder& monitors) const;
+
+      /**
+       * @brief Flag suspicious values in the output MET
+       * @param met The xAOD::TrigMissingET object to flag
+       */
+      virtual StatusCode flagMET(xAOD::TrigMissingET& met) const;
+
+    private:
+      /************************************************************************
+       * Properties (Not accessible from derived classes)
+       ***********************************************************************/
+      /// The output MET object
+      SG::WriteHandleKey<xAOD::TrigMissingETContainer> m_metContainerKey {
+        this, "METContainerKey", "HLT_MET", "Name of the output MET object"};
+      /// The monitoring tool
+      ToolHandle<GenericMonitoringTool> m_monTool{
+        this, "MonTool", "", "Monitoring tool"};
+      Gaudi::Property<float> m_maxComponentMetSumEtRatio{
+        "MaxComponentMetSumEtRatio", 1.,
+        "The maximum MET/SumEt ratio per component"};
+      Gaudi::Property<float> m_maxGlobalMetSumEtRatio{
+        "MaxGlobalMetSumEtRatio", 1.,
+        "The maximum MET/SumEt ratio for the total value"};
+
+      /************************************************************************
+       * Data members (Not accessible from derived classes)
+       ***********************************************************************/
+      /// Whether or not this class has been correctly initialised.
+      bool m_baseInitialised{false};
+  }; //> end class FexBase
+} } //> end namespace HTL::MET
+#endif //> !TRIGEFMISSINGET_FEXBASE_H
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/METComponent.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/METComponent.cxx
new file mode 100644
index 00000000000..f000813943f
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/METComponent.cxx
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+#include "TrigEFMissingET/METComponent.h"
+#include <cmath>
+
+namespace HLT { namespace MET {
+  METComponent::METComponent(const xAOD::TrigMissingET& met) :
+    mpx(met.ex() ),
+    mpy(met.ey() ),
+    mpz(met.ez() ),
+    sumE(met.sumE() ),
+    sumEt(met.sumEt() ),
+    status(met.flag() )
+  {}
+
+  METComponent::METComponent(
+      std::size_t idx, const xAOD::TrigMissingET& met) :
+    mpx(met.exComponent(idx) ),
+    mpy(met.eyComponent(idx) ),
+    mpz(met.ezComponent(idx) ),
+    sumE(met.sumEComponent(idx) ),
+    sumEt(met.sumEtComponent(idx) ),
+    status(met.statusComponent(idx) )
+  {}
+
+  float METComponent::met() const {
+    return sqrt(mpx*mpx+mpy*mpy);
+  }
+
+  float METComponent::magnitude() const {
+    return sqrt(mpx*mpx+mpy*mpy+mpz*mpz);
+  }
+
+  float METComponent::phi() const {
+    return std::atan2(mpy, mpx);
+  }
+
+  float METComponent::eta() const {
+    if (met() == 0)
+      return 0;
+    return std::asinh(mpz/met() );
+  }
+
+  METComponent operator+(
+      const METComponent& lhs,
+      const METComponent& rhs)
+  {
+    METComponent ret(lhs);
+    ret += rhs;
+    return ret;
+  }
+
+  METComponent& METComponent::operator+=(
+      const METComponent& other)
+  {
+    mpx    += other.mpx;
+    mpy    += other.mpy;
+    mpz    += other.mpz;
+    sumE   += other.sumE;
+    sumEt  += other.sumEt;
+    status |= other.status;
+    return *this;
+  }
+
+  METComponent& METComponent::operator+=(
+      const TLorentzVector& otherP4)
+  {
+    mpx   -= otherP4.Px();
+    mpy   -= otherP4.Py();
+    mpz   -= otherP4.Pz();
+    sumE  += otherP4.E();
+    sumEt += otherP4.Pt();
+    return *this;
+  }
+
+  METComponent& METComponent::operator+=(
+      const SignedKinematics& kin)
+  {
+    mpx   -= kin.px();
+    mpy   -= kin.py();
+    mpz   -= kin.pz();
+    sumE  += kin.energy();
+    sumEt += kin.pt();
+    return *this;
+  }
+
+  void METComponent::fillMET(xAOD::TrigMissingET& met) const
+  {
+    met.setEx(mpx);
+    met.setEy(mpy);
+    met.setEz(mpz);
+    met.setSumE(sumE);
+    met.setSumEt(sumEt);
+    met.setFlag(status);
+  }
+
+  void METComponent::fillMETComponent(
+      std::size_t ii, xAOD::TrigMissingET& met) const
+  {
+    met.setExComponent(ii, mpx);
+    met.setEyComponent(ii, mpy);
+    met.setEzComponent(ii, mpy);
+    met.setSumEComponent(ii, sumE);
+    met.setSumEtComponent(ii, sumEt);
+    met.setStatusComponent(ii, status);
+  }
+
+  std::ostream& operator<<(std::ostream& os, const METComponent& component)
+  {
+    os << "(mpx, mpy, mpz, met, sumEt, sumE) = (" 
+      << component.mpx << ", "
+      << component.mpy << ", "
+      << component.mpz << ", "
+      << component.met() << ", "
+      << component.sumEt << ", "
+      << component.sumE << ") [MeV]";
+    return os;
+  }
+} } //> end namespace HLT::MET
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.cxx
new file mode 100644
index 00000000000..48dc0b7ff8e
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.cxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @file MonGroupBuilder.cxx
+ *
+ * Implementation of monitoring group builder
+ * @author Jon Burr
+ *****************************************************************************/
+
+#include "MonGroupBuilder.h"
+
+namespace HLT { namespace MET {
+  void MonGroupBuilder::add(Monitored::IMonitoredVariable& variable)
+  {
+    m_references.push_back(variable);
+  }
+
+  Monitored::Group MonGroupBuilder::build(
+      const ToolHandle<GenericMonitoringTool>& tool) const
+  {
+    return Monitored::Group(tool, m_references);
+  }
+
+  void MonGroupBuilder::increaseCapacity(std::size_t value, bool owned)
+  {
+    m_references.reserve(value+m_references.capacity() );
+    if (owned)
+      m_ptrs.reserve(value+m_ptrs.capacity() );
+  }
+} } //> end namespace HLT::MET
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.h b/Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.h
new file mode 100644
index 00000000000..07436042a7d
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/MonGroupBuilder.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @class   MonGroupBuilder
+ *
+ * @brief Class to help with building monitoring groups
+ * @author Jon Burr
+ *****************************************************************************/
+
+#ifndef TRIGEFMISSINGET_MONGROUPBUILDER_H
+#define TRIGEFMISSINGET_MONGROUPBUILDER_H 1
+
+#include "AthenaMonitoringKernel/MonitoredGroup.h"
+#include <type_traits>
+#include <memory>
+
+/******************************************************************************
+ * @class MonGroupBuilder
+ *
+ * Class allowing incremental building of Monitored::Groups.
+ *
+ * Currently, it's only possible to load all members of a single
+ * Monitored::Group simultaneously, i.e. it's not possible to build up the group
+ * a few members at a time. However in these algorithms I found times where I
+ * wanted to monitor several variables at the same time, but trigger these from
+ * different locations.
+ *****************************************************************************/
+
+namespace HLT { namespace MET {
+  class MonGroupBuilder {
+    public:
+      /**
+       * @brief Add a new monitored variable
+       * @param variable The variable to monitor
+       *
+       * This overload will not take ownership of the variable
+       */
+      void add(Monitored::IMonitoredVariable& variable);
+
+      /**
+       * @brief Add a new monitored variable
+       * @tparam T The type of monitored variable
+       * @param variable The variable to monitor
+       *
+       * This overload will take ownership of the variable
+       */
+      template <typename T,
+                typename = std::enable_if_t<
+                  !std::is_lvalue_reference_v<T> &&
+                  std::is_base_of_v<Monitored::IMonitoredVariable, T>>>
+        void add(T&& variable)
+        {
+          m_ptrs.push_back(std::make_unique<T>(std::move(variable) ) );
+          add(*m_ptrs.back() );
+        }
+
+      /**
+       * @brief Build the monitored group
+       * @param tool The monitoring tool to add the group to
+       *
+       * Builds the monitored group. Note that if this builder owns any
+       * variables then fill *must* be called on the group before this object
+       * goes out of scope (remembering that groups call fill on destruction if
+       * it hasn't been called before this).
+       */
+      Monitored::Group build(
+          const ToolHandle<GenericMonitoringTool>& tool) const;
+
+      /**
+       * @brief Increase the internal capacity
+       * @param value The amount to increase capacity by
+       * @param owned Whether to increase the 'owned' capacity by the same
+       * amount
+       *
+       * Increase the capacity of the internal vectors. This function is used
+       * for performance only.
+       */
+      void increaseCapacity(std::size_t value, bool owned=false);
+    private:
+      /// References to monitored variables
+      std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>> m_references;
+      /// Any monitored variables that we own directly
+      std::vector<std::unique_ptr<Monitored::IMonitoredVariable>> m_ptrs;
+  }; //> end class MonGroupBuilder
+} } //> end namespace HLT::MET
+
+#endif //> !TRIGEFMISSINGET_MONGROUPBUILDER_H
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/SignedKinematics.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/SignedKinematics.cxx
new file mode 100644
index 00000000000..a85a4a40a68
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/SignedKinematics.cxx
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+#include "TrigEFMissingET/SignedKinematics.h"
+#include <TLorentzVector.h>
+#include "xAODBase/IParticle.h"
+#include <TVector2.h>
+
+namespace HLT { namespace MET {
+  SignedKinematics::SignedKinematics() {}
+  SignedKinematics::SignedKinematics(
+      double px, double py, double pz, double energy) :
+    m_p4(px, py, pz, energy)
+  {}
+
+  SignedKinematics::SignedKinematics(const TLorentzVector& tlv) :
+    m_p4(tlv.Px(), tlv.Py(), tlv.Pz(), tlv.E() )
+  {}
+
+  SignedKinematics::SignedKinematics(const xAOD::IParticle& particle) :
+    SignedKinematics(fromEnergyEtaPhiM(
+          particle.e(), particle.eta(), particle.phi(), particle.m() ) )
+  {}
+
+  SignedKinematics SignedKinematics::fromEnergyEtaPhi(
+      double energy, double eta, double phi)
+  {
+    return fromEnergyEtaPhiM(energy, eta, phi, 0.);
+  }
+
+  SignedKinematics SignedKinematics::fromEnergyEtaPhiM(
+      double energy, double eta, double phi, double mass)
+  {
+    double p = energy;
+    if (mass != 0) {
+      int sgn = (p > 0) - (p < 0);
+      p = sgn * sqrt(p*p - mass*mass);
+    }
+    double pt = p/std::cosh(eta);
+    double pz = pt*std::sinh(eta);
+    return SignedKinematics(
+        pt*std::cos(phi), pt*std::sin(phi), pz, energy);
+  }
+
+  SignedKinematics SignedKinematics::fromEtEtaPhi(
+      double et, double eta, double phi)
+  {
+    return fromEtEtaPhiM(et, eta, phi, 0.);
+  }
+
+  SignedKinematics SignedKinematics::fromEtEtaPhiM(
+      double et, double eta, double phi, double mass)
+  {
+    return fromEnergyEtaPhiM(et*std::cosh(eta), eta, phi, mass);
+  }
+
+  int SignedKinematics::sign() const {
+    return (energy() > 0) - (energy() < 0);
+  }
+
+  double SignedKinematics::eta() const {
+    return sign() * m_p4.Eta();
+  }
+
+  double SignedKinematics::phi() const {
+    double val = m_p4.Phi();
+    if (sign() < 0)
+      val += TMath::Pi();
+    return TVector2::Phi_0_2pi(val);
+  }
+
+  double SignedKinematics::sinPhi() const {
+    double thePt = pt();
+    // if pt() is 0 then the value is not determined.
+    // For this phi = 0
+    return (thePt == 0 ? 0 : py() / thePt );
+  }
+  double SignedKinematics::cosPhi() const {
+    double thePt = pt();
+    // if pt() is 0 then the value is not determined.
+    // Take phi = 0
+    return (thePt == 0 ? 1 : px() / thePt );
+  }
+
+  double SignedKinematics::sinhEta() const {
+    double thePt = pt();
+    // if pt is 0 then the value is not determined.
+    // Take eta = 0
+    return (thePt == 0 ? 0 : pz() / thePt );
+  }
+  double SignedKinematics::coshEta() const {
+    double thePt2 = pt2();
+    // if pt is 0 then the value is not determined.
+    // Take eta = 0
+    if (thePt2 == 0)
+      return 1;
+    // Otherwise, calculate sinh^2 eta, then use 
+    // cosh eta = sqrt(1 + sinh^2 eta)
+    return sqrt(1 + pz()*pz() / thePt2);
+  }
+
+  double SignedKinematics::p() const {
+    return sign() * absP();
+  }
+  double SignedKinematics::absP() const {
+    return sqrt(p2() );
+  }
+  double SignedKinematics::p2() const {
+    return pt2() + pz()*pz();
+  }
+  double SignedKinematics::pt() const {
+    return sign() * absPt();
+  }
+  double SignedKinematics::absPt() const {
+    return sqrt(pt2() );
+  }
+  double SignedKinematics::pt2() const {
+    return px()*px() + py()*py();
+  }
+  double SignedKinematics::px() const {
+    return m_p4.Px();
+  }
+  double SignedKinematics::py() const {
+    return m_p4.Py();
+  }
+  double SignedKinematics::pz() const {
+    return m_p4.Pz();
+  }
+
+  double SignedKinematics::energy() const {
+    return m_p4.E();
+  }
+  double SignedKinematics::absEnergy() const {
+    return abs(energy() );
+  }
+  double SignedKinematics::energy2() const {
+    return energy()*energy();
+  }
+  double SignedKinematics::et() const {
+    return energy() / coshEta();
+  }
+  double SignedKinematics::absEt() const {
+    return abs(et() );
+  }
+  double SignedKinematics::et2() const {
+    return et()*et();
+  }
+  double SignedKinematics::ex() const {
+    return et() * cosPhi();
+  }
+  double SignedKinematics::ey() const {
+    return et() * sinPhi();
+  }
+  double SignedKinematics::ez() const {
+    return et() * sinhEta();
+  }
+
+  double SignedKinematics::m2() const {
+    return energy2() - p2();
+  }
+
+  SignedKinematics::operator ROOT::Math::PxPyPzEVector() const {
+    return m_p4;
+  }
+
+  SignedKinematics& SignedKinematics::operator+=(const SignedKinematics& other)
+  {
+    m_p4.SetPx(px()+other.px() );
+    m_p4.SetPy(py()+other.py() );
+    m_p4.SetPz(pz()+other.pz() );
+    m_p4.SetE(energy()+other.energy() );
+    return *this;
+  }
+  SignedKinematics& SignedKinematics::operator-=(const SignedKinematics& other)
+  {
+    m_p4.SetPx(px()-other.px() );
+    m_p4.SetPy(py()-other.py() );
+    m_p4.SetPz(pz()-other.pz() );
+    m_p4.SetE(energy()-other.energy() );
+    return *this;
+  }
+
+  SignedKinematics operator+(const SignedKinematics& lhs, const SignedKinematics& rhs)
+  {
+    SignedKinematics val(lhs);
+    val += rhs;
+    return val;
+  }
+  SignedKinematics operator-(const SignedKinematics& lhs, const SignedKinematics& rhs)
+  {
+    SignedKinematics val(lhs);
+    val -= rhs;
+    return val;
+  }
+} } //> end namespace HLT::MET
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/StatusFlags.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/StatusFlags.cxx
new file mode 100644
index 00000000000..3252af0fc0c
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/StatusFlags.cxx
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+#include "TrigEFMissingET/StatusFlags.h"
+#include <stdexcept>
+
+namespace HLT { namespace MET { namespace StatusFlag {
+  std::string flagIndexToString(int idx) {
+    switch(idx) {
+      case 0:
+      case 4:
+      case 13:
+      case 14:
+      case 16:
+      case 17:
+      case 18:
+      case 19:
+      case 20:
+      case 21:
+      case 22:
+      case 23:
+      case 24:
+      case 25:
+      case 26:
+        return "UNUSED";
+      case 1:
+        return "BSConvError";
+      case 2:
+        return "MuonError";
+      case 3:
+        return "FEBError";
+      case 5:
+        return "ComponentBigMEtSEtRatio";
+      case 6:
+        return "BadComponentEnergy";
+      case 7:
+        return "BadEnergyRatio";
+      case 8:
+        return "NoisyEnergyRatio";
+      case 9:
+        return "BadCellQuality";
+      case 10:
+        return "BadCellEnergy";
+      case 11:
+        return "BadCellTime";
+      case 12:
+        return "NoMuonTrack";
+      case 15:
+        return "ComponentErrors";
+      case 27:
+        return "GlobalNoisyEnergyRatio";
+      case 28:
+        return "BadEMFraction";
+      case 29:
+        return "GlobalNoisyEnerygRatio";
+      case 30:
+        return "ObjectInCrack";
+      case 31:
+        return "GlobalErrors";
+      default:
+        throw std::out_of_range("Invalid index provided " + std::to_string(idx) );
+        // Make gcc happy
+        return "";
+    }
+  }
+} } } //> end namespace HLT::MET::StatusFlag
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.cxx
new file mode 100644
index 00000000000..7fe6951b2b8
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.cxx
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @file TrkMHTFex.cxx
+ *
+ * Implementation of trkmht fex class
+ * @author Jon Burr
+ *****************************************************************************/
+
+#include "TrkMHTFex.h"
+#include "TrigEFMissingET/METComponent.h"
+#include "StoreGate/DecorKeyHelpers.h"
+#include "StoreGate/ReadDecorHandle.h"
+#include "AthLinks/ElementLink.h"
+#include "xAODBase/IParticleContainer.h"
+#include <unordered_set>
+#include <numeric>
+
+namespace {
+  bool isCentral(const xAOD::Jet* ijet) {
+    return fabs(ijet->eta() ) < 2.4;
+  }
+}
+
+namespace HLT { namespace MET {
+  TrkMHTFex::TrkMHTFex(const std::string& name, ISvcLocator* pSvcLocator):
+    FexBase(name, pSvcLocator) 
+  {}
+
+  StatusCode TrkMHTFex::initialize()
+  {
+    CHECK( m_jetKey.initialize() );
+    CHECK( m_trackKey.initialize() );
+    CHECK( m_vertexKey.initialize() );
+    CHECK( m_tvaKey.initialize() );
+    CHECK( m_trackSelTool.retrieve() );
+
+    // Update the decor keys if necessary
+    if (m_jvtKey.key().find(".") == std::string::npos) {
+      m_jvtKey = m_jetKey.key() + "." + m_jvtKey.key();
+      CHECK( m_jvtKey.initialize() );
+    }
+    else if (SG::contKeyFromKey(m_jvtKey.key() ) != m_jetKey.key() ) {
+      ATH_MSG_ERROR("JVT container key does not match jet key!");
+      return StatusCode::FAILURE;
+    }
+    if (m_trackGAKey.key().find(".") == std::string::npos) {
+      m_trackGAKey = m_jetKey.key() + "." + m_trackGAKey.key();
+      CHECK( m_trackGAKey.initialize() );
+    }
+    else if (SG::contKeyFromKey(m_trackGAKey.key() ) != m_jetKey.key() ) {
+      ATH_MSG_ERROR("track links container key does not match jet key!");
+      return StatusCode::FAILURE;
+    }
+
+    return initializeBase(
+        {"JETB1", "JETB2", "JETE1", "JETE2", "TrackSoftTerm"});
+  }
+
+  StatusCode TrkMHTFex::fillMET(
+      xAOD::TrigMissingET& met,
+      const EventContext& context,
+      MonGroupBuilder&) const
+  {
+    // Retrieve the inputs
+    auto jets = SG::makeHandle(m_jetKey, context);
+    auto tracks = SG::makeHandle(m_trackKey, context);
+    auto vertices = SG::makeHandle(m_vertexKey, context);
+    auto tva = SG::makeHandle(m_tvaKey, context);
+    auto jvtAcc = SG::makeHandle<float>(m_jvtKey, context);
+    auto trackLinksAcc = SG::makeHandle<std::vector<ElementLink<xAOD::IParticleContainer>>>(m_trackGAKey, context);
+
+    // Work out which is the primary vertex
+    // I do not know if there is a way to see which vertex was counted as
+    // primary by the JVT calculation but I see no reason to suspect that it
+    // would be anything other than the one found by this method
+    const xAOD::Vertex* priVtx = nullptr;
+    for (const xAOD::Vertex* vtx : *vertices)
+      if (vtx->vertexType() == xAOD::VxType::PriVtx) {
+        priVtx = vtx;
+        break;
+      }
+
+    // Prepare the output values
+    std::array<METComponent, 4> mhtSums;
+    METComponent tstSum;
+
+    // If no primary vertex is identified, the algorithm will return 0
+    if (priVtx) {
+      // Prepare a list of primary vertex tracks
+      std::vector<const xAOD::TrackParticle*> tstTracks;
+      tstTracks.reserve(tracks->size() );
+      for (const xAOD::TrackParticle* itrk : *tracks)
+        if (tva->associatedVertex(itrk) == priVtx &&
+            itrk->pt() < m_tstPtCeil &&
+            m_trackSelTool->accept(*itrk, priVtx) )
+          tstTracks.push_back(itrk);
+      // Keep indices of tracks associated to hard-scatter jets
+      std::unordered_set<std::size_t> jetTrackIndices;
+      // Iterate over the jets
+      for (const xAOD::Jet* ijet : *jets) {
+        if (isCentral(ijet) ) {
+          if (ijet->pt() < m_minJvtJetPt)
+            // Skip central jets below the JVT pT threshold
+            continue;
+          if (ijet->pt() < m_maxJvtJetPt && jvtAcc(*ijet) < m_jvtSelection)
+            // Skip central jets below the maximum JVT pT threshold and with JVT
+            // below threshold
+            continue;
+        } // end if isCentral(ijet)
+        else {
+          if (ijet->pt() < m_forwardJetPt)
+            // Skip forward jets below the forward jet pT threshold
+            continue;
+        }
+        // The index of the mht METComponent
+        std::size_t componentIdx = 0;
+        if (ijet->eta() < 0)
+          ++componentIdx;
+        if (isCentral(ijet) )
+          componentIdx += 2;
+        // Add the jet into the correct component
+        mhtSums.at(componentIdx) += ijet->p4();
+        // Iterate over the ghost-associated tracks
+        for (const auto& link : trackLinksAcc(*ijet) ) {
+          if (link.getDataPtr() != tracks.ptr() ) {
+            ATH_MSG_ERROR("Tracks linked with key '" << m_trackGAKey
+                << "' do not match tracks retrieved with '"
+                << m_trackKey << "'!");
+            return StatusCode::FAILURE;
+          }
+          jetTrackIndices.insert(link.index() );
+        }
+      }
+      // Build up the track soft term
+      for (const xAOD::TrackParticle* itrk : tstTracks)
+        if (jetTrackIndices.count(itrk->index() ) == 0)
+          // Only include tracks that weren't associated to a hard-scatter jet
+          tstSum += itrk->p4();
+    } //> end if priVtx
+    // Save the sum over components
+    std::accumulate(mhtSums.begin(), mhtSums.end(), tstSum).fillMET(met);
+    // Save each component
+    for (std::size_t ii = 0; ii < 4; ++ii)
+      mhtSums.at(ii).fillMETComponent(ii, met);
+    // Save the tst
+    tstSum.fillMETComponent(4, met);
+
+    // Debugging information
+    ATH_MSG_DEBUG("Jets: "
+        << std::accumulate(mhtSums.begin(), mhtSums.end(), METComponent{}) );
+
+    return StatusCode::SUCCESS;
+  }
+
+} } //> end namespace HLT::MET
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.h b/Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.h
new file mode 100644
index 00000000000..d094100a374
--- /dev/null
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/TrkMHTFex.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+ */
+
+/******************************************************************************
+ * @package Trigger/TrigAlgorithms/TrigEFMissingET
+ * @class   TrkMHTFex
+ *
+ * @brief Fex class for trkmht algorithm
+ * @author Jon Burr, Ren-Jie Wang
+ *****************************************************************************/
+
+#ifndef TRIGEFMISSINGET_TRKMHTFEX_H
+#define TRIGEFMISSINGET_TRKMHTFEX_H 1
+
+#include "FexBase.h"
+#include "xAODJet/JetContainer.h"
+#include "xAODTracking/TrackParticleContainer.h"
+#include "xAODTracking/VertexContainer.h"
+#include "JetEDM/TrackVertexAssociation.h"
+#include "StoreGate/ReadDecorHandleKey.h"
+#include "InDetTrackSelectionTool/IInDetTrackSelectionTool.h"
+
+/******************************************************************************
+ * @class TrkMHTFex
+ *
+ * Class to create output from the trkmht algorithm.
+ *
+ * trkmht calculates the MET using jets with JVT suppression applied. There is
+ * an additional track soft term which adds in any tracks associated to the hard
+ * scatter vertex which are not associated to jets passing the JVT selection.
+ *****************************************************************************/
+
+namespace HLT { namespace MET {
+  class TrkMHTFex : public FexBase
+  {
+    public:
+      /// Constructor
+      TrkMHTFex(const std::string& name, ISvcLocator* pSvcLocator);
+
+      /// Initialise the fex
+      virtual StatusCode initialize() override;
+
+    private:
+      /************************************************************************
+       * Properties
+       ***********************************************************************/
+      /// Input jets and jvt
+      SG::ReadHandleKey<xAOD::JetContainer> m_jetKey{
+        this, "JetName", "jets", "Input jet collection"};
+      /// Input tracks
+      SG::ReadHandleKey<xAOD::TrackParticleContainer> m_trackKey{
+        this, "TrackName", "tracks",
+        "Input track collection. Should be ghost-associated to the jets"};
+      /// Input vertices
+      SG::ReadHandleKey<xAOD::VertexContainer> m_vertexKey{
+        this, "VertexName", "vertices", "Input vertex collection"};
+      /// The track-vertex assocation name
+      SG::ReadHandleKey<jet::TrackVertexAssociation> m_tvaKey{
+        this, "TVAName", "trackVtxAssoc",
+        "The name of the track-vertex association"};
+      /// The Jvt selection name
+      SG::ReadDecorHandleKey<xAOD::JetContainer> m_jvtKey{
+        this, "JvtName", "Jvt", "The name of the JVT decorator"};
+      /// The ghost-association aux element name
+      SG::ReadDecorHandleKey<xAOD::JetContainer> m_trackGAKey{
+        this, "TrackLinkName", "tracks", "The name of the jet track links"};
+      /// Pt selection on forward jets
+      Gaudi::Property<float> m_forwardJetPt{
+        this, "ForwardJetPt", 0., "The pT cut applied to forward jets"};
+      /// Minimum pt selection for JVT on central jets
+      Gaudi::Property<float> m_minJvtJetPt{
+        this, "MinimumJvtJetPt", 20. * Gaudi::Units::GeV,
+        "The minimum pT for central jets for JVT selections to be used"};
+      /// Maximum pt selection for JVT on central jets
+      Gaudi::Property<float> m_maxJvtJetPt{
+        this, "MaximumJvtJetPt", 50. * Gaudi::Units::GeV,
+        "The maximum pT for central jets for JVT selections to be used"};
+      /// The JVT selection
+      Gaudi::Property<float> m_jvtSelection{
+        this, "JVTCut", 0.9, "The JVT selection"};
+      /// The track selection tool
+      ToolHandle<InDet::IInDetTrackSelectionTool> m_trackSelTool{
+        this, "TrackSelTool", "InDet::InDetTrackSelectionTool/TrackSelTool",
+        "The track selection tool to use for tracks entering the track soft "
+        "term."};
+      /// The maximum pT for tracks in the soft term
+      Gaudi::Property<float> m_tstPtCeil{
+        this, "TrackSoftTermPtCeiling", 20. * Gaudi::Units::GeV,
+        "The maximum pT for tracks entering the track soft term."};
+
+      /************************************************************************
+       * Internal functions
+       ***********************************************************************/
+      /**
+       * @brief Calculate and fill the output MET value
+       * @param met The object to fill
+       * @param context The event context
+       * @param monitors[out] Any extra monitors to fill
+       */
+      virtual StatusCode fillMET(
+          xAOD::TrigMissingET& met,
+          const EventContext& context,
+          MonGroupBuilder& monitors) const override;
+  }; //> end class TrkMHTFex
+} } //> end namespace HLT::MET
+
+#endif //> !TRIGEFMISSINGET_TRKMHTFEX_H
diff --git a/Trigger/TrigAlgorithms/TrigEFMissingET/src/components/TrigEFMissingET_entries.cxx b/Trigger/TrigAlgorithms/TrigEFMissingET/src/components/TrigEFMissingET_entries.cxx
index 7f26a6b52de..d0439c10f39 100644
--- a/Trigger/TrigAlgorithms/TrigEFMissingET/src/components/TrigEFMissingET_entries.cxx
+++ b/Trigger/TrigAlgorithms/TrigEFMissingET/src/components/TrigEFMissingET_entries.cxx
@@ -19,6 +19,7 @@
 #include "../EFMissingETFromJetsMT.h"
 #include "../EFMissingETFlagsMT.h"
 #include "../EFMissingETComponentCopier.h"
+#include "../TrkMHTFex.h"
 
 DECLARE_COMPONENT( EFMissingET )
 DECLARE_COMPONENT( EFMissingETBaseTool )
@@ -40,4 +41,4 @@ DECLARE_COMPONENT( EFMissingETFromClustersMT )
 DECLARE_COMPONENT( EFMissingETFromClustersPufitMT )
 DECLARE_COMPONENT( EFMissingETFromJetsMT )
 DECLARE_COMPONENT( EFMissingETFlagsMT )
-
+DECLARE_COMPONENT( HLT::MET::TrkMHTFex )
diff --git a/Trigger/TrigValidation/TrigAnalysisTest/share/q221_RDOtoRDOTrig_mt1_build.ref b/Trigger/TrigValidation/TrigAnalysisTest/share/q221_RDOtoRDOTrig_mt1_build.ref
index 51d3609672a..e96f0af15b1 100644
--- a/Trigger/TrigValidation/TrigAnalysisTest/share/q221_RDOtoRDOTrig_mt1_build.ref
+++ b/Trigger/TrigValidation/TrigAnalysisTest/share/q221_RDOtoRDOTrig_mt1_build.ref
@@ -162,5 +162,7 @@ TrigSignatureMoniMT                                INFO HLT_xe30_mht_L1XE10
 TrigSignatureMoniMT                                INFO HLT_xe30_mht_L1XE10 decisions                     0         0         0         0         0         0
 TrigSignatureMoniMT                                INFO HLT_xe30_tcpufit_L1XE10       0         0         0         0         0         0         0         0         0
 TrigSignatureMoniMT                                INFO HLT_xe30_tcpufit_L1XE10 decisions                    0         0         0         0         0         0
+TrigSignatureMoniMT                                INFO HLT_xe30_trkmht_L1XE10        0         0         0         0         0         0         0         0         0
+TrigSignatureMoniMT                                INFO HLT_xe30_trkmht_L1XE10 decisions                    0         0         0         0         0         0
 TrigSignatureMoniMT                                INFO HLT_xe65_cell_L1XE50          0         0         0         0         0         0         0         0         0
 TrigSignatureMoniMT                                INFO HLT_xe65_cell_L1XE50 decisions                    0         0         0         0         0         0
diff --git a/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu_build.ref b/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu_build.ref
index fc41dfb568e..4c39dfe4022 100644
--- a/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu_build.ref
+++ b/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu_build.ref
@@ -172,5 +172,7 @@ TrigSignatureMoniMT                                 INFO HLT_xe30_mht_L1XE10
 TrigSignatureMoniMT                                 INFO HLT_xe30_mht_L1XE10 decisions                     17        0         0         0         0         0         
 TrigSignatureMoniMT                                 INFO HLT_xe30_tcpufit_L1XE10       20        20        3         0         0         0         0         0         3         
 TrigSignatureMoniMT                                 INFO HLT_xe30_tcpufit_L1XE10 decisions                    3         0         0         0         0         0         
+TrigSignatureMoniMT                                 INFO HLT_xe30_trkmht_L1XE10        20        20        9         0         0         0         0         0         9         
+TrigSignatureMoniMT                                 INFO HLT_xe30_trkmht_L1XE10 decisions                    9         0         0         0         0         0         
 TrigSignatureMoniMT                                 INFO HLT_xe65_cell_L1XE50          20        20        3         0         0         0         0         0         3         
 TrigSignatureMoniMT                                 INFO HLT_xe65_cell_L1XE50 decisions                    3         0         0         0         0         0         
diff --git a/Trigger/TrigValidation/TrigUpgradeTest/share/slice_met.ref b/Trigger/TrigValidation/TrigUpgradeTest/share/slice_met.ref
index 0e0a1342de7..04ce1025af2 100644
--- a/Trigger/TrigValidation/TrigUpgradeTest/share/slice_met.ref
+++ b/Trigger/TrigValidation/TrigUpgradeTest/share/slice_met.ref
@@ -1,10 +1,12 @@
-TrigSignatureMoniMT                                INFO HLT_xe30_cell_L1XE10          50        50        30        30        
-TrigSignatureMoniMT                                INFO HLT_xe30_cell_L1XE10 decisions                    30        
-TrigSignatureMoniMT                                INFO HLT_xe30_cell_xe30_tcpufit_L1XE1050        50        31        4         
-TrigSignatureMoniMT                                INFO HLT_xe30_cell_xe30_tcpufit_L1XE10 decisions                    39        
-TrigSignatureMoniMT                                INFO HLT_xe30_mht_L1XE10           50        50        39        39        
-TrigSignatureMoniMT                                INFO HLT_xe30_mht_L1XE10 decisions                     39        
-TrigSignatureMoniMT                                INFO HLT_xe30_tcpufit_L1XE10       50        50        5         5         
-TrigSignatureMoniMT                                INFO HLT_xe30_tcpufit_L1XE10 decisions                    5         
-TrigSignatureMoniMT                                INFO HLT_xe65_cell_L1XE50          50        50        7         7         
-TrigSignatureMoniMT                                INFO HLT_xe65_cell_L1XE50 decisions                    7         
+TrigSignatureMoniMT                                 INFO HLT_xe30_cell_L1XE10          50        50        30        30        
+TrigSignatureMoniMT                                 INFO HLT_xe30_cell_L1XE10 decisions                    30        
+TrigSignatureMoniMT                                 INFO HLT_xe30_cell_xe30_tcpufit_L1XE1050        50        31        4         
+TrigSignatureMoniMT                                 INFO HLT_xe30_cell_xe30_tcpufit_L1XE10 decisions                    39        
+TrigSignatureMoniMT                                 INFO HLT_xe30_mht_L1XE10           50        50        39        39        
+TrigSignatureMoniMT                                 INFO HLT_xe30_mht_L1XE10 decisions                     39        
+TrigSignatureMoniMT                                 INFO HLT_xe30_tcpufit_L1XE10       50        50        5         5         
+TrigSignatureMoniMT                                 INFO HLT_xe30_tcpufit_L1XE10 decisions                    5         
+TrigSignatureMoniMT                                 INFO HLT_xe30_trkmht_L1XE10        50        50        18        18        
+TrigSignatureMoniMT                                 INFO HLT_xe30_trkmht_L1XE10 decisions                    18        
+TrigSignatureMoniMT                                 INFO HLT_xe65_cell_L1XE50          50        50        7         7         
+TrigSignatureMoniMT                                 INFO HLT_xe65_cell_L1XE50 decisions                    7         
diff --git a/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py b/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py
index 092f9b29fea..95afbcfc28f 100644
--- a/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py
+++ b/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py
@@ -251,6 +251,9 @@ TriggerHLTListRun3 = [
     ('xAOD::TrigMissingETContainer#HLT_MET_tc',                            'BS ESD AODFULL AODSLIM AODVERYSLIM', 'MET'),
     ('xAOD::TrigMissingETAuxContainer#HLT_MET_tcAux.',                     'BS ESD AODFULL AODSLIM AODVERYSLIM', 'MET'),
 
+    ('xAOD::TrigMissingETContainer#HLT_MET_trkmht',                        'BS ESD AODFULL AODSLIM AODVERYSLIM', 'MET'),
+    ('xAOD::TrigMissingETAuxContainer#HLT_MET_trkmhtAux.',                 'BS ESD AODFULL AODSLIM AODVERYSLIM', 'MET'),
+
     ('xAOD::CaloClusterContainer#HLT_TopoCaloClustersFS',                  'BS ESD AODFULL AODSLIM AODVERYSLIM', 'MET'),
     ('xAOD::CaloClusterTrigAuxContainer#HLT_TopoCaloClustersFSAux.nCells', 'BS ESD AODFULL AODSLIM AODVERYSLIM', 'MET'),
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METChainConfiguration.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METChainConfiguration.py
index 9acbfa54f06..b0129c6d94c 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METChainConfiguration.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METChainConfiguration.py
@@ -7,7 +7,9 @@ log = logging.getLogger("TriggerMenuMT.HLTMenuConfig.MET.METChainConfiguration")
 
 from TriggerMenuMT.HLTMenuConfig.Menu.ChainConfigurationBase import ChainConfigurationBase
 
-from TriggerMenuMT.HLTMenuConfig.MET.METMenuSequences import metCellMenuSequence, metClusterPufitMenuSequence, metJetMenuSequence
+from TriggerMenuMT.HLTMenuConfig.MET.METMenuSequences import (
+        metCellMenuSequence, metClusterPufitMenuSequence, metJetMenuSequence,
+        metTrkMHTMenuSequence)
 
 #----------------------------------------------------------------
 # fragments generating configuration will be functions in New JO, 
@@ -24,7 +26,8 @@ def MetClusterPufitSequenceCfg( flags ):
 def MetJetSequenceCfg( flags ):    
    return metJetMenuSequence()
 
-
+def MetTrkMHTSequenceCfg( flags ):
+    return metTrkMHTMenuSequence()
 
 #----------------------------------------------------------------
 # Class to configure chain
@@ -47,6 +50,7 @@ class MetChainConfiguration(ChainConfigurationBase):
             "cell":[self.getMetCellStep()],
             "tcpufit":[self.getMetClusterPufitStep()],
             "mht":[self.getMetJetStep()],
+            "trkmht" : [self.getMetTrkMHTStep()],
         }
         
 
@@ -79,7 +83,9 @@ class MetChainConfiguration(ChainConfigurationBase):
     def getMetClusterPufitStep(self):
         return self.getStep(1,"met_clusterpufit", [MetClusterPufitSequenceCfg] )
      
-            
+    # Configuration of trkmht chain
+    def getMetTrkMHTStep(self):
+        return self.getStep(1, "met_trkmht", [MetTrkMHTSequenceCfg] )
 
         
                 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METMenuSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METMenuSequences.py
index 5541242f676..34c5a5bdd8d 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METMenuSequences.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METMenuSequences.py
@@ -72,3 +72,19 @@ def metJetMenuSequence():
                           Maker       = InputMakerAlg,
                           Hypo        = metHypoAlg,
                           HypoToolGen = TrigMETCellHypoToolFromDict )
+
+def metTrkMHTMenuSequence():
+    from TriggerMenuMT.HLTMenuConfig.MET.METRecoSequences import metTrkMHTAthSequence
+    reco_seq, input_alg, seq_out = RecoFragmentsPool.retrieve(
+            metTrkMHTAthSequence, ConfigFlags)
+
+    # The hypo
+    hypo = TrigMissingETHypoAlgMT("METHypoAlg_trkmht")
+    hypo.METContainerKey = seq_out
+
+    # NB - the function is called 'TrigMETCellHypoToolFromDict' but it isn't
+    # actually specific to cell at all... something to change in the future
+    return MenuSequence( Sequence    = reco_seq,
+                         Maker       = input_alg,
+                         Hypo        = hypo,
+                         HypoToolGen = TrigMETCellHypoToolFromDict )
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py
index 8b0674859c6..a0859aa9763 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py
@@ -5,10 +5,14 @@ from AthenaCommon.CFElements import seqAND
 from TrigEDMConfig.TriggerEDMRun3 import recordable
 from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import RecoFragmentsPool
 
-from TrigEFMissingET.TrigEFMissingETConf import EFMissingETAlgMT, EFMissingETFlagsMT
+from TrigEFMissingET.TrigEFMissingETConf import (
+        EFMissingETAlgMT, EFMissingETFlagsMT, HLT__MET__TrkMHTFex)
 from TrigEFMissingET.TrigEFMissingETMTConfig import getMETMonTool
 
 from TrigT2CaloCommon.CaloDef import clusterFSInputMaker
+import GaudiKernel.SystemOfUnits as Units
+from AthenaCommon.Logging import logging
+log = logging.getLogger(__name__)
 
 def metCellAthSequence(ConfigFlags):
     InputMakerAlg= clusterFSInputMaker()
@@ -152,3 +156,40 @@ def metJetRecoSequence(RoIs = 'FSJETRoI'):
     seqOut = metAlg.METContainerKey
     return (metJetRecoSequence, seqOut)
 
+def metTrkMHTAthSequence(ConfigFlags):
+    InputMakerAlg = clusterFSInputMaker()
+    reco_seq, seq_out = metTrkMHTRecoSequence()
+    ath_seq = seqAND("MetTrkMHTAthSequence", [InputMakerAlg, reco_seq])
+    return ath_seq, InputMakerAlg, seq_out
+
+def metTrkMHTRecoSequence():
+
+    # Prepare the inputs from the jet slice
+    from TriggerMenuMT.HLTMenuConfig.Jet.JetRecoSequences import jetRecoSequence
+    jetRecoDict={"recoAlg":"a4", "dataType": "tc", "calib": "em", "jetCalib": "subjesIS", "trkopt": "ftf", "cleaning": "noCleaning"}
+    (jetSeq, jetOut) = RecoFragmentsPool.retrieve( jetRecoSequence, None, dataSource="data", **jetRecoDict )
+
+    # These are the names set by the downstream algorithms. Unfortunately these
+    # aren't passed to us - we just have to 'know' them
+    tracks = "HLT_xAODTracks_FS"
+    vertices = "HLT_EFHistoPrmVtx"
+    tva = "JetTrackVtxAssoc_{trkopt}".format(**jetRecoDict)
+    track_links = "GhostTrack_{trkopt}".format(**jetRecoDict)
+
+    alg = HLT__MET__TrkMHTFex(
+            name="EFMET_trkmht",
+            METContainerKey = recordable("HLT_MET_trkmht"),
+            MonTool = getMETMonTool(),
+            JetName = jetOut,
+            TrackName = tracks,
+            VertexName = vertices,
+            TVAName = tva,
+            TrackLinkName = track_links)
+    alg.TrackSelTool.CutLevel = "Loose"
+    alg.TrackSelTool.maxZ0SinTheta = 1.5
+    alg.TrackSelTool.maxD0overSigmaD0 = 3
+    alg.TrackSelTool.minPt = 1 * Units.GeV
+
+    reco_seq = seqAND("metTrkMHTRecoSequence", [jetSeq, alg])
+    return (reco_seq, alg.METContainerKey)
+
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py
index bc5ecbceefe..7c133678a38 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py
@@ -81,6 +81,7 @@ def setupMenu():
         ChainProp(name='HLT_xe30_cell_L1XE10', groups=SingleMETGroup),
         ChainProp(name='HLT_xe30_mht_L1XE10', groups=SingleMETGroup),
         ChainProp(name='HLT_xe30_tcpufit_L1XE10', groups=SingleMETGroup),
+        ChainProp(name='HLT_xe30_trkmht_L1XE10', groups=SingleMETGroup),
 
         # MultiMET Chain
         ChainProp(name='HLT_xe30_cell_xe30_tcpufit_L1XE10',l1SeedThresholds=['XE10']*2, groups=MultiMETGroup), #must be FS seeded
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py
index b3206be7944..727248e60e0 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py
@@ -276,7 +276,7 @@ METChainParts = {
     'extra'        : ['noL1'],
     'calib'        : ['lcw',],    
     'L2recoAlg'    : [],
-    'EFrecoAlg'    : ['cell', 'tcpufit', 'mht'],
+    'EFrecoAlg'    : ['cell', 'tcpufit', 'mht', 'trkmht'],
     'L2muonCorr'   : [],
     'EFmuonCorr'   : [],
     'addInfo'      : ['FStracks'],
-- 
GitLab