From f1c44d5e402ec8aea36248cf4381f20b6c034536 Mon Sep 17 00:00:00 2001
From: Peter Alan Steinberg <peter.steinberg@bnl.gov>
Date: Fri, 25 Nov 2016 22:31:58 +0100
Subject: [PATCH] 'added pPb2016 mode to analyze delayed samples.  PreSampleAmp
 added to output.' (ZdcAnalysis-00-00-17)

---
 .../ZDC/ZdcAnalysis/Root/ZDCDataAnalyzer.cxx  | 131 +++-
 .../ZDC/ZdcAnalysis/Root/ZDCFitWrapper.cxx    |  69 ++-
 .../ZDC/ZdcAnalysis/Root/ZDCPulseAnalyzer.cxx | 575 +++++++++++++++++-
 .../ZDC/ZdcAnalysis/Root/ZdcAnalysisTool.cxx  | 188 +++++-
 .../ZdcAnalysis/ZdcAnalysis/ZDCDataAnalyzer.h |  10 +
 .../ZdcAnalysis/ZdcAnalysis/ZDCFitWrapper.h   |  82 ++-
 .../ZdcAnalysis/ZDCPulseAnalyzer.h            | 110 +++-
 .../ZdcAnalysis/ZdcAnalysis/ZdcAnalysisTool.h |   3 +
 .../ZDC/ZdcAnalysis/cmt/Makefile.RootCore     |   2 +-
 9 files changed, 1083 insertions(+), 87 deletions(-)

diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCDataAnalyzer.cxx b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCDataAnalyzer.cxx
index 8f989dcfb888..4bf56bd14c9b 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCDataAnalyzer.cxx
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCDataAnalyzer.cxx
@@ -29,6 +29,9 @@ ZDCDataAnalyzer::ZDCDataAnalyzer(int nSample, float deltaTSample, size_t preSamp
   _haveECalib(false),
   _currentLB(-1)
 {
+  _moduleDisabled[0] = {false, false, false, false};
+  _moduleDisabled[1] = {false, false, false, false};
+
   _moduleAnalyzers[0] = {0, 0, 0, 0};
   _moduleAnalyzers[1] = {0, 0, 0, 0};
 
@@ -85,6 +88,40 @@ ZDCDataAnalyzer::~ZDCDataAnalyzer()
   }
 }
 
+bool ZDCDataAnalyzer::DisableModule(size_t side, size_t module)
+{
+  if (side < 2 && module < 4) {
+    //
+    // Can't disable in the middle of analysis
+    //
+    if (_dataLoaded[side][module]) return false;
+    else {
+      _moduleDisabled[side][module] = true;
+      return true;
+    }
+  }
+  else {
+    return false;
+  }
+}
+
+void ZDCDataAnalyzer::EnableDelayed(float deltaT, const ZDCModuleFloatArray& undelayedDelayedPedestalDiff)
+{
+  for (size_t side : {0, 1}) {
+    for (size_t module : {0, 1, 2, 3}) {
+      _moduleAnalyzers[side][module]->EnableDelayed(deltaT, undelayedDelayedPedestalDiff[side][module]);
+    }
+  }
+}
+
+void ZDCDataAnalyzer::SetPeak2ndDerivMinTolerances(size_t tolerance) {
+  for (size_t side : {0, 1}) {
+    for (size_t module : {0, 1, 2, 3}) {
+      _moduleAnalyzers[side][module]->SetPeak2ndDerivMinTolerance(tolerance);
+    }
+  }
+}
+
 void ZDCDataAnalyzer::SetTauT0Values(const ZDCModuleBoolArray& fixTau1, const ZDCModuleBoolArray& fixTau2, 
 				     const ZDCModuleFloatArray& tau1, const ZDCModuleFloatArray& tau2, 
 				     const ZDCModuleFloatArray& t0HG, const ZDCModuleFloatArray& t0LG)
