From 74559966f45f4721cfc40da0dba0b534632f49cc Mon Sep 17 00:00:00 2001
From: Jack Cranshaw <cranshaw@anl.gov>
Date: Tue, 30 Aug 2016 15:16:57 +0200
Subject: [PATCH] Fix issue with athfile use in CutFlowHelpers.py
 (EventBookkeeperTools-00-00-92)

  * Fix issue with athfile use in CutFlowHelpers.py
  * Tagging EventBookkeeperTools-00-00-92

2016-05-04 Jack Cranshaw <cranshaw@anl.gov>
  * Add in factorized cutflowsvc FileCutFlowSvc and BookkeeperTool
  * No changes to existing behavior
  * Modified: BookkeeperTool.cxx, EventBookkeeperTools_entries.cxx,
  BookkeeperTool.h
  * Added: FileCutFlowSvc.h, src/FileCutFlowSvc.cxx
  * Tagging EventBookkeeperTools-00-00-91

2015-07-28  Karsten Koeneke  <karsten.koeneke@cern.ch>

  * Fix configuration to extract the input stream name.
  * Fix EventCounterAlg for otherMCWeights.
  * Tagging EventBookkeeperTools-00-00-90

  * Tagging EventBookkeeperTools-00-00-89.
  * Fix coverity warnings.
  * Tagging EventBookkeeperTools-00-00-88.
...
(Long ChangeLog diff - truncated)


Former-commit-id: f85724246d9ac4fff4740c4aebc099424da0206a
---
 .../EventBookkeeperTools/BookkeeperTool.h     |  21 +-
 .../Root/BookkeeperTool.cxx                   | 366 +++++++++---
 .../python/CutFlowHelpers.py                  |  83 ++-
 .../src/EventCounterAlg.cxx                   |   5 +-
 .../src/FileCutFlowSvc.cxx                    | 549 ++++++++++++++++++
 .../EventBookkeeperTools/src/FileCutFlowSvc.h | 204 +++++++
 .../EventBookkeeperTools_entries.cxx          |   3 +
 7 files changed, 1132 insertions(+), 99 deletions(-)
 create mode 100644 Event/EventBookkeeperTools/src/FileCutFlowSvc.cxx
 create mode 100644 Event/EventBookkeeperTools/src/FileCutFlowSvc.h

diff --git a/Event/EventBookkeeperTools/EventBookkeeperTools/BookkeeperTool.h b/Event/EventBookkeeperTools/EventBookkeeperTools/BookkeeperTool.h
index 40436dfb823..3982b9a0e77 100644
--- a/Event/EventBookkeeperTools/EventBookkeeperTools/BookkeeperTool.h
+++ b/Event/EventBookkeeperTools/EventBookkeeperTools/BookkeeperTool.h
@@ -53,23 +53,28 @@ public:
    virtual StatusCode finalize();
 
 private:
-  /// Update an existing (possibly empty) xAOD::CutBookkeeperContainer with
-  /// all the information from the container(s) form the input file
-  StatusCode updateCollFromInputStore( xAOD::CutBookkeeperContainer* coll,
-                                       const std::string &collName );
   
   /// Helper class to update a container with information from another one
   StatusCode updateContainer( xAOD::CutBookkeeperContainer* contToUpdate,
                               const xAOD::CutBookkeeperContainer* otherCont );
+
+  /// Fill Cutflow information
+  StatusCode addCutFlow();
  
   /// Pointer to cut flow svc 
   //ServiceHandle<ICutFlowSvc> m_cutflowsvc;
 
-  /// The name of the completed, i.e., fully processed, CutBookkeeperContainer
-  std::string m_completeCollName;
+  /// The name of the output CutBookkeeperContainer
+  std::string m_outputCollName;
   
-  /// The name of the incomplete, i.e., not fully processed (e.g. failed job), CutBookkeeperContainer
-  std::string  m_incompleteCollName;
+  /// The name of the input CutBookkeeperContainer
+  std::string  m_inputCollName;
+
+  /// The name of the CutFlowSvc CutBookkeeperContainer
+  std::string m_cutflowCollName;
+
+  bool m_cutflowTaken;
+
 };
 
 #endif
diff --git a/Event/EventBookkeeperTools/Root/BookkeeperTool.cxx b/Event/EventBookkeeperTools/Root/BookkeeperTool.cxx
index b36770f9c1a..b9c3a2e3f51 100644
--- a/Event/EventBookkeeperTools/Root/BookkeeperTool.cxx
+++ b/Event/EventBookkeeperTools/Root/BookkeeperTool.cxx
@@ -23,12 +23,15 @@
 
 
 BookkeeperTool::BookkeeperTool(const std::string& name)
-  : asg::AsgMetadataTool(name)
+  : asg::AsgMetadataTool(name),
+    m_cutflowTaken(false)
 {
-  declareProperty("OutputCollName", m_completeCollName="CutBookkeepers",  
-    "The default name of the xAOD::CutBookkeeperContainer for fully processed files");
-  declareProperty("OutputIncompleteCollName", m_incompleteCollName = "IncompleteCutBookkeepers",
-    "The default name of the xAOD::CutBookkeeperContainer for partially processed files");
+  declareProperty("OutputCollName", m_outputCollName="CutBookkeepers",  
+    "The default name of the xAOD::CutBookkeeperContainer for output files");
+  declareProperty("InputCollName", m_inputCollName = "CutBookkeepers",
+    "The default name of the xAOD::CutBookkeeperContainer for input files");
+  declareProperty("CutFlowCollName", m_cutflowCollName = "CutBookkeepersFile",
+    "The default name of the xAOD::CutBookkeeperContainer for CutFlowSvc");
 #ifdef ASGTOOL_ATHENA
   declareInterface< ::IMetaDataTool >( this );
 #endif // ASGTOOL_ATHENA
@@ -46,8 +49,10 @@ StatusCode
 BookkeeperTool::initialize()
 {
   ATH_MSG_DEBUG( "Initializing " << name() << " - package version " << PACKAGE_VERSION );
-  ATH_MSG_DEBUG("OutputCollName = " << m_completeCollName);
-  ATH_MSG_DEBUG("OutputIncompleteCollName = " << m_incompleteCollName);
+
+  ATH_MSG_DEBUG("InputCollName = " << m_inputCollName);
+  ATH_MSG_DEBUG("OutputCollName = " << m_outputCollName);
+  ATH_MSG_DEBUG("CutFlowCollName = " << m_cutflowCollName);
 
   return StatusCode::SUCCESS;
 }
@@ -63,43 +68,50 @@ StatusCode BookkeeperTool::beginInputFile()
   //    2a) if incomplete from input, directly propagate to output
   //    2b) if complete from input, wait for EndInputFile to decide what to do in output
 
+  // reset cutflow taken marker
+  m_cutflowTaken = false;
+
   // Get the incomplete bookkeeper collection of the input metadata store
   const xAOD::CutBookkeeperContainer* input_inc = 0;
-  StatusCode ssc = inputMetaStore()->retrieve( input_inc, m_incompleteCollName );
+  // Construct input and output incomplete names
+  std::string inCollName = "Incomplete" + m_inputCollName;
+  std::string outCollName = "Incomplete" + m_outputCollName;
+  StatusCode ssc = inputMetaStore()->retrieve( input_inc, inCollName );
   if (ssc.isSuccess()) {
-  //if ( (inputMetaStore()->retrieve( input_inc, m_incompleteCollName )).isSuccess() ) {
     // First make sure there is an incomplete container in the output store
-    if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(m_incompleteCollName)) ) {
+    if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(outCollName)) ) {
       xAOD::CutBookkeeperContainer* inc = new xAOD::CutBookkeeperContainer();
       xAOD::CutBookkeeperAuxContainer* auxinc = new xAOD::CutBookkeeperAuxContainer();
       inc->setStore(auxinc);
-      ATH_CHECK(outputMetaStore()->record(inc,m_incompleteCollName));
-      ATH_CHECK(outputMetaStore()->record(auxinc,m_incompleteCollName+"Aux."));
+      ATH_CHECK(outputMetaStore()->record(inc,outCollName));
+      ATH_CHECK(outputMetaStore()->record(auxinc,outCollName+"Aux."));
     }
     // retrieve the incomplete output container
     xAOD::CutBookkeeperContainer* incompleteBook(NULL);
