From 60e013e49bea323af3703ae2941f3b93dbb92fb6 Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 30 Nov 2020 16:29:29 +0100
Subject: [PATCH 1/8] final energy regression and ID for UL re-miniAOD

---
 .../lowPtElectronProducer_cff.py              |  8 ++-
 .../lowPtElectronSelector_cfi.py              |  6 +-
 .../plugins/LowPtGsfElectronFinalizer.cc      | 65 +++++++++++++++++++
 .../python/lowPtGsfElectronID_cff.py          | 10 +++
 .../python/lowPtGsfElectronSequence_cff.py    |  2 +-
 ... => lowPtGsfElectronsPreRegression_cfi.py} |  8 +--
 .../python/lowPtGsfElectrons_cff.py           | 55 ++++++++++++++++
 7 files changed, 146 insertions(+), 8 deletions(-)
 create mode 100644 RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronFinalizer.cc
 rename RecoEgamma/EgammaElectronProducers/python/{lowPtGsfElectrons_cfi.py => lowPtGsfElectronsPreRegression_cfi.py} (89%)
 create mode 100644 RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py

diff --git a/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py b/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
index 4a95b97d76edd..239ac2ce4a64a 100644
--- a/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
+++ b/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
@@ -26,8 +26,6 @@
         ID       = cms.InputTag("lowPtGsfElectronID"),
     ),
 
-    # Embedding of RECO/AOD items
-
     # Embedding of RECO/AOD items
     embedTrack                  = True,
     embedGsfElectronCore        = True,
@@ -71,10 +69,16 @@
 
 # Schedule rekeying of seed BDT ValueMaps by reco::GsfElectron for run2_miniAOD_UL and bParking
 from Configuration.ProcessModifiers.run2_miniAOD_UL_cff import run2_miniAOD_UL
+from Configuration.Eras.Modifier_run2_miniAOD_devel_cff import run2_miniAOD_devel
 from Configuration.Eras.Modifier_bParking_cff import bParking
+
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronSeedValueMaps_cff import rekeyLowPtGsfElectronSeedValueMaps
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtGsfElectrons
+
 _makePatLowPtElectronsTask = makePatLowPtElectronsTask.copy()
 _makePatLowPtElectronsTask.add(rekeyLowPtGsfElectronSeedValueMaps)
 _makePatLowPtElectronsTask.add(lowPtGsfElectronID)
 (bParking | run2_miniAOD_UL).toReplaceWith(makePatLowPtElectronsTask,_makePatLowPtElectronsTask)
+( (bParking & run2_miniAOD_UL) | (~bParking & run2_miniAOD_devel) ).toModify(
+    makePatLowPtElectronsTask, func = lambda t: t.add(lowPtGsfElectrons))
diff --git a/PhysicsTools/PatAlgos/python/selectionLayer1/lowPtElectronSelector_cfi.py b/PhysicsTools/PatAlgos/python/selectionLayer1/lowPtElectronSelector_cfi.py
index 7ea6b50afd8b8..f040fd6147284 100644
--- a/PhysicsTools/PatAlgos/python/selectionLayer1/lowPtElectronSelector_cfi.py
+++ b/PhysicsTools/PatAlgos/python/selectionLayer1/lowPtElectronSelector_cfi.py
@@ -9,9 +9,13 @@
     cut = cms.string("pt>1. && electronID('ID')>1.5"),
 )
 
+# Modifier for run2_miniAOD_devel
+from Configuration.Eras.Modifier_run2_miniAOD_devel_cff import run2_miniAOD_devel
+run2_miniAOD_devel.toModify(selectedPatLowPtElectrons,cut = "pt>1. && electronID('ID')>-0.25")
+
 # Modifier for bParking (fully open selection)
 from Configuration.Eras.Modifier_bParking_cff import bParking
-bParking.toModify(selectedPatLowPtElectrons,cut = "")
+bParking.toModify(selectedPatLowPtElectrons,cut = "pt>1.")
 
 # Modifiers for legacy AOD
 from Configuration.Eras.Modifier_run2_miniAOD_80XLegacy_cff import run2_miniAOD_80XLegacy
diff --git a/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronFinalizer.cc b/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronFinalizer.cc
new file mode 100644
index 0000000000000..701e0f0a77d81
--- /dev/null
+++ b/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronFinalizer.cc
@@ -0,0 +1,65 @@
+#include "CommonTools/CandAlgos/interface/ModifyObjectValueBase.h"
+#include "DataFormats/EgammaCandidates/interface/GsfElectron.h"
+#include "FWCore/Framework/interface/stream/EDProducer.h"
+#include "FWCore/Framework/interface/Event.h"
+#include "FWCore/Framework/interface/EventSetup.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
+#include "FWCore/ParameterSet/interface/ParameterSet.h"
+#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
+
+class LowPtGsfElectronFinalizer : public edm::stream::EDProducer<> {
+public:
+  explicit LowPtGsfElectronFinalizer(const edm::ParameterSet&);
+
+  void produce(edm::Event&, const edm::EventSetup&) override;
+
+  static void fillDescriptions(edm::ConfigurationDescriptions&);
+
+private:
+  const edm::EDGetTokenT<reco::GsfElectronCollection> previousGsfElectrons_;
+  std::unique_ptr<ModifyObjectValueBase> regression_;
+
+  const edm::EDPutTokenT<reco::GsfElectronCollection> putToken_;
+};
+
+using edm::InputTag;
+using reco::GsfElectronCollection;
+
+LowPtGsfElectronFinalizer::LowPtGsfElectronFinalizer(const edm::ParameterSet& cfg)
+    : previousGsfElectrons_{consumes<GsfElectronCollection>(cfg.getParameter<InputTag>("previousGsfElectronsTag"))},
+      putToken_{produces<GsfElectronCollection>()} {
+  auto const& iconf = cfg.getParameterSet("regressionConfig");
+  auto const& mname = iconf.getParameter<std::string>("modifierName");
+  auto cc = consumesCollector();
+  regression_.reset(ModifyObjectValueFactory::get()->create(mname, iconf, cc));
+}
+
+void LowPtGsfElectronFinalizer::produce(edm::Event& event, const edm::EventSetup& setup) {
+  // Setup regression for event
+  regression_->setEvent(event);
+  regression_->setEventContent(setup);
+
+  // Create new modified electron collection
+  reco::GsfElectronCollection outputElectrons;
+  for (auto const& electron : event.get(previousGsfElectrons_)) {
+    outputElectrons.emplace_back(electron);
+    auto& newElectron = outputElectrons.back();
+    regression_->modifyObject(newElectron);
+  }
+
+  // Emplace modified electrons to event
+  event.emplace(putToken_, std::move(outputElectrons));
+}
+
+void LowPtGsfElectronFinalizer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
+  edm::ParameterSetDescription desc;
+  desc.add<edm::InputTag>("previousGsfElectronsTag", {});
+  edm::ParameterSetDescription psd;
+  psd.setUnknown();
+  desc.add<edm::ParameterSetDescription>("regressionConfig", psd);
+  descriptions.addWithDefaultLabel(desc);
+}
+
+#include "FWCore/Framework/interface/MakerMacros.h"
+DEFINE_FWK_MODULE(LowPtGsfElectronFinalizer);
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronID_cff.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronID_cff.py
index 15cb46811b4eb..91a9aafe31b01 100644
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronID_cff.py
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronID_cff.py
@@ -11,6 +11,8 @@
     )
 
 from Configuration.ProcessModifiers.run2_miniAOD_UL_cff import run2_miniAOD_UL
+from Configuration.Eras.Modifier_run2_miniAOD_devel_cff import run2_miniAOD_devel
+from Configuration.Eras.Modifier_bParking_cff import bParking
 run2_miniAOD_UL.toModify(
     lowPtGsfElectronID,
     rho = "fixedGridRhoFastjetAll",
@@ -18,3 +20,11 @@
     ModelThresholds = [-99.],
     Version = "V1",
 )
+run2_miniAOD_devel.toModify(
+    lowPtGsfElectronID,
+    ModelWeights = ["RecoEgamma/ElectronIdentification/data/LowPtElectrons/LowPtElectrons_ID_2020Nov28.root"],
+)
+(bParking & run2_miniAOD_UL).toModify(
+    lowPtGsfElectronID,
+    ModelWeights = ["RecoEgamma/ElectronIdentification/data/LowPtElectrons/LowPtElectrons_ID_2021May17.root"],
+)
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
index 342eaf1a609e7..9f323046b8e52 100644
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
@@ -63,7 +63,7 @@
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronCores_cff import *
 
 # Low pT electrons
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import *
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtGsfElectrons
 
 # Low pT Electron value maps
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronSeedValueMaps_cff import lowPtGsfElectronSeedValueMaps
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronsPreRegression_cfi.py
similarity index 89%
rename from RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
rename to RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronsPreRegression_cfi.py
index 8e0cfcd1bee30..e6d2faa55c3da 100644
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronsPreRegression_cfi.py
@@ -5,7 +5,7 @@
 from RecoEgamma.EgammaIsolationAlgos.electronTrackIsolations_cfi import trkIsol03CfgV1,trkIsol04CfgV1
 from RecoEgamma.EgammaIsolationAlgos.electronTrackIsolations_cfi import trkIsol03CfgV2,trkIsol04CfgV2
 