@@ -231,8 +268,18 @@ void ZDCDataAnalyzer::StartEvent(int lumiBlock)
 
 void ZDCDataAnalyzer::LoadAndAnalyzeData(size_t side, size_t module, const std::vector<float> HGSamples, const std::vector<float> LGSamples)
 {
+  // We immediately return if this module is disabled
+  //
+  if (_moduleDisabled[side][module]) {
+    if (_debugLevel > 2) {
+      std::cout << "Skipping analysis of disabled mofule for event index " << _eventCount << ", side, module = " << side << ", " << module << std::endl;
+    }
+
+    return;
+  }
+
   if (_debugLevel > 1) {
-    std::cout << "Loading data for event index " << _eventCount << ", side, module = " << side << ", " << module << std::endl;
+    std::cout << "/n Loading data for event index " << _eventCount << ", side, module = " << side << ", " << module << std::endl;
     
     if (_debugLevel > 2) {
       std::cout << " Number of HG and LG samples = " << HGSamples.size() << ", " << LGSamples.size() << std::endl;
@@ -291,18 +338,96 @@ void ZDCDataAnalyzer::LoadAndAnalyzeData(size_t side, size_t module, const std::
   _moduleStatus[side][module] = pulseAna_p->GetStatusMask();
 }
 
+void ZDCDataAnalyzer::LoadAndAnalyzeData(size_t side, size_t module, const std::vector<float> HGSamples, const std::vector<float> LGSamples,
+					 const std::vector<float> HGSamplesDelayed, const std::vector<float> LGSamplesDelayed)
+{
+  // We immediately return if this module is disabled
+  //
+  if (_moduleDisabled[side][module]) {
+    if (_debugLevel > 2) {
+      std::cout << "Skipping analysis of disabled mofule for event index " << _eventCount << ", side, module = " << side << ", " << module << std::endl;
+    }
+
+    return;
+  }
+
+  if (_debugLevel > 1) {
+    std::cout << "Loading undelayed and delayed data for event index " << _eventCount << ", side, module = " << side << ", " << module << std::endl;
+    
+    if (_debugLevel > 2) {
+      std::cout << " Number of HG and LG samples = " << HGSamples.size() << ", " << LGSamples.size() << std::endl;
+      if (_debugLevel > 3) {
+	for (size_t sample = 0; sample < HGSamples.size(); sample++) {
+	  std::cout << "HGSample[" << sample << "] = " << HGSamples[sample] << std::endl;
+	}
+
+	for (size_t sample = 0; sample < HGSamples.size(); sample++) {
+	  std::cout << "HGSampleDelayed[" << sample << "] = " << HGSamplesDelayed[sample] << std::endl;
+	}
+      }
+
+    }
+  }
+
+  ZDCPulseAnalyzer* pulseAna_p = _moduleAnalyzers[side][module];
+
+  bool result = pulseAna_p->LoadAndAnalyzeData(HGSamples, LGSamples, HGSamplesDelayed, LGSamplesDelayed);
+  _dataLoaded[side][module] = true;
+
+  if (result) {
+    if (!pulseAna_p->BadT0() && !pulseAna_p->BadChisq()) {
+      int moduleMaskBit = 4*side + module;
+      _moduleMask |= 1<< moduleMaskBit;
+      
+      float amplitude = pulseAna_p->GetAmplitude();
+      float ampError = pulseAna_p->GetAmpError();
+
+      _calibAmplitude[side][module] = amplitude*_currentECalibCoeff[side][module];
+
+      float calibAmpError = ampError * _currentECalibCoeff[side][module];
+
+      float timeCalib = pulseAna_p->GetT0Corr();
+      if (pulseAna_p->UseLowGain()) timeCalib -= _currentT0OffsetsLG[side][module];
+      else timeCalib -= _currentT0OffsetsHG[side][module];
+
+      _calibTime[side][module] = timeCalib;
+
+      _moduleSum[side] += amplitude;
+      _moduleSumErrSq[side] += ampError*ampError;
+
+      _moduleSumPreSample[side] += pulseAna_p->GetPreSampleAmp();
+
+      _calibModuleSum[side] += _calibAmplitude[side][module];
+      _calibModuleSumErrSq[side] += calibAmpError*calibAmpError;
+
+      _averageTime[side] += _calibTime[side][module]*_calibAmplitude[side][module];
+    }
+  }
+  else {
+    if (pulseAna_p->Failed()) {
+      if (_debugLevel >= 0) {
+	std::cout << "ZDCPulseAnalyzer::LoadData() returned fail for event " << _eventCount << ", side, module = " << side << ", " << module << std::endl;
+      }
+
+      _fail[side] = true;
+    }
+  }
+
+  _moduleStatus[side][module] = pulseAna_p->GetStatusMask();
+}
+
 bool ZDCDataAnalyzer::FinishEvent()
 {
   // First make sure that all data is loaded
   //
   for (size_t side : {0, 1}) {
     for (size_t module : {0, 1, 2, 3}) {
-      if (!_dataLoaded[side][module]) return false;
+      if (!_dataLoaded[side][module] && !_moduleDisabled[side][module]) return false;
     }
 
     // Divide the average times by the calibrated module sums  
     //
-    if (_calibModuleSum[side] > 0) {
+    if (_calibModuleSum[side] > 1e-6) {
       _averageTime[side] /= _calibModuleSum[side]; 
     }
     else {
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCFitWrapper.cxx b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCFitWrapper.cxx
index 0d146a734436..bce7327df6c6 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCFitWrapper.cxx
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCFitWrapper.cxx
@@ -20,7 +20,7 @@ ZDCFitExpFermiVariableTaus::ZDCFitExpFermiVariableTaus(std::string tag, float tm
   theTF1->SetParLimits(1, tmin, tmax);
   theTF1->SetParLimits(2, 2.5, 6);
   theTF1->SetParLimits(3, 10, 60);
-  theTF1->SetParLimits(4, -0.005, 10);
+  theTF1->SetParLimits(4, -0.05, 0.05);
 
   if (_fixTau1) theTF1->FixParameter(2, _tau1);
   if (_fixTau2) theTF1->FixParameter(3, _tau2);
@@ -47,7 +47,7 @@ ZDCFitExpFermiFixedTaus::ZDCFitExpFermiFixedTaus(std::string tag, float tmin, fl
 
   theTF1->SetParLimits(0, 0, 1024);
   theTF1->SetParLimits(1, tmin, tmax);
-  theTF1->SetParLimits(2, -0.005, 10);
+  theTF1->SetParLimits(2, -0.05, 0.05);
 
   theTF1->SetParName(0, "Amp");
   theTF1->SetParName(1, "T0");
@@ -103,14 +103,13 @@ ZDCFitExpFermiPrePulse::ZDCFitExpFermiPrePulse(std::string tag, float tmin, floa
   theTF1->SetParLimits(1, tmin, tmax);
   theTF1->SetParLimits(2, 0, 1024);
   theTF1->SetParLimits(3, -20, 10);
-  theTF1->SetParLimits(4, -0.005, 10);
+  theTF1->SetParLimits(4, -0.05, 0.05);
 
   theTF1->SetParName(0, "Amp");
   theTF1->SetParName(1, "T0");
   theTF1->SetParName(2, "Amp_{pre}");
   theTF1->SetParName(3, "T0_{pre}");
   theTF1->SetParName(4, "s_{b}");
-
 }
 
 void ZDCFitExpFermiPrePulse::Initialize(float initialAmp, float initialT0)
@@ -122,6 +121,68 @@ void ZDCFitExpFermiPrePulse::Initialize(float initialAmp, float initialT0)
   GetWrapperTF1()->SetParameter(4, 0);
 }
 
+ZDCFitExpFermiPulseSequence::ZDCFitExpFermiPulseSequence(std::string tag, float tmin, float tmax, float nominalT0, float deltaT,  float tau1, float tau2) :
+  ZDCFitWrapper(0), _tau1(tau1), _tau2(tau2)
+{
+  // Create the reference function that we use to evaluate ExpFermiFit more efficiently
+  //
+  std::string funcNameRefFunc = "ExpFermiPulseSequenceRefFunc" + tag;
+  
+  _expFermiFunc = new TF1(funcNameRefFunc.c_str(), ZDCFermiExpFit, -50, 100, 5);
+
+  _expFermiFunc->SetParameter(0, 1);
+  _expFermiFunc->SetParameter(1, 0);
+  _expFermiFunc->SetParameter(2, _tau1);
+  _expFermiFunc->SetParameter(3, _tau2);
+  _expFermiFunc->SetParameter(4, 0);
+
+  _norm = 1./_expFermiFunc->GetMaximum();
+  _timeCorr = 0.5*_tau1*std::log(_tau2/_tau1 - 1.0);
+
+  // Calculate how many pulses we have to analyze
+  //
+  size_t numPrePulse = std::ceil((nominalT0 - tmin)/deltaT) + 1;
+  for (size_t ipre = 0; ipre <  numPrePulse; ipre++) {
+    _pulseDeltaT.push_back(((float) ipre) * -deltaT);
+  }
+
+  size_t numPostPulse = std::floor((tmax - nominalT0)/deltaT);
+  for (size_t ipost = 0; ipost <  numPrePulse; ipost++) {
+    _pulseDeltaT.push_back(((float) ipost) * deltaT);
+  }
+
+  _numPulses = 1 + numPrePulse + numPostPulse;
+
+  // Now set up the actual TF1
+  //
+  
+  _fitFunc = new TF1(("ExpFermiPulseSequence" + tag).c_str(), this, tmin, tmax, _numPulses + 1),
+
+  _fitFunc->SetParName(0, "Amp");
+  _fitFunc->SetParName(1, "T0");
+
+  _fitFunc->SetParLimits(0, 0, 1024);
+  _fitFunc->SetParLimits(1, nominalT0 - deltaT/2, nominalT0 + deltaT/2);
+
+  for (size_t ipulse = 0; ipulse < _numPulses - 1; ipulse++) {
+    std::string name = "Amp" + std::to_string(ipulse + 1);
+    _fitFunc->SetParName(ipulse + 2, name.c_str());
+    _fitFunc->SetParLimits(ipulse + 2, 0, 1024);
+  }
+}
+
+void ZDCFitExpFermiPulseSequence::Initialize(float initialAmp, float initialT0)
+{
+  _fitFunc->SetParameter(0, initialAmp);
+  _fitFunc->SetParameter(1, initialT0);
+
+  for (size_t ipulse = 0; ipulse < _numPulses - 1; ipulse++) {
+    _fitFunc->SetParameter(ipulse + 2, 0);
+  }
+}
+
+
+
 double ZDCFermiExpFit(double* xvec, double* pvec)
 {
   static const float offsetScale = 0;
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCPulseAnalyzer.cxx b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCPulseAnalyzer.cxx
index 141c2859f98a..e731d3b73916 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCPulseAnalyzer.cxx
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZDCPulseAnalyzer.cxx
@@ -9,9 +9,71 @@
 
 #include "TFitResult.h"
 #include "TFitResultPtr.h"
+#include "TVirtualFitter.h"
+#include "TFitter.h"
+#include "TList.h"
+#include "TMinuit.h"
+
+
+extern int gErrorIgnoreLevel;
 
 bool ZDCPulseAnalyzer::_quietFits = true;
 std::string ZDCPulseAnalyzer::_fitOptions = "";
+TH1* ZDCPulseAnalyzer::_undelayedFitHist = 0;
+TH1* ZDCPulseAnalyzer::_delayedFitHist = 0;
+TF1* ZDCPulseAnalyzer::_combinedFitFunc = 0;
+
+void ZDCPulseAnalyzer::CombinedPulsesFCN(int& numParam, double*, double& f, double* par, int flag)
+{
+  //  The first parameter is a correction factor to account for decrease in beam intensity between x 
+  //    and y scan. It is applied here and not passed to the actual fit function
+  //
+
+  if (flag >=1 && flag <= 3) {
+    f = 0;
+    return;
+  }
+
+  double chiSquare = 0;
+
+  float delayBaselineAdjust = par[0];
+
+  size_t nSamples = _undelayedFitHist->GetNbinsX();
+  if (_delayedFitHist->GetNbinsX() != nSamples) throw;
+
+  // undelayed 
+  //
+  for (size_t isample = 0; isample < nSamples; isample++) {
+    double histValue = _undelayedFitHist->GetBinContent(isample + 1);
+    double histError = std::max(_undelayedFitHist->GetBinError(isample + 1), 1.0);
+    double t = _undelayedFitHist->GetBinCenter(isample + 1);
+    
+    double funcVal = _combinedFitFunc->EvalPar(&t, &par[1]);
+
+    //    std::cout << "Calculating chis^2, undelayed sample " << isample << "t = " << t << ", data = " << histValue << ", func = " << funcVal << std::endl;
+    
+    double pull = (histValue - funcVal)/histError;
+    chiSquare += pull*pull;
+  }
+
+  // delayed 
+  //
+  for (size_t isample = 0; isample < nSamples; isample++) {
+    double histValue = _delayedFitHist->GetBinContent(isample + 1);
+    double histError = std::max(_delayedFitHist->GetBinError(isample + 1), 1.0);
+    double t = _delayedFitHist->GetBinCenter(isample + 1);
+    
+    double funcVal = _combinedFitFunc->EvalPar(&t, &par[1]) + delayBaselineAdjust;
+    double pull = (histValue - funcVal)/histError;
+
+    //    std::cout << "Calculating chis^2, delayed sample " << isample << "t = " << t << ", data = " << histValue << ", func = " << funcVal << std::endl;
+
+    chiSquare += pull*pull;
+  }
+
+  f = chiSquare;
+}
+
 
 ZDCPulseAnalyzer::ZDCPulseAnalyzer(std::string tag, int Nsample, float deltaTSample, size_t preSampleIdx, int pedestal, 
 				   float gainHG, std::string fitFunction, int peak2ndDerivMinSample, 
@@ -19,9 +81,11 @@ ZDCPulseAnalyzer::ZDCPulseAnalyzer(std::string tag, int Nsample, float deltaTSam
   _tag(tag), _Nsample(Nsample), _deltaTSample(deltaTSample), _preSampleIdx(preSampleIdx), 
   _pedestal(pedestal), _gainHG(gainHG), _forceLG(false), _fitFunction(fitFunction),
   _peak2ndDerivMinSample(peak2ndDerivMinSample), 
+  _peak2ndDerivMinTolerance(1),
   _peak2ndDerivMinThreshHG(peak2ndDerivMinThreshHG),
   _peak2ndDerivMinThreshLG(peak2ndDerivMinThreshLG),
   _haveTimingCorrections(false), _haveNonlinCorr(false), _initializedFits(false),
+  _useDelayed(false), _delayedHist(0), _prePulseCombinedFitter(0), _defaultCombinedFitter(0),
   _ADCSamplesHGSub(Nsample, 0), _ADCSamplesLGSub(Nsample, 0), 
   _ADCSSampSigHG(Nsample, 1), _ADCSSampSigLG(Nsample, 1), // By default the ADC uncertainties are set to one
   _samplesSub(Nsample, 0),
@@ -47,6 +111,17 @@ ZDCPulseAnalyzer::~ZDCPulseAnalyzer()
   if (!_prePulseFitWrapper) delete _prePulseFitWrapper;  
 }
 
+void ZDCPulseAnalyzer::EnableDelayed(float deltaT, float pedestalShift)
+{
+  _useDelayed = true;
+  _delayedDeltaT = deltaT;
+  _delayedPedestalDiff = pedestalShift;
+
+  _delayedHist = new TH1F((std::string(_fitHist->GetName()) + "delayed").c_str(), "", _Nsample, _tmin + _delayedDeltaT, _tmax + _delayedDeltaT);
+
+  _ADCSamplesHGSub.assign(2*_Nsample, 0);
+}
+
 void ZDCPulseAnalyzer::SetDefaults()
 {
   _nominalTau1 = 4;
@@ -97,10 +172,12 @@ void ZDCPulseAnalyzer::Reset()
   _badChisq = false;
 
   _fitAmplitude = 0;
-  _fitT0 = -100;
+  _fitTime = -100;
+  _fitTimeSub = -100;
+  _fitTimeCorr = -100;
+
   _fitChisq = 0;
   
-  _fitT0Corr = -100;
   _amplitude = 0;
   _ampError = 0;
   _preSampleAmp = 0;
@@ -109,6 +186,12 @@ void ZDCPulseAnalyzer::Reset()
   _samplesSub.clear();
   _samplesDeriv.clear();
   _samplesDeriv2nd.clear();
+
+  int sampleVecSize = _Nsample;
+  if (_useDelayed) sampleVecSize *= 2;
+
+  _ADCSamplesHGSub.assign(sampleVecSize, 0);
+  _ADCSamplesLGSub.assign(sampleVecSize, 0);
 }
 
 void ZDCPulseAnalyzer::SetTauT0Values(bool fixTau1, bool fixTau2, float tau1, float tau2, float t0HG, float t0LG)
@@ -172,6 +255,9 @@ void ZDCPulseAnalyzer::SetupFitFunctions()
 
 bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::vector<float>  ADCSamplesLG) 
 {
+  //  std::cout << "Use delayed = " << _useDelayed << std::endl;
+
+  if (_useDelayed) throw;
   if (!_initializedFits) SetupFitFunctions();
 
   // Clear any transient data 
@@ -185,6 +271,8 @@ bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::
     return false;
   }
 
+
+
   // Now do pedestal subtraction and check for overflows
   //
   for (size_t isample = 0; isample < _Nsample; isample++) {
@@ -219,6 +307,162 @@ bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::
     _ADCSamplesLGSub[isample] = ADCLG - _pedestal;
   }
 
+  //  std::cout << "Loaded data" << std::endl;
+
+  //  if (_fail) return false;
+
+  // Now we actually do the analysis using the high or low gain samples as determined
+  //  on the basis of the above analysis
+  //
+  _useLowGain = _HGUnderflow || _HGOverflow || _forceLG;
+
+  if (_useLowGain) {
+    bool result = AnalyzeData(_Nsample, _preSampleIdx, _ADCSamplesLGSub, _ADCSSampSigLG, _LGT0CorrParams, _chisqDivAmpCutLG, 
+			      _T0CutLowLG, _T0CutHighLG, _peak2ndDerivMinThreshLG);
+    if (result) {
+      // 
+      // Multiply amplitude by gain factor
+      //
+      _amplitude = _fitAmplitude * _gainHG;
+      _ampError = _fitAmpError * _gainHG;
+      _preSampleAmp = _preSample * _gainHG;
+    }
+
+    return result;
+  }
+  else {
+    bool result = AnalyzeData(_Nsample, _preSampleIdx, _ADCSamplesHGSub, _ADCSSampSigHG, _HGT0CorrParams, _chisqDivAmpCutHG, 
+			      _T0CutLowHG, _T0CutHighHG, _peak2ndDerivMinThreshHG);
+    if (result) {
+      _preSampleAmp = _preSample;
+      _amplitude = _fitAmplitude;
+      _ampError = _fitAmpError;
+
+      //  If we have a non-linear correction, apply it here
+      //
+      if (_haveNonlinCorr) {
+	float ampCorrFact = _amplitude/1000. - 0.5;
+	float corrFactor = 1./(1. + _nonLinCorrParams[0]*ampCorrFact + _nonLinCorrParams[1]*ampCorrFact*ampCorrFact);
+
+	_amplitude *= corrFactor;
+	_ampError *= corrFactor;
+      }
+    }
+
+    return result;
+  }
+}
+
+bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::vector<float>  ADCSamplesLG,
+					  std::vector<float> ADCSamplesHGDelayed, std::vector<float> ADCSamplesLGDelayed)
+{
+  if (!_useDelayed) throw;
+  if (!_initializedFits) SetupFitFunctions();
+
+  // Clear any transient data 
+  //
+  Reset();
+
+  // Make sure we have the right number of samples. Should never fail. necessry?
+  //
+  if (ADCSamplesHG.size() != _Nsample || ADCSamplesLG.size() != _Nsample ||
+      ADCSamplesHGDelayed.size() != _Nsample || ADCSamplesLGDelayed.size() != _Nsample) {
+    _fail = true;
+    return false;
+  }
+
+
+  // Now do pedestal subtraction and check for overflows
+  //
+  for (size_t isample = 0; isample < _Nsample; isample++) {
+    float ADCHG = ADCSamplesHG[isample];
+    float ADCLG = ADCSamplesLG[isample];
+
+    float ADCHGDelay = ADCSamplesHGDelayed[isample];
+    float ADCLGDelay = ADCSamplesLGDelayed[isample];
+    
+    if (ADCHG > _HGOverflowADC) {
+      _HGOverflow = true;
+      
+      if (isample == _preSampleIdx) _PSHGOverUnderflow = true;
+    }
+    else if (ADCHG < _HGUnderflowADC) {
+      _HGUnderflow = true;
+      
+      if (isample == _preSampleIdx) _PSHGOverUnderflow  = true;
+    }
+    
+    if (ADCHGDelay > _HGOverflowADC) {
+      _HGOverflow = true;
+    }
+    else if (ADCHGDelay < _HGUnderflowADC) {
+      _HGUnderflow = true;
+    }
+
+    if (ADCLG > _LGOverflowADC) {
+      _LGOverflow = true;
+      _fail = true;
+      _amplitude = 1024 * _gainHG; // Give a value here even though we know it's wrong because
+                                   //   the user may not check the return value and we know that 
+                                   //   amplitude is bigger than this
+    }
+    else if (ADCLG == 0) {
+      _LGUnderflow = true;
+      _fail = true;
+    }
+    
+    if (ADCLGDelay > _LGOverflowADC) {
+      _LGOverflow = true;
+      _fail = true;
+      _amplitude = 1024 * _gainHG; // Give a value here even though we know it's wrong because
+                                   //   the user may not check the return value and we know that 
+                                   //   amplitude is bigger than this
+    }
+    else if (ADCLGDelay == 0) {
+      _LGUnderflow = true;
+      _fail = true;
+    }
+
+    _ADCSamplesHGSub[isample*2] = ADCHG - _pedestal;
+    _ADCSamplesLGSub[isample*2] = ADCLG - _pedestal;
+
+    _ADCSamplesHGSub[isample*2 + 1] = ADCHGDelay - _pedestal - _delayedPedestalDiff;
+    _ADCSamplesLGSub[isample*2 + 1] = ADCLGDelay - _pedestal - _delayedPedestalDiff;
+  }
+
+  // if (!_PSHGOverUnderflow) {
+
+  //   std::cout << "_ADCSamplesHGSub = ";
+  //   for (size_t sample = 0; sample < _ADCSamplesHGSub.size(); sample++) {
+  //     std::cout << ", [" << sample << "] = " << _ADCSamplesHGSub[sample];
+  //   }
+
+  //   //  Attempt to address up front cases where we have significant offsets between the delayed and undelayed
+  //   //
+  //   float delta10 = _ADCSamplesHGSub[1] - _ADCSamplesHGSub[0];
+  //   float delta21 = _ADCSamplesHGSub[2] - _ADCSamplesHGSub[1];
+  //   float delta32 = _ADCSamplesHGSub[3] - _ADCSamplesHGSub[2];
+
+  //   std::cout << "testing delayed shift: delta10 = " << delta10 << ", delta21 = " << delta21 << ", delta32 = " << delta32 << std::endl;
+
+  //   if (std::abs(delta10 + delta21) <= 2 && std::abs(delta10 - delta32) <= 2) {
+  //     //  Very likely we have an offset between the delayed and undelayed
+  //     //
+      
+  //     std::cout << "correcting HG delayed ssamples down by " << 0.5*(delta10 + delta32) << std::endl;
+
+  //     for (int isample = 0; isample < _Nsample; isample++) {
+  // 	_ADCSamplesHGSub[isample*2 + 1] -= 0.5*(delta10 + delta32);
+  //     }
+
+  //     std::cout << " corrected _ADCSamplesHGSub = ";
+  //     for (size_t sample = 0; sample < _ADCSamplesHGSub.size(); sample++) {
+  // 	std::cout << ", [" << sample << "] = " << _ADCSamplesHGSub[sample];
+  //     }
+
+  //   }
+  // }
+
   //  if (_fail) return false;
 
   // Now we actually do the analysis using the high or low gain samples as determined
@@ -227,7 +471,8 @@ bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::
   _useLowGain = _HGUnderflow || _HGOverflow || _forceLG;
 
   if (_useLowGain) {
-    bool result = AnalyzeData(_ADCSamplesLGSub, _ADCSSampSigLG, _LGT0CorrParams, _chisqDivAmpCutLG, 
+
+    bool result = AnalyzeData(_Nsample*2, _preSampleIdx*2, _ADCSamplesLGSub, _ADCSSampSigLG, _LGT0CorrParams, _chisqDivAmpCutLG, 
 			      _T0CutLowLG, _T0CutHighLG, _peak2ndDerivMinThreshLG);
     if (result) {
       // 
@@ -241,7 +486,7 @@ bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::
     return result;
   }
   else {
-    bool result = AnalyzeData(_ADCSamplesHGSub, _ADCSSampSigHG, _HGT0CorrParams, _chisqDivAmpCutHG, 
+    bool result = AnalyzeData(_Nsample*2, _preSampleIdx*2, _ADCSamplesHGSub, _ADCSSampSigHG, _HGT0CorrParams, _chisqDivAmpCutHG, 
 			      _T0CutLowHG, _T0CutHighHG, _peak2ndDerivMinThreshHG);
     if (result) {
       _preSampleAmp = _preSample;
@@ -263,7 +508,8 @@ bool ZDCPulseAnalyzer::LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::
   }
 }
 
-bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        // The samples used for this event
+bool ZDCPulseAnalyzer::AnalyzeData(size_t nSamples, size_t preSampleIdx, 
+				   const std::vector<float>& samples,        // The samples used for this event
 				   const std::vector<float>& samplesSig,     // The "resolution" on the ADC value
 				   const std::vector<float>& t0CorrParams,   // The parameters used to correct the t0               
 				   float maxChisqDivAmp,                     // The maximum chisq / amplitude ratio
@@ -273,10 +519,44 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
 {
   //  Do the presample subtraction using a lambda function
   //
-  float preSample = samples[_preSampleIdx];
+  float preSample = samples[preSampleIdx];
   _preSample = preSample;
   _samplesSub = samples;
 
+
+  // std::cout << "_samplesSub = ";
+  // for (size_t sample = 0; sample < _samplesSub.size(); sample++) {
+  //   std::cout << ", [" << sample << "] = " << _samplesSub[sample];
+  // }
+  
+  if (_useDelayed) {
+    //  Attempt to address up front cases where we have significant offsets between the delayed and undelayed
+    //
+    // float delta10 = _samplesSub[1] - _samplesSub[0];
+    // float delta21 = _samplesSub[2] - _samplesSub[1];
+    // float delta32 = _samplesSub[3] - _samplesSub[2];
+    
+    // //    std::cout << "testing delayed shift: delta10 = " << delta10 << ", delta21 = " << delta21 << ", delta32 = " << delta32 << std::endl;
+    
+    // if (std::abs(delta10 + delta21) <= 0.1*std::abs(delta10 - delta21)/2 && std::abs(delta10 - delta32) <= 0.1*std::abs(delta10 + delta32)/2) {
+    //   //  Very likely we have an offset between the delayed and undelayed
+    //   //
+      
+      //      std::cout << "correcting HG delayed ssamples down by " << 0.5*(delta10 + delta32) << std::endl;
+
+    
+    double baselineCorr = 0.5*(_samplesSub[1] - _samplesSub[0] + _samplesSub[3] - _samplesSub[4]); 
+      
+    for (int isample = 0; isample < nSamples; isample++) {
+      if (isample%2) _samplesSub[isample] -= baselineCorr;
+    }
+      
+      // std::cout << " corrected _samplesSub = ";
+      // for (size_t sample = 0; sample < _samplesSub.size(); sample++) {
+      // 	std::cout << ", [" << sample << "] = " << _samplesSub[sample];
+      // } 
+  }
+
   std::for_each(_samplesSub.begin(), _samplesSub.end(), [=] (float& adcUnsub) {return adcUnsub -= preSample;} );
 
   // Find maximum and minimum values
@@ -295,14 +575,14 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
 
   // Calculate first and 2nd "derivatives" 
   //
-  std::vector<float> deriv(_Nsample, 0);
-  std::adjacent_difference(samples.begin(), samples.end(), deriv.begin());
+  std::vector<float> deriv(nSamples, 0);
+  std::adjacent_difference(_samplesSub.begin(), _samplesSub.end(), deriv.begin());
 
   // Save the derivatives, dropping the useless first element
   //
   _samplesDeriv.insert(_samplesDeriv.begin(), deriv.begin() + 1, deriv.end());
 
-  std::vector<float> deriv2nd(_Nsample, 0);
+  std::vector<float> deriv2nd(nSamples, 0);
   std::adjacent_difference(_samplesDeriv.begin(), _samplesDeriv.end(), deriv2nd.begin());
 
   // Save the second derivatives, dropping the useless first element
@@ -319,7 +599,7 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
   //  The ket test for presence of a pulse: is the minimum 2nd derivative in the right place and is it 
   //    large enough (sufficiently negative) 
   //
-  if (std::abs(_minDeriv2ndIndex - _peak2ndDerivMinSample) <= 1  && _minDeriv2nd <= peak2ndDerivMinThresh) {
+  if (std::abs(_minDeriv2ndIndex - (int) _peak2ndDerivMinSample) <= _peak2ndDerivMinTolerance  && _minDeriv2nd <= peak2ndDerivMinThresh) {
     _havePulse = true;
   }
   else {
@@ -328,34 +608,55 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
 
   // Now decide whether we have a preceeding pulse or not
   //   
+  // std::cout << "_samplesSDeriv = ";
+  // for (size_t sample = 0; sample < _samplesDeriv.size(); sample++) {
+  //   std::cout << ", [" << sample << "] = " << _samplesDeriv[sample];
+  // }
+
 
   // Have to be careful with the derivative indices, but test up to minDeriv2ndIndex - 1
   //
-  bool haveNegDeriv = false;
+  bool lastNegDeriv = false;
 
   for (int isampl = 0; isampl < _minDeriv2ndIndex; isampl++) {
 
     // If any of the derivatives prior to the peak (as determined by the 2nd derivative) is negative
     //   Then we are on the tailing edge of a previous pulse.
     //
-    if (_samplesDeriv[isampl] < 0) {
+    if (_samplesDeriv[isampl] < -0.5) {
 
       //
       //  Put a threshold on the derivative as a fraction of the difference between max and min so that
       //     we don't waste lots of time for insignificant prepulses.
       //
-      if (std::abs(_samplesDeriv[isampl]) / (_maxADCValue - _minADCValue + 1) > 0.05) _prePulse = true;
+      if (std::abs(_samplesDeriv[isampl]) / (_maxADCValue - _minADCValue + 1) > 0.05) {
+
+	// std:: cout << "Setting prepulse due to large negative derivative: sample = " << isampl << ", value = " << _samplesDeriv[isampl]  
+	// 	   << ", threshold = " << (_maxADCValue - _minADCValue + 1)*0.05  << std::endl;
+	_prePulse = true;
+      }
 
       // Also, if we have two negative derivatives in a row, we have a prepulse
       //
-      if (haveNegDeriv) _prePulse = true;
-      else haveNegDeriv = true;
+      if (lastNegDeriv) {
+	_prePulse = true;
+	//	std:: cout << "Setting prepulse due to successive negative derivatives" << std::endl;
+      }
+      else lastNegDeriv = true;
+    }
+    else {
+      lastNegDeriv = false;
     }
 
     //  If any of the 2nd derivatives before the minium are negative, we have the possibility of positive
     //    preceeding pulse
     //
-    if (_samplesDeriv2nd[isampl] < 0 && _samplesDeriv2nd[isampl + 1] > 0) _prePulse = true;
+    if (_samplesDeriv2nd[isampl] < 0 && _samplesDeriv2nd[isampl + 1] > 0) {
+      if  (_samplesDeriv2nd[isampl] < 0.1*_minDeriv2nd) {
+	_prePulse = true;
+	// std:: cout << "Setting prepulse due to negative 2nd derivative: sample = " << isampl << ", value =  " << _samplesDeriv2nd[isampl] << ", threshold = " << 0.1*_minDeriv2nd << std::endl;
+      }
+    }
   }
 
   // Additional specific tests for pre pulses etc.
@@ -365,8 +666,8 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
     //  Don't do this check for the 40 MHz data as the pulse often drops below baseline 
     //    due to overshoot, not pre-pulses
     //
-    if (_samplesSub[_Nsample - 1] <=  0 ||
-	_samplesSub[_Nsample - 1] <=  _samplesSub[_preSampleIdx+1] ) _prePulse = true;
+    //if //_samplesSub[_Nsample - 1] <=  -_maxADCValue*0.05 ||
+    //    if	(_samplesSub[_Nsample - 1] - _samplesSub[_preSampleIdx+1] < -_maxADCValue*0.05) _prePulse = true;
 
   }
 
@@ -377,10 +678,11 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
   //
   if (_fail || !_havePulse) return false;
 
-  DoFit();
+  if (!_useDelayed) DoFit();
+  else DoFitCombined();
 
   if (!FitFailed()) {
-    _fitT0Corr = _fitT0;
+    _fitTimeCorr = _fitTimeSub;
 
     if (_haveTimingCorrections) {
 
@@ -392,14 +694,14 @@ bool ZDCPulseAnalyzer::AnalyzeData(const std::vector<float>& samples,        //
       float correction = (t0CorrParams[0] + t0CorrParams[1]*t0CorrFact + t0CorrParams[2]*t0CorrFact*t0CorrFact +
 			  t0CorrParams[3]*t0CorrFact*t0CorrFact*t0CorrFact);
 
-      _fitT0Corr -= correction;
+      _fitTimeCorr -= correction;
     }
 
 
     // Now check for valid chisq and valid time
     //
-    if (_fitChisq / _fitAmplitude > maxChisqDivAmp) _badChisq = true;
-    if (_fitT0Corr < minT0Corr || _fitT0Corr > maxT0Corr) _badT0 = true;
+    if (_fitChisq / (_fitAmplitude + 1.0e-6) > maxChisqDivAmp) _badChisq = true;
+    if (_fitTimeCorr < minT0Corr || _fitTimeCorr > maxT0Corr) _badT0 = true;
 
     //    std::cout << "Testing t0 cut: t0corr = " << _fitT0Corr << ", min max cuts = " << minT0Corr << ", " << maxT0Corr << std::endl;
   }
@@ -440,13 +742,14 @@ void ZDCPulseAnalyzer::DoFit()
   //  if (!_fitFailed) {
   _bkgdMaxFraction = fitWrapper->GetBkgdMaxFraction();
   if (std::abs(_bkgdMaxFraction) > 0.25) {
-    options += "e";
-    _fitHist->Fit(fitWrapper->GetWrapperTF1(), options.c_str());
+    std::string tempOptions = options + "e";
+    _fitHist->Fit(fitWrapper->GetWrapperTF1(), tempOptions.c_str());
     _bkgdMaxFraction = fitWrapper->GetBkgdMaxFraction();
   }
 
   _fitAmplitude = fitWrapper->GetAmplitude();
-  _fitT0 = fitWrapper->GetTime() - t0Initial;
+  _fitTime = fitWrapper->GetTime();
+  _fitTimeSub = _fitTime - t0Initial;
   _fitChisq = result_ptr->Chi2();
 
   _fitTau1 = fitWrapper->GetTau1();
@@ -456,12 +759,230 @@ void ZDCPulseAnalyzer::DoFit()
   //  }
 }
 
+void ZDCPulseAnalyzer::DoFitCombined()
+{
+  // Set the initial values
+  //
+  float ampInitial = _maxADCValue - _minADCValue;
+  float t0Initial = (_useLowGain ? _nominalT0LG : _nominalT0HG);
+
+  ZDCFitWrapper* fitWrapper = _defaultFitWrapper;
+  if (PrePulse()) fitWrapper = _prePulseFitWrapper;
+
+  fitWrapper->Initialize(ampInitial, t0Initial);
+  if (PrePulse()) {
+    if (_samplesDeriv[0] < 0) {
+      (static_cast<ZDCPrePulseFitWrapper*>(_prePulseFitWrapper))->SetInitialPrePulseT0(-10);
+    }
+    else {
+      (static_cast<ZDCPrePulseFitWrapper*>(_prePulseFitWrapper))->SetInitialPrePulseT0(0);
+    }
+  }
+
+  // Set up the virtual fitter
+  //
+  TFitter* theFitter = 0;
+
+  if (PrePulse()) {
+    if (!_prePulseCombinedFitter) _prePulseCombinedFitter = MakeCombinedFitter(fitWrapper->GetWrapperTF1());
+    theFitter = _prePulseCombinedFitter;
+
+    //    std::cout << "Testing prepulse wrapper, npar = " << theFitter->GetNumberTotalParameters() << std::endl;
+  }
+  else {
+    if (!_defaultCombinedFitter) {
+      _defaultCombinedFitter = MakeCombinedFitter(fitWrapper->GetWrapperTF1());
+    }
+    theFitter = _defaultCombinedFitter;
+
+    //    std::cout << "Testing default wrapper, npar = " << theFitter->GetNumberTotalParameters() << std::endl;
+  }
+  
+  // Set the static pointers to histograms and function for use in FCN
+  //
+  _undelayedFitHist = _fitHist;
+  _delayedFitHist = _delayedHist;
+  _combinedFitFunc = fitWrapper->GetWrapperTF1();
+
+  size_t numFitPar = theFitter->GetNumberTotalParameters();
+
+  theFitter->GetMinuit()->fISW[4] = -1;
+
+  double arglist[100];
+  // arglist[0] = 1;
+  // arglist[1] = 0;
+
+  // theFitter->ExecuteCommand("SET PAR ", arglist, 2);
+  
+  theFitter->SetParameter(0, "delayBaselineAdjust", 0, 0.01, 0, 0);
+
+  for (size_t ipar = 0; ipar < numFitPar - 1; ipar++) {
+    double parLimitLow, parLimitHigh;
+
+    _combinedFitFunc->GetParLimits(ipar, parLimitLow, parLimitHigh);
+
+    // std::cout << "Setting minuit parameter " << ipar + 1 << " to value " << _combinedFitFunc->GetParameter(ipar) << ", limits = " 
+    // 	      << parLimitLow << " to " << parLimitHigh << std::endl;
+
+    theFitter->SetParameter(ipar + 1, _combinedFitFunc->GetParName(ipar), _combinedFitFunc->GetParameter(ipar), 0.01, parLimitLow, parLimitHigh);
+
+    // arglist[0] = ipar + 2;;
+    // arglist[1] = _combinedFitFunc->GetParameter(ipar);
+    // theFitter->ExecuteCommand("SET PAR ", arglist, 2);
+
+    //    theFitter->GetMinuit()->Command(("SET PAR " + std::to_string(ipar + 2) + " " + std::to_string(_combinedFitFunc->GetParameter(ipar))).c_str() );
+  }
+
+  // Now perform the fit
+  //
+  //  double arglist[100];
+
+  if (_quietFits) {
+    theFitter->GetMinuit()->fISW[4] = -1;
+
+    int  ierr= 0; 
+    theFitter->GetMinuit()->mnexcm("SET NOWarnings",0,0,ierr);
+  }
+  else theFitter->GetMinuit()->fISW[4] = 0;
+
+  arglist[0] = 2000; // number of function calls
+  arglist[1] = 0.01; // tolerance
+  int status = theFitter->ExecuteCommand("MIGRAD",arglist,2);
+
+  double fitAmp = theFitter->GetParameter(1);
+  if (status || fitAmp < 1) {
+    
+    // We first fit with no baseline adjust
+    //
+    theFitter->SetParameter(0, "delayBaselineAdjust", 0, 0.01, 0, 0);
+    theFitter->FixParameter(0);
+
+    for (size_t ipar = 0; ipar < numFitPar - 1; ipar++) {
+      double parLimitLow, parLimitHigh;
+      
+      _combinedFitFunc->GetParLimits(ipar, parLimitLow, parLimitHigh);
+      theFitter->SetParameter(ipar + 1, _combinedFitFunc->GetParName(ipar), _combinedFitFunc->GetParameter(ipar), 0.01, parLimitLow, parLimitHigh); 
+    }
+
+    arglist[0] = 2000; // number of function calls
+    arglist[1] = 0.01; // tolerance
+    status = theFitter->ExecuteCommand("MIGRAD",arglist,2);
+    if (!status) {
+      theFitter->ReleaseParameter(0);
+      _fitFailed = true;
+    }
+    else {
+      theFitter->ReleaseParameter(0);
+      status = theFitter->ExecuteCommand("MIGRAD",arglist,2);
+
+      if (status || fitAmp < 1) {_fitFailed = false;}
+    }
+  }
+  else _fitFailed = false;
+
+  if (!_quietFits) theFitter->GetMinuit()->fISW[4] = -1;
+
+  std::vector<double> funcParams(numFitPar - 1);
+  std::vector<double> funcParamErrs(numFitPar - 1);
+
+  // Save the baseline shift between delayed and undelayed samples
+  //
+  _delayedBaselineShift = theFitter->GetParameter(0);
+
+  // Capture and store the fit function parameteds and errors
+  //
+  for (size_t ipar = 1; ipar < numFitPar; ipar++) {
+    funcParams[ipar - 1] = theFitter->GetParameter(ipar);
+    funcParamErrs[ipar - 1] = theFitter->GetParError(ipar);
+  }
+
+  _combinedFitFunc->SetParameters(&funcParams[0]);
+  _combinedFitFunc->SetParErrors(&funcParamErrs[0]);
+
+  // Cpature the chi-square etc.
+  //
+  double chi2, edm, errdef;
+  int nvpar, nparx;
+
+  theFitter->GetStats(chi2, edm, errdef, nvpar, nparx);
+
+  int ndf = 2*_Nsample - nvpar;
+
+  _combinedFitFunc->SetChisquare(chi2);
+  _combinedFitFunc->SetNDF(ndf);
+
+    // add to list of functions
+  _undelayedFitHist->GetListOfFunctions()->Add(_combinedFitFunc);
+  _delayedFitHist->GetListOfFunctions()->Add(_combinedFitFunc);
+
+  //  if (QuietFits()) options += "Q0";
+
+  // TFitResultPtr result_ptr = _fitHist->Fit(fitWrapper->GetWrapperTF1(), options.c_str());
+
+  // int fitStatus = result_ptr;
+  // if (fitStatus != 0) {
+  //   TFitResultPtr try2Result_ptr = _fitHist->Fit(fitWrapper->GetWrapperTF1(), options.c_str());
+  //   if ((int) try2Result_ptr != 0) _fitFailed = true;
+  // }
+  // else _fitFailed = false;
+
+  // //  if (!_fitFailed) {
+  // _bkgdMaxFraction = fitWrapper->GetBkgdMaxFraction();
+  // if (std::abs(_bkgdMaxFraction) > 0.25) {
+  //   //    std::string tempOptions = options + "e";
+  //   _fitHist->Fit(fitWrapper->GetWrapperTF1(), tempOptions.c_str());
+  //   _bkgdMaxFraction = fitWrapper->GetBkgdMaxFraction();
+  // }
+
+  _fitAmplitude = fitWrapper->GetAmplitude();
+  _fitTime = fitWrapper->GetTime();
+  _fitTimeSub = _fitTime - t0Initial;
+  _fitChisq = chi2;
+
+  _fitTau1 = fitWrapper->GetTau1();
+  _fitTau2 = fitWrapper->GetTau2();
+
+  _fitAmpError = fitWrapper->GetAmpError();
+  //  }
+}
+
+
+TFitter* ZDCPulseAnalyzer::MakeCombinedFitter(TF1* func) 
+{
+  //  gErrorIgnoreLevel = 1001;
+  TVirtualFitter::SetDefaultFitter("Minuit");
+
+  size_t nFitParams = func->GetNpar() + 1;
+  TFitter* fitter = new TFitter(nFitParams);
+  
+  fitter->GetMinuit()->fISW[4] = -1;
+  fitter->SetParameter(0, "delayBaselineAdjust", 0, 0.01, 0, 0);
+  
+  for (size_t ipar = 0; ipar < nFitParams - 1; ipar++) {
+    double parLimitLow, parLimitHigh;
+
+    func->GetParLimits(ipar, parLimitLow, parLimitHigh);
+    fitter->SetParameter(ipar + 1, func->GetParName(ipar), func->GetParameter(ipar), 0.01, parLimitLow, parLimitHigh);
+  }
+  
+  fitter->SetFCN(ZDCPulseAnalyzer::CombinedPulsesFCN);
+
+  double arglist[100];
+  arglist[0] = -1;
+
+  return fitter;
+}
+
 void ZDCPulseAnalyzer::Dump() const
 {
   std::cout << "ZDCPulseAnalyzer dump for tag = " << _tag << std::endl;
 
   std::cout << "Presample index, value = " << _preSampleIdx << ", " << _preSample << std::endl;
 
+  if (_useDelayed) {
+    std::cout << "using delayed samples with delta T = " << _delayedDeltaT << ", and pedestalDiff == " << _delayedPedestalDiff << std::endl;
+  }
+
   std::cout << "samplesSub ";
   for (size_t sample = 0; sample < _samplesSub.size(); sample++) {
     std::cout << ", [" << sample << "] = " << _samplesSub[sample];
@@ -497,8 +1018,8 @@ unsigned int ZDCPulseAnalyzer::GetStatusMask() const
   if (LGOverflow()) statusMask |= 1 << LGOverflowBit;
   if (LGUnderflow()) statusMask |= 1 << LGUnderflowBit;
 
-  if (FitFailed()) statusMask |= 1 << FitFailedBit;
   if (PrePulse()) statusMask |= 1 << PrePulseBit;
+  if (FitFailed()) statusMask |= 1 << FitFailedBit;
   if (BadChisq()) statusMask |= 1 << BadChisqBit;
   if (BadT0()) statusMask |= 1 << BadT0Bit;
 
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZdcAnalysisTool.cxx b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZdcAnalysisTool.cxx
index 535aa78f78b8..cdd0a11d1514 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZdcAnalysisTool.cxx
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/Root/ZdcAnalysisTool.cxx
@@ -48,6 +48,9 @@ namespace ZDC
   declareProperty("NumSampl", m_numSample = 7); 
   declareProperty("DeltaTSample", m_deltaTSample = 25); 
   declareProperty("Presample", m_presample = 0); 
+  declareProperty("CombineDelay", m_combineDelay = false); 
+  declareProperty("DelayDeltaT", m_delayDeltaT = -12.5); 
+
   declareProperty("PeakSample", m_peakSample = 2);
   declareProperty("Peak2ndDerivThresh", m_Peak2ndDerivThresh = 10);
 
@@ -230,9 +233,89 @@ ZDCDataAnalyzer* ZdcAnalysisTool::initializeDefault()
   zdcDataAnalyzer->SetTauT0Values(fixTau1Arr, fixTau2Arr, tau1, tau2, t0, t0);
   zdcDataAnalyzer->SetCutValues(chisqDivAmpCut, chisqDivAmpCut, deltaT0CutLow, deltaT0CutHigh, deltaT0CutLow, deltaT0CutHigh);
 
+  if (m_combineDelay) {
+    ZDCDataAnalyzer::ZDCModuleFloatArray defaultPedestalShifts = {0, 0, 0, 0, 0, 0, 0, 0};
+
+    zdcDataAnalyzer->EnableDelayed(m_delayDeltaT, defaultPedestalShifts);
+  }
+
   return zdcDataAnalyzer;
 }
 
+ZDCDataAnalyzer* ZdcAnalysisTool::initializepPb2016()
+{
+  //
+  //   For now, we continue to use hard-coded values for the maximum and minimum ADC values
+  //   For now we also use the FermiExp pulse model.
+
+  ZDCDataAnalyzer::ZDCModuleFloatArray tau1Arr, tau2Arr, peak2ndDerivMinSamples, t0;
+  ZDCDataAnalyzer::ZDCModuleFloatArray peak2ndDerivMinThresholdsHG, peak2ndDerivMinThresholdsLG;
+  ZDCDataAnalyzer::ZDCModuleFloatArray deltaT0CutLow, deltaT0CutHigh, chisqDivAmpCut;
+  ZDCDataAnalyzer::ZDCModuleBoolArray fixTau1Arr, fixTau2Arr;
+    
+  //  For now we allow the tau values to be controlled by the job properties until they are better determined
+  //
+  const int peakSample = 5;
+  const float peak2ndDerivThreshHG = -12;
+  const float peak2ndDerivThreshLG = -10;
+  const float tau1 = 4.5;
+  const float tau2 = 22.;
+
+
+  for (size_t side : {0, 1}) {
+    for (size_t module : {0, 1, 2, 3}) {
+      fixTau1Arr[side][module] = m_fixTau1;
+      fixTau2Arr[side][module] = m_fixTau2;
+      tau1Arr[side][module] = tau1;
+      tau2Arr[side][module] = tau2;
+
+      peak2ndDerivMinSamples[side][module] = peakSample;
+      peak2ndDerivMinThresholdsHG[side][module] = peak2ndDerivThreshHG;
+      peak2ndDerivMinThresholdsLG[side][module] = peak2ndDerivThreshLG;
+
+      t0[side][module] = m_t0;
+      deltaT0CutLow[side][module] = -m_deltaTCut;
+      deltaT0CutHigh[side][module] = m_deltaTCut;
+      chisqDivAmpCut[side][module] = m_ChisqRatioCut;
+    }
+  }
+
+  //  ATH_MSG_INFO( "Default: delta t cut, value low = " << deltaT0CutLow[0][0] << ", high = " << deltaT0CutHigh[0][0] );
+
+  ZDCDataAnalyzer::ZDCModuleFloatArray HGOverFlowADC = {800, 800, 800, 800, 800, 800, 800, 800};
+  ZDCDataAnalyzer::ZDCModuleFloatArray HGUnderFlowADC = {10, 10, 10, 10, 10, 10, 10, 10};
+  ZDCDataAnalyzer::ZDCModuleFloatArray LGOverFlowADC = {1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020};
+
+  //  Construct the data analyzer
+  //
+  //  We adopt hard-coded values for the number of samples and the frequency which we kept fixed for all physics data
+  //
+  ZDCDataAnalyzer* zdcDataAnalyzer = new ZDCDataAnalyzer(7, 25, 1, "FermiExp", peak2ndDerivMinSamples,
+							 peak2ndDerivMinThresholdsHG, peak2ndDerivMinThresholdsLG, m_lowGainOnly);
+
+  // Open up tolerances on the position of the peak for now
+  //
+  zdcDataAnalyzer->SetPeak2ndDerivMinTolerances(2);
+
+  // We alwyas disable the 12EM (sideC) module which was not present (LHCf)
+  //
+  zdcDataAnalyzer->DisableModule(0,0);
+
+  zdcDataAnalyzer->SetADCOverUnderflowValues(HGOverFlowADC, HGUnderFlowADC, LGOverFlowADC);
+  zdcDataAnalyzer->SetTauT0Values(fixTau1Arr, fixTau2Arr, tau1Arr, tau2Arr, t0, t0);
+  zdcDataAnalyzer->SetCutValues(chisqDivAmpCut, chisqDivAmpCut, deltaT0CutLow, deltaT0CutHigh, deltaT0CutLow, deltaT0CutHigh);
+
+  // We allow the combineDelay to be controlled by the properties
+  //
+  //  if (m_combineDelay) {
+  m_combineDelay = true;
+    ZDCDataAnalyzer::ZDCModuleFloatArray defaultPedestalShifts = {0, 0, 0, 0, 0, 0, 0, 0};
+
+    zdcDataAnalyzer->EnableDelayed(-12.5, defaultPedestalShifts);
+    //  }
+
+  return zdcDataAnalyzer;
+}
 
 void ZdcAnalysisTool::initialize40MHz()
 {
@@ -437,26 +520,6 @@ StatusCode ZdcAnalysisTool::initializeTool()
   m_zdcTriggerEffParamsFileName = std::string(env.GetValue("ZdcTriggerEffFileName", "ZdcTriggerEffParameters_v4.root"));
   ATH_MSG_INFO("ZDC trigger efficiencies filename " << m_zdcTriggerEffParamsFileName);
 
-  ATH_MSG_INFO("Configuration: "<<m_configuration);
-  ATH_MSG_INFO("FlipEMDelay: "<<m_flipEMDelay);
-  ATH_MSG_INFO("LowGainOnly: "<<m_lowGainOnly);
-  ATH_MSG_INFO("WriteAux: "<<m_writeAux);
-  ATH_MSG_INFO("AuxSuffix: "<<m_auxSuffix);
-  ATH_MSG_INFO("DoCalib: "<<m_doCalib);
-  ATH_MSG_INFO("ForceCalibRun: "<<m_forceCalibRun);
-  ATH_MSG_INFO("ForceCalibLB: "<<m_forceCalibLB);
-  ATH_MSG_INFO("NumSampl: "<<m_numSample); 
-  ATH_MSG_INFO("DeltaTSample: "<<m_deltaTSample); 
-  ATH_MSG_INFO("Presample: "<<m_presample); 
-  ATH_MSG_INFO("PeakSample: "<<m_peakSample);
-  ATH_MSG_INFO("Peak2ndDerivThresh: "<<m_Peak2ndDerivThresh);
-  ATH_MSG_INFO("T0: "<<m_t0);
-  ATH_MSG_INFO("Tau1: "<<m_tau1);
-  ATH_MSG_INFO("Tau2: "<<m_tau2);
-  ATH_MSG_INFO("FixTau1: "<<m_fixTau1);
-  ATH_MSG_INFO("FixTau2: "<<m_fixTau2);
-  ATH_MSG_INFO("DeltaTCut: "<<m_deltaTCut);
-  ATH_MSG_INFO("ChisqRatioCut: "<<m_ChisqRatioCut);
 
   if (m_forceCalibRun>-1) {
     ATH_MSG_INFO("CAREFUL: forcing calibration run/LB =" << m_forceCalibRun << "/" << m_forceCalibLB);
@@ -478,6 +541,9 @@ StatusCode ZdcAnalysisTool::initializeTool()
     
     m_zdcDataAnalyzer = m_zdcDataAnalyzer_80MHz; // default
   }
+  else if (m_configuration == "pPb2016") {
+    m_zdcDataAnalyzer = initializepPb2016();
+  }
   else {
     ATH_MSG_ERROR("Unknown configuration: "  << m_configuration);
     return StatusCode::FAILURE;
@@ -489,6 +555,32 @@ StatusCode ZdcAnalysisTool::initializeTool()
     m_auxSuffix = "_" + m_auxSuffix;
   }
 
+  ATH_MSG_INFO("Configuration: "<<m_configuration);
+  ATH_MSG_INFO("FlipEMDelay: "<<m_flipEMDelay);
+  ATH_MSG_INFO("LowGainOnly: "<<m_lowGainOnly);
+
+  ATH_MSG_INFO("Using Combined delayed and undelayed samples: "<< m_combineDelay);
+
+  ATH_MSG_INFO("WriteAux: "<<m_writeAux);
+  ATH_MSG_INFO("AuxSuffix: "<<m_auxSuffix);
+  ATH_MSG_INFO("DoCalib: "<<m_doCalib);
+  ATH_MSG_INFO("ForceCalibRun: "<<m_forceCalibRun);
+  ATH_MSG_INFO("ForceCalibLB: "<<m_forceCalibLB);
+  ATH_MSG_INFO("NumSampl: "<<m_numSample); 
+  ATH_MSG_INFO("DeltaTSample: "<<m_deltaTSample); 
+  ATH_MSG_INFO("Presample: "<<m_presample); 
+  ATH_MSG_INFO("PeakSample: "<<m_peakSample);
+  ATH_MSG_INFO("Peak2ndDerivThresh: "<<m_Peak2ndDerivThresh);
+
+  if (m_combineDelay)  ATH_MSG_INFO("DelayDeltaT: "<< m_delayDeltaT);
+
+  ATH_MSG_INFO("T0: "<<m_t0);
+  ATH_MSG_INFO("Tau1: "<<m_tau1);
+  ATH_MSG_INFO("Tau2: "<<m_tau2);
+  ATH_MSG_INFO("FixTau1: "<<m_fixTau1);
+  ATH_MSG_INFO("FixTau2: "<<m_fixTau2);
+  ATH_MSG_INFO("DeltaTCut: "<<m_deltaTCut);
+  ATH_MSG_INFO("ChisqRatioCut: "<<m_ChisqRatioCut);
 
   m_init = true;
   return StatusCode::SUCCESS;
@@ -608,8 +700,11 @@ StatusCode ZdcAnalysisTool::recoZdcModules(const xAOD::ZdcModuleContainer& modul
   ATH_MSG_DEBUG("Starting event processing");
   m_zdcDataAnalyzer->StartEvent(calibLumiBlock);
 
-  const std::vector<unsigned short>* adc0;
-  const std::vector<unsigned short>* adc1;
+  const std::vector<unsigned short>* adcUndelayLG = 0;
+  const std::vector<unsigned short>* adcUndelayHG = 0;
+
+  const std::vector<unsigned short>* adcDelayLG = 0;
+  const std::vector<unsigned short>* adcDelayHG = 0;
 
   ATH_MSG_DEBUG("Processing modules");
   for (const auto zdcModule : moduleContainer)
@@ -619,27 +714,56 @@ StatusCode ZdcAnalysisTool::recoZdcModules(const xAOD::ZdcModuleContainer& modul
 
       if (zdcModule->zdcModule()==0 && m_flipEMDelay) // flip delay/non-delay for EM big tube
 	{
-	  adc0 = &(*(zdcModule->TTg0d1Link()))->adc();
-	  adc1 = &(*(zdcModule->TTg1d1Link()))->adc();
+	  adcUndelayLG = &(*(zdcModule->TTg0d1Link()))->adc();
+	  adcUndelayHG = &(*(zdcModule->TTg1d1Link()))->adc();
+
+	  adcDelayLG = &(*(zdcModule->TTg0d0Link()))->adc();
+	  adcDelayHG = &(*(zdcModule->TTg1d0Link()))->adc();
 	}
       else
 	{
-	  adc0 = &(*(zdcModule->TTg0d0Link()))->adc();
-	  adc1 = &(*(zdcModule->TTg1d0Link()))->adc();
+	  adcUndelayLG = &(*(zdcModule->TTg0d0Link()))->adc();
+	  adcUndelayHG = &(*(zdcModule->TTg1d0Link()))->adc();
+
+	  adcDelayLG = &(*(zdcModule->TTg0d1Link()))->adc();
+	  adcDelayHG = &(*(zdcModule->TTg1d1Link()))->adc();
 	}
       
-      static std::vector<float> HGADCSamples(m_numSample);
-      static std::vector<float> LGADCSamples(m_numSample);
+      static std::vector<float> HGUndelADCSamples(m_numSample);
+      static std::vector<float> LGUndelADCSamples(m_numSample);
 
-      std::copy(adc0->begin(),adc0->end(),LGADCSamples.begin());
-      std::copy(adc1->begin(),adc1->end(),HGADCSamples.begin());
+      std::copy(adcUndelayLG->begin(), adcUndelayLG->end(), LGUndelADCSamples.begin());
+      std::copy(adcUndelayHG->begin(), adcUndelayHG->end(), HGUndelADCSamples.begin());
       
       int side = (zdcModule->side()==-1) ? 0 : 1 ;
 
       //std::cout << "LG: ";for (int i = 0;i<7;i++){std::cout<<LGADCSamples.at(i)<< " ";};std::cout<<std::endl;
       //std::cout << "HG: ";for (int i = 0;i<7;i++){std::cout<<HGADCSamples.at(i)<< " ";};std::cout<<std::endl;
 
-      m_zdcDataAnalyzer->LoadAndAnalyzeData(side,zdcModule->zdcModule(),HGADCSamples, LGADCSamples);
+      if (!m_combineDelay) {
+	m_zdcDataAnalyzer->LoadAndAnalyzeData(side,zdcModule->zdcModule(), HGUndelADCSamples, LGUndelADCSamples);
+      }
+      else {
+	std::vector<float> HGDelayADCSamples(m_numSample);
+	std::vector<float> LGDelayADCSamples(m_numSample);
+
+	std::copy(adcDelayLG->begin(),adcDelayLG->end(), LGDelayADCSamples.begin());
+	std::copy(adcDelayHG->begin(),adcDelayHG->end(), HGDelayADCSamples.begin());
+
+	// If the delayed channels actually come earlier (as in the pPb in 2016), we invert the meaning of delayed and undelayed
+	//   see the initialization sections for similar inversion on the sign of the pedestal difference
+	//
+	if (m_delayDeltaT > 0) {
+	  m_zdcDataAnalyzer->LoadAndAnalyzeData(side,zdcModule->zdcModule(), 
+						HGUndelADCSamples, LGUndelADCSamples,
+						HGDelayADCSamples, LGDelayADCSamples);
+	}
+	else {
+	  m_zdcDataAnalyzer->LoadAndAnalyzeData(side,zdcModule->zdcModule(), 
+						HGDelayADCSamples, LGDelayADCSamples,
+						HGUndelADCSamples, LGUndelADCSamples);
+	}
+      }
     }
 
   ATH_MSG_DEBUG("Finishing event processing");
@@ -678,6 +802,8 @@ StatusCode ZdcAnalysisTool::recoZdcModules(const xAOD::ZdcModuleContainer& modul
 	zdcModule->auxdecor<float>("FitAmpError" + m_auxSuffix) = pulseAna_p->GetAmpError();
 	zdcModule->auxdecor<float>("FitT0" + m_auxSuffix) = pulseAna_p->GetFitT0();
 	zdcModule->auxdecor<float>("BkgdMaxFraction" + m_auxSuffix) = pulseAna_p->GetBkgdMaxFraction();
+	zdcModule->auxdecor<float>("PreSampleAmp" + m_auxSuffix) = pulseAna_p->GetPreSampleAmp();
+	//zdcModule->auxdecor<float>("Presample" + m_auxSuffix) = pulseAna_p->GetPresample();
       }
       /*      
       std::cout << "side = " << side << " module=" << zdcModule->zdcModule() << " CalibEnergy=" << zdcModule->auxdecor<float>("CalibEnergy") 
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCDataAnalyzer.h b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCDataAnalyzer.h
index 9bcafdd3cd29..e45dc44efcdc 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCDataAnalyzer.h
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCDataAnalyzer.h
@@ -24,6 +24,7 @@ private:
   std::string _fitFunction;
   bool _forceLG;
 
+  ZDCModuleBoolArray _moduleDisabled;
   std::array<std::array<ZDCPulseAnalyzer*, 4>, 2> _moduleAnalyzers;
 
   int _debugLevel;
@@ -81,6 +82,8 @@ public:
     else ZDCPulseAnalyzer::SetQuietFits(false);
   }
 
+  void EnableDelayed(float deltaT, const ZDCModuleFloatArray& undelayedDelayedPedestalDiff);
+
   unsigned int GetModuleMask() const {return _moduleMask;}
 
   float GetModuleSum(size_t side) const {return _moduleSum.at(side);}
@@ -104,6 +107,10 @@ public:
 
   const ZDCPulseAnalyzer* GetPulseAnalyzer(size_t side, size_t module) const {return _moduleAnalyzers.at(side).at(module);}
 
+  bool DisableModule(size_t side, size_t module);
+
+  void SetPeak2ndDerivMinTolerances(size_t tolerance);
+
   void SetADCOverUnderflowValues(const ZDCModuleFloatArray& HGOverflowADC, const ZDCModuleFloatArray& HGUnderflowADC, 
 				 const ZDCModuleFloatArray& LGOverflowADC);
 
@@ -147,6 +154,9 @@ public:
 
   void LoadAndAnalyzeData(size_t side, size_t module, const std::vector<float> HGSamples, const std::vector<float> LGSamples); 
 
+  void LoadAndAnalyzeData(size_t side, size_t module, const std::vector<float> HGSamples, const std::vector<float> LGSamples,
+			  const std::vector<float> HGSamplesDelayed, const std::vector<float> LGSamplesDelayed); 
+
   bool FinishEvent();
 
 };
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCFitWrapper.h b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCFitWrapper.h
index e24e80bb0ee3..e4760b15688a 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCFitWrapper.h
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCFitWrapper.h
@@ -190,8 +190,8 @@ public:
   virtual float GetAmplitude() const {return GetWrapperTF1()->GetParameter(0); }
   virtual float GetAmpError() const {return GetWrapperTF1()->GetParError(0); }
 
-  virtual float GetTau1() const {return GetWrapperTF1()->GetParameter(2);}
-  virtual float GetTau2() const {return GetWrapperTF1()->GetParameter(3);}
+  virtual float GetTau1() const {return _tau1;}
+  virtual float GetTau2() const {return _tau2;}
 
   virtual float GetTime() const {
     float fitT0 =  GetWrapperTF1()->GetParameter(1);
@@ -252,4 +252,82 @@ public:
   }
 };
 
+class ZDCFitExpFermiPulseSequence : public ZDCFitWrapper
+{
+  float _tau1;
+  float _tau2;
+  float _norm;
+  float _timeCorr;
+
+  size_t _numPulses;
+  std::vector<float> _pulseDeltaT;
+
+  TF1* _fitFunc;
+  TF1* _expFermiFunc;
+
+
+public:
+  ZDCFitExpFermiPulseSequence(std::string tag, float tmin, float tmax, float nominalT0, float deltaT,  float tau1, float tau2);
+
+  ~ZDCFitExpFermiPulseSequence() 
+  {
+    delete _fitFunc;
+    delete _expFermiFunc;
+  }
+
+  virtual TF1* GetWrapperTF1() {return _fitFunc;}
+  virtual const TF1* GetWrapperTF1() const {return _fitFunc;}
+
+  virtual void Initialize(float initialAmp, float initialT0);
+  //  void InitializePrePulseT0(float initPreT0) {  GetWrapperTF1()->SetParameter(3, initPreT0);}
+
+  virtual float GetAmplitude() const {return _fitFunc->GetParameter(0); }
+  virtual float GetAmpError() const {return _fitFunc->GetParError(0); }
+
+  virtual float GetTau1() const {return _tau1;}
+  virtual float GetTau2() const {return _tau2;}
+
+  virtual float GetTime() const {
+    float fitT0 =  _fitFunc->GetParameter(1);
+
+    // Correct the time to the maximum (the factor of 1/2 is still not fully understood)
+    //
+    fitT0 += _timeCorr; 
+    return fitT0;
+  }
+
+  virtual float GetShapeParameter(size_t index) const
+  {
+    if (index == 0) return _tau1;
+    else if (index == 1) return _tau2;
+    else if (index < _numPulses + 2) return _fitFunc->GetParameter(index);
+    else throw;
+  }
+
+  virtual float GetBkgdMaxFraction() const
+  {
+    return 0;
+  }
+
+  virtual double operator() (double *x, double *p) 
+  {
+    double t = x[0];
+
+    double mainAmp = p[0];
+    double t0 = p[1];
+
+    double mainDeltaT = t - t0;
+    double funcValue =  mainAmp*_norm*_expFermiFunc->operator()(mainDeltaT);
+
+    for (size_t ipulse = 0; ipulse < _numPulses - 1; ipulse++) {
+      double deltaT = mainDeltaT - _pulseDeltaT[ipulse];
+      double amp = p[2 + ipulse];
+
+      double pulse = amp*_norm*_expFermiFunc->operator()(deltaT);
+      funcValue += pulse;
+    }
+
+    return funcValue;
+  }
+};
 #endif
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCPulseAnalyzer.h b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCPulseAnalyzer.h
index 8939f8170715..7da2b6605167 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCPulseAnalyzer.h
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZDCPulseAnalyzer.h
@@ -11,15 +11,19 @@
 
 #include <TF1.h>
 #include <TH1.h>
+#include <TGraph.h>
+#include <TList.h>
 
 #include "ZdcAnalysis/ZDCFitWrapper.h"
 
+class TFitter;
+
 class ZDCPulseAnalyzer
 {
 public:
-  enum {PulseBit = 0, LowGainBit, FailBit, HGOverflowBit, 
-	HGUnderflowBit, PSHGOverUnderflowBit, LGOverflowBit, LGUnderflowBit,
-	PrePulseBit, PostPulseBit, FitFailedBit, BadChisqBit, BadT0Bit};
+  enum {PulseBit = 0, LowGainBit = 1, FailBit = 2, HGOverflowBit = 3, 
+	HGUnderflowBit = 4, PSHGOverUnderflowBit = 5, LGOverflowBit = 6, LGUnderflowBit = 7,
+	PrePulseBit = 8, PostPulseBit = 9, FitFailedBit = 10, BadChisqBit = 11, BadT0Bit = 12};
 
 private:
   typedef std::vector<float>::const_iterator SampleCIter;
@@ -28,6 +32,9 @@ private:
   //
   static std::string _fitOptions;
   static bool _quietFits;
+  static TH1* _undelayedFitHist;
+  static TH1* _delayedFitHist;
+  static TF1* _combinedFitFunc;
 
   // Quantities provided/set in the constructor
   //
@@ -40,8 +47,10 @@ private:
   bool _forceLG;
   float _tmin;
   float _tmax;
+
   std::string _fitFunction;
-  float _peak2ndDerivMinSample;
+  size_t _peak2ndDerivMinSample;
+  size_t _peak2ndDerivMinTolerance;
   float _peak2ndDerivMinThreshLG;
   float _peak2ndDerivMinThreshHG;
 
@@ -84,10 +93,15 @@ private:
   ZDCFitWrapper* _defaultFitWrapper;
   ZDCFitWrapper* _prePulseFitWrapper;
 
-  //  TH1* _LGFitHist;
-  // TH1* _fitHist;
-  // TF1* _FitFuncNoPrePulse;
-  // TF1* _FitFuncPrePulse;
+  // Delayed pulse emembers
+  //
+  bool _useDelayed;
+  float _delayedDeltaT;
+  float _delayedPedestalDiff;
+  mutable TH1* _delayedHist;
+
+  TFitter* _prePulseCombinedFitter;
+  TFitter* _defaultCombinedFitter;
 
   // Dynamic data loaded for each pulse (event)
   // ==========================================
@@ -128,8 +142,9 @@ private:
 
   float _fitAmplitude;
   float _fitAmpError;
-  float _fitT0;
-  float _fitT0Corr;
+  float _fitTime;
+  float _fitTimeSub;
+  float _fitTimeCorr;
   float _fitTau1;
   float _fitTau2;
   float _fitChisq;
@@ -137,6 +152,7 @@ private:
   float _ampError;
   float _preSampleAmp;
   float _bkgdMaxFraction;
+  float _delayedBaselineShift;
 
   std::vector<float> _ADCSamplesHGSub;
   std::vector<float> _ADCSamplesLGSub;
@@ -154,7 +170,8 @@ private:
   void SetDefaults();
   void SetupFitFunctions();
 
-  bool AnalyzeData(const std::vector<float>& samples,        // The samples used for this event
+  bool AnalyzeData(size_t nSamples, size_t preSample, 
+		   const std::vector<float>& samples,        // The samples used for this event
 		   const std::vector<float>& samplesSig,     // The "resolution" on the ADC value
 		   const std::vector<float>& toCorrParams,   // The parameters used to correct the t0               
 		   float maxChisqDivAmp,                     // The maximum chisq / amplitude ratio
@@ -165,14 +182,36 @@ private:
 
   void FillHistogram(const std::vector<float>& samples, const std::vector<float>& samplesSig) const
   {
-    // Set the data and errors in the histogram object
-    //
-    for (size_t isample = 0; isample < _Nsample; isample++) {
-      _fitHist->SetBinContent(isample + 1, samples[isample]);
-      _fitHist->SetBinError(isample + 1, samplesSig[isample]);
+    if (!_useDelayed) {
+      // Set the data and errors in the histogram object
+      //
+      for (size_t isample = 0; isample < _Nsample; isample++) {
+	_fitHist->SetBinContent(isample + 1, samples[isample]);
+	_fitHist->SetBinError(isample + 1, samplesSig[isample]);
+      }
+    }
+    else {
+      // Set the data and errors in the histogram object
+      //
+      for (size_t isample = 0; isample < _Nsample; isample++) {
+	_fitHist->SetBinContent(isample + 1, samples[isample*2]);
+	_delayedHist->SetBinContent(isample + 1, samples[isample*2 + 1]);
+
+	_fitHist->SetBinError(isample + 1, samplesSig[isample]); // ***** horrible hack: fix ASAP
+      }
+
     }
   }
 
+  void DoFit();
+  void DoFitCombined();
+
+  static TFitter* MakeCombinedFitter(TF1* func);
+
+  //  The minuit FCN used for fitting combined undelayed and delayed pulses
+  //
+  static void CombinedPulsesFCN(int& numParam, double*, double& f, double* par, int flag);
+
 public:
 
   ZDCPulseAnalyzer(std::string tag, int Nsample, float deltaTSample, size_t preSampleIdx, int pedestal, float gainHG,
@@ -184,6 +223,10 @@ public:
   static void SetQuietFits(bool quiet) {_quietFits = quiet;}
   static bool QuietFits() {return _quietFits;}
 
+  void EnableDelayed(float deltaT, float pedestalShift);
+
+  void SetPeak2ndDerivMinTolerance(size_t tolerance) {_peak2ndDerivMinTolerance = tolerance;}
+
   void SetForceLG(bool forceLG) {_forceLG = forceLG;}
   bool ForceLG() const {return _forceLG;}
 
@@ -219,6 +262,9 @@ public:
 
   bool LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::vector<float> ADCSamplesLG);
 
+  bool LoadAndAnalyzeData(std::vector<float> ADCSamplesHG, std::vector<float> ADCSamplesLG,
+			  std::vector<float> ADCSamplesHGDelayed, std::vector<float> ADCSamplesLGDelayed);
+
   bool HaveData() const {return _haveData;}
   bool HavePulse() const {return _havePulse;}
   bool Failed() const {return _fail;}
@@ -236,8 +282,9 @@ public:
   bool PSHGOverUnderflow() const {return _PSHGOverUnderflow;}
 
   float GetFitAmplitude() const {return _fitAmplitude;}
-  float GetFitT0() const {return _fitT0;}
-  float GetT0Corr() const {return _fitT0Corr;}
+  float GetFitT0() const {return _fitTime;}
+  float GetT0Sub() const {return _fitTimeSub;}
+  float GetT0Corr() const {return _fitTimeCorr;}
   float GetChisq() const {return _fitChisq;}
 
   float GetFitTau1() const {return _fitTau1;}
@@ -277,7 +324,32 @@ public:
     return _fitHist;
   }
 
-  void DoFit();
+  TGraph* GetCombinedGraph() const {
+    //
+    // We defer filling the histogram if we don't have a pulse until the histogram is requested
+    // 
+    GetHistogramPtr();
+
+    TGraph* theGraph = new TGraph(2*_Nsample);
+    size_t npts = 0;
+
+    for (size_t ipt = 0; ipt < _fitHist->GetNbinsX(); ipt++) {
+      theGraph->SetPoint(npts++, _fitHist->GetBinCenter(ipt + 1), _fitHist->GetBinContent(ipt + 1));
+    }
+
+    for (size_t iDelayPt = 0; iDelayPt < _delayedHist->GetNbinsX(); iDelayPt++) {
+      theGraph->SetPoint(npts++, _delayedHist->GetBinCenter(iDelayPt + 1), _delayedHist->GetBinContent(iDelayPt + 1) - _delayedBaselineShift);
+    }
+
+    TF1* func_p = (TF1*) _fitHist->GetListOfFunctions()->Last();
+    theGraph->GetListOfFunctions()->Add(func_p);
+    theGraph->SetName(( std::string(_fitHist->GetName()) + "combinaed").c_str());
+
+    theGraph->SetMarkerStyle(20);
+    theGraph->SetMarkerColor(1);
+
+    return theGraph;
+  }
 
   void Dump() const;
 
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZdcAnalysisTool.h b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZdcAnalysisTool.h
index 278e115d3683..ee752e0ff923 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZdcAnalysisTool.h
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/ZdcAnalysis/ZdcAnalysisTool.h
@@ -71,6 +71,7 @@ class ZdcAnalysisTool : public virtual IZdcAnalysisTool, public asg::AsgTool
   // Private methods
   //
   ZDCDataAnalyzer* initializeDefault();
+  ZDCDataAnalyzer* initializepPb2016();
 
   StatusCode configureNewRun(unsigned int runNumber);
 
@@ -98,6 +99,7 @@ class ZdcAnalysisTool : public virtual IZdcAnalysisTool, public asg::AsgTool
   const xAOD::ZdcModuleContainer* m_zdcModules;
   bool m_flipEMDelay;
   bool m_lowGainOnly;
+  bool m_combineDelay;
   bool m_doCalib;
   int m_forceCalibRun;
   int m_forceCalibLB;
@@ -110,6 +112,7 @@ class ZdcAnalysisTool : public virtual IZdcAnalysisTool, public asg::AsgTool
   unsigned int m_peakSample;
   float m_Peak2ndDerivThresh;
   float m_t0;
+  float m_delayDeltaT;
   float m_tau1;
   float m_tau2;
   bool m_fixTau1;
diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/cmt/Makefile.RootCore b/ForwardDetectors/ZDC/ZdcAnalysis/cmt/Makefile.RootCore
index 4b26464a70b3..9f3ee50d3b7b 100644
--- a/ForwardDetectors/ZDC/ZdcAnalysis/cmt/Makefile.RootCore
+++ b/ForwardDetectors/ZDC/ZdcAnalysis/cmt/Makefile.RootCore
@@ -10,7 +10,7 @@
 PACKAGE              = ZdcAnalysis
 
 # the libraries to link with this one:
-PACKAGE_PRELOAD      = 
+PACKAGE_PRELOAD      = Minuit
 
 # additional compilation flags to pass (not propagated to dependent packages):
 PACKAGE_CXXFLAGS     = 
-- 
GitLab