-    ATH_CHECK(outputMetaStore()->retrieve( incompleteBook, m_incompleteCollName));
+    ATH_CHECK(outputMetaStore()->retrieve( incompleteBook, outCollName));
     // update incomplete output with any incomplete input
     ATH_CHECK(this->updateContainer(incompleteBook,input_inc));
     ATH_MSG_DEBUG("Successfully merged input incomplete bookkeepers with output");
   }
   else {
-    ATH_MSG_INFO("No incomplete bookkeepers in this file " << m_incompleteCollName);
+    ATH_MSG_INFO("No incomplete bookkeepers in this file " << inCollName);
   }
 
   // Get the complete bookkeeper collection of the input metadata store
   const xAOD::CutBookkeeperContainer* input_com = 0;
-  if ( (inputMetaStore()->retrieve( input_com, m_completeCollName )).isSuccess() ) {
+  inCollName = m_inputCollName;
+  outCollName = m_outputCollName;
+  if ( (inputMetaStore()->retrieve( input_com, inCollName )).isSuccess() ) {
     // Check if a tmp is there. IT SHOULD NOT BE
     //xAOD::CutBookkeeperContainer* incompleteBook(NULL);
-    if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(m_completeCollName+"tmp")) ) {
+    if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(outCollName+"tmp")) ) {
       // Now create the tmp container
       xAOD::CutBookkeeperContainer* tmp = new xAOD::CutBookkeeperContainer();
       xAOD::CutBookkeeperAuxContainer* auxtmp = new xAOD::CutBookkeeperAuxContainer();
       tmp->setStore(auxtmp);
       if (updateContainer(tmp,input_com).isSuccess()) {
-        ATH_CHECK(outputMetaStore()->record(tmp,m_completeCollName+"tmp"));
-        ATH_CHECK(outputMetaStore()->record(auxtmp,m_completeCollName+"tmpAux."));
+        ATH_CHECK(outputMetaStore()->record(tmp,outCollName+"tmp"));
+        ATH_CHECK(outputMetaStore()->record(auxtmp,outCollName+"tmpAux."));
       }
       else {
         ATH_MSG_WARNING("Could not update tmp container from input complete conatiner");
@@ -111,39 +123,53 @@ StatusCode BookkeeperTool::beginInputFile()
     }
     ATH_MSG_DEBUG("Successfully copied complete bookkeepers to temp container");
   }
-  
-  return StatusCode::SUCCESS;
-}
-
 
-StatusCode BookkeeperTool::endInputFile()
-{
-  //CLOSING INPUT FILE
-  //Things to do:
+  //  Now make sure the output containers are in the output store
+  //
   //  Make sure complete container exists in output
-  if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(m_completeCollName)) ) {
+  if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(m_outputCollName)) ) {
       // Now create the complete container
     xAOD::CutBookkeeperContainer* inc = new xAOD::CutBookkeeperContainer();
     xAOD::CutBookkeeperAuxContainer* auxinc = new xAOD::CutBookkeeperAuxContainer();
     inc->setStore(auxinc);
-    ATH_CHECK(outputMetaStore()->record(inc,m_completeCollName));
-    ATH_CHECK(outputMetaStore()->record(auxinc,m_completeCollName+"Aux."));
+    ATH_CHECK(outputMetaStore()->record(inc,m_outputCollName));
+    ATH_CHECK(outputMetaStore()->record(auxinc,m_outputCollName+"Aux."));
   }
   else {
     ATH_MSG_WARNING("complete collection already exists");
     //return StatusCode::SUCCESS;
   }