-lowPtGsfElectrons = cms.EDProducer(
+lowPtGsfElectronsPreRegression = cms.EDProducer(
     "LowPtGsfElectronProducer",
     
     # input collections
@@ -63,8 +63,8 @@
     )
 
 from Configuration.Eras.Modifier_fastSim_cff import fastSim
-fastSim.toModify(lowPtGsfElectrons,ctfTracksTag = cms.InputTag("generalTracksBeforeMixing"))
+fastSim.toModify(lowPtGsfElectronsPreRegression,ctfTracksTag = cms.InputTag("generalTracksBeforeMixing"))
 
 from Configuration.Eras.Modifier_pp_on_AA_2018_cff import pp_on_AA_2018
-pp_on_AA_2018.toModify(lowPtGsfElectrons.preselection, minSCEtBarrel = 15.0)
-pp_on_AA_2018.toModify(lowPtGsfElectrons.preselection, minSCEtEndcaps = 15.0)
+pp_on_AA_2018.toModify(lowPtGsfElectronsPreRegression.preselection, minSCEtBarrel = 15.0)
+pp_on_AA_2018.toModify(lowPtGsfElectronsPreRegression.preselection, minSCEtEndcaps = 15.0)
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py
new file mode 100644
index 0000000000000..ac32ecc504f42
--- /dev/null
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py
@@ -0,0 +1,55 @@
+import FWCore.ParameterSet.Config as cms
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronsPreRegression_cfi import lowPtGsfElectronsPreRegression
+
+lowPtGsfElectrons = lowPtGsfElectronsPreRegression.clone()
+
+################################################################################
+# LowPtGsfElectronProducer above is run by default in RECO
+# LowPtGsfElectronFinalizer below is scheduled for run2_miniAOD_UL
+
+from RecoEgamma.EgammaTools.regressionModifier_cfi import regressionModifier106XUL
+_lowPtRegressionModifier = regressionModifier106XUL.clone(
+    modifierName = 'EGRegressionModifierV3',
+    rhoTag = 'fixedGridRhoFastjetAll',
+    eleRegs = dict(
+        ecalOnlyMean = dict(
+            lowEtHighEtBoundary = 20.,
+            ebLowEtForestName = "lowPtElectron_eb_ecalOnly_05To50_mean",
+            ebHighEtForestName = "lowPtElectron_eb_ecalOnly_05To50_mean",
+            eeLowEtForestName = "lowPtElectron_ee_ecalOnly_05To50_mean",
+            eeHighEtForestName = "lowPtElectron_ee_ecalOnly_05To50_mean",
+            ),
+        ecalOnlySigma = dict(
+            lowEtHighEtBoundary = 20.,
+            ebLowEtForestName = "lowPtElectron_eb_ecalOnly_05To50_sigma",
+            ebHighEtForestName = "lowPtElectron_eb_ecalOnly_05To50_sigma",
+            eeLowEtForestName = "lowPtElectron_ee_ecalOnly_05To50_sigma",
+            eeHighEtForestName = "lowPtElectron_ee_ecalOnly_05To50_sigma",
+            ),
+        epComb = dict(
+            ecalTrkRegressionConfig = dict(
+                lowEtHighEtBoundary = 20.,
+                ebLowEtForestName = "lowPtElectron_eb_ecalTrk_05To50_mean",
+                ebHighEtForestName = "lowPtElectron_eb_ecalTrk_05To50_mean",
+                eeLowEtForestName = "lowPtElectron_ee_ecalTrk_05To50_mean",
+                eeHighEtForestName = "lowPtElectron_ee_ecalTrk_05To50_mean",
+                ),
+            ecalTrkRegressionUncertConfig = dict(
+                lowEtHighEtBoundary = 20.,
+                ebLowEtForestName = "lowPtElectron_eb_ecalTrk_05To50_sigma",
+                ebHighEtForestName = "lowPtElectron_eb_ecalTrk_05To50_sigma",
+                eeLowEtForestName = "lowPtElectron_ee_ecalTrk_05To50_sigma",
+                eeHighEtForestName = "lowPtElectron_ee_ecalTrk_05To50_sigma",
+                ),
+        )
+    ),
+)
+
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronFinalizer_cfi import lowPtGsfElectronFinalizer
+_lowPtGsfElectrons = lowPtGsfElectronFinalizer.clone(
+    previousGsfElectronsTag = "lowPtGsfElectrons::@skipCurrentProcess",
+    regressionConfig = _lowPtRegressionModifier,
+)
+
+from Configuration.ProcessModifiers.run2_miniAOD_UL_cff import run2_miniAOD_UL
+run2_miniAOD_UL.toReplaceWith(lowPtGsfElectrons,_lowPtGsfElectrons)

From 6c930923954a193fe5faa7c7ecffd8fd8b51c1f4 Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Tue, 4 May 2021 18:09:56 +0200
Subject: [PATCH 2/8] add nanoAOD table for low pT electrons

---
 .../EgammaCandidates/interface/GsfElectron.h  |   5 +-
 .../EgammaCandidates/src/GsfElectron.cc       |  27 ++-
 .../NanoAOD/python/lowPtElectrons_cff.py      | 205 +++++++++++++++++
 PhysicsTools/NanoAOD/python/nanoDQM_cff.py    |   1 +
 PhysicsTools/NanoAOD/python/nanoDQM_cfi.py    |  47 ++++
 PhysicsTools/NanoAOD/python/nano_cff.py       |   7 +-
 .../lowPtElectronProducer_cff.py              |   2 +-
 .../slimming/slimmedLowPtElectrons_cfi.py     |   6 +
 .../LowPtGsfElectronIDHeavyObjectCache.h      |   3 +-
 .../plugins/LowPtGsfElectronIDProducer.cc     |  88 +++++---
 .../python/lowPtGsfElectronSequence_cff.py    |   2 +-
 .../python/lowPtGsfElectrons_cff.py           |   4 +-
 .../python/lowPtGsfElectrons_cfi.py           |  55 +++++
 .../src/LowPtGsfElectronFeatures.cc           |   9 +-
 .../src/LowPtGsfElectronIDHeavyObjectCache.cc |  44 ++--
 RecoEgamma/EgammaTools/BuildFile.xml          |   4 +
 .../EgammaTools/interface/LowPtConversion.h   |  68 ++++++
 .../plugins/EGRegressionModifierV3.cc         |   4 +-
 .../plugins/LowPtElectronsModifier.cc         |  82 +++++++
 .../python/lowPtElectronModifier_cfi.py       |   9 +
 RecoEgamma/EgammaTools/src/LowPtConversion.cc | 212 ++++++++++++++++++
 21 files changed, 801 insertions(+), 83 deletions(-)
 create mode 100644 PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
 create mode 100644 RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
 create mode 100644 RecoEgamma/EgammaTools/interface/LowPtConversion.h
 create mode 100644 RecoEgamma/EgammaTools/plugins/LowPtElectronsModifier.cc
 create mode 100644 RecoEgamma/EgammaTools/python/lowPtElectronModifier_cfi.py
 create mode 100644 RecoEgamma/EgammaTools/src/LowPtConversion.cc

diff --git a/DataFormats/EgammaCandidates/interface/GsfElectron.h b/DataFormats/EgammaCandidates/interface/GsfElectron.h
index 9ede5d64d8409..396b0249ee802 100644
--- a/DataFormats/EgammaCandidates/interface/GsfElectron.h
+++ b/DataFormats/EgammaCandidates/interface/GsfElectron.h
@@ -836,6 +836,7 @@ class GsfElectron : public RecoCandidate
     // setters
     void setCorrectedEcalEnergyError( float newEnergyError ) ;
     void setCorrectedEcalEnergy( float newEnergy ) ;
+    void setCorrectedEcalEnergy(float newEnergy, bool rescaleDependentValues);
     void setTrackMomentumError( float trackMomentumError ) ;
     void setP4( P4Kind kind, const LorentzVector & p4, float p4Error, bool setCandidate ) ;
     using RecoCandidate::setP4 ;
@@ -861,9 +862,9 @@ class GsfElectron : public RecoCandidate
     //bool isMomentumCorrected() const { return corrections_.isMomentumCorrected ; }
     float caloEnergy() const { return correctedEcalEnergy() ; }
     bool isEnergyScaleCorrected() const { return isEcalEnergyCorrected() ; }
-    void correctEcalEnergy( float newEnergy, float newEnergyError )
+    void correctEcalEnergy(float newEnergy, float newEnergyError, bool rescaleDependentValues = true)
      {
-      setCorrectedEcalEnergy(newEnergy) ;
+      setCorrectedEcalEnergy(newEnergy,rescaleDependentValues) ;
       setEcalEnergyError(newEnergyError) ;
      }
     void correctMomentum( const LorentzVector & p4, float trackMomentumError, float p4Error )
diff --git a/DataFormats/EgammaCandidates/src/GsfElectron.cc b/DataFormats/EgammaCandidates/src/GsfElectron.cc
index 2bb17a115565e..300b2c4b5fb2a 100644
--- a/DataFormats/EgammaCandidates/src/GsfElectron.cc
+++ b/DataFormats/EgammaCandidates/src/GsfElectron.cc
@@ -179,18 +179,21 @@ bool GsfElectron::ecalDriven() const
 void GsfElectron::setCorrectedEcalEnergyError( float energyError )
  { corrections_.correctedEcalEnergyError = energyError ; }
 
-void GsfElectron::setCorrectedEcalEnergy( float newEnergy )
- {
-  math::XYZTLorentzVectorD momentum = p4() ;
-  momentum *= newEnergy/momentum.e() ;
-  setP4(momentum) ;
-  showerShape_.hcalDepth1OverEcal *= corrections_.correctedEcalEnergy/newEnergy ;
-  showerShape_.hcalDepth2OverEcal *= corrections_.correctedEcalEnergy/newEnergy ;
-  trackClusterMatching_.eSuperClusterOverP *= newEnergy/corrections_.correctedEcalEnergy ;
-  corrections_.correctedEcalEnergyError *= newEnergy/corrections_.correctedEcalEnergy ;
-  corrections_.correctedEcalEnergy = newEnergy ;
-  corrections_.isEcalEnergyCorrected = true ;
- }
+void GsfElectron::setCorrectedEcalEnergy(float newEnergy) { setCorrectedEcalEnergy(newEnergy, true); }
+
+void GsfElectron::setCorrectedEcalEnergy(float newEnergy, bool rescaleDependentValues) {
+  math::XYZTLorentzVectorD momentum = p4();
+  momentum *= newEnergy / momentum.e();
+  setP4(momentum);
+  if (corrections_.correctedEcalEnergy > 0. && rescaleDependentValues) {
+    showerShape_.hcalDepth1OverEcal *= corrections_.correctedEcalEnergy / newEnergy;
+    showerShape_.hcalDepth2OverEcal *= corrections_.correctedEcalEnergy / newEnergy;
+    trackClusterMatching_.eSuperClusterOverP *= newEnergy / corrections_.correctedEcalEnergy;
+    corrections_.correctedEcalEnergyError *= newEnergy / corrections_.correctedEcalEnergy;
+  }
+  corrections_.correctedEcalEnergy = newEnergy;
+  corrections_.isEcalEnergyCorrected = true;
+}
 
 void GsfElectron::setTrackMomentumError( float trackErr )
  { corrections_.trackMomentumError = trackErr ; }
diff --git a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
new file mode 100644
index 0000000000000..9caa8f29e315a
--- /dev/null
+++ b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
@@ -0,0 +1,205 @@
+import FWCore.ParameterSet.Config as cms
+from PhysicsTools.NanoAOD.nano_eras_cff import *
+from PhysicsTools.NanoAOD.common_cff import *
+
+################################################################################
+# Modules
+################################################################################
+
+from RecoEgamma.EgammaTools.lowPtElectronModifier_cfi import lowPtElectronModifier
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import lowPtRegressionModifier
+modifiedLowPtElectrons = cms.EDProducer(
+    "ModifiedElectronProducer",
+    src = cms.InputTag("slimmedLowPtElectrons"),
+    modifierConfig = cms.PSet(
+        modifications = cms.VPSet(lowPtElectronModifier,lowPtRegressionModifier)
+    )
+)
+
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID
+lowPtPATElectronID = lowPtGsfElectronID.clone(
+    usePAT = True,
+    electrons = "modifiedLowPtElectrons",
+    unbiased = "",
+    ModelWeights = [
+        'RecoEgamma/ElectronIdentification/data/LowPtElectrons/LowPtElectrons_ID_2020Nov28.root',
+    ],
+    Version = cms.string('V1'),
+    rho = "fixedGridRhoFastjetAll",
+)
+
+isoForLowPtEle = cms.EDProducer(
+    "EleIsoValueMapProducer",
+    src = cms.InputTag("modifiedLowPtElectrons"),
+    relative = cms.bool(False),
+    rho_MiniIso = cms.InputTag("fixedGridRhoFastjetAll"),
+    rho_PFIso = cms.InputTag("fixedGridRhoFastjetAll"),
+    EAFile_MiniIso = cms.FileInPath("RecoEgamma/ElectronIdentification/data/Fall17/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_94X.txt"),
+    EAFile_PFIso = cms.FileInPath("RecoEgamma/ElectronIdentification/data/Fall17/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_94X.txt"),
+)
+
+updatedLowPtElectronsWithUserData = cms.EDProducer(
+    "PATElectronUserDataEmbedder",
+    src = cms.InputTag("modifiedLowPtElectrons"),
+    userFloats = cms.PSet(
+        ID = cms.InputTag("lowPtPATElectronID"),
+        miniIsoChg = cms.InputTag("isoForLowPtEle:miniIsoChg"),
+        miniIsoAll = cms.InputTag("isoForLowPtEle:miniIsoAll"),
+    ),
+    userIntFromBools = cms.PSet(),
+    userInts = cms.PSet(),
+    userCands = cms.PSet(),
+)
+
+finalLowPtElectrons = cms.EDFilter(
+    "PATElectronRefSelector",
+    src = cms.InputTag("updatedLowPtElectronsWithUserData"),
+    cut = cms.string("pt > 1. && userFloat('ID') > -0.25"),
+)
+
+################################################################################
+# electronTable 
+################################################################################
+
+lowPtElectronTable = cms.EDProducer(
+    "SimpleCandidateFlatTableProducer",
+    src = cms.InputTag("finalLowPtElectrons"),
+    cut = cms.string(""),
+    name= cms.string("LowPtElectron"),
+    doc = cms.string("slimmedLowPtElectrons after basic selection (" + finalLowPtElectrons.cut.value()+")"),
+    singleton = cms.bool(False), # the number of entries is variable
+    extension = cms.bool(False), # this is the main table for the electrons
+    variables = cms.PSet(
+        # Basic variables
+        CandVars,
+        # BDT scores and WPs
+        embeddedID = Var("electronID('ID')",float,doc="ID, BDT (raw) score"),
+        ID = Var("userFloat('ID')",float,doc="New ID, BDT (raw) score"),
+        unbiased = Var("electronID('unbiased')",float,doc="ElectronSeed, pT- and dxy- agnostic BDT (raw) score"),
+        ptbiased = Var("electronID('ptbiased')",float,doc="ElectronSeed, pT- and dxy- dependent BDT (raw) score"),
+        # Isolation
+        miniPFRelIso_chg = Var("userFloat('miniIsoChg')/pt",float,
+                               doc="mini PF relative isolation, charged component"),
+        miniPFRelIso_all = Var("userFloat('miniIsoAll')/pt",float,
+                               doc="mini PF relative isolation, total (with scaled rho*EA PU corrections)"),
+        # Conversions
+        convVeto = Var("passConversionVeto()",bool,doc="pass conversion veto"),
+        convWP = Var("userInt('convOpen')*1 + userInt('convLoose')*2 + userInt('convTight')*4",
+                     int,doc="conversion flag bit map: 1=Veto, 2=Loose, 3=Tight"),
+        convVtxRadius = Var("userFloat('convVtxRadius')",float,doc="conversion vertex radius (cm)",precision=7),
+        # Tracking
+        lostHits = Var("gsfTrack.hitPattern.numberOfLostHits('MISSING_INNER_HITS')","uint8",doc="number of missing inner hits"),
+        # Cluster-related
+        energyErr = Var("p4Error('P4_COMBINATION')",float,doc="energy error of the cluster-track combination",precision=6),
+        deltaEtaSC = Var("superCluster().eta()-eta()",float,doc="delta eta (SC,ele) with sign",precision=10),
+        r9 = Var("full5x5_r9()",float,doc="R9 of the SC, calculated with full 5x5 region",precision=10),
+        sieie = Var("full5x5_sigmaIetaIeta()",float,doc="sigma_IetaIeta of the SC, calculated with full 5x5 region",precision=10),
+        eInvMinusPInv = Var("(1-eSuperClusterOverP())/ecalEnergy()",float,doc="1/E_SC - 1/p_trk",precision=10),
+        scEtOverPt = Var("(superCluster().energy()/(pt*cosh(superCluster().eta())))-1",float,doc="(SC energy)/pt-1",precision=8),
+        hoe = Var("hadronicOverEm()",float,doc="H over E",precision=8),
+        # Displacement
+        dxy = Var("dB('PV2D')",float,doc="dxy (with sign) wrt first PV, in cm",precision=10),
+        dxyErr = Var("edB('PV2D')",float,doc="dxy uncertainty, in cm",precision=6),
+        dz = Var("dB('PVDZ')",float,doc="dz (with sign) wrt first PV, in cm",precision=10),
+        dzErr = Var("abs(edB('PVDZ'))",float,doc="dz uncertainty, in cm",precision=6),
+        ip3d = Var("abs(dB('PV3D'))",float,doc="3D impact parameter wrt first PV, in cm",precision=10),
+        sip3d = Var("abs(dB('PV3D')/edB('PV3D'))",float,doc="3D impact parameter significance wrt first PV, in cm",precision=10),
+        # Cross-referencing
+        #jetIdx
+        #photonIdx
+    ),
+)
+
+################################################################################
+# electronTable (MC)
+################################################################################
+
+from PhysicsTools.NanoAOD.particlelevel_cff import particleLevel
+particleLevelForMatchingLowPt = particleLevel.clone(
+    lepMinPt = cms.double(1.),
+    phoMinPt = cms.double(1),
+)
+
+tautaggerForMatchingLowPt = cms.EDProducer(
+    "GenJetTauTaggerProducer",
+    src = cms.InputTag('particleLevelForMatchingLowPt:leptons')
+)
+
+matchingLowPtElecPhoton = cms.EDProducer(
+    "GenJetGenPartMerger",
+    srcJet =cms.InputTag("particleLevelForMatchingLowPt:leptons"),
+    srcPart=cms.InputTag("particleLevelForMatchingLowPt:photons"),
+    hasTauAnc=cms.InputTag("tautaggerForMatchingLowPt"),
+)
+
+lowPtElectronsMCMatchForTableAlt = cms.EDProducer(
+    "GenJetMatcherDRPtByDR",                # cut on deltaR, deltaPt/Pt; pick best by deltaR
+    src         = lowPtElectronTable.src,   # final reco collection
+    matched     = cms.InputTag("matchingLowPtElecPhoton:merged"), # final mc-truth particle collection
+    mcPdgId     = cms.vint32(11,22),        # one or more PDG ID (11 = el, 22 = pho); absolute values (see below)
+    checkCharge = cms.bool(False),          # True = require RECO and MC objects to have the same charge
+    mcStatus    = cms.vint32(),
+    maxDeltaR   = cms.double(0.3),          # Minimum deltaR for the match
+    maxDPtRel   = cms.double(0.5),          # Minimum deltaPt/Pt for the match
+    resolveAmbiguities    = cms.bool(True), # Forbid two RECO objects to match to the same GEN object
+    resolveByMatchQuality = cms.bool(True), # False = just match input in order; True = pick lowest deltaR pair first
+) 
+
+lowPtElectronsMCMatchForTable = cms.EDProducer(
+    "MCMatcher",                            # cut on deltaR, deltaPt/Pt; pick best by deltaR
+    src         = lowPtElectronTable.src,   # final reco collection
+    matched     = cms.InputTag("finalGenParticles"), # final mc-truth particle collection
+    mcPdgId     = cms.vint32(11),           # one or more PDG ID (11 = ele); absolute values (see below)
+    checkCharge = cms.bool(False),          # True = require RECO and MC objects to have the same charge
+    mcStatus    = cms.vint32(1),            # PYTHIA status code (1 = stable, 2 = shower, 3 = hard scattering)
+    maxDeltaR   = cms.double(0.3),          # Minimum deltaR for the match
+    maxDPtRel   = cms.double(0.5),          # Minimum deltaPt/Pt for the match
+    resolveAmbiguities    = cms.bool(True), # Forbid two RECO objects to match to the same GEN object
+    resolveByMatchQuality = cms.bool(True), # False = just match input in order; True = pick lowest deltaR pair first
+)
+
+from PhysicsTools.NanoAOD.electrons_cff import electronMCTable
+lowPtElectronMCTable = cms.EDProducer(
+    "CandMCMatchTableProducer",
+    src = lowPtElectronTable.src,
+    mcMapDressedLep = cms.InputTag("lowPtElectronsMCMatchForTableAlt"),
+    mcMap = cms.InputTag("lowPtElectronsMCMatchForTable"),
+    mapTauAnc = cms.InputTag("matchingLowPtElecPhoton:hasTauAnc"),
+    objName = lowPtElectronTable.name,
+    objType = electronMCTable.objType,
+    branchName = cms.string("genPart"),
+    docString = cms.string("MC matching to status==1 electrons or photons"),
+    genparticles = cms.InputTag("finalGenParticles"), 
+)
+
+################################################################################
+# Sequences
+################################################################################
+
+lowPtElectronSequence = cms.Sequence(modifiedLowPtElectrons
+                                     +lowPtPATElectronID
+                                     +isoForLowPtEle
+                                     +updatedLowPtElectronsWithUserData
+                                     +finalLowPtElectrons)
+lowPtElectronTables = cms.Sequence(lowPtElectronTable)
+lowPtElectronMC = cms.Sequence(
+    particleLevelForMatchingLowPt
+    +tautaggerForMatchingLowPt
+    +matchingLowPtElecPhoton
+    +lowPtElectronsMCMatchForTable
+    +lowPtElectronsMCMatchForTableAlt
+    +lowPtElectronMCTable)
+
+################################################################################
+# Modifiers
+################################################################################
+
+_modifiers = ( run2_miniAOD_80XLegacy |
+               run2_nanoAOD_94XMiniAODv1 |
+               run2_nanoAOD_94XMiniAODv2 |
+               run2_nanoAOD_94X2016 |
+               run2_nanoAOD_102Xv1 |
+               run2_nanoAOD_106Xv1 )
+(_modifiers).toReplaceWith(lowPtElectronSequence,cms.Sequence())
+(_modifiers).toReplaceWith(lowPtElectronTables,cms.Sequence())
+(_modifiers).toReplaceWith(lowPtElectronMC,cms.Sequence())
diff --git a/PhysicsTools/NanoAOD/python/nanoDQM_cff.py b/PhysicsTools/NanoAOD/python/nanoDQM_cff.py
index 64f0a42b0cbe1..4211f6f1c35bb 100644
--- a/PhysicsTools/NanoAOD/python/nanoDQM_cff.py
+++ b/PhysicsTools/NanoAOD/python/nanoDQM_cff.py
@@ -146,6 +146,7 @@
 ## MC
 nanoDQMMC = nanoDQM.clone()
 nanoDQMMC.vplots.Electron.sels.Prompt = cms.string("genPartFlav == 1")
+nanoDQMMC.vplots.LowPtElectron.sels.Prompt = cms.string("genPartFlav == 1")
 nanoDQMMC.vplots.Muon.sels.Prompt = cms.string("genPartFlav == 1")
 nanoDQMMC.vplots.Photon.sels.Prompt = cms.string("genPartFlav == 1")
 nanoDQMMC.vplots.Tau.sels.Prompt = cms.string("genPartFlav == 5")
diff --git a/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py b/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
index e09841d031466..d1ec4b5f9f471 100644
--- a/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
+++ b/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
@@ -112,6 +112,53 @@
                 Plot1D('dEsigmaDown', 'dEsigmaDown', 100, -0.1, 0.1, '#Delta E sigmaDown'),
             )
         ),