+  //  Make sure incomplete container exists in output
+  std::string inc_name = "Incomplete"+m_outputCollName;
+  if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(inc_name)) ) {
+      // Now create the complete container
+    xAOD::CutBookkeeperContainer* coll = new xAOD::CutBookkeeperContainer();
+    xAOD::CutBookkeeperAuxContainer* auxcoll = new xAOD::CutBookkeeperAuxContainer();
+    coll->setStore(auxcoll);
+    ATH_CHECK(outputMetaStore()->record(coll,inc_name));
+    ATH_CHECK(outputMetaStore()->record(auxcoll,inc_name+"Aux."));
+  }
+  else {
+    ATH_MSG_WARNING("incomplete collection already exists");
+  }
+  
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode BookkeeperTool::endInputFile()
+{
 
   // Get the complete bookkeeper collection of the output meta-data store
   xAOD::CutBookkeeperContainer* completeBook(NULL); 
-  if( !(outputMetaStore()->retrieve( completeBook, m_completeCollName) ).isSuccess() ) {
+  if( !(outputMetaStore()->retrieve( completeBook, m_outputCollName) ).isSuccess() ) {
     ATH_MSG_ERROR( "Could not get complete CutBookkeepers from output MetaDataStore" );
     return StatusCode::FAILURE;
   }
 
   // Get the tmp bookkeeper from the input
   const xAOD::CutBookkeeperContainer* tmpCompleteBook(NULL);
-  if( !(outputMetaStore()->retrieve( tmpCompleteBook, m_completeCollName+"tmp") ).isSuccess() ) {
+  if( !(outputMetaStore()->retrieve( tmpCompleteBook, m_outputCollName+"tmp") ).isSuccess() ) {
     ATH_MSG_WARNING( "Could not get tmp CutBookkeepers from output MetaDataStore" );
   }
   else {
@@ -154,7 +180,36 @@ StatusCode BookkeeperTool::endInputFile()
     ATH_CHECK(outputMetaStore()->removeDataAndProxy(tmpCompleteBook));
     ATH_CHECK(outputMetaStore()->removeDataAndProxy(tmpCompleteBookAux));
   }
-  
+
+/*
+  if (!m_cutflowTaken) {
+    // Get the bookkeeper from the current processing
+    const xAOD::CutBookkeeperContainer* fileCompleteBook(NULL);
+    if( !(outputMetaStore()->retrieve( fileCompleteBook, m_cutflowCollName) ).isSuccess() ) {
+      ATH_MSG_WARNING( "Could not get CutFlowSvc CutBookkeepers from output MetaDataStore" );
+    }
+    else {
+      // update the complete output with the complete input
+      ATH_CHECK(this->updateContainer(completeBook,fileCompleteBook));
+      // Set flag for cutflow container to false
+      m_cutflowTaken = true;
+    }
+  }
+  else {
+    ATH_MSG_DEBUG("Cutflow information written into container before endInputFile");
+  }
+
+*/
+  if (!m_cutflowTaken) {
+    if (addCutFlow().isFailure()) {
+      ATH_MSG_ERROR("Could not add CutFlow information");
+    }
+    m_cutflowTaken = true;
+  }
+  else {
+    ATH_MSG_DEBUG("Cutflow information written into container before endInputFile");
+  }
+    
   return StatusCode::SUCCESS;
 }
 
@@ -165,29 +220,19 @@ StatusCode BookkeeperTool::metaDataStop()
   // 1) Create new incomplete CutBookkeepers if relevant
   // 2) Print cut flow summary
   // 3) Write root file if requested
-  //  Make sure complete container exists in output
-  if( !(outputMetaStore()->contains<xAOD::CutBookkeeperContainer>(m_incompleteCollName)) ) {
-      // Now create the complete container
-    xAOD::CutBookkeeperContainer* coll = new xAOD::CutBookkeeperContainer();
-    xAOD::CutBookkeeperAuxContainer* auxcoll = new xAOD::CutBookkeeperAuxContainer();
-    coll->setStore(auxcoll);
-    ATH_CHECK(outputMetaStore()->record(coll,m_incompleteCollName));
-    ATH_CHECK(outputMetaStore()->record(auxcoll,m_incompleteCollName+"Aux."));
-  }
-  else {
-    ATH_MSG_WARNING("incomplete collection already exists");
-  }
+  //  Make sure incomplete container exists in output
+  std::string inc_name = "Incomplete"+m_outputCollName;
 
   // Get the complete bookkeeper collection of the output meta-data store
   xAOD::CutBookkeeperContainer* incompleteBook(NULL); 
-  if( !(outputMetaStore()->retrieve( incompleteBook, m_incompleteCollName) ).isSuccess() ) {
+  if( !(outputMetaStore()->retrieve( incompleteBook, inc_name) ).isSuccess() ) {
     ATH_MSG_WARNING( "Could not get incomplete CutBookkeepers from output MetaDataStore" );
   }
 
   // Get the tmp bookkeeper from the input
   const xAOD::CutBookkeeperContainer* tmpCompleteBook(NULL);
-  if( !(outputMetaStore()->retrieve( tmpCompleteBook, m_completeCollName+"tmp") ).isSuccess() ) {
-    ATH_MSG_WARNING( "Could not get tmp CutBookkeepers from output MetaDataStore for " << m_completeCollName+"tmp");
+  if( !(outputMetaStore()->retrieve( tmpCompleteBook, m_outputCollName+"tmp") ).isSuccess() ) {
+    ATH_MSG_WARNING( "Could not get tmp CutBookkeepers from output MetaDataStore for " << m_outputCollName+"tmp");
   }
   else {
     // update the complete output with the complete input
@@ -198,6 +243,36 @@ StatusCode BookkeeperTool::metaDataStop()
     ATH_CHECK(outputMetaStore()->removeDataAndProxy(tmpCompleteBookAux));
   }
 
+/*
+  if (!m_cutflowTaken) {
+    // Get the bookkeeper from the current processing
+    const xAOD::CutBookkeeperContainer* fileCompleteBook(NULL);
+    if( !(outputMetaStore()->retrieve( fileCompleteBook, m_cutflowCollName) ).isSuccess() ) {
+      ATH_MSG_WARNING( "Could not get CutFlowSvc CutBookkeepers from output MetaDataStore" );
+    }
+    else {
+      // update the complete output with the complete input
+      ATH_CHECK(this->updateContainer(incompleteBook,fileCompleteBook));
+      // Set flag for cutflow container to false
+      m_cutflowTaken = true;
+    }
+  }
+  else {
+    ATH_MSG_DEBUG("Cutflow information written into container before metaDataStop");
+  }
+*/  
+  if (!m_cutflowTaken) {
+    if (addCutFlow().isFailure()) {
+      ATH_MSG_ERROR("Could not add CutFlow information");
+    }
+  }
+  else {
+    ATH_MSG_DEBUG("Cutflow information written into container before metaDataStop");
+  }
+
+  // Reset after metadata stop
+  m_cutflowTaken = false;
+  
   return StatusCode::SUCCESS;
 }
 
@@ -209,40 +284,7 @@ BookkeeperTool::finalize()
   return StatusCode::SUCCESS;
 }
 
-
-
-StatusCode
-BookkeeperTool::updateCollFromInputStore(xAOD::CutBookkeeperContainer* coll,
-                                     const std::string &collName)
-{
-  ATH_MSG_DEBUG("calling updateCollFromInputStore(" << collName << ") with coll-size=" << coll->size() );
-
-  if ( inputMetaStore()->contains<xAOD::CutBookkeeperContainer>(collName) ) {
-    ATH_MSG_VERBOSE("Input MetaData contains type xAOD::CutBookkeeperContainer with name" << collName);
-
-    // There can always only be a single object in the input store. As the store
-    // is connected to just a single input file.
-    const xAOD::CutBookkeeperContainer* tmpColl = 0;
-    ATH_CHECK( inputMetaStore()->retrieve( tmpColl, collName ) );
-
-    // Check that we succeeded:
-    if( ! tmpColl->hasStore() ) {
-       ATH_MSG_FATAL( "Object doesn't have an auxiliary store" );
-       return StatusCode::FAILURE;
-    }
-
-    //...and update coll with each list.
-    ATH_CHECK( this->updateContainer(coll, tmpColl) );
-  }
-  else {
-    ATH_MSG_INFO( "Cannot find xAOD::CutBookkeeperContainer "
-                  << "with name " << collName << " in the input file.");
-  }
-  return StatusCode::SUCCESS;
-}
-
-
-
+/*
 StatusCode
 BookkeeperTool::updateContainer( xAOD::CutBookkeeperContainer* contToUpdate,
                              const xAOD::CutBookkeeperContainer* otherCont ) {
@@ -379,5 +421,167 @@ BookkeeperTool::updateContainer( xAOD::CutBookkeeperContainer* contToUpdate,
   } // Done fixing all cross references
   return StatusCode::SUCCESS;
 }
+*/
+
+
+StatusCode BookkeeperTool::addCutFlow()
+{
+  // Add the information from the current processing to the complete output
+  // --> same paradigm as original CutFlowSvc
+  // Get the complete bookkeeper collection of the output meta-data store
+  xAOD::CutBookkeeperContainer* completeBook(NULL); 
+  if( !(outputMetaStore()->retrieve( completeBook, m_outputCollName) ).isSuccess() ) {
+    ATH_MSG_ERROR( "Could not get complete CutBookkeepers from output MetaDataStore" );
+    return StatusCode::FAILURE;
+  }
+
+  // Get the bookkeeper from the current processing
+  const xAOD::CutBookkeeperContainer* fileCompleteBook(NULL);
+  if( !(outputMetaStore()->retrieve( fileCompleteBook, m_cutflowCollName) ).isSuccess() ) {
+    ATH_MSG_WARNING( "Could not get CutFlowSvc CutBookkeepers from output MetaDataStore" );
+  }
+  else {
+    // update the complete output with the complete input
+    ATH_CHECK(this->updateContainer(completeBook,fileCompleteBook));
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+namespace {
+
+
+xAOD::CutBookkeeper*
+resolveLink (const xAOD::CutBookkeeper* old,
+             xAOD::CutBookkeeperContainer& contToUpdate,
+             const xAOD::CutBookkeeperContainer& otherCont,
+             const std::vector<size_t>& otherIndices)
+{
+  {
+    xAOD::CutBookkeeperContainer::iterator matchIter = 
+      std::find( contToUpdate.begin(),
+                 contToUpdate.end(),
+                 old );
+    if (matchIter != contToUpdate.end())
+      return *matchIter;
+  }
+
+  {
+    xAOD::CutBookkeeperContainer::const_iterator matchIter = 
+      std::find( otherCont.begin(),
+                 otherCont.end(),
+                 old );
+    if (matchIter != contToUpdate.end()) {
+      size_t pos = matchIter - otherCont.begin();
+      return contToUpdate[otherIndices[pos]];
+    }
+  }
+
+  // If we didn't find it, we need to add it
+  xAOD::CutBookkeeper* newEBK = new xAOD::CutBookkeeper();
+  if ( newEBK->usingPrivateStore() ) { newEBK->releasePrivateStore(); }
+  newEBK->makePrivateStore(old);
+  contToUpdate.push_back( newEBK );
+  return newEBK;
+}
+
+
+} // anonymous namespace
+
+
+StatusCode
+BookkeeperTool::updateContainer( xAOD::CutBookkeeperContainer* contToUpdate,
+                             const xAOD::CutBookkeeperContainer* otherCont ) 
+{
+  ATH_MSG_DEBUG("calling updateContainer(...)" );
+  ATH_MSG_VERBOSE("Have container to update with size=" << contToUpdate->size()
+                  << ", and other container with size=" << otherCont->size() );
+
+  size_t origSize = contToUpdate->size();
+
+  // Vector of indices in contToUpdate of elements in otherCont.
+  std::vector< std::size_t > otherIndices (otherCont->size());
+  // Loop through otherCont.
+  // If element already in contToUpdate, update event counts, otherwise create new element
+  for ( std::size_t i=0; i<otherCont->size(); ++i ) {
+    const xAOD::CutBookkeeper* otherEBK = otherCont->at(i);
+    ATH_MSG_VERBOSE("Looping through otherCont at index " << i);
+    ATH_MSG_VERBOSE("Have otherEBK with: name=" << otherEBK->name()
+                    << ", cycle=" << otherEBK->cycle()
+                    << ", nAcceptedEvents=" << otherEBK->nAcceptedEvents()
+                    << ", inputStream=" << otherEBK->inputStream() );
+
+
+    // Loop through the container to be updated (contToUpdate) and see if we find a match
+    bool foundEBKToUpdate(false);
+    for ( std::size_t j=0; j<contToUpdate->size(); ++j ) {
+      xAOD::CutBookkeeper* ebkToUpdate = contToUpdate->at(j);
+      // Check if they are identical, if so, update; else add otherEBK
+      if ( otherEBK->isEqualTo(ebkToUpdate) ) {
+        ebkToUpdate->setPayload( ebkToUpdate->payload() + otherEBK->payload() );
+        otherIndices[i] = j;
+        foundEBKToUpdate = true;
+        break;
+      }
+    } // End: Inner loop over contToUpdate
+    if (!foundEBKToUpdate) {
+      xAOD::CutBookkeeper* newEBK = new xAOD::CutBookkeeper();
+      if ( newEBK->usingPrivateStore() ) { newEBK->releasePrivateStore(); }
+      newEBK->makePrivateStore(otherEBK);
+      contToUpdate->push_back( newEBK );
+      std::size_t ebIdx = newEBK->index();
+      otherIndices[i] = ebIdx;
+    }
+  } // End: Outer loop over contToUpdate
 
+  // Now, we still need to fix the cross-referencing of the newly added CutBookkkeepers
+  for ( std::size_t i=origSize; i<contToUpdate->size(); ++i ) {
+    xAOD::CutBookkeeper* ebkToModify = contToUpdate->at(i);
+
+    // Parent check
+    if ( ebkToModify->hasParent() ) {
+      const xAOD::CutBookkeeper* oldParent = ebkToModify->parent();
+      const xAOD::CutBookkeeper* newParent = resolveLink (oldParent,
+                                                          *contToUpdate,
+                                                          *otherCont,
+                                                          otherIndices);
+      ebkToModify->setParent (newParent);
+    } // Done fixing parent
+
+    // Children check
+    std::vector< xAOD::CutBookkeeper* > newChildren;
+    for ( std::size_t oldIdx=0; oldIdx<ebkToModify->nChildren(); ++oldIdx ) {
+      const xAOD::CutBookkeeper* oldEBK = ebkToModify->child(oldIdx);
+      newChildren.push_back (resolveLink (oldEBK,
+                                          *contToUpdate,
+                                          *otherCont,
+                                          otherIndices));
+    } // Done fixing children
+    ebkToModify->setChildren (newChildren);
+
+    // Used others check
+    std::vector< xAOD::CutBookkeeper* > newOthers;
+    for ( std::size_t oldIdx=0; oldIdx<ebkToModify->nUsedOthers(); ++oldIdx ) {
+      const xAOD::CutBookkeeper* oldEBK = ebkToModify->usedOther(oldIdx);
+      newOthers.push_back (resolveLink (oldEBK,
+                                        *contToUpdate,
+                                        *otherCont,
+                                        otherIndices));
+    } // Done fixing used others
+    ebkToModify->setUsedOthers (newOthers);
+
+    // Siblings check
+    std::vector< xAOD::CutBookkeeper* > newSiblings;
+    for ( std::size_t oldIdx=0; oldIdx<ebkToModify->nSiblings(); ++oldIdx ) {
+      const xAOD::CutBookkeeper* oldEBK = ebkToModify->sibling(oldIdx);
+      newSiblings.push_back (resolveLink (oldEBK,
+                                          *contToUpdate,
+                                          *otherCont,
+                                          otherIndices));
+    } // Done fixing siblings
+    ebkToModify->setSiblings (newSiblings);
+  } // Done fixing all cross references
+  return StatusCode::SUCCESS;
+}
 
diff --git a/Event/EventBookkeeperTools/python/CutFlowHelpers.py b/Event/EventBookkeeperTools/python/CutFlowHelpers.py
index c71361fa188..5282feb88df 100644
--- a/Event/EventBookkeeperTools/python/CutFlowHelpers.py
+++ b/Event/EventBookkeeperTools/python/CutFlowHelpers.py
@@ -43,9 +43,7 @@ def GetCurrentStreamName( msg, athFile=None ):
 
 
 
-
-
-def CreateCutFlowSvc( svcName="CutFlowSvc", athFile=None, seq=None, addAlgInPlace=False, addMetaDataToAllOutputFiles=True ):
+def CreateCutFlowSvc( svcName="CutFlowSvc", athFile=None, seq=None, addAlgInPlace=False, addMetaDataToAllOutputFiles=True, SGkey="CutBookkeepers" ):
     """
     Helper to create the CutFlowSvc, extract the needed information from
     the input file, and also schedule all the needed stuff.
@@ -61,11 +59,50 @@ def CreateCutFlowSvc( svcName="CutFlowSvc", athFile=None, seq=None, addAlgInPlac
     inputStreamName = GetCurrentStreamName( msg=msg, athFile=athFile )
     msg.debug("CreateCutFlowSvc: Have inputStreamName = %s" % (inputStreamName) )
 
-    # Create the CutFlowSvc instance
+    # Create the CutFlowSvc instance(s)
     import AthenaCommon.CfgMgr as CfgMgr
     if not hasattr(svcMgr,"CutFlowSvc"): svcMgr += CfgMgr.CutFlowSvc()
     svcMgr.CutFlowSvc.InputStream   = inputStreamName
-    # svcMgr.CutFlowSvc.OutputLevel = 1 #=VERBOSE
+    #if not hasattr(svcMgr,"FileCutFlowSvc"): svcMgr += CfgMgr.FileCutFlowSvc()
+    #svcMgr.FileCutFlowSvc.InputStream   = inputStreamName
+
+    # Make sure MetaDataSvc is ready
+    if not hasattr(svcMgr,'MetaDataSvc'):
+      from EventSelectorAthenaPool.EventSelectorAthenaPoolConf import MetaDataSvc
+      svcMgr += MetaDataSvc( "MetaDataSvc" )
+
+    # Add BookkeeperTools
+    from EventBookkeeperTools.EventBookkeeperToolsConf import BookkeeperTool
+
+    # Standard event bookkeepers
+    inname = "CutBookkeepers"
+    outname = "FileBookkeepers"
+    cutflowtool = BookkeeperTool(outname,
+                                 OutputLevel = 2, 
+                                 InputCollName = inname,
+                                 OutputCollName= outname) 
+    svcMgr.ToolSvc += cutflowtool
+
+    # Add tool to MetaDataSvc
+    svcMgr.MetaDataSvc.MetaDataTools += [cutflowtool]
+
+    # Add pdf sum of weights counts if appropriate
+    from AthenaCommon.GlobalFlags  import globalflags
+    if globalflags.DataSource() == 'geant4':
+        #from PyUtils import AthFile
+        #afc = AthFile.fopen( svcMgr.EventSelector.InputCollections[0] )
+
+        # PDF
+        name = "PDFSumOfWeights"
+        pdfweighttool = BookkeeperTool(name,
+                                       OutputLevel = 2, 
+                                       OutputCollName= name, 
+                                       InputCollName = name)
+        svcMgr.ToolSvc += pdfweighttool
+
+        # Add tool to MetaDataSvc
+        svcMgr.MetaDataSvc.MetaDataTools += [pdfweighttool]
+
 
     # Check if we have a sequence given
     if not seq :
@@ -104,10 +141,38 @@ def CreateCutFlowSvc( svcName="CutFlowSvc", athFile=None, seq=None, addAlgInPlac
         from OutputStreamAthenaPool.MultipleStreamManager import MSMgr
         # Explicitely add file metadata from input and from transient store,
         # but only the ones that we always create.
-        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperContainer#CutBookkeepers" )
-        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperAuxContainer#CutBookkeepersAux.*" )
-        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperContainer#IncompleteCutBookkeepers" )
-        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperAuxContainer#IncompleteCutBookkeepersAux.*" )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperContainer#"+SGkey )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperAuxContainer#"+SGkey+"Aux.*" )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperContainer#Incomplete"+SGkey )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperAuxContainer#Incomplete"+SGkey+"Aux.*" )
+        SGkey = "FileBookkeepers"
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperContainer#"+SGkey )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperAuxContainer#"+SGkey+"Aux.*" )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperContainer#Incomplete"+SGkey )
+        MSMgr.AddMetaDataItemToAllStreams( "xAOD::CutBookkeeperAuxContainer#Incomplete"+SGkey+"Aux.*" )
         pass
 
     return
+
+def CreateBookkeeperTool( name="CutBookkeepers" ):
+
+  from AthenaCommon.AppMgr  import ServiceMgr as svcMgr
+
+  # Make sure MetaDataSvc is ready
+  if not hasattr(svcMgr,'MetaDataSvc'):
+    from EventSelectorAthenaPool.EventSelectorAthenaPoolConf import MetaDataSvc
+    svcMgr += MetaDataSvc( "MetaDataSvc" )
+
+  # Add BookkeeperTools
+  from EventBookkeeperTools.EventBookkeeperToolsConf import BookkeeperTool
+
+  # Standard event bookkeepers
+  cutflowtool = BookkeeperTool(name,
+                               InputCollName=name,
+                               OutputCollName = name)
+  svcMgr.ToolSvc += cutflowtool
+
+  # Add tool to MetaDataSvc
+  svcMgr.MetaDataSvc.MetaDataTools += [cutflowtool]
+
+  return
diff --git a/Event/EventBookkeeperTools/src/EventCounterAlg.cxx b/Event/EventBookkeeperTools/src/EventCounterAlg.cxx
index 21f3e991ede..6483fbc951e 100644
--- a/Event/EventBookkeeperTools/src/EventCounterAlg.cxx
+++ b/Event/EventBookkeeperTools/src/EventCounterAlg.cxx
@@ -109,7 +109,10 @@ StatusCode EventCounterAlg::execute()
         std::stringstream sstm2;
         sstm2 << "non-nominal MC event weight number " << i;
         const std::string& cutDescription = sstm2.str();
-        CutIdentifier cutID = cutFlowSvc()->registerCut( cutName, cutDescription, this->cutID() );
+        CutIdentifier cutID = cutFlowSvc()->registerTopFilter( cutName,
+                                                               cutDescription,
+                                                               xAOD::CutBookkeeper::CutLogic::ALLEVENTSPROCESSED,
+                                                               "AllStreams" );
         m_mcCutIDs.push_back(cutID);
       }
     }
diff --git a/Event/EventBookkeeperTools/src/FileCutFlowSvc.cxx b/Event/EventBookkeeperTools/src/FileCutFlowSvc.cxx
new file mode 100644
index 00000000000..ef0957997f8
--- /dev/null
+++ b/Event/EventBookkeeperTools/src/FileCutFlowSvc.cxx
@@ -0,0 +1,549 @@
+///////////////////////// -*- C++ -*- /////////////////////////////
+
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Implementation file for class FileCutFlowSvc
+// Authors: Joao Firmino da Costa <joao.costa@cern.ch> and David Cote <david.cote@cern.ch>
+///////////////////////////////////////////////////////////////////
+
+#include "FileCutFlowSvc.h"
+
+// STL include
+#include <algorithm>
+
+// #include "FillEBCFromFlat.h"
+
+#include "GaudiKernel/Incident.h"
+#include "GaudiKernel/FileIncident.h"
+#include "StoreGate/StoreClearedIncident.h"
+#include "GaudiKernel/IIncidentSvc.h"
+#include <TTree.h>
+#include <TFile.h>
+
+// EDM includes
+#include "xAODCutFlow/CutBookkeeper.h"
+#include "xAODCutFlow/CutBookkeeperContainer.h"
+#include "xAODCutFlow/CutBookkeeperAuxContainer.h"
+#include "EventBookkeeperMetaData/EventBookkeeperCollection.h"
+#include "EventBookkeeperMetaData/EventBookkeeper.h"
+#include "xAODEventInfo/EventInfo.h"
+#include "EventInfo/EventStreamInfo.h"
+
+
+FileCutFlowSvc::FileCutFlowSvc(const std::string& name,
+                       ISvcLocator* pSvcLocator ) :
+  AthService(name, pSvcLocator),
+  m_outMetaDataStore("StoreGateSvc/MetaDataStore", name),
+  m_inMetaDataStore("StoreGateSvc/InputMetaDataStore", name),
+  m_eventStore("StoreGateSvc", name),
+  m_completeCollName(""),
+  m_incompleteCollName(""),
+  m_skimmingCycle(0),
+  m_fileCollName("CutBookkeepersFile"),
+  m_inputStream(""),
+  m_alreadyDeterminedCycleNumber(false)
+{
+  declareProperty("OutputCollName",           m_completeCollName="CutBookkeepers",
+    "DEFUNCT - handled by tool now");
+  declareProperty("OutputIncompleteCollName", m_incompleteCollName = "IncompleteCutBookkeepers",
+    "DEFUNCT - handled by tool now");
+  declareProperty("SkimmingCycle",            m_skimmingCycle = 0, "DEFUNCT - handled automatically");
+  declareProperty("InputStream",              m_inputStream = "N/A", "The name of the input file stream");
+  assert( pSvcLocator );
+}
+
+
+
+FileCutFlowSvc::~FileCutFlowSvc()
+{
+}
+
+
+
+StatusCode
+FileCutFlowSvc::initialize()
+{
+  ATH_MSG_DEBUG( "Initializing " << name() << " - package version " << PACKAGE_VERSION );
+
+  //Get output MetaData StoreGate
+  ATH_CHECK( m_outMetaDataStore.retrieve() );
+  //Get input MetaData StoreGate
+  ATH_CHECK( m_inMetaDataStore.retrieve() );
+  //Get Event StoreGate
+  ATH_CHECK( m_eventStore.retrieve() );
+
+  // Align File name with complete name
+  m_fileCollName = m_completeCollName + "File";
+
+  //Get IncidentSvc
+  ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", this->name());
+  ATH_CHECK( incSvc.retrieve() );
+  incSvc->addListener(this, IncidentType::BeginInputFile, 60); // pri has to be < 100 to be after MetaDataSvc.
+  incSvc->addListener(this, IncidentType::EndInputFile, 50); // pri has to be > 10 to be before MetaDataSvc.
+  incSvc->addListener(this, "MetaDataStop", 50);
+
+  xAOD::CutBookkeeperContainer* fileBook(NULL);
+  fileBook = new xAOD::CutBookkeeperContainer();
+  ATH_CHECK( recordCollection( fileBook, m_fileCollName) );
+  if (m_outMetaDataStore->retrieve(m_completeBook,m_fileCollName).isFailure()) {
+    ATH_MSG_ERROR("Could not retrieve handle to cutflowsvc bookkeeper");
+    //return StatusCode::RECOVERABLE;
+  }
+
+  // Determine the skimming cycle number that we should use now from the input file
+  ATH_MSG_VERBOSE("Have currently the cycle number = " << m_skimmingCycle );
+  ATH_CHECK( this->determineCycleNumberFromInput(m_completeCollName) );
+  ATH_MSG_VERBOSE("Will use cycle number = " << m_skimmingCycle );
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode
+FileCutFlowSvc::stop()
+{
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode
+FileCutFlowSvc::finalize()
+{
+  ATH_MSG_DEBUG( "Finalizing " << name() << " - package version " << PACKAGE_VERSION );
+  return StatusCode::SUCCESS;
+}
+
+
+
+StatusCode
+FileCutFlowSvc::queryInterface( const InterfaceID& riid, void** ppvi )
+{
+  // valid placeholder?
+  if ( 0 == ppvi ) { return StatusCode::FAILURE ; }  // RETURN
+  if ( ICutFlowSvc::interfaceID() == riid ) {
+    *ppvi = static_cast<ICutFlowSvc*>(this);
+    addRef(); // NB! : inrement the reference count!
+    return StatusCode::SUCCESS;                     // RETURN
+  }
+  // Interface is not directly availible: try out a base class
+  return AthService::queryInterface( riid, ppvi );
+}
+
+
+CutIdentifier FileCutFlowSvc::registerFilter( const std::string& name,
+                                          const std::string& description )
+{
+  ATH_MSG_DEBUG("calling registerFilter(" << name << ", " << description << ")" );
+
+  xAOD::CutBookkeeper* tmpEBK = new xAOD::CutBookkeeper();
+  tmpEBK->setName(name);
+  tmpEBK->setDescription(description);
+  tmpEBK->setInputStream(m_inputStream);
+  tmpEBK->setCycle(m_skimmingCycle);
+  CutIdentifier cutID = tmpEBK->uniqueIdentifier();
+
+  // Let's see if an CutBookkeeper of this name already exists
+  ATH_MSG_VERBOSE("in registerFilter(" << name << ", " << description << "): "
+                  << "Going to search if this CutBookkeeper already exists" );
+  bool existsAlready=false;
+  for ( std::size_t i=0; i<m_completeBook->size(); ++i ) {
+    xAOD::CutBookkeeper* ebk = m_completeBook->at(i);
+    if( tmpEBK->isEqualTo( ebk) ) {
+      ATH_MSG_DEBUG("The CutBookkeeper with name " << name
+                    << " and cutID " << cutID << " already exists... not adding!" );
+      existsAlready = true;
+      cutID = ebk->uniqueIdentifier();
+      break;
+    }
+  }
+
+  ATH_MSG_VERBOSE("in registerFilter(" << name << ", " << description << "): "
+                  << "Found it: " << existsAlready );
+
+  // If this CutBookkeeper already exists, delete the temporary
+  // and return the existing cutID
+  if ( existsAlready ) {
+    delete tmpEBK;
+    return cutID;
+  }
+
+  // If it is a new CutBookkeeper, add it to the container
+  ATH_MSG_DEBUG( "You are DECLARING a new filter of name " << name
+                 << " and cutID " << cutID );
+  m_completeBook->push_back(tmpEBK);
+
+  ATH_MSG_VERBOSE("done calling registerFilter(" << name << ", " << description << ")" );
+  return cutID;
+}
+
+
+
+
+CutIdentifier FileCutFlowSvc::registerCut( const std::string& name,
+                                       const std::string& description,
+                                       CutIdentifier originCutID )
+{
+  ATH_MSG_DEBUG("calling registerCut(" << name << ", " << description
+                << ", " << originCutID << ")" );
+
+  // Get the CutBookkeeper of the origin Filter Algorithm/Tool
+  xAOD::CutBookkeeper *originEBK = this->getCutBookkeeper(originCutID);
+  if ( !originEBK ) {
+    ATH_MSG_ERROR( "(file: " __FILE__ << ", line: " << __LINE__ << ") "
+                   << "Couldn't find CutBookkeeper with cutID " << originCutID );
+    return 0;
+  }
+
+  // Call the registerFilter method and get the correct CutBookkeeper
+  // from the returned cutID
+  CutIdentifier cutID = this->registerFilter( name, description );
+  xAOD::CutBookkeeper* ebk = this->getCutBookkeeper(cutID);
+  if ( !ebk ) {
+    ATH_MSG_ERROR( "(file: " __FILE__ << ", line: " << __LINE__ << ") "
+                   << "Couldn't find CutBookkeeper with cutID " << cutID );
+    return 0;
+  }
+  originEBK->addUsedOther( ebk );
+
+  return cutID;
+}
+
+
+
+
+// This method is probably only called by the DecionsSvc
+CutIdentifier FileCutFlowSvc::registerTopFilter( const std::string& name,
+                                             const std::string& description,
+                                             unsigned int logic,
+                                             const std::string& outputStream ) 
+{
+  ATH_MSG_DEBUG("calling registerTopFilter(" << name << ", " << description
+                << ", " << logic << ", " << outputStream << ")" );
+  // Call the registerFilter method and get the correct CutBookkeeper
+  // from the returned cutID
+  CutIdentifier cutID = this->registerFilter( name, description );
+  xAOD::CutBookkeeper* ebk = this->getCutBookkeeper(cutID);
+  if ( !ebk ) {
+    ATH_MSG_ERROR( "(file: " __FILE__ << ", line: " << __LINE__ << ") "
+                   << "Couldn't find CutBookkeeper with cutID " << cutID );
+    return 0;
+  }
+
+  //Then set the logic and outputStream of the relevant CutBookkeeper
+  // toFind->setDescription(description);
+  ebk->setCutLogic(xAOD::CutBookkeeper::CutLogic(logic));
+  ebk->setTopFilter(true);
+  ebk->addOutputStreamForAllUsed(outputStream);
+
+  return cutID;
+}
+
+
+
+CutIdentifier FileCutFlowSvc::declareUsedOtherFilter( const std::string& name,
+                                                  CutIdentifier originCutID )
+{
+  ATH_MSG_DEBUG("calling declareUsedOtherFilter(" << name << ", " << originCutID << ")" );
+
+  // Get the CutBookkeeper of the origin cut
+  xAOD::CutBookkeeper *originEBK = this->getCutBookkeeper(originCutID);
+  if ( !originEBK ) {
+    ATH_MSG_ERROR( "(file: " << __FILE__ << ", line: " << __LINE__ << ") "
+                   << "Couldn't find CutBookkeeper with cutID " << originCutID );
+    return 0;
+  }
+
+  // Create a temporary CutBookkeeper object
+  xAOD::CutBookkeeper* tmpEBK = new xAOD::CutBookkeeper();
+  // tmpEBK->makePrivateStore();
+  tmpEBK->setName(name);
+  tmpEBK->setInputStream(m_inputStream);
+  tmpEBK->setCycle(m_skimmingCycle);
+  CutIdentifier cutID = tmpEBK->uniqueIdentifier();
+
+  // See if the CutBookkeeper already exists or not
+  bool existsAlready = false;
+  for ( std::size_t i=0; i<m_completeBook->size(); ++i ) {
+    xAOD::CutBookkeeper* ebk = m_completeBook->at(i);
+    if( tmpEBK->isEqualTo( ebk ) ) {
+      originEBK->addUsedOther( ebk );
+      cutID = ebk->uniqueIdentifier();
+      existsAlready = true;
+    }
+  }
+
+  // If this CutBookkeeper already exists, delete the temporary
+  // and return the existing cutID
+  if ( existsAlready ) {
+    delete tmpEBK;
+    return cutID;
+  }
+
+  // Otherwise, add the new one to the collection
+  tmpEBK->setDescription( "Registered by origin filter" );
+  originEBK->addUsedOther( tmpEBK );
+  m_completeBook->push_back( tmpEBK );
+
+  return cutID;
+}
+
+
+
+void
+FileCutFlowSvc::setFilterDescription( CutIdentifier cutID,
+                                  const std::string& descr )
+{
+  ATH_MSG_DEBUG("calling setFilterDescription(" << cutID << ", " << descr << ")" );
+
+  xAOD::CutBookkeeper* ebk = this->getCutBookkeeper(cutID);
+  ebk->setDescription(descr);
+  return;
+}
+
+
+
+
+void
+FileCutFlowSvc::addEvent( CutIdentifier cutID )
+{
+  ATH_MSG_INFO("calling addEvent(" << cutID << ")" );
+
+  double evtWeight=1.0;
+
+  const xAOD::EventInfo* evtInfo = 0;
+  StatusCode sc = m_eventStore->retrieve(evtInfo);
+  if ( sc.isFailure() || NULL == evtInfo ) {
+    ATH_MSG_WARNING("Could not retrieve EventInfo from StoreGate  ");
+    evtWeight=-1000.;
+  } else {
+    // Only try to access the mcEventWeight is we are running on Monte Carlo, duhhh!
+    if ( evtInfo->eventType(xAOD::EventInfo::IS_SIMULATION) ) {
+      evtWeight = evtInfo->mcEventWeight();
+    }
+  }
+
+  addEvent(cutID,evtWeight);
+
+  return;
+}
+
+
+
+void
+FileCutFlowSvc::addEvent( CutIdentifier cutID, double weight)
+{
+  ATH_MSG_INFO("calling addEvent(" << cutID << ", " << weight << ")" );
+
+  xAOD::CutBookkeeper* eb = this->getCutBookkeeper(cutID);
+  if ( !eb ) {
+    ATH_MSG_INFO("Could not find eb");
+
+    // Iterate over the complete bookkeepers and update the cutID-to-bookkeeper map
+    ATH_MSG_DEBUG( "addEvent: Going to re-populate the map. Have "
+                   << m_completeBook->size() << " CutBookkeepers"
+                   << " and skimming cycle " << m_skimmingCycle
+                   << " and input Stream name " << m_inputStream );
+    xAOD::CutBookkeeperContainer::iterator iter    = m_completeBook->begin();
+    xAOD::CutBookkeeperContainer::iterator iterEnd = m_completeBook->end();
+    for ( ; iter != iterEnd; ++iter ) {
+      xAOD::CutBookkeeper* ebk = *iter;
+      CutIdentifier cutID = ebk->uniqueIdentifier();
+      ATH_MSG_VERBOSE( "addEvent: Have CutBookkeeper with"
+                       << " skimming cycle " << ebk->cycle()
+                       << " and input Stream name " << ebk->inputStream()
+                       << " and logic " << ebk->cutLogic()
+                       << " isTopFilter " << ebk->isTopFilter()
+                       << " and cutID " << cutID
+                       << " and name " << ebk->name() );
+      if ( m_skimmingCycle == ebk->cycle() ) {
+        if ( m_inputStream == ebk->inputStream() ) {
+          CutIDMap_t::iterator mapIter = m_ebkMap.find(cutID);
+          ATH_MSG_DEBUG( "BeginInputFile: Have CutBookkeeper with"
+                         << " cutID " << cutID
+                         <<  " and name " << ebk->name() );
+          if ( mapIter != m_ebkMap.end() ) { // we found this CutBookkeeper in the existing map
+            (*mapIter).second = ebk;
+          }
+        }
+      }
+    } // End: Loop over all complete CutBookkeepers
+    eb = this->getCutBookkeeper(cutID);
+  }
+  if ( !eb ) {
+    ATH_MSG_ERROR ( "Got a zero pointer to an CutBookkeeper with cutID="
+                    << cutID << "! This should never happen..." );
+    return;
+  }
+  ATH_MSG_VERBOSE( "addEvent: have cutID " << cutID
+                   << " and CutBookkeeper name " << eb->name() );
+  eb->addNAcceptedEvents(1);
+  eb->addSumOfEventWeights(weight);
+  eb->addSumOfEventWeightsSquared(weight*weight);
+  return;
+}
+
+
+
+//__________________________________________________________________________
+void FileCutFlowSvc::handle( const Incident& inc )
+{
+  ATH_MSG_VERBOSE( "Start incident " << inc.type() );
+
+  //OPENING NEW INPUT FILE
+  //Things to do:
+  // 1) note that a file is currently opened
+  // 2) Load CutBookkeepers from input file
+  //    2a) if incomplete from input, directly propagate to output
+  //    2b) if complete from input, wait for EndInputFile to decide what to do in output
+
+  if ( inc.type() == IncidentType::BeginInputFile ) {
+    // Check the stream name
+    const EventStreamInfo* esi;
+    if (m_inMetaDataStore->retrieve(esi).isFailure()) {
+      ATH_MSG_WARNING("No EventStreamInfo taking stream from property InputStream");
+    }
+    else {
+      std::string inputstream = *(esi->getProcessingTags().begin());
+      if (m_inputStream.empty()) {m_inputStream=inputstream;}
+      else if (m_inputStream!=inputstream) {
+        const FileIncident* finc = dynamic_cast<const FileIncident*>(&inc);
+        ATH_MSG_FATAL("File " << finc->fileName() 
+                              << " stream " << inputstream 
+                              << " does not match previous file " 
+                              << m_inputStream);
+        return;
+      }
+    }
+
+    // Clear the file bookkeeper
+    if (m_completeBook.isValid()) {
+      // Reset existing container
+      for (xAOD::CutBookkeeperContainer::iterator it = m_completeBook->begin();
+           it != m_completeBook->end(); ++it) {
+        (*it)->setNAcceptedEvents(0);
+        (*it)->setSumOfEventWeights(0);
+        (*it)->setSumOfEventWeightsSquared(0);
+      }
+    }
+  }
+
+  // Clean up the bookkeeper before output
+  //if ( inc.type() == "MetaDataStop" || inc.type() == "EndInputFile") {
+  if ( inc.type() == "MetaDataStop" ) {
+    if (m_completeBook.isValid()) {
+      // Reset existing container
+      for (xAOD::CutBookkeeperContainer::iterator it = m_completeBook->begin();
+           it != m_completeBook->end(); ++it) {
+      }
+    }
+    const xAOD::CutBookkeeperContainer* fileBook(NULL);
+    if( !(m_outMetaDataStore->retrieve(fileBook, m_fileCollName) ).isSuccess() ) {
+      ATH_MSG_WARNING( "Could not get " << m_fileCollName 
+                       << " CutBookkeepers from output MetaDataStore" );
+    }
+    else {
+      const SG::IConstAuxStore* fileBookAux = fileBook->getConstStore();
+      if (m_outMetaDataStore->removeDataAndProxy(fileBook).isFailure()) {
+        ATH_MSG_ERROR("Unable to remove " << m_fileCollName);
+      }
+      if (m_outMetaDataStore->removeDataAndProxy(fileBookAux).isFailure()) {
+        ATH_MSG_ERROR("Unable to remove " << m_fileCollName);
+      }
+    }
+  }
+
+  ATH_MSG_DEBUG( "End incident " << inc.type() );
+  return;
+}
+
+
+void
+FileCutFlowSvc::print()
+{
+  return;
+}
+
+
+StatusCode FileCutFlowSvc::determineCycleNumberFromInput( const std::string& collName )
+{
+  ATH_MSG_DEBUG("calling determineCycleNumberFromInput('" << collName
+                  << "')... have currently cycle number = " << m_skimmingCycle );
+
+  // Try to get CutBookkeepers from the input file
+  if ( m_inMetaDataStore->contains<xAOD::CutBookkeeperContainer>(collName) ) {
+    ATH_MSG_VERBOSE("Found xAOD::CutBookkeeperContainer in input MetaStore with name: " << collName);
+
+    // There can always only be a single object in the input store. As the store
+    // is connected to just a single input file.
+    const xAOD::CutBookkeeperContainer* constColl = 0;
+    ATH_CHECK( m_inMetaDataStore->retrieve( constColl, collName ) );
+    xAOD::CutBookkeeperContainer* tmpColl = const_cast<xAOD::CutBookkeeperContainer*>(constColl);
+    if ( !(tmpColl->hasStore()) ) {
+      ATH_MSG_VERBOSE("Setting store of xAOD::CutBookkeeperContainer");
+      // Get also the auxilliary store
+      // const SG::IConstAuxStore* auxColl = 0;
+      const xAOD::CutBookkeeperAuxContainer* auxColl = 0;
+      ATH_CHECK( m_inMetaDataStore->retrieve(auxColl, collName+"Aux.") );
+      tmpColl->setConstStore( auxColl );
+    }
+    // Now, iterate over all CutBookkeepers and search for the highest cycle number
+    int maxCycle=0;
+    for ( std::size_t i=0; i<tmpColl->size(); ++i ) {
+      // Get the current old EBK
+      const xAOD::CutBookkeeper* cbk = tmpColl->at(i);
+      int inCycle = cbk->cycle();
+      if (inCycle > maxCycle) maxCycle = inCycle;
+    }
+    m_skimmingCycle = std::max(m_skimmingCycle,maxCycle+1);
+  }
+
+  ATH_MSG_DEBUG("done calling determineCycleNumberFromInput('" << collName
+                  << "')... have now cycle number = " << m_skimmingCycle );
+  return StatusCode::SUCCESS;
+}
+
+
+
+StatusCode
+FileCutFlowSvc::recordCollection( xAOD::CutBookkeeperContainer * coll,
+                              const std::string &collName) {
+  ATH_MSG_VERBOSE("calling recordCollection(..., " << collName << ")" );
+
+  //Expected that FileCutFlowSvc is the only one allowed to record *coll
+  if( m_outMetaDataStore->contains<xAOD::CutBookkeeperContainer>(collName) ) {
+    ATH_MSG_ERROR( "xAOD::CutBookkeeperContainer " << collName
+      << " already exists in output MetaData store. This is unexpected." );
+    return StatusCode::FAILURE;
+  }
+
+  // Record the container
+  ATH_CHECK( m_outMetaDataStore->record(coll, collName) );
+  ATH_MSG_VERBOSE("Recorded xAOD::CutBookkeeperContainer " << collName);
+
+
+  // Take care of the peculiarities of the new xAOD EDM, i.e., create the needed AuxStore
+  xAOD::CutBookkeeperAuxContainer* auxCont = new xAOD::CutBookkeeperAuxContainer;
+  coll->setStore( auxCont ); //gives it a new associated aux container
+
+  // Record the aux container
+  ATH_CHECK( m_outMetaDataStore->record( auxCont, collName+"Aux." ) );
+  ATH_MSG_VERBOSE( "Recorded xAOD::CutBookkeeperContainer " << collName << "Aux." );
+
+  return StatusCode::SUCCESS;
+}
+
+
+
+xAOD::CutBookkeeper*
+FileCutFlowSvc::getCutBookkeeper( const CutIdentifier cutID ) {
+  xAOD::CutBookkeeperContainer::iterator it = m_completeBook->begin();
+  xAOD::CutBookkeeperContainer::iterator ite = m_completeBook->end();
+  while (it != ite) {
+    if ((*it)->uniqueIdentifier()==cutID) return (*it);
+    ++it;
+  }
+  return 0;
+}
+
diff --git a/Event/EventBookkeeperTools/src/FileCutFlowSvc.h b/Event/EventBookkeeperTools/src/FileCutFlowSvc.h
new file mode 100644
index 00000000000..4060db8fbe1
--- /dev/null
+++ b/Event/EventBookkeeperTools/src/FileCutFlowSvc.h
@@ -0,0 +1,204 @@
+///////////////////////// -*- C++ -*- /////////////////////////////
+
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Header file for class FileCutFlowSvc
+// Authors: Joao Firmino da Costa <joao.costa@cern.ch> and David Cote <david.cote@cern.ch>
+
+///////////////////////////////////////////////////////////////////
+#ifndef FILECUTFLOWSVC_H
+#define FILECUTFLOWSVC_H
+
+/**
+ * @class FileCutFlowSvc
+ * @brief This implementes the methods for ICutFlowSvc
+ */
+
+// for size_t
+#include <cassert>
+
+// STL includes
+#include <string>
+#include <vector>
+
+// FrameWork includes
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/IIncidentListener.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "StoreGate/DataHandle.h"
+
+// Athena includes
+#include "AthenaBaseComps/AthService.h"
+#include "AthenaKernel/ICutFlowSvc.h"
+#include "StoreGate/StoreGateSvc.h"
+
+// EDM includes
+#include "xAODCutFlow/CutBookkeeper.h"
+#include "xAODCutFlow/CutBookkeeperContainer.h"
+#include "xAODCutFlow/CutBookkeeperAuxContainer.h"
+
+// STL include(s):
+#if __cplusplus < 201100
+#   include <tr1/unordered_map>
+namespace MAP_NS = std::tr1;
+#else
+#   include <unordered_map>
+namespace MAP_NS = std;
+#endif // C++
+
+// forward declarations
+class EventBookkeeperCollection;
+class EventBookkeeper;
+
+
+
+class FileCutFlowSvc :
+  virtual public ICutFlowSvc,
+  virtual public IIncidentListener,
+          public AthService
+{
+
+public:
+
+/// Constructor
+  FileCutFlowSvc(const std::string& name, ISvcLocator* pSvcLocator );
+
+  /// Destructor
+  virtual ~FileCutFlowSvc();
+
+  /// Gaudi Service Implementation
+  //@{
+  StatusCode initialize();
+  StatusCode stop();
+  StatusCode finalize();
+  StatusCode queryInterface( const InterfaceID& riid, void** ppvi );
+  //@}
+
+
+  /// Incident service handle listening for BeginFile and EndFile.
+  void handle(const Incident& incident);
+
+  ///////////////////////////////////////////////////////////////////
+  // Non-const methods:
+  ///////////////////////////////////////////////////////////////////
+
+public:
+
+  /// Register filter in the FileCutFlowSvc and returns the CutID of the
+  /// corresponding CutBookkeeper.
+  /// This method should be used by filters that register themselves.
+  CutIdentifier registerFilter( const std::string& name,
+                                const std::string& description ) override final;
+
+  /// Register cut as child of a filter in the FileCutFlowSvc and returns the CutID
+  /// of the corresponding CutBookkeeper. This method should be used by
+  /// filters to register their internal cuts that are not the Algs themselves.
+  CutIdentifier registerCut( const std::string& name,
+                             const std::string& description,
+                             CutIdentifier originCutID ) override final;
+
+  /// Tells FileCutFlowSvc that a filter is used directly by an outputStream with
+  /// a given logical context. The only foreseen client should the DecisionSvc,
+  /// with its Accept/Require/Veto.
+  CutIdentifier registerTopFilter( const std::string& name,
+                                   const std::string& description,
+                                   unsigned int logic,
+                                   const std::string& outputStream ) override final;
+
+  /// Tells FileCutFlowSvc that a filter should not be treated as as being used by
+  /// another filter. This should be used by filters that use other filter Algs
+  /// internally, e.g., like the LogicalFilterCombiner
+  CutIdentifier declareUsedOtherFilter( const std::string& name,
+                                        CutIdentifier originCutID ) override final;
+
+  /// Set the description of an existing CutBookkeeper
+  void setFilterDescription( CutIdentifier cutID,
+                             const std::string& descr ) override final;
+
+  /// Tells FileCutFlowSvc to update the event counter of a CutIdentifier cutID,
+  /// using the CutIdentifier returned by selfRegisterFilter or registerCut
+  /// Internally, the Monte Carlo event weight will be retrieved from the
+  /// xAOD::EventInfo object as evtWeight = evtInfo->mcEventWeight();
+  void addEvent( CutIdentifier cutID ) override final;
+
+  /// Tells FileCutFlowSvc to update the weighted event counter of a CutIdentifier cutID,
+  /// using CutIdentifier returned by selfRegisterFilter or registerCut
+  void addEvent( CutIdentifier cutID, double weight ) override final;
+
+  /// Get a CutBookkeeper given a CutID
+  xAOD::CutBookkeeper* getCutBookkeeper( const CutIdentifier cutID );
+
+  void print();
+
+private:
+  /// Helper function to determine the processing cycle number from the
+  /// input meta-data store
+  StatusCode determineCycleNumberFromInput( const std::string& collName );
+
+  /// Helper function to record the collection (and its aux store) to the
+  /// output MetaData store
+  StatusCode recordCollection( xAOD::CutBookkeeperContainer* coll,
+                               const std::string &collName );
+
+  /// Create a typedef
+  typedef ServiceHandle<StoreGateSvc> StoreGateSvc_t;
+
+  /// The output meta-data store
+  StoreGateSvc_t m_outMetaDataStore;
+
+  /// The input meta-data store
+  StoreGateSvc_t m_inMetaDataStore;
+
+  /// The event store
+  StoreGateSvc_t m_eventStore;
+
+  DataHandle<xAOD::CutBookkeeperContainer> m_completeBook;
+
+  /// The name of the completed, i.e., fully processed, CutBookkeeperContainer
+  std::string m_completeCollName;
+
+  /// The name of the incomplete, i.e., not fully processed (e.g. failed job), CutBookkeeperContainer
+  std::string m_incompleteCollName;
+
+  /// The current skimming cycle, i.e., how many processing stages we already had
+  int m_skimmingCycle;
+
+  /// The name of the container in storegate with cutflow values for a file. 
+  std::string m_fileCollName;
+
+  /// The name of the currently used input file stream
+  std::string m_inputStream;
+
+  /// A flag to say if the input file is currently open or not
+  //bool m_fileCurrentlyOpened;
+
+  /// Declare a simple typedef for the internal map
+  typedef MAP_NS::unordered_map<CutIdentifier, xAOD::CutBookkeeper*> CutIDMap_t;
+
+  /// This internal map keeps the association between the instance identifier of each algorithm
+  /// to the pointer of associated CutBookkeeper
+  CutIDMap_t m_ebkMap;
+
+  /// Internal flag to track if we have already determined the cycle number from the first input file
+  bool m_alreadyDeterminedCycleNumber;
+
+public:
+
+  /// Publish the interface for this service
+  static const InterfaceID& interfaceID();
+
+};
+
+
+
+///////////////////////////////////////////////////////////////////
+// Inline methods:
+///////////////////////////////////////////////////////////////////
+
+inline const InterfaceID& FileCutFlowSvc::interfaceID() {
+  return ICutFlowSvc::interfaceID();
+}
+
+#endif //> !FILECUTFLOWSVC_H
diff --git a/Event/EventBookkeeperTools/src/components/EventBookkeeperTools_entries.cxx b/Event/EventBookkeeperTools/src/components/EventBookkeeperTools_entries.cxx
index 383b2149695..d02d62cf31e 100644
--- a/Event/EventBookkeeperTools/src/components/EventBookkeeperTools_entries.cxx
+++ b/Event/EventBookkeeperTools/src/components/EventBookkeeperTools_entries.cxx
@@ -2,6 +2,7 @@
 
 #include "EventBookkeeperTools/myCppFilterTest.h"
 #include "../SkimDecisionMultiFilter.h"
+#include "../FileCutFlowSvc.h"
 #include "../CutFlowSvc.h"
 #include "EventBookkeeperTools/BookkeeperTool.h"
 #include "../EventSelectorCounterTool.h"
@@ -13,6 +14,7 @@ DECLARE_ALGORITHM_FACTORY( EventCounterAlg )
 DECLARE_TOOL_FACTORY( EventSelectorCounterTool )
 DECLARE_TOOL_FACTORY( BookkeeperTool )
 DECLARE_SERVICE_FACTORY( CutFlowSvc )
+DECLARE_SERVICE_FACTORY( FileCutFlowSvc )
 
 DECLARE_FACTORY_ENTRIES(EventBookkeeperTools) {
   DECLARE_ALGORITHM( myCppFilterTest );
@@ -21,4 +23,5 @@ DECLARE_FACTORY_ENTRIES(EventBookkeeperTools) {
   DECLARE_ALGTOOL( EventSelectorCounterTool );
   DECLARE_ALGTOOL( BookkeeperTool );
   DECLARE_SERVICE( CutFlowSvc );
+  DECLARE_SERVICE( FileCutFlowSvc );
 }
-- 
GitLab