+
+        LowPtElectron = cms.PSet(
+            sels = cms.PSet(
+                Good = cms.string('pt > 1. && ID > 5.')
+            ),
+            plots = cms.VPSet(
+                #
+                Count1D('_size', 8, -0.5, 7.5, 'slimmedLowPtElectrons after basic selection'),
+                # CandVars
+                Plot1D('charge', 'charge', 3, -1.5, 1.5, 'electric charge'),
+                Plot1D('eta', 'eta', 20, -3., 3., 'eta'),
+                NoPlot('mass'),
+                Plot1D('pdgId', 'pdgId', 101, -50.5, 50.5, 'PDG code assigned by the event reconstruction (not by MC truth)'),
+                Plot1D('phi', 'phi', 20, -3.14159, 3.14159, 'phi'),
+                Plot1D('pt', 'pt', 40, 0., 20., 'pt (corrected)'),
+                # BDT scores and WPs
+                Plot1D('embeddedID', 'embeddedID', 40, -10., 10., 'Embedded ID, BDT (raw) score'),
+                Plot1D('ID', 'ID', 40, -10., 10., 'ID, BDT (raw) score'),
+                Plot1D('unbiased', 'unbiased', 40, -10., 10., 'ElectronSeed, pT- and dxy- agnostic BDT (raw) score'),
+                Plot1D('ptbiased', 'ptbiased', 40, -10., 10., 'ElectronSeed, pT- and dxy- dependent BDT (raw) score'),
+                # Isolation
+                Plot1D('miniPFRelIso_chg', 'miniPFRelIso_chg', 20, 0, 2, 'mini PF relative isolation, charged component'),
+                Plot1D('miniPFRelIso_all', 'miniPFRelIso_all', 20, 0, 2, 'mini PF relative isolation, total (with scaled rho*EA PU corrections)'),
+                # Conversions
+                Plot1D('convVeto', 'convVeto', 2, -0.5, 1.5, 'pass conversion veto'),
+                Plot1D('convWP', 'convWP', 8, -0.5, 7.5, 'conversion flag bit map: 1=Veto, 2=Loose, 3=Tight'),
+                Plot1D('convVtxRadius', 'convVtxRadius', 40, 0., 20.0, 'conversion vertex radius (cm)'),
+                # Tracking
+                Plot1D('lostHits', 'lostHits', 4, -0.5, 3.5, 'number of missing inner hits'),
+                # Cluster-related
+                Plot1D('energyErr', 'energyErr', 40, 0., 20., 'energy error of the cluster from regression'),
+                Plot1D('deltaEtaSC', 'deltaEtaSC', 20, -0.2, 0.2, 'delta eta (SC,ele) with sign'),
+                Plot1D('r9', 'r9', 20, 0, 1.1, 'R9 of the supercluster, calculated with full 5x5 region'),
+                Plot1D('sieie', 'sieie', 20, 0, 0.05, 'sigma_IetaIeta of the supercluster, calculated with full 5x5 region'),
+                Plot1D('eInvMinusPInv', 'eInvMinusPInv', 20, -0.1, 0.1, '1/E_SC - 1/p_trk'),
+                Plot1D('scEtOverPt', 'scEtOverPt', 20, -0.5, 0.5, '(supercluster transverse energy)/pt - 1'),
+                Plot1D('hoe', 'hoe', 20, 0, 0.6, 'H over E'),
+                # Displacement
+                Plot1D('dxy', 'dxy', 20, -0.1, 0.1, 'dxy (with sign) wrt first PV, in cm'),
+                Plot1D('dz', 'dz', 20, -0.3, 0.3, 'dz (with sign) wrt first PV, in cm'),
+                Plot1D('dxyErr', 'dxyErr', 20, 0., 0.2, 'dxy uncertainty, in cm'),
+                Plot1D('dzErr', 'dzErr', 20, 0., 0.2, 'dz uncertainty, in cm'),
+                Plot1D('ip3d', 'ip3d', 20, 0., 0.2, '3D impact parameter wrt first PV, in cm'),
+                Plot1D('sip3d', 'sip3d', 20, 0., 20., '3D impact parameter significance wrt first PV, in cm'),
+            ),
+        ),
+
         FatJet = cms.PSet(
             sels = cms.PSet(),
             plots = cms.VPSet(
diff --git a/PhysicsTools/NanoAOD/python/nano_cff.py b/PhysicsTools/NanoAOD/python/nano_cff.py
index 529f1d18fcab6..4d902347cc43f 100644
--- a/PhysicsTools/NanoAOD/python/nano_cff.py
+++ b/PhysicsTools/NanoAOD/python/nano_cff.py
@@ -7,6 +7,7 @@
 from PhysicsTools.NanoAOD.taus_cff import *
 from PhysicsTools.NanoAOD.boostedTaus_cff import *
 from PhysicsTools.NanoAOD.electrons_cff import *
+from PhysicsTools.NanoAOD.lowPtElectrons_cff import *
 from PhysicsTools.NanoAOD.photons_cff import *
 from PhysicsTools.NanoAOD.globals_cff import *
 from PhysicsTools.NanoAOD.extraflags_cff import *
@@ -107,10 +108,10 @@
 (run2_miniAOD_80XLegacy | run2_nanoAOD_94X2016 | run2_nanoAOD_94XMiniAODv1 | run2_nanoAOD_94XMiniAODv2 | run2_nanoAOD_102Xv1).toModify(l1bits, storeUnprefireableBit=False)
 
 nanoSequenceCommon = cms.Sequence(
-        nanoMetadata + jetSequence + muonSequence + tauSequence + boostedTauSequence + electronSequence+photonSequence+vertexSequence+
+        nanoMetadata + jetSequence + muonSequence + tauSequence + boostedTauSequence + electronSequence + lowPtElectronSequence + photonSequence+vertexSequence+
         isoTrackSequence + jetLepSequence + # must be after all the leptons
         linkedObjects  +
-        jetTables + muonTables + tauTables + boostedTauTables + electronTables + photonTables +  globalTables +vertexTables+ metTables+simpleCleanerTable + isoTrackTables
+        jetTables + muonTables + tauTables + boostedTauTables + electronTables + lowPtElectronTables + photonTables +  globalTables +vertexTables+ metTables+simpleCleanerTable + isoTrackTables
         )
 #remove boosted tau from previous eras
 (run2_miniAOD_80XLegacy | run2_nanoAOD_92X | run2_nanoAOD_94XMiniAODv1 | run2_nanoAOD_94X2016 | run2_nanoAOD_94XMiniAODv2 | run2_nanoAOD_102Xv1 | run2_nanoAOD_106Xv1).toReplaceWith(nanoSequenceCommon, nanoSequenceCommon.copyAndExclude([boostedTauSequence, boostedTauTables]))
@@ -123,7 +124,7 @@
 
 ( run2_nanoAOD_106Xv1 & ~run2_nanoAOD_devel).toReplaceWith(nanoSequence, nanoSequence.copyAndExclude([nanoSequenceOnlyData]))
 
-nanoSequenceFS = cms.Sequence(genParticleSequence + genVertexTables + particleLevelSequence + nanoSequenceCommon + jetMC + muonMC + electronMC + photonMC + tauMC + boostedTauMC + metMC + ttbarCatMCProducers +  globalTablesMC + btagWeightTable + genWeightsTable + genVertexTable + genParticleTables + particleLevelTables + lheInfoTable  + ttbarCategoryTable )
+nanoSequenceFS = cms.Sequence(genParticleSequence + genVertexTables + particleLevelSequence + nanoSequenceCommon + jetMC + muonMC + electronMC + lowPtElectronMC + photonMC + tauMC + boostedTauMC + metMC + ttbarCatMCProducers +  globalTablesMC + btagWeightTable + genWeightsTable + genVertexTable + genParticleTables + particleLevelTables + lheInfoTable  + ttbarCategoryTable )
 
 (run2_nanoAOD_92X | run2_miniAOD_80XLegacy | run2_nanoAOD_94X2016 | run2_nanoAOD_94X2016 | \
     run2_nanoAOD_94XMiniAODv1 | run2_nanoAOD_94XMiniAODv2 | \
diff --git a/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py b/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
index 239ac2ce4a64a..97a8a7f540b6c 100644
--- a/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
+++ b/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
@@ -74,7 +74,7 @@
 
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronSeedValueMaps_cff import rekeyLowPtGsfElectronSeedValueMaps
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtGsfElectrons
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import lowPtGsfElectrons
 
 _makePatLowPtElectronsTask = makePatLowPtElectronsTask.copy()
 _makePatLowPtElectronsTask.add(rekeyLowPtGsfElectronSeedValueMaps)
diff --git a/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py b/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py
index 684512a4ce0fd..0bcd2fb7a5000 100644
--- a/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py
+++ b/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py
@@ -43,3 +43,9 @@
    )
 )
 
+from RecoEgamma.EgammaTools.lowPtElectronModifier_cfi import lowPtElectronModifier
+from Configuration.Eras.Modifier_run2_miniAOD_devel_cff import run2_miniAOD_devel
+from Configuration.Eras.Modifier_bParking_cff import bParking
+_slimmedLowPtElectrons = slimmedLowPtElectrons.clone()
+_slimmedLowPtElectrons.modifierConfig.modifications += [lowPtElectronModifier]
+(~bParking & run2_miniAOD_devel).toReplaceWith(slimmedLowPtElectrons,_slimmedLowPtElectrons)
diff --git a/RecoEgamma/EgammaElectronProducers/interface/LowPtGsfElectronIDHeavyObjectCache.h b/RecoEgamma/EgammaElectronProducers/interface/LowPtGsfElectronIDHeavyObjectCache.h
index 0356c3de940b3..4592ce7ebfd02 100644
--- a/RecoEgamma/EgammaElectronProducers/interface/LowPtGsfElectronIDHeavyObjectCache.h
+++ b/RecoEgamma/EgammaElectronProducers/interface/LowPtGsfElectronIDHeavyObjectCache.h
@@ -45,7 +45,8 @@ namespace lowptgsfeleid {
     float ele_pt_ = -1.;
   public:
     std::vector<float> get();
-    void set( const reco::GsfElectronRef& ele, double rho );
+    inline void set( const reco::GsfElectronRef& ele, double rho ) { set(*ele, rho); }
+    void set( const reco::GsfElectron& ele, double rho );
   };
   
   class HeavyObjectCache {
diff --git a/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronIDProducer.cc b/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronIDProducer.cc
index 6390a6e3d9fc3..1a3c5c2e1f478 100644
--- a/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronIDProducer.cc
+++ b/RecoEgamma/EgammaElectronProducers/plugins/LowPtGsfElectronIDProducer.cc
@@ -35,11 +35,13 @@ class LowPtGsfElectronIDProducer final : public edm::global::EDProducer<> {
   static void fillDescriptions(edm::ConfigurationDescriptions&);
 
 private:
-  double eval(const std::string& name, const reco::GsfElectronRef&, double rho, float unbiased, float field_z) const;
+  double eval(const std::string& name, const edm::Ptr<reco::GsfElectron>&, double rho, float unbiased, float field_z) const;
 
-  const edm::EDGetTokenT<reco::GsfElectronCollection> electrons_;
+  const bool usePAT_;
+  edm::EDGetTokenT<reco::GsfElectronCollection> electrons_;
+  edm::EDGetTokenT<pat::ElectronCollection> patElectrons_;
   const edm::EDGetTokenT<double> rho_;
-  const edm::EDGetTokenT<edm::ValueMap<float> > unbiased_;
+  edm::EDGetTokenT<edm::ValueMap<float> > unbiased_;
   const std::vector<std::string> names_;
   const bool passThrough_;
   const double minPtThreshold_;
@@ -52,15 +54,23 @@ class LowPtGsfElectronIDProducer final : public edm::global::EDProducer<> {
 ////////////////////////////////////////////////////////////////////////////////
 //
 LowPtGsfElectronIDProducer::LowPtGsfElectronIDProducer(const edm::ParameterSet& conf)
-    : electrons_(consumes<reco::GsfElectronCollection>(conf.getParameter<edm::InputTag>("electrons"))),
+    : usePAT_(conf.getParameter<bool>("usePAT")),
+      electrons_(),
+      patElectrons_(),
       rho_(consumes<double>(conf.getParameter<edm::InputTag>("rho"))),
-      unbiased_(consumes<edm::ValueMap<float> >(conf.getParameter<edm::InputTag>("unbiased"))),
+      unbiased_(),
       names_(conf.getParameter<std::vector<std::string> >("ModelNames")),
       passThrough_(conf.getParameter<bool>("PassThrough")),
       minPtThreshold_(conf.getParameter<double>("MinPtThreshold")),
       maxPtThreshold_(conf.getParameter<double>("MaxPtThreshold")),
       thresholds_(conf.getParameter<std::vector<double> >("ModelThresholds")),
       version_(conf.getParameter<std::string>("Version")) {
+  if (usePAT_) {
+    patElectrons_ = consumes<pat::ElectronCollection>(conf.getParameter<edm::InputTag>("electrons"));
+  } else {
+    electrons_ = consumes<reco::GsfElectronCollection>(conf.getParameter<edm::InputTag>("electrons"));
+    unbiased_ = consumes<edm::ValueMap<float> >(conf.getParameter<edm::InputTag>("unbiased"));
+  }
   for (auto& weights : conf.getParameter<std::vector<std::string> >("ModelWeights")) {
     models_.push_back(createGBRForest(edm::FileInPath(weights)));
   }
@@ -97,39 +107,52 @@ void LowPtGsfElectronIDProducer::produce(edm::StreamID, edm::Event& event, const
     throw cms::Exception("InvalidHandle", os.str());
   }
 
-  // Retrieve GsfElectrons from Event
+  // Retrieve pat::Electrons or reco::GsfElectrons from Event
+  edm::Handle<pat::ElectronCollection> patElectrons;
   edm::Handle<reco::GsfElectronCollection> electrons;
-  event.getByToken(electrons_, electrons);
-  if (!electrons.isValid()) {
-    std::ostringstream os;
-    os << "Problem accessing low-pT electrons collection" << std::endl;
-    throw cms::Exception("InvalidHandle", os.str());
+  if (usePAT_) {
+    event.getByToken(patElectrons_, patElectrons);
+  } else {
+    event.getByToken(electrons_, electrons);
   }
 
   // ElectronSeed unbiased BDT
   edm::Handle<edm::ValueMap<float> > unbiasedH;
-  event.getByToken(unbiased_, unbiasedH);
+  if (!unbiased_.isUninitialized()) {
+    event.getByToken(unbiased_, unbiasedH);
+  }
 
   // Iterate through Electrons, evaluate BDT, and store result
   std::vector<std::vector<float> > output;
+  unsigned int nElectrons = usePAT_ ? patElectrons->size() : electrons->size();
   for (unsigned int iname = 0; iname < names_.size(); ++iname) {
-    output.emplace_back(electrons->size(), -999.);
+    output.emplace_back(nElectrons, -999.);
   }
-  for (unsigned int iele = 0; iele < electrons->size(); iele++) {
-    reco::GsfElectronRef ele(electrons, iele);
 
-    if (ele->core().isNull()) {
-      continue;
-    }
-    const auto& gsf = ele->core()->gsfTrack();  // reco::GsfTrackRef
-    if (gsf.isNull()) {
-      continue;
+  if (usePAT_) {
+    for (unsigned int iele = 0; iele < nElectrons; iele++) {
+      edm::Ptr<pat::Electron> ele(patElectrons, iele);
+      if (!ele->isElectronIDAvailable("unbiased")) {
+        continue;
+      }
+      for (unsigned int iname = 0; iname < names_.size(); ++iname) {
+        output[iname][iele] = eval(names_[iname], ele, *rho, ele->electronID("unbiased"), zfield.z());
+      }
     }
-    float unbiased = (*unbiasedH)[gsf];
-
-    //if ( !passThrough_ && ( ele->pt() < minPtThreshold_ ) ) { continue; }
-    for (unsigned int iname = 0; iname < names_.size(); ++iname) {
-      output[iname][iele] = eval(names_[iname], ele, *rho, unbiased, zfield.z());
+  } else {
+    for (unsigned int iele = 0; iele < nElectrons; iele++) {
+      edm::Ptr<reco::GsfElectron> ele(electrons, iele);
+      if (ele->core().isNull()) {
+        continue;
+      }
+      const auto& gsf = ele->core()->gsfTrack();  // reco::GsfTrackRef
+      if (gsf.isNull()) {
+        continue;
+      }
+      float unbiased = (*unbiasedH)[gsf];
+      for (unsigned int iname = 0; iname < names_.size(); ++iname) {
+        output[iname][iele] = eval(names_[iname], ele, *rho, unbiased, zfield.z());
+      }
     }
   }
 
@@ -137,7 +160,11 @@ void LowPtGsfElectronIDProducer::produce(edm::StreamID, edm::Event& event, const
   for (unsigned int iname = 0; iname < names_.size(); ++iname) {
     auto ptr = std::make_unique<edm::ValueMap<float> >(edm::ValueMap<float>());
     edm::ValueMap<float>::Filler filler(*ptr);
-    filler.insert(electrons, output[iname].begin(), output[iname].end());
+    if (usePAT_) {
+      filler.insert(patElectrons, output[iname].begin(), output[iname].end());
+    } else {
+      filler.insert(electrons, output[iname].begin(), output[iname].end());
+    }
     filler.fill();
     event.put(std::move(ptr), names_[iname]);
   }
@@ -146,14 +173,14 @@ void LowPtGsfElectronIDProducer::produce(edm::StreamID, edm::Event& event, const
 //////////////////////////////////////////////////////////////////////////////////////////
 //
 double LowPtGsfElectronIDProducer::eval(
-    const std::string& name, const reco::GsfElectronRef& ele, double rho, float unbiased, float field_z) const {
+  const std::string& name, const edm::Ptr<reco::GsfElectron>& ele, double rho, float unbiased, float field_z) const {
   auto iter = std::find(names_.begin(), names_.end(), name);
   if (iter != names_.end()) {
     int index = std::distance(names_.begin(), iter);
     std::vector<float> inputs;
     if (version_.empty()) {  // Original XML model
       lowptgsfeleid::Features features;
-      features.set(ele, rho);
+      features.set(*ele, rho);
       inputs = features.get();
     } else if (version_ == "V0") {
       inputs = lowptgsfeleid::features_V0(*ele, rho, unbiased);
@@ -171,8 +198,9 @@ double LowPtGsfElectronIDProducer::eval(
 //
 void LowPtGsfElectronIDProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
   edm::ParameterSetDescription desc;
+  desc.add<bool>("usePAT", false);
   desc.add<edm::InputTag>("electrons", edm::InputTag("lowPtGsfElectrons"));
-  desc.add<edm::InputTag>("unbiased", edm::InputTag("lowPtGsfElectronSeedValueMaps:unbiased"));
+  desc.addOptional<edm::InputTag>("unbiased", edm::InputTag("lowPtGsfElectronSeedValueMaps:unbiased"));
   desc.add<edm::InputTag>("rho", edm::InputTag("fixedGridRhoFastjetAllTmp"));
   desc.add<std::vector<std::string> >("ModelNames", std::vector<std::string>());
   desc.add<std::vector<std::string> >("ModelWeights", std::vector<std::string>());
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
index 9f323046b8e52..85484aab0bfbf 100644
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
@@ -63,7 +63,7 @@
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronCores_cff import *
 
 # Low pT electrons
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtGsfElectrons
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import lowPtGsfElectrons
 
 # Low pT Electron value maps
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronSeedValueMaps_cff import lowPtGsfElectronSeedValueMaps
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py
index ac32ecc504f42..30a9f5f11af9d 100644
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cff.py
@@ -8,7 +8,7 @@
 # LowPtGsfElectronFinalizer below is scheduled for run2_miniAOD_UL
 
 from RecoEgamma.EgammaTools.regressionModifier_cfi import regressionModifier106XUL
-_lowPtRegressionModifier = regressionModifier106XUL.clone(
+lowPtRegressionModifier = regressionModifier106XUL.clone(
     modifierName = 'EGRegressionModifierV3',
     rhoTag = 'fixedGridRhoFastjetAll',
     eleRegs = dict(
@@ -48,7 +48,7 @@
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronFinalizer_cfi import lowPtGsfElectronFinalizer
 _lowPtGsfElectrons = lowPtGsfElectronFinalizer.clone(
     previousGsfElectronsTag = "lowPtGsfElectrons::@skipCurrentProcess",
-    regressionConfig = _lowPtRegressionModifier,
+    regressionConfig = lowPtRegressionModifier,
 )
 
 from Configuration.ProcessModifiers.run2_miniAOD_UL_cff import run2_miniAOD_UL
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
new file mode 100644
index 0000000000000..30a9f5f11af9d
--- /dev/null
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
@@ -0,0 +1,55 @@
+import FWCore.ParameterSet.Config as cms
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronsPreRegression_cfi import lowPtGsfElectronsPreRegression
+
+lowPtGsfElectrons = lowPtGsfElectronsPreRegression.clone()
+
+################################################################################
+# LowPtGsfElectronProducer above is run by default in RECO
+# LowPtGsfElectronFinalizer below is scheduled for run2_miniAOD_UL
+
+from RecoEgamma.EgammaTools.regressionModifier_cfi import regressionModifier106XUL
+lowPtRegressionModifier = regressionModifier106XUL.clone(
+    modifierName = 'EGRegressionModifierV3',
+    rhoTag = 'fixedGridRhoFastjetAll',
+    eleRegs = dict(
+        ecalOnlyMean = dict(
+            lowEtHighEtBoundary = 20.,
+            ebLowEtForestName = "lowPtElectron_eb_ecalOnly_05To50_mean",
+            ebHighEtForestName = "lowPtElectron_eb_ecalOnly_05To50_mean",
+            eeLowEtForestName = "lowPtElectron_ee_ecalOnly_05To50_mean",
+            eeHighEtForestName = "lowPtElectron_ee_ecalOnly_05To50_mean",
+            ),
+        ecalOnlySigma = dict(
+            lowEtHighEtBoundary = 20.,
+            ebLowEtForestName = "lowPtElectron_eb_ecalOnly_05To50_sigma",
+            ebHighEtForestName = "lowPtElectron_eb_ecalOnly_05To50_sigma",
+            eeLowEtForestName = "lowPtElectron_ee_ecalOnly_05To50_sigma",
+            eeHighEtForestName = "lowPtElectron_ee_ecalOnly_05To50_sigma",
+            ),
+        epComb = dict(
+            ecalTrkRegressionConfig = dict(
+                lowEtHighEtBoundary = 20.,
+                ebLowEtForestName = "lowPtElectron_eb_ecalTrk_05To50_mean",
+                ebHighEtForestName = "lowPtElectron_eb_ecalTrk_05To50_mean",
+                eeLowEtForestName = "lowPtElectron_ee_ecalTrk_05To50_mean",
+                eeHighEtForestName = "lowPtElectron_ee_ecalTrk_05To50_mean",
+                ),
+            ecalTrkRegressionUncertConfig = dict(
+                lowEtHighEtBoundary = 20.,
+                ebLowEtForestName = "lowPtElectron_eb_ecalTrk_05To50_sigma",
+                ebHighEtForestName = "lowPtElectron_eb_ecalTrk_05To50_sigma",
+                eeLowEtForestName = "lowPtElectron_ee_ecalTrk_05To50_sigma",
+                eeHighEtForestName = "lowPtElectron_ee_ecalTrk_05To50_sigma",
+                ),
+        )
+    ),
+)
+
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronFinalizer_cfi import lowPtGsfElectronFinalizer
+_lowPtGsfElectrons = lowPtGsfElectronFinalizer.clone(
+    previousGsfElectronsTag = "lowPtGsfElectrons::@skipCurrentProcess",
+    regressionConfig = lowPtRegressionModifier,
+)
+
+from Configuration.ProcessModifiers.run2_miniAOD_UL_cff import run2_miniAOD_UL
+run2_miniAOD_UL.toReplaceWith(lowPtGsfElectrons,_lowPtGsfElectrons)
diff --git a/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronFeatures.cc b/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronFeatures.cc
index 5ab6abae8129a..8488c7af30947 100644
--- a/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronFeatures.cc
+++ b/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronFeatures.cc
@@ -3,6 +3,7 @@
 #include "DataFormats/GsfTrackReco/interface/GsfTrack.h"
 #include "DataFormats/ParticleFlowReco/interface/PFCluster.h"
 #include "DataFormats/ParticleFlowReco/interface/PFClusterFwd.h"
+#include "DataFormats/PatCandidates/interface/Electron.h"
 #include "DataFormats/TrackReco/interface/Track.h"
 #include "DataFormats/TrackReco/interface/TrackFwd.h"
 #include "TVector3.h"
@@ -62,7 +63,7 @@ namespace lowptgsfeleid {
 
     // GSF tracks
     if (ele.core().isNonnull()) {
-      reco::GsfTrackRef gsf = ele.core()->gsfTrack();
+      reco::GsfTrackRef gsf = ele.gsfTrack();
       if (gsf.isNonnull()) {
         gsf_mode_p = gsf->pMode();
         eid_gsf_nhits = (float)gsf->found();
@@ -77,7 +78,7 @@ namespace lowptgsfeleid {
 
     // Super clusters
     if (ele.core().isNonnull()) {
-      reco::SuperClusterRef sc = ele.core()->superCluster();
+      reco::SuperClusterRef sc = ele.superCluster();
       if (sc.isNonnull()) {
         eid_sc_E = sc->energy();
         eid_sc_eta = sc->eta();
@@ -109,9 +110,9 @@ namespace lowptgsfeleid {
 
     // Clusters
     if (ele.core().isNonnull()) {
-      reco::GsfTrackRef gsf = ele.core()->gsfTrack();
+      reco::GsfTrackRef gsf = ele.gsfTrack();
       if (gsf.isNonnull()) {
-        reco::SuperClusterRef sc = ele.core()->superCluster();
+        reco::SuperClusterRef sc = ele.superCluster();
         if (sc.isNonnull()) {
           // Propagate electron track to ECAL surface
           double mass2 = 0.000511 * 0.000511;
diff --git a/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronIDHeavyObjectCache.cc b/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronIDHeavyObjectCache.cc
index c15899fb0208d..8fcf71aabcf66 100644
--- a/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronIDHeavyObjectCache.cc
+++ b/RecoEgamma/EgammaElectronProducers/src/LowPtGsfElectronIDHeavyObjectCache.cc
@@ -44,11 +44,11 @@ namespace lowptgsfeleid {
   
   ////////////////////////////////////////////////////////////////////////////////
   //
-  void Features::set( const reco::GsfElectronRef& ele, double rho ) {
+  void Features::set( const reco::GsfElectron& ele, double rho ) {
 
     // KF tracks
-    if ( ele->core().isNonnull() ) {
-      reco::TrackRef trk = ele->core()->ctfTrack(); //@@ is this what we want?!
+    if ( ele.core().isNonnull() ) {
+      reco::TrackRef trk = ele.core()->ctfTrack(); //@@ is this what we want?!
       if ( trk.isNonnull() ) {
 	trk_p_ = float(trk->p());
 	trk_nhits_ = float(trk->found());
@@ -57,8 +57,8 @@ namespace lowptgsfeleid {
     }
 
     // GSF tracks
-    if ( ele->core().isNonnull() ) {
-      reco::GsfTrackRef gsf = ele->core()->gsfTrack();
+    if ( ele.core().isNonnull() ) {
+      reco::GsfTrackRef gsf = ele.core()->gsfTrack();
       if ( gsf.isNonnull() ) {
 	gsf_nhits_ = gsf->found();
 	gsf_chi2red_ = gsf->normalizedChi2();
@@ -66,8 +66,8 @@ namespace lowptgsfeleid {
     }
 
     // Super clusters
-    if ( ele->core().isNonnull() ) {
-      reco::SuperClusterRef sc = ele->core()->superCluster();
+    if ( ele.core().isNonnull() ) {
+      reco::SuperClusterRef sc = ele.core()->superCluster();
       if ( sc.isNonnull() ) {
 	sc_E_ = sc->energy();
 	sc_eta_ = sc->eta();
@@ -77,29 +77,23 @@ namespace lowptgsfeleid {
     }
 
     // Track-cluster matching
-    if ( ele.isNonnull() ) {
-      match_seed_dEta_ = ele->deltaEtaSeedClusterTrackAtCalo();
-      match_eclu_EoverP_ = (1./ele->ecalEnergy()) - (1./ele->p());
-      match_SC_EoverP_ = ele->eSuperClusterOverP();
-      match_SC_dEta_ = ele->deltaEtaSuperClusterTrackAtVtx();
-      match_SC_dPhi_ = ele->deltaPhiSuperClusterTrackAtVtx();
-    }      
+    match_seed_dEta_ = ele.deltaEtaSeedClusterTrackAtCalo();
+    match_eclu_EoverP_ = (1./ele.ecalEnergy()) - (1./ele.p());
+    match_SC_EoverP_ = ele.eSuperClusterOverP();
+    match_SC_dEta_ = ele.deltaEtaSuperClusterTrackAtVtx();
+    match_SC_dPhi_ = ele.deltaPhiSuperClusterTrackAtVtx();
 
     // Shower shape vars
-    if ( ele.isNonnull() ) {
-      shape_full5x5_sigmaIetaIeta_ = ele->full5x5_sigmaIetaIeta();
-      shape_full5x5_sigmaIphiIphi_ = ele->full5x5_sigmaIphiIphi();
-      shape_full5x5_HoverE_    = ele->full5x5_hcalOverEcal();
-      shape_full5x5_r9_ = ele->full5x5_r9();
-      shape_full5x5_circularity_   = 1. - ele->full5x5_e1x5() / ele->full5x5_e5x5();
-    }
+    shape_full5x5_sigmaIetaIeta_ = ele.full5x5_sigmaIetaIeta();
+    shape_full5x5_sigmaIphiIphi_ = ele.full5x5_sigmaIphiIphi();
+    shape_full5x5_HoverE_    = ele.full5x5_hcalOverEcal();
+    shape_full5x5_r9_ = ele.full5x5_r9();
+    shape_full5x5_circularity_   = 1. - ele.full5x5_e1x5() / ele.full5x5_e5x5();
 
     // Misc
     rho_ = rho;
-    if ( ele.isNonnull() ) {
-      brem_frac_ = ele->fbrem();
-      ele_pt_ = ele->pt();
-    }
+    brem_frac_ = ele.fbrem();
+    ele_pt_ = ele.pt();
     
   };
 
diff --git a/RecoEgamma/EgammaTools/BuildFile.xml b/RecoEgamma/EgammaTools/BuildFile.xml
index b68da922f2348..92acfdf688c8a 100644
--- a/RecoEgamma/EgammaTools/BuildFile.xml
+++ b/RecoEgamma/EgammaTools/BuildFile.xml
@@ -13,6 +13,10 @@
 <use   name="CondCore/DBOutputService"/>
 <use   name="DataFormats/ParticleFlowReco"/>
 <use   name="CommonTools/Utils"/>
+<use   name="CommonTools/Statistics"/>
+<use   name="DataFormats/Common"/>
+<use   name="DataFormats/PatCandidates"/>
+<use   name="DataFormats/TrackReco"/>
 <export>
   <lib   name="1"/>
 </export>
diff --git a/RecoEgamma/EgammaTools/interface/LowPtConversion.h b/RecoEgamma/EgammaTools/interface/LowPtConversion.h
new file mode 100644
index 0000000000000..744bce5caff8f
--- /dev/null
+++ b/RecoEgamma/EgammaTools/interface/LowPtConversion.h
@@ -0,0 +1,68 @@
+#ifndef RecoEgamma_EgammaTools_LowPtConversion_h
+#define RecoEgamma_EgammaTools_LowPtConversion_h
+
+#include "CommonTools/Statistics/interface/ChiSquaredProbability.h"
+#include "DataFormats/BeamSpot/interface/BeamSpot.h"
+#include "DataFormats/EgammaCandidates/interface/Conversion.h"
+#include "DataFormats/Common/interface/Handle.h"
+#include "DataFormats/Common/interface/RefToBase.h"
+#include "DataFormats/Common/interface/View.h"
+#include "DataFormats/PatCandidates/interface/Electron.h"
+#include "DataFormats/TrackReco/interface/Track.h"
+
+class LowPtConversion {
+public:
+  LowPtConversion() = default;
+  ~LowPtConversion() = default;
+
+  bool wpOpen() const;   // Matched to any conversion (without selections)
+  bool wpLoose() const;  // Nancy's baseline selections for conversions
+  bool wpTight() const;  // Nancy's selection for analysis of conversions
+
+  void addUserVars(pat::Electron& ele) const;       // adds minimal set of flags to electron userData
+  void addExtraUserVars(pat::Electron& ele) const;  // adds all variables to electron userData
+
+  bool match(const reco::BeamSpot& beamSpot, const reco::ConversionCollection& conversions, const pat::Electron& ele);
+
+  static float mee(float ipx1, float ipy1, float ipz1, float ipx2, float ipy2, float ipz2);
+
+private:
+  // quality
+  bool valid_ = false;
+  float chi2prob_ = -1.;
+  bool quality_high_purity_ = false;
+  bool quality_high_efficiency_ = false;
+
+  // tracks
+  uint ntracks_ = 0;
+  float min_trk_pt_ = -1.;
+  int ilead_ = -1;
+  int itrail_ = -1;
+
+  // displacement
+  float l_xy_ = -1.;
+  float vtx_radius_ = -1.;
+
+  // invariant mass
+  float mass_from_conv_ = -1.;
+  float mass_from_Pin_ = -1.;
+  float mass_before_fit_ = -1.;
+  float mass_after_fit_ = -1.;
+
+  // hits before vertex
+  uint lead_nhits_before_vtx_ = 0;
+  uint trail_nhits_before_vtx_ = 0;
+  uint max_nhits_before_vtx_ = 0;
+  uint sum_nhits_before_vtx_ = 0;
+  int delta_expected_nhits_inner_ = 0;
+
+  // opening angle
+  float delta_cot_from_Pin_ = -1.;
+
+  // match?
+  bool matched_ = false;
+  edm::RefToBase<reco::Track> matched_lead_;
+  edm::RefToBase<reco::Track> matched_trail_;
+};
+
+#endif  // RecoEgamma_EgammaTools_LowPtConversion_h
diff --git a/RecoEgamma/EgammaTools/plugins/EGRegressionModifierV3.cc b/RecoEgamma/EgammaTools/plugins/EGRegressionModifierV3.cc
index 9fcab37d305e8..31b976611bc4b 100644
--- a/RecoEgamma/EgammaTools/plugins/EGRegressionModifierV3.cc
+++ b/RecoEgamma/EgammaTools/plugins/EGRegressionModifierV3.cc
@@ -116,7 +116,7 @@ void EGRegressionModifierV3::modifyObject(reco::GsfElectron& ele) const
   if( superClus->seed()->seed().det() == DetId::Forward ) return;
 
   // do not apply corrections in case of missing info (slimmed MiniAOD electrons)
-  if(!superClus->clusters().isAvailable()) return; 
+  bool rescaleDependentValues = superClus->clusters().isAvailable();
 
   //check if fbrem is filled as its needed for E/p combination so abort if its set to the default value 
   //this will be the case for <5 (or current cuts) for miniAOD electrons
@@ -148,7 +148,7 @@ void EGRegressionModifierV3::modifyObject(reco::GsfElectron& ele) const
   const float corrEnergy = (rawEnergy + rawESEnergy)*ecalMeanCorr;
   const float corrEnergyErr = corrEnergy*ecalSigma;
 
-  ele.setCorrectedEcalEnergy(corrEnergy);
+  ele.setCorrectedEcalEnergy(corrEnergy, rescaleDependentValues);
   ele.setCorrectedEcalEnergyError(corrEnergyErr);
 
   std::pair<float,float> combEnergyAndErr =  eleRegs_->epComb.combine(ele);
diff --git a/RecoEgamma/EgammaTools/plugins/LowPtElectronsModifier.cc b/RecoEgamma/EgammaTools/plugins/LowPtElectronsModifier.cc
new file mode 100644
index 0000000000000..22b55a300e21c
--- /dev/null
+++ b/RecoEgamma/EgammaTools/plugins/LowPtElectronsModifier.cc
@@ -0,0 +1,82 @@
+#include "CommonTools/CandAlgos/interface/ModifyObjectValueBase.h"
+#include "DataFormats/BeamSpot/interface/BeamSpot.h"
+#include "DataFormats/VertexReco/interface/VertexFwd.h"
+#include "FWCore/Framework/interface/ConsumesCollector.h"
+#include "FWCore/ParameterSet/interface/ParameterSet.h"
+#include "RecoEgamma/EgammaTools/interface/LowPtConversion.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+class LowPtElectronModifier : public ModifyObjectValueBase {
+public:
+  LowPtElectronModifier(const edm::ParameterSet& conf, edm::ConsumesCollector&);
+  ~LowPtElectronModifier() override = default;
+
+  void setEvent(const edm::Event&) final;
+  void setEventContent(const edm::EventSetup&) final;
+
+  void modifyObject(pat::Electron& ele) const final;
+
+private:
+  const edm::EDGetTokenT<reco::ConversionCollection> convT_;
+  reco::ConversionCollection const* conv_ = nullptr;
+  const edm::EDGetTokenT<reco::BeamSpot> beamSpotT_;
+  reco::BeamSpot const* beamSpot_ = nullptr;
+  const edm::EDGetTokenT<reco::VertexCollection> verticesT_;
+  reco::VertexCollection const* vertices_ = nullptr;
+  bool extra_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+LowPtElectronModifier::LowPtElectronModifier(const edm::ParameterSet& conf, edm::ConsumesCollector& cc)
+    : ModifyObjectValueBase(conf),
+      convT_(cc.consumes<reco::ConversionCollection>(conf.getParameter<edm::InputTag>("conversions"))),
+      conv_(),
+      beamSpotT_(cc.consumes<reco::BeamSpot>(conf.getParameter<edm::InputTag>("beamSpot"))),
+      beamSpot_(),
+      verticesT_(cc.consumes<reco::VertexCollection>(conf.getParameter<edm::InputTag>("vertices"))),
+      vertices_(),
+      extra_(conf.getParameter<bool>("addExtraUserVars")) {
+  ;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LowPtElectronModifier::setEvent(const edm::Event& iEvent) {
+  conv_ = &iEvent.get(convT_);
+  beamSpot_ = &iEvent.get(beamSpotT_);
+  vertices_ = &iEvent.get(verticesT_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LowPtElectronModifier::setEventContent(const edm::EventSetup& iSetup) {}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LowPtElectronModifier::modifyObject(pat::Electron& ele) const {
+  // Embed Conversion info
+  LowPtConversion conv;
+  conv.match(*beamSpot_, *conv_, ele);
+  conv.addUserVars(ele);
+  if (extra_) {
+    conv.addExtraUserVars(ele);
+  }
+  // Set impact parameters
+  auto const& gsfTrack = *ele.gsfTrack();
+  if (!vertices_->empty()) {
+    const reco::Vertex& pv = vertices_->front();
+    ele.setDB(gsfTrack.dxy(pv.position()),
+              gsfTrack.dxyError(pv.position(), pv.covariance()),
+              pat::Electron::PV2D);  // PV2D
+    ele.setDB(gsfTrack.dz(pv.position()), std::hypot(gsfTrack.dzError(), pv.zError()),
+              pat::Electron::PVDZ);  // PVDZ
+  }
+  ele.setDB(gsfTrack.dxy(*beamSpot_), gsfTrack.dxyError(*beamSpot_),
+            pat::Electron::BS2D);  // BS2D
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+DEFINE_EDM_PLUGIN(ModifyObjectValueFactory, LowPtElectronModifier, "LowPtElectronModifier");
diff --git a/RecoEgamma/EgammaTools/python/lowPtElectronModifier_cfi.py b/RecoEgamma/EgammaTools/python/lowPtElectronModifier_cfi.py
new file mode 100644
index 0000000000000..72636d5f00c3a
--- /dev/null
+++ b/RecoEgamma/EgammaTools/python/lowPtElectronModifier_cfi.py
@@ -0,0 +1,9 @@
+import FWCore.ParameterSet.Config as cms
+
+lowPtElectronModifier = cms.PSet(
+    modifierName = cms.string('LowPtElectronModifier'),
+    beamSpot = cms.InputTag('offlineBeamSpot'),
+    conversions = cms.InputTag('gsfTracksOpenConversions:gsfTracksOpenConversions'),
+    addExtraUserVars = cms.bool(True),
+    vertices = cms.InputTag("offlineSlimmedPrimaryVertices"),
+)
diff --git a/RecoEgamma/EgammaTools/src/LowPtConversion.cc b/RecoEgamma/EgammaTools/src/LowPtConversion.cc
new file mode 100644
index 0000000000000..1862490d4a180
--- /dev/null
+++ b/RecoEgamma/EgammaTools/src/LowPtConversion.cc
@@ -0,0 +1,212 @@
+#include "RecoEgamma/EgammaTools/interface/LowPtConversion.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Matched to any conversion (without selections)
+//
+bool LowPtConversion::wpOpen() const { return matched_; }
+
+////////////////////////////////////////////////////////////////////////////////
+// Nancy's baseline selections for conversions
+// Based on: https://github.com/CMSBParking/BParkingNANO/blob/b2664ed/BParkingNano/plugins/ConversionSelector.cc#L253-L300
+bool LowPtConversion::wpLoose() const {
+  return (wpOpen() && ntracks_ == 2 && valid_ && quality_high_purity_ && chi2prob_ > 0.0005);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Nancy's selection for analysis of conversions
+// Based on: slide 20 of https://indico.cern.ch/event/814402/contributions/3401312/
+bool LowPtConversion::wpTight() const {
+  return (wpLoose() && sum_nhits_before_vtx_ <= 1 && l_xy_ > 0. && mass_from_conv_ > 0. &&  // sanity check
+          mass_from_conv_ < 0.05);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// adds minimal set of flags to electron userData
+void LowPtConversion::addUserVars(pat::Electron& ele) const {
+  ele.addUserInt("convOpen", matched_ ? 1 : 0);
+  ele.addUserInt("convLoose", wpLoose() ? 1 : 0);
+  ele.addUserInt("convTight", wpTight() ? 1 : 0);
+  ele.addUserInt("convLead", matched_lead_.isNonnull() ? 1 : 0);
+  ele.addUserInt("convTrail", matched_trail_.isNonnull() ? 1 : 0);
+  if (ele.hasUserInt("convExtra") == false) {
+    ele.addUserInt("convExtra", 0);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// adds all variables to electron userData
+void LowPtConversion::addExtraUserVars(pat::Electron& ele) const {
+  // Flag that indicates if extra variables are added to electron userData
+  ele.addUserInt("convExtra", 1, true);  // overwrite
+
+  // quality
+  ele.addUserInt("convValid", valid_ ? 1 : 0);
+  ele.addUserFloat("convChi2Prob", chi2prob_);
+  ele.addUserInt("convQualityHighPurity", quality_high_purity_ ? 1 : 0);
+  ele.addUserInt("convQualityHighEff", quality_high_efficiency_ ? 1 : 0);
+
+  // tracks
+  ele.addUserInt("convTracksN", ntracks_);
+  ele.addUserFloat("convMinTrkPt", min_trk_pt_);
+  ele.addUserInt("convLeadIdx", ilead_);
+  ele.addUserInt("convTrailIdx", itrail_);
+
+  // displacement
+  ele.addUserFloat("convLxy", l_xy_);
+  ele.addUserFloat("convVtxRadius", vtx_radius_);
+
+  // invariant mass
+  ele.addUserFloat("convMass", mass_from_conv_);
+  ele.addUserFloat("convMassFromPin", mass_from_Pin_);
+  ele.addUserFloat("convMassBeforeFit", mass_before_fit_);
+  ele.addUserFloat("convMassAfterFit", mass_after_fit_);
+
+  // hits before vertex
+  ele.addUserInt("convLeadNHitsBeforeVtx", lead_nhits_before_vtx_);
+  ele.addUserInt("convTrailNHitsBeforeVtx", trail_nhits_before_vtx_);
+  ele.addUserInt("convMaxNHitsBeforeVtx", max_nhits_before_vtx_);
+  ele.addUserInt("convSumNHitsBeforeVtx", sum_nhits_before_vtx_);
+  ele.addUserInt("convDeltaExpectedNHitsInner", delta_expected_nhits_inner_);
+
+  // opening angle
+  ele.addUserFloat("convDeltaCotFromPin", delta_cot_from_Pin_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool LowPtConversion::match(const reco::BeamSpot& beamSpot,
+                            const reco::ConversionCollection& conversions,
+                            const pat::Electron& ele) {
+  // Iterate through conversions and calculate quantities (requirement from Nancy)
+  for (const auto& conv : conversions) {
+    // Filter
+    if (conv.tracks().size() != 2) {
+      continue;
+    }
+
+    // Quality
+    valid_ = conv.conversionVertex().isValid();                                                         // (=true)
+    chi2prob_ = ChiSquaredProbability(conv.conversionVertex().chi2(), conv.conversionVertex().ndof());  // (<0.005)
+    quality_high_purity_ = conv.quality(reco::Conversion::highPurity);                                  // (=true)
+    quality_high_efficiency_ = conv.quality(reco::Conversion::highEfficiency);                          // (none)
+
+    // Tracks
+    ntracks_ = conv.tracks().size();  // (=2)
+    min_trk_pt_ = -1.;                // (>0.5)
+    for (const auto& trk : conv.tracks()) {
+      if (trk.isNonnull() && trk.isAvailable() && (min_trk_pt_ < 0. || trk->pt() < min_trk_pt_)) {
+        min_trk_pt_ = trk->pt();
+      }
+    }
+    ilead_ = -1;
+    itrail_ = -1;
+    if (conv.tracks().size() == 2) {
+      const edm::RefToBase<reco::Track>& trk1 = conv.tracks().front();
+      const edm::RefToBase<reco::Track>& trk2 = conv.tracks().back();
+      if (trk1.isNonnull() && trk1.isAvailable() && trk2.isNonnull() && trk2.isAvailable()) {
+        if (trk1->pt() > trk2->pt()) {
+          ilead_ = 0;
+          itrail_ = 1;
+        } else {
+          ilead_ = 1;
+          itrail_ = 0;
+        }
+      }
+    }
+
+    // Transverse displacement (with respect to beamspot) and vertex radius
+    math::XYZVectorF p_refitted = conv.refittedPairMomentum();
+    float dx = conv.conversionVertex().x() - beamSpot.x0();
+    float dy = conv.conversionVertex().y() - beamSpot.y0();
+    l_xy_ = (p_refitted.x() * dx + p_refitted.y() * dy) / p_refitted.rho();
+    vtx_radius_ = sqrt(conv.conversionVertex().position().perp2());  // (1.5<r<4.)
+
+    // invariant mass from track pair from conversion
+    mass_from_conv_ = conv.pairInvariantMass();
+
+    // Invariant mass from Pin before fit to common vertex
+    if (conv.tracksPin().size() >= 2 && ilead_ > -1 && itrail_ > -1) {
+      math::XYZVectorF lead_Pin = conv.tracksPin().at(ilead_);
+      math::XYZVectorF trail_Pin = conv.tracksPin().at(itrail_);
+      mass_from_Pin_ = mee(lead_Pin.x(), lead_Pin.y(), lead_Pin.z(), trail_Pin.x(), trail_Pin.y(), trail_Pin.z());
+      // Opening angle
+      delta_cot_from_Pin_ = 1. / tan(trail_Pin.theta()) - 1. / tan(lead_Pin.theta());
+    }
+
+    // Invariant mass before fit to common vertex
+    if (conv.tracks().size() >= 2 && ilead_ > -1 && itrail_ > -1) {
+      auto lead_before_vtx_fit = conv.tracks().at(ilead_)->momentum();
+      auto trail_before_vtx_fit = conv.tracks().at(itrail_)->momentum();
+      mass_before_fit_ = mee(lead_before_vtx_fit.x(),
+                             lead_before_vtx_fit.y(),
+                             lead_before_vtx_fit.z(),
+                             trail_before_vtx_fit.x(),
+                             trail_before_vtx_fit.y(),
+                             trail_before_vtx_fit.z());
+    }
+
+    // Invariant mass after the fit to common vertex
+    if (conv.conversionVertex().refittedTracks().size() >= 2 && ilead_ > -1 && itrail_ > -1) {
+      auto const& lead_after_vtx_fit = conv.conversionVertex().refittedTracks().at(ilead_);
+      auto const& trail_after_vtx_fit = conv.conversionVertex().refittedTracks().at(itrail_);
+      mass_after_fit_ = mee(lead_after_vtx_fit.px(),
+                            lead_after_vtx_fit.py(),
+                            lead_after_vtx_fit.pz(),
+                            trail_after_vtx_fit.px(),
+                            trail_after_vtx_fit.py(),
+                            trail_after_vtx_fit.pz());
+      // Difference in expeted hits
+      delta_expected_nhits_inner_ =
+          lead_after_vtx_fit.hitPattern().numberOfLostHits(reco::HitPattern::MISSING_INNER_HITS) -
+          trail_after_vtx_fit.hitPattern().numberOfLostHits(reco::HitPattern::MISSING_INNER_HITS);
+    }
+
+    // Hits prior to vertex
+    if (ilead_ > -1 && itrail_ > -1) {
+      auto const& nHits = conv.nHitsBeforeVtx();
+      bool enoughTracks = nHits.size() > 1;
+      lead_nhits_before_vtx_ = enoughTracks ? nHits.at(ilead_) : 0;
+      trail_nhits_before_vtx_ = enoughTracks ? nHits.at(itrail_) : 0;
+      max_nhits_before_vtx_ = enoughTracks ? std::max(nHits[0], nHits[1]) : 0;
+      sum_nhits_before_vtx_ = enoughTracks ? nHits[0] + nHits[1] : 0;
+    }
+
+    // Attempt to match conversion track to electron
+    for (uint itrk = 0; itrk < conv.tracks().size(); ++itrk) {
+      const edm::RefToBase<reco::Track> trk = conv.tracks()[itrk];
+      if (trk.isNull()) {
+        continue;
+      }
+      reco::GsfTrackRef ref = ele.core()->gsfTrack();
+      reco::GsfTrackRef gsf = ele.gsfTrack();
+      if (gsf.isNull()) {
+        continue;
+      }
+      if (ref.id() == trk.id() && ref.key() == trk.key()) {
+        matched_ = true;
+        if (static_cast<int>(itrk) == ilead_) {
+          matched_lead_ = trk;
+        }
+        if (static_cast<int>(itrk) == itrail_) {
+          matched_trail_ = trk;
+        }
+      }
+    }  // track loop
+  }    // conversions loop
+
+  return matched_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+float LowPtConversion::mee(float px1, float py1, float pz1, float px2, float py2, float pz2) {
+  const float m = 0.000511;
+  const float px = px1 + px2;
+  const float py = py1 + py2;
+  const float pz = pz1 + pz2;
+  const float p1 = px1 * px1 + py1 * py1 + pz1 * pz1;
+  const float p2 = px2 * px2 + py2 * py2 + pz2 * pz2;
+  const float e = sqrt(p1 + m * m) + sqrt(p2 + m * m);
+  const float mass = (e * e - px * px - py * py - pz * pz);
+  return mass > 0. ? sqrt(mass) : -1.;
+}

From f6b05b9eb2adc79d611feadd02af1405bc3743aa Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 7 Jun 2021 12:06:57 +0200
Subject: [PATCH 3/8] lowPtElectrons: cfi --> cff, consistent with #33589

---
 .../lowPtElectronProducer_cff.py              |  2 +-
 .../python/lowPtGsfElectronSequence_cff.py    |  2 +-
 .../python/lowPtGsfElectrons_cfi.py           | 55 -------------------
 3 files changed, 2 insertions(+), 57 deletions(-)
 delete mode 100644 RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py

diff --git a/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py b/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
index 97a8a7f540b6c..239ac2ce4a64a 100644
--- a/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
+++ b/PhysicsTools/PatAlgos/python/producersLayer1/lowPtElectronProducer_cff.py
@@ -74,7 +74,7 @@
 
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronSeedValueMaps_cff import rekeyLowPtGsfElectronSeedValueMaps
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import lowPtGsfElectrons
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtGsfElectrons
 
 _makePatLowPtElectronsTask = makePatLowPtElectronsTask.copy()
 _makePatLowPtElectronsTask.add(rekeyLowPtGsfElectronSeedValueMaps)
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
index 85484aab0bfbf..9f323046b8e52 100644
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
+++ b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectronSequence_cff.py
@@ -63,7 +63,7 @@
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronCores_cff import *
 
 # Low pT electrons
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import lowPtGsfElectrons
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtGsfElectrons
 
 # Low pT Electron value maps
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronSeedValueMaps_cff import lowPtGsfElectronSeedValueMaps
diff --git a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py b/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
deleted file mode 100644
index 30a9f5f11af9d..0000000000000
--- a/RecoEgamma/EgammaElectronProducers/python/lowPtGsfElectrons_cfi.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import FWCore.ParameterSet.Config as cms
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronsPreRegression_cfi import lowPtGsfElectronsPreRegression
-
-lowPtGsfElectrons = lowPtGsfElectronsPreRegression.clone()
-
-################################################################################
-# LowPtGsfElectronProducer above is run by default in RECO
-# LowPtGsfElectronFinalizer below is scheduled for run2_miniAOD_UL
-
-from RecoEgamma.EgammaTools.regressionModifier_cfi import regressionModifier106XUL
-lowPtRegressionModifier = regressionModifier106XUL.clone(
-    modifierName = 'EGRegressionModifierV3',
-    rhoTag = 'fixedGridRhoFastjetAll',
-    eleRegs = dict(
-        ecalOnlyMean = dict(
-            lowEtHighEtBoundary = 20.,
-            ebLowEtForestName = "lowPtElectron_eb_ecalOnly_05To50_mean",
-            ebHighEtForestName = "lowPtElectron_eb_ecalOnly_05To50_mean",
-            eeLowEtForestName = "lowPtElectron_ee_ecalOnly_05To50_mean",
-            eeHighEtForestName = "lowPtElectron_ee_ecalOnly_05To50_mean",
-            ),
-        ecalOnlySigma = dict(
-            lowEtHighEtBoundary = 20.,
-            ebLowEtForestName = "lowPtElectron_eb_ecalOnly_05To50_sigma",
-            ebHighEtForestName = "lowPtElectron_eb_ecalOnly_05To50_sigma",
-            eeLowEtForestName = "lowPtElectron_ee_ecalOnly_05To50_sigma",
-            eeHighEtForestName = "lowPtElectron_ee_ecalOnly_05To50_sigma",
-            ),
-        epComb = dict(
-            ecalTrkRegressionConfig = dict(
-                lowEtHighEtBoundary = 20.,
-                ebLowEtForestName = "lowPtElectron_eb_ecalTrk_05To50_mean",
-                ebHighEtForestName = "lowPtElectron_eb_ecalTrk_05To50_mean",
-                eeLowEtForestName = "lowPtElectron_ee_ecalTrk_05To50_mean",
-                eeHighEtForestName = "lowPtElectron_ee_ecalTrk_05To50_mean",
-                ),
-            ecalTrkRegressionUncertConfig = dict(
-                lowEtHighEtBoundary = 20.,
-                ebLowEtForestName = "lowPtElectron_eb_ecalTrk_05To50_sigma",
-                ebHighEtForestName = "lowPtElectron_eb_ecalTrk_05To50_sigma",
-                eeLowEtForestName = "lowPtElectron_ee_ecalTrk_05To50_sigma",
-                eeHighEtForestName = "lowPtElectron_ee_ecalTrk_05To50_sigma",
-                ),
-        )
-    ),
-)
-
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronFinalizer_cfi import lowPtGsfElectronFinalizer
-_lowPtGsfElectrons = lowPtGsfElectronFinalizer.clone(
-    previousGsfElectronsTag = "lowPtGsfElectrons::@skipCurrentProcess",
-    regressionConfig = lowPtRegressionModifier,
-)
-
-from Configuration.ProcessModifiers.run2_miniAOD_UL_cff import run2_miniAOD_UL
-run2_miniAOD_UL.toReplaceWith(lowPtGsfElectrons,_lowPtGsfElectrons)

From adc59ec63fbba2220851fad40348ead0434d4145 Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 7 Jun 2021 12:38:31 +0200
Subject: [PATCH 4/8] lowPtElectrons: cfi-->cff

---
 PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
index 9caa8f29e315a..e53c5e072e8a3 100644
--- a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
+++ b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
@@ -7,7 +7,7 @@
 ################################################################################
 
 from RecoEgamma.EgammaTools.lowPtElectronModifier_cfi import lowPtElectronModifier
-from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cfi import lowPtRegressionModifier
+from RecoEgamma.EgammaElectronProducers.lowPtGsfElectrons_cff import lowPtRegressionModifier
 modifiedLowPtElectrons = cms.EDProducer(
     "ModifiedElectronProducer",
     src = cms.InputTag("slimmedLowPtElectrons"),

From f74c46f6d6d5f46be156b7589efa49ca25b134f0 Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 7 Jun 2021 18:14:34 +0200
Subject: [PATCH 5/8] add low-pT ele tables to nano for (run2_nanoAOD_106Xv2 |
 run2_nanoAOD_devel)

---
 PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
index e53c5e072e8a3..8749a02b5879c 100644
--- a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
+++ b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
@@ -194,12 +194,7 @@
 # Modifiers
 ################################################################################
 
-_modifiers = ( run2_miniAOD_80XLegacy |
-               run2_nanoAOD_94XMiniAODv1 |
-               run2_nanoAOD_94XMiniAODv2 |
-               run2_nanoAOD_94X2016 |
-               run2_nanoAOD_102Xv1 |
-               run2_nanoAOD_106Xv1 )
-(_modifiers).toReplaceWith(lowPtElectronSequence,cms.Sequence())
-(_modifiers).toReplaceWith(lowPtElectronTables,cms.Sequence())
-(_modifiers).toReplaceWith(lowPtElectronMC,cms.Sequence())
+_modifiers = ~(run2_nanoAOD_106Xv2 | run2_nanoAOD_devel)
+_modifiers.toReplaceWith(lowPtElectronSequence,cms.Sequence())
+_modifiers.toReplaceWith(lowPtElectronTables,cms.Sequence())
+_modifiers.toReplaceWith(lowPtElectronMC,cms.Sequence())

From 7d724471012dddc4977e9c6b9cbd8bc3df414c9b Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 7 Jun 2021 18:17:26 +0200
Subject: [PATCH 6/8] add LeptonUpdater, computeMiniIso=True;
 isoForLowPtEle.relative=True; DQM: fix miniPFRelIso_chg/all, remove s/ip3d
 histos

---
 .../NanoAOD/python/lowPtElectrons_cff.py      | 27 +++++++++++++------
 PhysicsTools/NanoAOD/python/nanoDQM_cfi.py    |  6 ++---
 2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
index 8749a02b5879c..526a615064311 100644
--- a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
+++ b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
@@ -16,10 +16,22 @@
     )
 )
 
+import PhysicsTools.PatAlgos.producersLayer1.electronProducer_cfi
+updatedLowPtElectrons = cms.EDProducer(
+    "PATElectronUpdater",
+    src = cms.InputTag("modifiedLowPtElectrons"),
+    vertices = cms.InputTag("offlineSlimmedPrimaryVertices"),
+    computeMiniIso = cms.bool(True),
+    fixDxySign = cms.bool(False),
+    pfCandsForMiniIso = cms.InputTag(""),
+    miniIsoParamsB = cms.vdouble(),
+    miniIsoParamsE = cms.vdouble(),
+)
+
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID
 lowPtPATElectronID = lowPtGsfElectronID.clone(
     usePAT = True,
-    electrons = "modifiedLowPtElectrons",
+    electrons = "updatedLowPtElectrons",
     unbiased = "",
     ModelWeights = [
         'RecoEgamma/ElectronIdentification/data/LowPtElectrons/LowPtElectrons_ID_2020Nov28.root',
@@ -30,8 +42,8 @@
 
 isoForLowPtEle = cms.EDProducer(
     "EleIsoValueMapProducer",
-    src = cms.InputTag("modifiedLowPtElectrons"),
-    relative = cms.bool(False),
+    src = cms.InputTag("updatedLowPtElectrons"),
+    relative = cms.bool(True),
     rho_MiniIso = cms.InputTag("fixedGridRhoFastjetAll"),
     rho_PFIso = cms.InputTag("fixedGridRhoFastjetAll"),
     EAFile_MiniIso = cms.FileInPath("RecoEgamma/ElectronIdentification/data/Fall17/effAreaElectrons_cone03_pfNeuHadronsAndPhotons_94X.txt"),
@@ -40,7 +52,7 @@
 
 updatedLowPtElectronsWithUserData = cms.EDProducer(
     "PATElectronUserDataEmbedder",
-    src = cms.InputTag("modifiedLowPtElectrons"),
+    src = cms.InputTag("updatedLowPtElectrons"),
     userFloats = cms.PSet(
         ID = cms.InputTag("lowPtPATElectronID"),
         miniIsoChg = cms.InputTag("isoForLowPtEle:miniIsoChg"),
@@ -78,9 +90,9 @@
         unbiased = Var("electronID('unbiased')",float,doc="ElectronSeed, pT- and dxy- agnostic BDT (raw) score"),
         ptbiased = Var("electronID('ptbiased')",float,doc="ElectronSeed, pT- and dxy- dependent BDT (raw) score"),
         # Isolation
-        miniPFRelIso_chg = Var("userFloat('miniIsoChg')/pt",float,
+        miniPFRelIso_chg = Var("userFloat('miniIsoChg')",float,
                                doc="mini PF relative isolation, charged component"),
-        miniPFRelIso_all = Var("userFloat('miniIsoAll')/pt",float,
+        miniPFRelIso_all = Var("userFloat('miniIsoAll')",float,
                                doc="mini PF relative isolation, total (with scaled rho*EA PU corrections)"),
         # Conversions
         convVeto = Var("passConversionVeto()",bool,doc="pass conversion veto"),
@@ -102,8 +114,6 @@
         dxyErr = Var("edB('PV2D')",float,doc="dxy uncertainty, in cm",precision=6),
         dz = Var("dB('PVDZ')",float,doc="dz (with sign) wrt first PV, in cm",precision=10),
         dzErr = Var("abs(edB('PVDZ'))",float,doc="dz uncertainty, in cm",precision=6),
-        ip3d = Var("abs(dB('PV3D'))",float,doc="3D impact parameter wrt first PV, in cm",precision=10),
-        sip3d = Var("abs(dB('PV3D')/edB('PV3D'))",float,doc="3D impact parameter significance wrt first PV, in cm",precision=10),
         # Cross-referencing
         #jetIdx
         #photonIdx
@@ -177,6 +187,7 @@
 ################################################################################
 
 lowPtElectronSequence = cms.Sequence(modifiedLowPtElectrons
+                                     +updatedLowPtElectrons
                                      +lowPtPATElectronID
                                      +isoForLowPtEle
                                      +updatedLowPtElectronsWithUserData
diff --git a/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py b/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
index d1ec4b5f9f471..fe65a86e96786 100644
--- a/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
+++ b/PhysicsTools/NanoAOD/python/nanoDQM_cfi.py
@@ -133,8 +133,8 @@
                 Plot1D('unbiased', 'unbiased', 40, -10., 10., 'ElectronSeed, pT- and dxy- agnostic BDT (raw) score'),
                 Plot1D('ptbiased', 'ptbiased', 40, -10., 10., 'ElectronSeed, pT- and dxy- dependent BDT (raw) score'),
                 # Isolation
-                Plot1D('miniPFRelIso_chg', 'miniPFRelIso_chg', 20, 0, 2, 'mini PF relative isolation, charged component'),
-                Plot1D('miniPFRelIso_all', 'miniPFRelIso_all', 20, 0, 2, 'mini PF relative isolation, total (with scaled rho*EA PU corrections)'),
+                Plot1D('miniPFRelIso_chg', 'miniPFRelIso_chg', 20, 0., 1., 'mini PF relative isolation, charged component'),
+                Plot1D('miniPFRelIso_all', 'miniPFRelIso_all', 20, 0., 1., 'mini PF relative isolation, total (with scaled rho*EA PU corrections)'),
                 # Conversions
                 Plot1D('convVeto', 'convVeto', 2, -0.5, 1.5, 'pass conversion veto'),
                 Plot1D('convWP', 'convWP', 8, -0.5, 7.5, 'conversion flag bit map: 1=Veto, 2=Loose, 3=Tight'),
@@ -154,8 +154,6 @@
                 Plot1D('dz', 'dz', 20, -0.3, 0.3, 'dz (with sign) wrt first PV, in cm'),
                 Plot1D('dxyErr', 'dxyErr', 20, 0., 0.2, 'dxy uncertainty, in cm'),
                 Plot1D('dzErr', 'dzErr', 20, 0., 0.2, 'dz uncertainty, in cm'),
-                Plot1D('ip3d', 'ip3d', 20, 0., 0.2, '3D impact parameter wrt first PV, in cm'),
-                Plot1D('sip3d', 'sip3d', 20, 0., 20., '3D impact parameter significance wrt first PV, in cm'),
             ),
         ),
 

From bda1a88ab731d7e90eafa0a549a370163ffc14a6 Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 7 Jun 2021 22:05:34 +0200
Subject: [PATCH 7/8] updatedLowPtElectrons: add missing params

---
 PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
index 526a615064311..4f3f64d5f851e 100644
--- a/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
+++ b/PhysicsTools/NanoAOD/python/lowPtElectrons_cff.py
@@ -23,9 +23,9 @@
     vertices = cms.InputTag("offlineSlimmedPrimaryVertices"),
     computeMiniIso = cms.bool(True),
     fixDxySign = cms.bool(False),
-    pfCandsForMiniIso = cms.InputTag(""),
-    miniIsoParamsB = cms.vdouble(),
-    miniIsoParamsE = cms.vdouble(),
+    pfCandsForMiniIso = cms.InputTag("packedPFCandidates"),
+    miniIsoParamsB = PhysicsTools.PatAlgos.producersLayer1.electronProducer_cfi.patElectrons.miniIsoParamsB,
+    miniIsoParamsE = PhysicsTools.PatAlgos.producersLayer1.electronProducer_cfi.patElectrons.miniIsoParamsE,
 )
 
 from RecoEgamma.EgammaElectronProducers.lowPtGsfElectronID_cff import lowPtGsfElectronID

From ce41e4b7377ef799c4357378bcd62fbde855acc2 Mon Sep 17 00:00:00 2001
From: bainbrid <bainbrid@users.noreply.github.com>
Date: Mon, 7 Jun 2021 22:06:16 +0200
Subject: [PATCH 8/8] slava review

---
 DataFormats/EgammaCandidates/interface/GsfElectron.h         | 4 ++--
 .../PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py    | 5 ++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/DataFormats/EgammaCandidates/interface/GsfElectron.h b/DataFormats/EgammaCandidates/interface/GsfElectron.h
index 396b0249ee802..7c9d7423828ec 100644
--- a/DataFormats/EgammaCandidates/interface/GsfElectron.h
+++ b/DataFormats/EgammaCandidates/interface/GsfElectron.h
@@ -862,9 +862,9 @@ class GsfElectron : public RecoCandidate
     //bool isMomentumCorrected() const { return corrections_.isMomentumCorrected ; }
     float caloEnergy() const { return correctedEcalEnergy() ; }
     bool isEnergyScaleCorrected() const { return isEcalEnergyCorrected() ; }
-    void correctEcalEnergy(float newEnergy, float newEnergyError, bool rescaleDependentValues = true)
+    void correctEcalEnergy(float newEnergy, float newEnergyError, bool corrEovP = true)
      {
-      setCorrectedEcalEnergy(newEnergy,rescaleDependentValues) ;
+      setCorrectedEcalEnergy(newEnergy, corrEovP) ;
       setEcalEnergyError(newEnergyError) ;
      }
     void correctMomentum( const LorentzVector & p4, float trackMomentumError, float p4Error )
diff --git a/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py b/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py
index 0bcd2fb7a5000..ece1ecce9e275 100644
--- a/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py
+++ b/PhysicsTools/PatAlgos/python/slimming/slimmedLowPtElectrons_cfi.py
@@ -46,6 +46,5 @@
 from RecoEgamma.EgammaTools.lowPtElectronModifier_cfi import lowPtElectronModifier
 from Configuration.Eras.Modifier_run2_miniAOD_devel_cff import run2_miniAOD_devel
 from Configuration.Eras.Modifier_bParking_cff import bParking
-_slimmedLowPtElectrons = slimmedLowPtElectrons.clone()
-_slimmedLowPtElectrons.modifierConfig.modifications += [lowPtElectronModifier]
-(~bParking & run2_miniAOD_devel).toReplaceWith(slimmedLowPtElectrons,_slimmedLowPtElectrons)
+(~bParking & run2_miniAOD_devel).toModify(slimmedLowPtElectrons.modifierConfig.modifications,
+                                          func = lambda m: m.append(lowPtElectronModifier))