From 076f790d18359cae10da98f06404f812df06be5a Mon Sep 17 00:00:00 2001
From: Sebastien Binet <sebastien.binet@cern.ch>
Date: Fri, 26 Aug 2011 08:20:06 +0200
Subject: [PATCH] migration to HepMC v2.06: removed the IO_xxx classes
 (McParticleUtils-00-07-00)

---
 .../McParticleUtils/DecayParser.h             | 134 ++++
 .../McParticleUtils/McParticleUtils/McUtils.h |  32 +
 .../McParticleUtils/McVtxFilter.h             | 295 +++++++
 .../McParticleUtils/cmt/requirements          |  44 +
 .../McParticleUtils/python/DecayParser.py     |  52 ++
 .../McParticleUtils/python/__init__.py        |   3 +
 .../McParticleUtils/src/DecayParser.cxx       | 419 ++++++++++
 .../McParticleUtils/src/McUtils.cxx           | 104 +++
 .../McParticleUtils/src/McVtxFilter.cxx       | 508 ++++++++++++
 .../McParticleUtils/test/McParticleUtils.sh   |  13 +
 .../test/McVtxFilterTest_CppUnit.cxx          | 752 ++++++++++++++++++
 11 files changed, 2356 insertions(+)
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/DecayParser.h
 create mode 100644 PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McUtils.h
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McVtxFilter.h
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/cmt/requirements
 create mode 100644 PhysicsAnalysis/TruthParticleID/McParticleUtils/python/DecayParser.py
 create mode 100644 PhysicsAnalysis/TruthParticleID/McParticleUtils/python/__init__.py
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/src/DecayParser.cxx
 create mode 100644 PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McUtils.cxx
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McVtxFilter.cxx
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McParticleUtils.sh
 create mode 100755 PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McVtxFilterTest_CppUnit.cxx

diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/DecayParser.h b/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/DecayParser.h
new file mode 100755
index 000000000000..c61858937508
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/DecayParser.h
@@ -0,0 +1,134 @@
+/////////////////// -*- C++ -*- //////////////////////////////////// 
+
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// DecayParser.h 
+// Header file for class Object
+// Author: S.Binet<binet@cern.ch>
+/////////////////////////////////////////////////////////////////// 
+#ifndef MCPARTICLEUTILS_DECAYPARSER_H 
+#define MCPARTICLEUTILS_DECAYPARSER_H 
+
+/** DecayParser is the class which parses and models decay patterns.
+ *  You can describe <i>via</i> a std::string the decay pattern you are
+ *  looking for.
+ *  <i>Eg:</i> you just have to say "Z0[e+;e-]" to create a pattern which
+ *  can then be used to filter for vertices where such a decay occurs.
+ */
+
+// STL includes
+#include <string>
+#include <vector>
+
+// EventKernel includes
+#include "EventKernel/PdtPdg.h"
+
+// fwd declare
+struct _object;
+typedef _object PyObject;
+
+namespace McUtils {
+  typedef std::vector<std::string> Strings;
+}
+
+class DecayParser
+{
+  /////////////////////////////////////////////////////////////////// 
+  // Public methods: 
+  /////////////////////////////////////////////////////////////////// 
+ public: 
+
+  /// Constructor with parameters: 
+  DecayParser( const std::string& cmd );
+
+  /// Destructor: 
+  virtual ~DecayParser(); 
+
+  /////////////////////////////////////////////////////////////////// 
+  // Const methods: 
+  ///////////////////////////////////////////////////////////////////
+  const std::vector<McUtils::Strings>& getParents() const;
+  const std::vector<McUtils::Strings>& getChildren()  const;
+
+  void dump() const;
+
+  PDG::pidType pdgId( const std::string& pdgIdString ) const;
+
+  /////////////////////////////////////////////////////////////////// 
+  // Non-const methods: 
+  /////////////////////////////////////////////////////////////////// 
+  void parse( const std::string& cmd );
+
+
+  /////////////////////////////////////////////////////////////////// 
+  // Protected methods: 
+  /////////////////////////////////////////////////////////////////// 
+ protected: 
+
+  /// Default constructor: 
+  DecayParser(); //> not implemented
+
+  /// Copy constructor: 
+  DecayParser( const DecayParser& rhs ); //> not implemented
+
+  /// Assignment operator: 
+  DecayParser &operator=(const DecayParser &obj); //> not implemented
+
+  /////////////////////////////////////////////////////////////////// 
+  // Protected data: 
+  /////////////////////////////////////////////////////////////////// 
+ protected: 
+
+  /** Print the content of a vector of McUtils::Strings to std::cout
+   */
+  void printMcUtilsStrings( const std::vector<McUtils::Strings>& list ) const;
+
+  /////////////////////////////////////////////////////////////////// 
+  // Protected data: 
+  /////////////////////////////////////////////////////////////////// 
+ protected: 
+
+  /** python function to parse the input string modeling the decay pattern
+   *  to look for.
+   */
+  PyObject *m_parseFct;
+
+  /** List of parents : each slot of the vector is a list of candidates
+   *  So one could have something like : [ ["22","23"] ] to model a decay
+   *  of a gamma or a Z0
+   */
+  std::vector<McUtils::Strings> m_parents;
+
+  /** List of children : each slot of the vector is a list of candidates
+   *  So one could have something like : [ ["11","13"], ["-11","-13" ] ]
+   *  to model a decay into a pair of electrons or muons
+   */
+  std::vector<McUtils::Strings> m_children;
+
+}; 
+
+/// I/O operators
+//////////////////////
+//MsgStream& operator<<( MsgStream & msg, const DecayParser &obj );
+
+/////////////////////////////////////////////////////////////////// 
+/// Inline methods: 
+/////////////////////////////////////////////////////////////////// 
+
+inline
+const std::vector<McUtils::Strings>& 
+DecayParser::getParents() const
+{ 
+  return m_parents;
+}
+
+inline 
+const std::vector<McUtils::Strings>&
+DecayParser::getChildren() const
+{ 
+  return m_children;
+}
+
+#endif //> MCPARTICLEUTILS_DECAYPARSER_H
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McUtils.h b/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McUtils.h
new file mode 100644
index 000000000000..0b31016b43ef
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McUtils.h
@@ -0,0 +1,32 @@
+///////////////////////// -*- C++ -*- /////////////////////////////
+
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// McUtils.h 
+// Header file for various Mc utils
+// Author: S.Binet<binet@cern.ch>
+/////////////////////////////////////////////////////////////////// 
+#ifndef MCPARTICLEUTILS_MCUTILS_H 
+#define MCPARTICLEUTILS_MCUTILS_H 
+
+// STL includes
+
+// HepMC includes
+
+// Forward declaration
+namespace HepPDT  { class ParticleDataTable; }
+
+namespace McUtils {
+
+  /** @brief Helper function to get the charge of a particle given its PDG Id.
+   *         Also handles (some) particles which are not in the PDGTABLE.MeV file
+   *         This function internally uses HepPDT.
+   */
+  double chargeFromPdgId( const int pdgId, 
+			  const HepPDT::ParticleDataTable* pdt );
+
+} //> end namespace McUtils
+
+#endif //> MCPARTICLEUTILS_MCUTILS_H 
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McVtxFilter.h b/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McVtxFilter.h
new file mode 100755
index 000000000000..3c1687e879a9
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/McParticleUtils/McVtxFilter.h
@@ -0,0 +1,295 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/////////////////////////////////////////////////////////////////// 
+// McVtxFilter.h 
+// Header file for class McVtxFilter
+// Author: S.Binet<binet@cern.ch>
+/////////////////////////////////////////////////////////////////// 
+#ifndef MCPARTICLEUTILS_MCVTXFILTER_H 
+#define MCPARTICLEUTILS_MCVTXFILTER_H 
+
+/** McVtxFilter allows to select HepMC::GenParticle based on the decay pattern.
+ *  Hopefully one will be able to also select the particles by pt (in a coming
+ *  patch).
+ *  It uses internally the DecayPattern class to build the filtering decision
+ *  The basic <i>modus operandi</i> is to loop on HepMC::GenVertex (of a given
+ *  HepMC::GenEvent) to select the ones which are of interest (and fulfill 
+ *  the decay pattern).
+ */
+
+// STL includes
+#include <sstream>
+#include <ostream>
+
+// DataModel
+#include "DataModel/DataVector.h"
+
+// Framework includes
+#include "AthenaKernel/getMessageSvc.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/GaudiException.h"
+
+// HepMC includes
+#include "HepMC/GenEvent.h"
+#include "HepMC/GenParticle.h"
+#include "HepMC/GenVertex.h"
+
+// EventKernel includes
+#include "EventKernel/PdtPdg.h"
+
+// AnalysisUtils includes
+#include "AnalysisUtils/IFilterCuts.h"
+#include "AnalysisUtils/ParticleCandidateList.h"
+
+// McParticleUtils includes
+#include "McParticleUtils/DecayParser.h"
+
+// Forward declaration
+
+class McVtxFilter : virtual public IFilterCuts
+{ 
+  
+  /////////////////////////////////////////////////////////////////// 
+  // Public methods: 
+  /////////////////////////////////////////////////////////////////// 
+  public: 
+  
+  /** Default constructor: 
+   */
+  McVtxFilter();
+  
+  /** Copy constructor: 
+   */
+  McVtxFilter( const McVtxFilter& rhs );
+  
+  /** Constructor with parameters: 
+   */
+  McVtxFilter( const std::string& decayPattern,
+	       const bool matchSign = false, 
+	       const bool matchBranches = false );
+  
+  /** Constructor with parameters: 
+   */
+  McVtxFilter( const bool matchSign, const bool matchBranches );
+  
+  /** Destructor: 
+   */
+  virtual ~McVtxFilter(); 
+  
+  /** Assignment operator: 
+   */
+  McVtxFilter &operator=(const McVtxFilter &rhs); 
+  
+  /////////////////////////////////////////////////////////////////// 
+  // Const methods: 
+  ///////////////////////////////////////////////////////////////////
+
+  /** Main filter method. This method takes the decision whether or not
+   *  the filter has to accept the vertex.
+   *  It checks if the vertex has the required number of ingoing particles
+   *  the required number of outgoing particles. Then for each branch (parent
+   *  child) it checks if the particle can match an asked for particle.
+   */
+  virtual bool isAccepted( const HepMC::GenVertex * vtx ) const;
+
+  /// Dump to std::ostream (default = std::cout) the decay pattern the 
+  /// filter one is looking for
+  void dump( std::ostream& out = std::cout ) const;
+
+  /** returns true if the vertex filter requires a parent AND a child list
+   *  <i>ie:</i> returns false if the filter looks only for a lonely particle
+   *  (eg: a photon or an electron, see TruthParticleAlgs for a concrete example)
+   */
+  bool isFullVtx() const;
+
+  /** return true if the filter discriminates the sign of the particles
+   */
+  bool matchSign() const;
+
+  /** return true if the filter strictly matchs the number of output particles
+   *  eg: a vertex t->Wbgg won't be selected if matchBranches == true and
+   *      decay pattern = "#id 6[24,5]"
+   */
+  bool matchBranches() const;
+
+  /** return the decay pattern used by this filter
+   */
+  const std::string& decayPattern() const;
+
+  /** return the list of particle candidates for the input particles (parent)
+   */
+  const DataVector<const ParticleCandidateList>& parentList() const;
+    
+  /** return the list of particle candidates for the output particles (child)
+   */
+  const DataVector<const ParticleCandidateList>& childList() const;
+    
+  /////////////////////////////////////////////////////////////////// 
+  // Non-const methods: 
+  /////////////////////////////////////////////////////////////////// 
+
+  /** Set filter cut options
+   */
+  virtual void setFilter( const IFilterCuts * filter );
+  
+  /**Set if one wants to discriminate the sign of the particles
+   */
+  void setMatchSign( const bool matchSign );
+  
+  /**Set if one wants to strictly match the number of output particles
+   */
+  void setMatchBranches( const bool matchVtxBranches );
+  
+  /**Set the decay pattern to look for : it correctly setups the parent 
+   * and children according to the LaTeX-like string which is given as
+   * argument
+   */
+  void setDecayPattern( const std::string& decayPattern );
+
+  /////////////////////////////////////////////////////////////////// 
+  // Protected methods: 
+  /////////////////////////////////////////////////////////////////// 
+  protected: 
+  
+  /**Check if the parent branch fulfills the requirements
+   */
+  bool checkParentBranch( const HepMC::GenVertex * vtx ) const;
+
+  /**Check if the child branch fulfills the requirements
+   */
+  bool checkChildBranch ( const HepMC::GenVertex * vtx ) const;
+  
+  /// Check the branches for the special case of a 1->2 body decay
+  /// (there is some room for optimisation in that particular case)
+  bool checkTwoBodyDecay( const HepMC::GenVertex * vtx ) const;
+
+  /// Populate vtx by HepPDT::ParticleData
+  //void addParent( const HepPDT::ParticleData& pdt );
+  //void addChild ( const HepPDT::ParticleData& pdt );
+  
+  /**Populate in-going particles' vtx by particle identification
+   */
+  //void addParent( const PDG::pidType& parentPID );
+  /**Populate out-going particles' vtx by particle identification
+   */
+  //void addChild ( const PDG::pidType& childPID  );
+  
+  /// Populate vtx by particle name
+  //void addParent( const std::string& parentName );
+  //void addChild ( const std::string& childName  );
+  
+  /** Populate in-going particles' vtx list by group name
+   */
+  //void addParentList( const std::string& parentClass );
+
+  /** Populate out-going particles' vtx list by group name
+   */
+  //void addChildList ( const std::string& childClass  );
+  
+  /// Populate parents' vtx list by copy
+  /// McVtxFilter takes ownership of the given list
+  //void addParentList( const ParticleCandidateList* parentList );
+  /// Populate children's vtx list by copy : 
+  /// McVtxFilter takes ownership of the given list
+  //void addChildList ( const ParticleCandidateList* childList  );
+  
+  /////////////////////////////////////////////////////////////////// 
+  // Protected data: 
+  /////////////////////////////////////////////////////////////////// 
+  protected: 
+
+  /// MsgStream instance (a std::cout like with print-out levels)
+  mutable MsgStream m_msg;
+
+  /// Discrimination between particles and conjugates
+  bool m_matchSign;
+  
+  ///  Tell if one wants a strict vertex branches matching.
+  ///  This is to cope with radiations in the output branches
+  ///  If true then a decay t->Wbggg will not be kept if one asked for
+  ///  a top into W+b decay...
+  ///  Default : false
+  bool m_matchBranches;
+
+  /// The decay pattern one looks for.
+  /// Ex: 23 -> -11 + 11 to model a Z0 -> e+ e- decay
+  std::string m_decayPattern;
+  
+  /// The list of the matching particles Ids : each item in DataVector
+  /// stands for an entering branch to the vertex
+  DataVector<const ParticleCandidateList> m_parentList;
+  
+  /// The list of the matching particles Ids : each item in DataVector
+  /// stands for an out-going branch to the vertex
+  DataVector<const ParticleCandidateList> m_childList;
+  
+}; 
+
+/// I/O operators
+//////////////////////
+MsgStream& operator<<( MsgStream & msg, const McVtxFilter &obj );
+std::ostream& operator<<( std::ostream& out, const McVtxFilter &obj );
+
+/////////////////////////////////////////////////////////////////// 
+// Inline methods: 
+/////////////////////////////////////////////////////////////////// 
+
+inline McVtxFilter::~McVtxFilter(){}
+
+/////////////////////////////////////////////////////////////////// 
+// Const methods: 
+///////////////////////////////////////////////////////////////////
+
+inline bool McVtxFilter::isFullVtx() const
+{
+  if ( m_parentList.empty() || m_childList.empty() ) {
+    return false;
+  } else {
+    return true;
+  }
+}
+
+inline bool McVtxFilter::matchSign() const
+{
+  return m_matchSign;
+}
+
+inline bool McVtxFilter::matchBranches() const
+{
+  return m_matchBranches;
+}
+
+inline const std::string& McVtxFilter::decayPattern() const
+{
+  return m_decayPattern;
+}
+
+inline 
+const DataVector<const ParticleCandidateList>& McVtxFilter::parentList() const
+{
+  return m_parentList;
+}
+
+inline    
+const DataVector<const ParticleCandidateList>& McVtxFilter::childList() const
+{
+  return m_childList;
+}
+
+/////////////////////////////////////////////////////////////////// 
+// Non-const methods: 
+/////////////////////////////////////////////////////////////////// 
+
+inline void McVtxFilter::setMatchSign( const bool matchSign )
+{
+  m_matchSign = matchSign;
+}
+
+inline void McVtxFilter::setMatchBranches( const bool matchBranches )
+{
+  m_matchBranches = matchBranches;
+}
+
+#endif //> MCPARTICLEUTILS_MCVTXFILTER_H
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/cmt/requirements b/PhysicsAnalysis/TruthParticleID/McParticleUtils/cmt/requirements
new file mode 100755
index 000000000000..c7e77212516a
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/cmt/requirements
@@ -0,0 +1,44 @@
+package McParticleUtils
+
+author Sebastien Binet <binet@cern.ch>
+
+use AtlasPolicy         AtlasPolicy-*
+
+use GaudiInterface      GaudiInterface-*		External
+use AtlasHepMC          AtlasHepMC-*          		External
+
+use EventKernel         EventKernel-*                	Event
+
+use DataModel           DataModel-*      		Control
+use AthenaKernel	AthenaKernel-*   		Control
+use DataModel           DataModel-*      		Control
+
+use AnalysisUtils	AnalysisUtils-*		PhysicsAnalysis/AnalysisCommon
+
+## private uses
+private
+use AtlasBoost		AtlasBoost-*			External
+use AtlasPython		AtlasPython-*			External
+use AtlasCLHEP          AtlasCLHEP-*     		External
+use HepPDT		v*				LCG_Interfaces
+end_private
+
+branches src McParticleUtils doc test run share
+
+library McParticleUtils *.cxx
+
+apply_pattern installed_library
+
+#########################################################
+# Unit tests: CppUnit tests for McParticleUtils classes #
+#########################################################
+private
+use TestPolicy 	   TestPolicy-*                 -no_auto_imports
+use TestTools      TestTools-*        AtlasTest -no_auto_imports
+use StoreGate	   StoreGate-*	      Control
+use AtlasCppUnit   AtlasCppUnit-*     External  -no_auto_imports
+apply_pattern CppUnit name=cppUnit_mcVtxFilterTest files="-s=${McParticleUtils_root}/test McVtxFilterTest_CppUnit.cxx"
+macro_append HepPDT_linkopts " -lHepPID"
+
+apply_pattern declare_python_modules files="*.py"
+end_private
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/python/DecayParser.py b/PhysicsAnalysis/TruthParticleID/McParticleUtils/python/DecayParser.py
new file mode 100644
index 000000000000..e5bc7e2ee8a0
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/python/DecayParser.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# @file: McParticleUtils/python/DecayParser.py
+# @purpose: implement the parsing/tokenization of decay-patterns' strings
+# @author:  Sebastien Binet <binet@cern.ch>
+
+import re
+
+_slot_separator = "+"
+_candidate_sep  = "|"
+_decay_arrow    = "->"
+_wild_card      = "*"
+
+def py_parse (cmd):
+    # remove all spaces to ease further parsing
+    cmd = re.sub(pattern=" ",
+                 repl="",
+                 string=cmd)
+##     print "=> [%s]"%cmd
+
+    if cmd == _decay_arrow:
+        # special case of a single arrow without any parent nor child:
+        # this decay pattern will select every single vertex
+        return (0, None, None)
+    buf = cmd.split (_decay_arrow)
+    assert (len(buf)==2)
+    
+##     print "==> buf:",buf
+    parents = process_block (buf[0])
+    children= process_block (buf[1])
+    
+    return (0, parents, children)
+
+def process_block (cmd):
+    if cmd == "":
+        return None
+    candidates = cmd.split (_slot_separator)
+    return [list(set(c.split(_candidate_sep))) for c in candidates]
+
+
+if __name__ == "__main__":
+    print ":"*80
+    print "::: tests..."
+    for cmd in ("-1|-2|-3|-4|-5|-6|21 + 1|2|3|4|5|6|21 -> ",
+                "-> 91|92|94"
+                ):
+        print "::: testing [%s]..." % cmd
+        print "::: result:",py_parse (cmd)
+
+    print "::: bye."
+    print ":"*80
+    
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/python/__init__.py b/PhysicsAnalysis/TruthParticleID/McParticleUtils/python/__init__.py
new file mode 100644
index 000000000000..8056e12aa60d
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# hook for McParticleUtils python package
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/DecayParser.cxx b/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/DecayParser.cxx
new file mode 100755
index 000000000000..4115e4d6e484
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/DecayParser.cxx
@@ -0,0 +1,419 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/////////////////////////////////////////////////////////////////// 
+// DecayParser.cxx 
+// Implementation file for class DecayParser
+// Author: S.Binet<binet@cern.ch>
+/////////////////////////////////////////////////////////////////// 
+
+// Python includes
+#include "Python.h"
+
+// STL includes
+#include <iostream>
+#include <list>
+#include <stdexcept>
+#include <sstream>
+
+// boost includes
+// #include <boost/algorithm/string/split.hpp>
+// #include <boost/algorithm/string/erase.hpp>
+// #include <boost/algorithm/string/regex.hpp>
+// #include <boost/algorithm/string/trim.hpp>
+// #include <boost/algorithm/string/compare.hpp>
+
+// McParticleUtils includes
+#include "McParticleUtils/DecayParser.h"
+
+// namespace balg = boost::algorithm;
+
+namespace {
+  PyObject *fetch_py_parse_fct();
+  bool py_to_cpp (PyObject* candidates,
+		  std::vector<McUtils::Strings>& parsed);
+}
+
+/////////////////////////////////////////////////////////////////// 
+// Static data
+/////////////////////////////////////////////////////////////////// 
+
+// const std::string  DecayParser::m_slotSep =          "+"    ;
+// const boost::regex DecayParser::m_candidateSep     ( "\\|" );
+// const boost::regex DecayParser::m_decayArrow       ( "->"  );
+// const boost::regex DecayParser::m_wildCardParticle ( "\\*" );
+
+/////////////////////////////////////////////////////////////////// 
+/// Public methods: 
+/////////////////////////////////////////////////////////////////// 
+
+/// Constructors
+////////////////
+// DecayParser::DecayParser() :
+//   m_parseFct (0),
+//   m_parents  ( ),
+//   m_children ( )
+// {}
+
+DecayParser::DecayParser( const std::string& cmd ) :
+  m_parseFct (0),
+  m_parents  ( ),
+  m_children ( )
+{
+  m_parseFct = ::fetch_py_parse_fct();
+  parse(cmd);
+}
+
+// DecayParser::DecayParser( const DecayParser& rhs ) :
+//   m_parseFct (0),
+//   m_parents  ( rhs.m_parents   ),
+//   m_children ( rhs.m_children  )
+// {}
+
+/// Destructor
+///////////////
+DecayParser::~DecayParser() 
+{
+  Py_XDECREF (m_parseFct);
+}
+
+/////////////////////////////////////////////////////////////////// 
+/// Const methods: 
+///////////////////////////////////////////////////////////////////
+void DecayParser::dump() const
+{
+  std::cout << "--- Parents ---" << std::endl;
+  printMcUtilsStrings( m_parents );
+
+  std::cout << "--- Children ---" << std::endl;
+  printMcUtilsStrings( m_children );
+}
+
+PDG::pidType DecayParser::pdgId( const std::string& pdgIdString ) const
+{
+  PDG::pidType pdgID = PDG::null;
+
+  /// We have been setup to directly use PdgIds
+  /// so here we just convert a PDG-ID string into a bare number
+  /// Note: if we were in 1990's we could have used atoi(char*)
+  /// but, hey, let us use ANSI-C++ : (need to go through a temporary integer
+  /// because explicit cast of PDG::pidType into integer does not work
+  /// with int std::stringstream::operator>>() ==> Why ? //FIXME
+  int iPDG = 0;
+  std::stringstream( pdgIdString ) >> iPDG;
+  pdgID = static_cast<PDG::pidType>(iPDG);
+
+  return pdgID;
+}
+/////////////////////////////////////////////////////////////////// 
+/// Non-const methods: 
+/////////////////////////////////////////////////////////////////// 
+void DecayParser::parse( const std::string& inputCmd ) 
+{
+  if ( inputCmd.empty() ) {
+    return;
+  }
+
+  // Reset the parents and children lists
+  m_parents.clear();
+  m_children.clear();
+
+//   /// Test if there is an arrow to enforce our syntax
+//   if ( inputCmd.find (m_decayArrow.str()) == std::string::npos ) {
+//     // Malformed command
+//     std::string error = "";
+//     error += "Malformed command : <"+inputCmd+"> !!\n";
+//     error += "You should enter an arrow... ex: 23 -> -13 +13 or 22-> 11 + -11";
+//     throw std::invalid_argument(error);
+//   }
+
+//   std::string cmd = inputCmd;
+
+//   /// remove spaces to ease further parsing
+//   rmSpaces(cmd);
+
+//   if ( cmd == m_decayArrow.str() ) {
+//   }
+
+  // real parsing takes place now.
+  PyObject *res = PyObject_CallFunction (m_parseFct,
+					 (char*)"s",
+					 inputCmd.c_str());
+  if (!res) {
+    Py_XDECREF (res);
+    std::string error = "problem while parsing command [" + inputCmd +"]";
+    throw std::runtime_error (error);
+  }
+  
+  if (!PyTuple_Check (res)) {
+    Py_DECREF (res);
+    std::string error = "expected a python tuple";
+    throw std::runtime_error (error);
+  }
+
+  if (PyTuple_GET_SIZE (res) != 3) {
+    Py_DECREF (res);
+    std::string error = "expected a python tuple of size 3";
+    throw std::runtime_error (error);
+  }
+
+  PyObject *sc = PyTuple_GET_ITEM (res, 0);
+  Py_XINCREF (sc);
+  if (!sc || !PyInt_Check (sc)) {
+    Py_XDECREF (sc);
+    Py_DECREF  (res);
+    std::string error = "corrupted return code";
+    throw std::runtime_error (error);
+  }
+
+  Py_ssize_t status = PyInt_AsSsize_t (sc);
+  if (status != 0) {
+    Py_DECREF (sc);
+    Py_DECREF (res);
+    std::string error = "failed to parse command ["+inputCmd+"]";
+    throw std::runtime_error (error);
+  }
+  Py_DECREF (sc);
+
+  PyObject *parents = PyTuple_GET_ITEM (res, 1);
+  Py_XINCREF (parents);
+  if (!parents) {
+    Py_DECREF (res);
+    std::string error = "corrupted parents' list";
+    throw std::runtime_error (error);
+  }
+
+  PyObject *children= PyTuple_GET_ITEM (res, 2);
+  Py_XINCREF (children);
+  if (!children) {
+    Py_DECREF (parents);
+    Py_DECREF (res);
+    std::string error = "corrupted children' list";
+    throw std::runtime_error (error);
+  }
+  Py_DECREF (res);
+
+  if (parents==Py_None && children==Py_None) {
+    // special case of a single arrow without any parent nor child :
+    // this decay pattern will select every single vertex
+    Py_DECREF (parents);
+    Py_DECREF (children);
+    return;
+  }
+  
+  if (!py_to_cpp (parents, m_parents)) {
+    Py_DECREF (parents);
+    Py_DECREF (children);
+    std::string error = "could not translate parents' list";
+    throw std::runtime_error (error);
+  }
+
+  if (!py_to_cpp (children, m_children)) {
+    Py_DECREF (parents);
+    Py_DECREF (children);
+    std::string error = "could not translate children' list";
+    throw std::runtime_error (error);
+  }
+
+  return;
+}
+
+
+/////////////////////////////////////////////////////////////////// 
+/// Protected methods: 
+/////////////////////////////////////////////////////////////////// 
+
+// void 
+// DecayParser::parseCandidates( const std::string& inString,
+// 			      McUtils::Strings& outList ) const
+// {
+//   McUtils::Strings tmpList;
+//   balg::split_regex (tmpList, inString, m_candidateSep);
+//   outList.clear();
+//   outList.reserve (tmpList.size());
+//   const McUtils::Strings::const_iterator tmpListEnd = tmpList.end();
+//   for ( McUtils::Strings::const_iterator i = tmpList.begin();
+// 	i != tmpListEnd;
+// 	++i ) {
+//     if ( !i->empty() ) {
+//       outList.push_back( *i );
+//     }
+//   }
+//   return;
+// }
+
+void 
+DecayParser::printMcUtilsStrings( const std::vector<McUtils::Strings>& list ) const
+{
+  unsigned int iSlot = 0;
+  for( std::vector<McUtils::Strings>::const_iterator itr = list.begin();
+       itr != list.end();
+       ++itr,++iSlot ) {
+    std::stringstream iSlotStr;
+    iSlotStr << iSlot;
+    const McUtils::Strings::const_iterator candEnd = itr->end();
+    std::cout << "slot #" << iSlotStr.str() << ": candidates= [ ";
+    for( McUtils::Strings::const_iterator candidate = itr->begin();
+	 candidate != candEnd;
+	 ++candidate ) {
+      std::cout << *candidate;
+      if ( candidate+1 != candEnd ) {
+	std::cout << " | ";
+      }
+    }
+    std::cout << " ]" << std::endl;
+  }
+  return;
+}
+
+// McUtils::Strings 
+// DecayParser::tokenizeString( const std::string& stringList ) const
+// {
+//   std::cerr << "==DecayParser== tokenizeString (" << stringList << ")...\n";
+// //   std::cerr << "  ==> slotSep: [" << m_slotSep << "]\n"
+// // 	    << "  ==> candSep: [" << m_candidateSep << "]\n"
+// // 	    << "  ==> dcyArrw: [" << m_decayArrow << "]\n"
+// // 	    << "  ==> wildCrd: [" << m_wildCardParticle << "]\n";
+
+//   std::string nameList = stringList;
+//   /// Removes all the spaces in the strings : " " -> ""
+//   rmSpaces(nameList);
+
+//   McUtils::Strings out;
+//   balg::split (out, nameList, balg::is_any_of(m_slotSep));
+//   return out;
+// }
+
+// void DecayParser::fillToken( const McUtils::Strings& token,
+// 			     std::vector<McUtils::Strings>& tokens ) const
+// {
+//   std::cerr << "==DecayParser== fillToken (" << token.size() << ")...\n";
+//   for ( McUtils::Strings::const_iterator itr = token.begin();
+// 	  itr != token.end();
+// 	++itr ) {
+//     //std::cout << "Parsing : <" << *itr << ">..." << std::endl;
+//     /// this is the list of matching candidates for each child
+//     McUtils::Strings candidates;
+//     /// parse the child string to unmangle the possible choices for each
+//     /// of the children ( resolves that e-|mu- can be either a muon or
+//     /// an electron )
+//     parseCandidates( *itr, candidates );
+//     if ( !candidates.empty() ) {
+//       tokens.push_back( candidates );
+//     }
+//   }
+//   return;
+// }
+
+// void DecayParser::processBlock( const std::string& tokenString,
+// 				std::vector<McUtils::Strings>& tokens ) const
+// {
+//   std::cerr << "==DecayParser== processBlock ("
+// 	    << tokenString <<", tokens=" << tokens.size() 
+// 	    << ")...\n";
+
+//   // cut the tokenString into slots
+//   McUtils::Strings tokenNames = tokenizeString( tokenString );
+
+//   // cut each slot into a list of candidates
+//   fillToken( tokenNames, tokens );
+// }
+
+/////////////////////////////////////////////////////////////////// 
+// Operators: 
+/////////////////////////////////////////////////////////////////// 
+DecayParser & DecayParser::operator=(const DecayParser& rhs )
+{
+  if ( this != &rhs ) {
+    m_parents      = rhs.m_parents;
+    m_children     = rhs.m_children;
+  }
+  return *this;
+}
+
+namespace {
+
+PyObject*
+fetch_py_parse_fct()
+{
+  // need to ensure the python interpreter has been initialized...
+  if (!Py_IsInitialized()) {
+    Py_Initialize();
+  }
+
+  const std::string n = "McParticleUtils.DecayParser";
+  PyObject *module = PyImport_ImportModule (const_cast<char*>(n.c_str()));
+  if (!module || !PyModule_Check (module)) {
+    Py_XDECREF (module);
+    std::string error = "could not import module ["+n+"]";
+    throw std::runtime_error (error);
+  }
+
+  const std::string fct_name = "py_parse";
+  PyObject *fct = PyDict_GetItemString (PyModule_GetDict (module),
+					const_cast<char*>(fct_name.c_str()));
+  // borrowed ref.
+  Py_XINCREF (fct);
+  // don't need the module anymore
+  Py_DECREF (module);
+
+  if (!fct || !PyFunction_Check (fct)) {
+    std::string error = "could not get '"+fct_name+"' from module ["+n+"] or not a function";
+    throw std::runtime_error (error);
+  }
+
+  return fct;
+}
+
+bool 
+py_to_cpp (PyObject* candidates,
+	   std::vector<McUtils::Strings>& parsed)
+{
+  bool all_good = true;
+  if (candidates==Py_None) {
+    // nothing to do
+    return true;
+  }
+
+  if (!PySequence_Check (candidates)) {
+    return false;
+  }
+  Py_ssize_t isz = PySequence_Size (candidates);
+  if (isz==-1) {
+    return false;
+  }
+  parsed.resize (isz);
+
+  for (Py_ssize_t i = 0; i!=isz; ++i) {
+    PyObject *cand = PySequence_GetItem(candidates, i);
+    if (!cand) {
+      return false;
+    }
+    if (!PySequence_Check (cand)) {
+      Py_DECREF (cand);
+      return false;
+    }
+    Py_ssize_t jsz = PySequence_Size (cand);
+    if (jsz==-1) {
+      Py_DECREF (cand);
+      return false;
+    }
+
+    parsed[i].resize(jsz);
+
+    for (Py_ssize_t j = 0; j!=jsz; ++j) {
+      PyObject *pdgid = PySequence_GetItem(cand, j);
+      if (!pdgid) {
+	Py_DECREF (cand);
+	return false;
+      }
+
+      parsed[i][j] = std::string(PyString_AS_STRING(pdgid));
+    }
+
+    Py_DECREF (cand);
+  }
+  return all_good;
+}
+} //> anon-namespace
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McUtils.cxx b/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McUtils.cxx
new file mode 100644
index 000000000000..7670d5a96fa2
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McUtils.cxx
@@ -0,0 +1,104 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/////////////////////////////////////////////////////////////////// 
+// McUtils.cxx
+// Implementation file for various Mc helpers and methods
+// Author: S.Binet<binet@cern.ch>
+/////////////////////////////////////////////////////////////////// 
+
+// STL includes
+#include <cmath>
+#include <stdexcept>
+#include <cstring>
+#include <string>
+
+// boost includes
+#include "boost/array.hpp"
+
+// FrameWork includes
+#include "GaudiKernel/IPartPropSvc.h"
+#include "GaudiKernel/ServiceHandle.h"
+
+// CLHEP/HepMC includes
+#include "HepPDT/ParticleData.hh"
+#include "HepPDT/ParticleDataTable.hh"
+
+// McParticleUtils includes
+#include "McParticleUtils/McUtils.h"
+
+namespace {
+
+/** 3*charge for basic pdgId codes -- used to parse unknown id's
+    Fix from Frank for the charge of the MC Truth Particle */
+static const boost::array<int, 100> qcharge =
+  {{+0, -1, +2, -1, +2, -1, +2, -1, +2, +0,  //  0- 9
+    +0, -3, +0, -3, +0, -3, +0, -3, +0, +0,  // 10-19
+    +0, +0, +0, +3, +0, +0, +0, +0, +0, +0,  // 20-29
+    +0, +0, +0, +3, +0, +0, +0, +3, +0, +0,  // 30-39
+    +0, +0, +0, +0, +0, +0, +0, +0, +0, +0,
+    +0, +0, +0, +0, +0, +0, +0, +0, +0, +0,
+    +0, +0, +0, +0, +0, +0, +0, +0, +0, +0,
+    +0, +0, +0, +0, +0, +0, +0, +0, +0, +0,
+    +0, +0, +0, +0, +0, +0, +0, +0, +0, +0,
+    +0, +0, +0, +0, +0, +0, +0, +0, +0, +0}};
+
+}
+
+
+double McUtils::chargeFromPdgId( const int pdgId, 
+				 const HepPDT::ParticleDataTable* pdt )
+{
+  using std::abs;
+  if ( !pdt ) {
+    throw std::invalid_argument( "Null pointer to HepPDT::ParticleDataTable !" );
+  }
+
+  /** FIXME : should use HepPDT::ParticleDataTable to retrieve
+      the charge of the MC particle
+      Problem : how do we handle particles which are not in the
+      PDGTABLE.MeV file ? */
+  const HepPDT::ParticleData* ap = pdt->particle( abs( pdgId ) );
+  if ( ap ) {
+    return ( pdgId < 0 ) 
+      ? -(ap->charge()) 
+      :  ap->charge();
+  } else if ( 0 == pdgId ) {
+    // we are trying to compute a charge for a non existing particle...
+    //throw std::invalid_argument( "Non existing PDG-id" );
+    return -999.;
+  } else {
+    /** Set charge using PDG convention:
+	id = nnnnijkl
+	i == 0, j == 0:   see qcharge[100]
+	i == 0:           meson, j kbar quarks    l = 2*spin+1
+	i != 0:           baryon, i j k quarks    l = 2*spin+1
+	Default is 0; */
+    const int idmod = abs(pdgId) % 10000;
+    const int q1 = (idmod/10) % 10;
+    const int q2 = (idmod/100) % 10;
+    const int q3 = (idmod/1000) % 10;
+    double q = 0;
+
+    if (abs(pdgId) >= 1000000000) {
+      // Seems to be a nucleus: 100pppnnn0
+      q = (abs(pdgId) / 10000) % 1000;
+    }
+    else if( idmod < 100 ) {
+      q = qcharge[idmod]/3.;
+    }
+    else if ( idmod < 1000 ) {
+      q = (qcharge[q1]-qcharge[q2])/3.;
+      if ( qcharge[q2] == 2 ) {
+	q *= -1.;
+      }
+    }
+    else if( idmod < 10000 ) {
+      q = (qcharge[q3]+qcharge[q2]+qcharge[q1])/3.;
+    }
+    if (q == 0) q = 0; // Change -0 -> 0.
+    return (pdgId < 0) ? -q : q;
+  }
+}
+
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McVtxFilter.cxx b/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McVtxFilter.cxx
new file mode 100755
index 000000000000..84cae9ef52e9
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/src/McVtxFilter.cxx
@@ -0,0 +1,508 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/////////////////////////////////////////////////////////////////// 
+// McVtxFilter.cxx 
+// Implementation file for class McVtxFilter
+// Author: S.Binet<binet@cern.ch>
+/////////////////////////////////////////////////////////////////// 
+
+
+// STL includes
+
+// Framework includes
+#include "AthenaKernel/getMessageSvc.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/GaudiException.h"
+
+// HepMC includes
+#include "HepMC/GenParticle.h"
+#include "HepMC/GenVertex.h"
+
+// AnalysisUtils includes
+#include "AnalysisUtils/AnalysisPermutation.h"
+
+// McParticleUtils includes
+#include "McParticleUtils/McVtxFilter.h"
+
+/////////////////////////////////////////////////////////////////// 
+/// Public methods: 
+/////////////////////////////////////////////////////////////////// 
+
+/// Constructors
+////////////////
+McVtxFilter::McVtxFilter() : 
+  IFilterCuts(),
+  m_msg ( Athena::getMessageSvc(), "McVtxFilter" ),
+  m_matchSign( false ),
+  m_matchBranches( false ),
+  m_decayPattern( "" ),
+  m_parentList(),
+  m_childList()
+{}
+
+McVtxFilter::McVtxFilter( const std::string& decayPattern,
+			  const bool matchSign, 
+			  const bool matchBranches ) : 
+  IFilterCuts(),
+  m_msg ( Athena::getMessageSvc(), "McVtxFilter" ),
+  m_matchSign( matchSign ),
+  m_matchBranches( matchBranches ),
+  m_decayPattern( decayPattern ),
+  m_parentList(),
+  m_childList()
+{
+  setDecayPattern(decayPattern);
+}
+
+McVtxFilter::McVtxFilter( const bool matchSign, 
+			  const bool matchBranches ) : 
+  IFilterCuts(),
+  m_msg ( Athena::getMessageSvc(), "McVtxFilter" ),
+  m_matchSign( matchSign ),
+  m_matchBranches( matchBranches ),
+  m_decayPattern( "" ),
+  m_parentList(),
+  m_childList()
+{}
+
+McVtxFilter::McVtxFilter( const McVtxFilter& rhs ) : 
+  IFilterCuts    ( rhs                 ),
+  m_msg          ( rhs.m_msg           ),
+  m_matchSign    ( rhs.m_matchSign     ),
+  m_matchBranches( rhs.m_matchBranches ),
+  m_decayPattern ( rhs.m_decayPattern  ),
+  m_parentList(),
+  m_childList()
+{
+  // deep copy of the parent branch
+  m_parentList.reserve( rhs.m_parentList.size() );
+  for ( DataVector<const ParticleCandidateList>::const_iterator itr = 
+	  rhs.m_parentList.begin();
+	itr != rhs.m_parentList.end();
+	++itr ) {
+    m_parentList.push_back( new ParticleCandidateList(**itr) );
+  }
+
+  // deep copy of the child branch
+  m_childList.reserve( rhs.m_childList.size() );
+  for ( DataVector<const ParticleCandidateList>::const_iterator itr = 
+	  rhs.m_childList.begin();
+	itr != rhs.m_childList.end();
+	++itr ) {
+    m_childList.push_back( new ParticleCandidateList(**itr) );
+  }
+}
+
+McVtxFilter & McVtxFilter::operator=(const McVtxFilter &rhs)
+{
+  if ( this != &rhs ) {
+    IFilterCuts::operator=(rhs);
+    m_matchSign     = rhs.m_matchSign;
+    m_matchBranches = rhs.m_matchBranches;
+    m_decayPattern  = rhs.m_decayPattern;
+
+    // deep copy of the parent branch
+    m_parentList.reserve( rhs.m_parentList.size() );
+    for ( DataVector<const ParticleCandidateList>::const_iterator itr = 
+	    rhs.m_parentList.begin();
+	  itr != rhs.m_parentList.end();
+	  ++itr ) {
+      m_parentList.push_back( new ParticleCandidateList(**itr) );
+    }
+
+    // deep copy of the child branch
+    m_childList.reserve( rhs.m_childList.size() );
+    for ( DataVector<const ParticleCandidateList>::const_iterator itr = 
+	    rhs.m_childList.begin();
+	  itr != rhs.m_childList.end();
+	  ++itr ) {
+      m_childList.push_back( new ParticleCandidateList(**itr) );
+    }
+  }
+  return *this;
+}
+
+/// Destructor
+///////////////
+
+/////////////////////////////////////////////////////////////////// 
+/// Const methods: 
+///////////////////////////////////////////////////////////////////
+bool McVtxFilter::isAccepted( const HepMC::GenVertex * vtx ) const
+{
+  m_msg << MSG::VERBOSE
+	<< "In McVtxFilter::isAccepted(...)" << endreq;
+
+  ////////////////////////////////////////////////////////////////
+  /// First handle special case where the filter has only 1 child
+  /// and no parent : one is looking for a stable particle with
+  /// no end_vertex
+  if ( m_childList.size()        == static_cast<unsigned int>( 1 ) &&
+       m_parentList.size()       == static_cast<unsigned int>( 0 ) &&
+       vtx->particles_out_size() == static_cast<unsigned int>( 1 ) ) {
+    const HepMC::GenParticle * part = *(vtx->particles_out_const_begin());
+    const ParticleCandidateList * item = *( m_childList.begin() );
+    if ( item->hasInList( static_cast<PDG::pidType>(part->pdg_id()), 
+			  m_matchSign ) ) {
+      return true;
+    } else { 
+      return false;
+    }
+  }
+
+  /// Skip vertices which don't match the number of branches
+  /// we are looking for : check if number of parents 
+  /// and of children is OK
+  if ( !m_matchBranches ) {
+
+    //
+    // Test only if there is enough *output* branches to match for
+    //
+    if ( static_cast<unsigned int>(vtx->particles_in_size())  < m_parentList.size() ||
+	 static_cast<unsigned int>(vtx->particles_out_size()) < m_childList.size()  ) {
+      return false;
+    }
+
+  } else {
+    //
+    // Strict match of output branches
+    //
+    if ( static_cast<unsigned int>(vtx->particles_in_size())  != m_parentList.size() ||
+	 static_cast<unsigned int>(vtx->particles_out_size()) != m_childList.size()  ) {
+      return false;
+    }
+  }
+
+  //////////////////////////////////////////////////////////////
+  /// Then handle the case we are looking for a 1->2 body decay
+  ///
+  if ( m_matchBranches                                           &&
+       m_parentList.size()       == static_cast<unsigned int>(1) &&
+       m_childList.size()        == static_cast<unsigned int>(2) &&
+       vtx->particles_out_size() >= 2 ) {
+    return checkTwoBodyDecay( vtx );
+  } //> two-body decay
+
+
+  ////////////////////////////////
+  /// Handle other generic cases
+  ///
+  m_msg << MSG::VERBOSE << "trying checkParentBranch(...)" << endreq;
+  if ( checkParentBranch( vtx ) == false ) return false;
+
+  m_msg << MSG::VERBOSE << "trying checkChildBranch(...)" << endreq;
+  if ( checkChildBranch ( vtx ) == false ) return false;
+
+  m_msg << MSG::VERBOSE << "McVtxFilter::isAccepted(...) => DONE" << endreq;
+  return true;
+}
+
+void McVtxFilter::dump( std::ostream& out ) const
+{
+  out << ">>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<" << std::endl;
+  out << ">>> Parents : " << std::endl;
+  for ( DataVector<const ParticleCandidateList>::const_iterator itr = m_parentList.begin();
+	itr != m_parentList.end();
+	++itr ) {
+    out << "\nList for : " << *itr  
+	<< " (" << (*itr)->size() << ")" << std::endl;
+    (*itr)->dropList();
+  }
+
+  out << ">>> Children : " << std::endl;
+  for ( DataVector<const ParticleCandidateList>::const_iterator itr = m_childList.begin();
+	itr != m_childList.end();
+	++itr ) {
+    out << "\nList for : " << *itr  
+	<< " (" << (*itr)->size() << ")" << std::endl;
+    (*itr)->dropList();
+  }
+  out << ">>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<" << std::endl;
+
+  return;
+}
+
+
+/////////////////////////////////////////////////////////////////// 
+/// Non-const methods: 
+/////////////////////////////////////////////////////////////////// 
+
+/// Set filter cut options
+void McVtxFilter::setFilter( const IFilterCuts * filter )
+{
+  if ( filter ) {
+    const McVtxFilter * vtxFilter = 
+      dynamic_cast<const McVtxFilter*>(filter);
+    
+    if ( vtxFilter ) {
+      operator=(*vtxFilter);
+    } else {
+      m_msg << MSG::ERROR
+	    << "Can't dynamic_cast " << typeid(filter).name() 
+	    << " to a McVtxFilter"
+	    << endreq;
+    }
+  } //> filter is a valid pointer
+}
+
+/// Set the filter from a decay pattern
+void McVtxFilter::setDecayPattern( const std::string& decayPattern )
+{
+  // clear parent and child candidates
+  m_parentList.clear();
+  m_childList.clear();
+
+  m_decayPattern = decayPattern;
+  
+  DecayParser parser( m_decayPattern );
+  
+  //std::cout << "Populate parent list" << std::endl;
+  std::vector<std::vector<std::string> > parents = parser.getParents();
+  for(std::vector<std::vector<std::string> >::const_iterator itr = parents.begin();
+      itr != parents.end();
+      ++itr ) {
+    ParticleCandidateList * list = new ParticleCandidateList();
+    const std::vector<std::string>::const_iterator candEnd  = itr->end();
+    for( std::vector<std::string>::const_iterator candidate = itr->begin();
+	 candidate != candEnd;
+	 ++candidate ) {
+      PDG::pidType pdgID = parser.pdgId( *candidate );
+      list->push_back( pdgID );
+    }
+    if ( ! list->empty() ) {
+      m_parentList.push_back( list );
+    } else {
+      delete list;
+    }
+  }
+  //std::cout << "Populate children list" << std::endl;
+  std::vector<std::vector<std::string> > children = parser.getChildren();
+  for(std::vector<std::vector<std::string> >::const_iterator itr = children.begin();
+      itr != children.end();
+      ++itr ) {
+    ParticleCandidateList * list = new ParticleCandidateList();
+    const std::vector<std::string>::const_iterator candEnd  = itr->end();
+    for( std::vector<std::string>::const_iterator candidate = itr->begin();
+	 candidate != candEnd;
+	 ++candidate ) {
+      PDG::pidType pdgID = parser.pdgId( *candidate );
+      list->push_back( pdgID );
+    }
+    if ( ! list->empty() ) {
+      m_childList.push_back( list );
+    } else {
+      delete list;
+    }
+  }
+
+  return;
+}
+
+
+/////////////////////////////////////////////////////////////////// 
+/// Protected methods: 
+/////////////////////////////////////////////////////////////////// 
+
+bool McVtxFilter::checkParentBranch( const HepMC::GenVertex * vtx ) const
+{
+  m_msg << MSG::VERBOSE << "In checkParentBranch..." << endreq;
+
+  /// Check we aren't in the "any particle" case
+  if ( m_parentList.empty() ) {
+    return true;
+  }
+  
+  if ( m_msg.level() <= MSG::VERBOSE ) {
+    vtx->print();
+  }
+
+  /// Check if number of parents is OK
+  if ( static_cast<unsigned int>(vtx->particles_in_size()) < m_parentList.size() ){
+    return false;
+  }
+
+  if ( m_msg.level() <= MSG::VERBOSE ) {
+    m_msg << MSG::VERBOSE 
+	  << "Number of list of parents : " 
+	  << m_parentList.size() 
+	  << endreq;
+    m_parentList.front()->dropList();
+  }
+
+  std::vector<int> parentIds;
+  for ( HepMC::GenVertex::particles_in_const_iterator itrPart = vtx->particles_in_const_begin();
+	itrPart != vtx->particles_in_const_end();
+	++itrPart ) {
+    parentIds.push_back( (*itrPart)->pdg_id() );
+  }
+
+  AnalysisUtils::Permutation<std::vector<int> > permute( &parentIds,
+							 m_parentList.size() );
+  std::vector<int> parents;
+
+  bool accepted = false;
+  while ( permute.get(parents) && !accepted ) {
+    accepted = true;
+    const unsigned int iMax = std::min( m_parentList.size(), parentIds.size() );
+    for ( unsigned int i = 0; i != iMax; ++i ) {
+      const bool hasInList = 
+	m_parentList[i]->hasInList( static_cast<PDG::pidType>(parents[i]), 
+				    m_matchSign );
+      if ( !hasInList ) {
+	// this permutation is not suiting, going to the next one
+	// (if any)
+	accepted = false;
+	break;
+      }
+    }
+
+    // everything went fine for this permutation !
+    if ( accepted ) {
+      break;
+    }
+  }
+
+  m_msg << MSG::VERBOSE << ">>> CheckParentBranch is DONE : " 
+	<< ( accepted ? "accept" : "reject" )
+	<< " vtx= " << vtx->barcode()
+	<< endreq;
+  return accepted;
+}
+
+bool McVtxFilter::checkChildBranch( const HepMC::GenVertex * vtx ) const
+{
+  m_msg << MSG::VERBOSE << "In checkChildBranch..." << endreq;
+
+  if ( m_msg.level() <= MSG::VERBOSE ) {
+    vtx->print();
+  }
+
+  /// Check we aren't in the "any particle" case
+  if ( m_childList.empty() ) {
+    return true;
+  }
+
+  /// Check there is enough outgoing particles in the current vertex
+  if ( static_cast<unsigned int>(vtx->particles_out_size()) < m_childList.size() ) return false;
+
+  m_msg << MSG::VERBOSE << "Number of list of children : " 
+	<< m_childList.size() << endreq;
+
+  std::vector<int> childIds;
+  for ( HepMC::GenVertex::particles_out_const_iterator itrPart = vtx->particles_out_const_begin(); 
+	itrPart != vtx->particles_out_const_end();
+	++itrPart ) {
+    childIds.push_back( (*itrPart)->pdg_id() );
+  }
+
+  AnalysisUtils::Permutation<std::vector<int> > permute( &childIds, 
+							 m_childList.size() );
+  std::vector<int> children;
+
+  bool accepted = false;
+  while ( permute.get(children) && !accepted ) {
+    accepted = true;
+    const unsigned int iMax = std::min( m_childList.size(), childIds.size() );
+    for ( unsigned int i = 0; i != iMax; ++i ) {
+      const bool hasInList = 
+	m_childList[i]->hasInList( static_cast<PDG::pidType>(children[i]), 
+				   m_matchSign );
+      if ( !hasInList ) {
+	// this permutation is not suiting, going to the next one
+	// (if any)
+	accepted = false;
+	break;
+      }
+    }
+  }
+
+  m_msg << MSG::VERBOSE << ">>> CheckChildBranch is DONE : " 
+	<< ( accepted ? "accept" : "reject" )
+	<< " vtx= " << vtx->barcode()
+	<< endreq;
+  return accepted;
+}
+
+bool McVtxFilter::checkTwoBodyDecay( const HepMC::GenVertex * vtx ) const
+{
+  m_msg << MSG::VERBOSE << "In checkTwoBodyDecay..." << endreq;
+
+  /// First check the parent branch matching decision
+  /// if it doesn't fulfil our requirements, it is not worth
+  /// continuing the process
+  const bool parentTree = checkParentBranch( vtx );
+  if ( parentTree == false ) {
+    return false;
+  }
+
+  /// Now, handle the child branch
+  m_msg << MSG::VERBOSE << ">>> Check child branch" << endreq;
+
+  /// Cache the 2 particle candidate lists
+  const ParticleCandidateList * children1 = m_childList[0];
+  const ParticleCandidateList * children2 = m_childList[1];
+
+  /// Cache the id of the outgoing particles of the vertex being analysed
+  HepMC::GenVertex::particles_out_const_iterator 
+    itrPart = vtx->particles_out_const_begin();
+  const PDG::pidType pdgId1 = static_cast<PDG::pidType>((*itrPart)->pdg_id());
+  ++itrPart;
+  const PDG::pidType pdgId2 = static_cast<PDG::pidType>((*itrPart)->pdg_id());
+
+  /// Loop over candidates for the 1st child
+  for( ParticleCandidateList::const_iterator itr1 = children1->begin();
+       itr1 != children1->end();
+       ++itr1 ) {
+    /// Loop over candidate for the 2nd child
+    for( ParticleCandidateList::const_iterator itr2 = children2->begin();
+	 itr2 != children2->end();
+	 ++itr2 ) {
+      m_msg << MSG::VERBOSE << "Checking the pair : " 
+	    << (*itr1) << "/" << (*itr2) 
+	    << endreq;
+
+      /// If the strict match sign has been required, we check if
+      /// the PDG ids are matching
+      if ( m_matchSign && 
+	   ( ( (*itr1) == pdgId1 && (*itr2) == pdgId2 ) ||
+	     ( (*itr1) == pdgId2 && (*itr2) == pdgId1 ) ) ) {
+	m_msg << MSG::VERBOSE << "Strict sign matching found !" << endreq;
+	return true;
+      /// if we are in a loose sign request, we check only that the
+      /// absolute values of the pdg IDs are matching
+      } else if ( ( std::abs(*itr1) == std::abs(pdgId1) && 
+		    std::abs(*itr2) == std::abs(pdgId2) )   ||
+		  ( std::abs(*itr1) == std::abs(pdgId2) && 
+		    std::abs(*itr2) == std::abs(pdgId1) ) ) {
+	m_msg << MSG::VERBOSE << "Loose sign matching found !" << endreq;
+	return true;
+      }
+      m_msg << MSG::VERBOSE << "Checking next pair" << endreq;
+    }//> loop over 2nd child's candidates
+  }//> loop over 1st child's candidates
+
+  /// If we are here, then the vertex candidate didn't fulfil the 
+  /// requirements we have setup
+  m_msg << MSG::VERBOSE << ">>> CheckTwoBodyDecay is DONE." << endreq;
+  return false;
+}
+
+/////////////////////////////////////////////////////////////////// 
+// Operators 
+/////////////////////////////////////////////////////////////////// 
+
+MsgStream& operator<<( MsgStream & msg, const McVtxFilter &obj )
+{
+  std::stringstream out;
+  obj.dump( out );
+  msg << out.str() << endreq;
+  return msg;
+}
+
+std::ostream& operator<<( std::ostream& out, const McVtxFilter &obj )
+{
+  obj.dump( out );
+  return out;
+}
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McParticleUtils.sh b/PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McParticleUtils.sh
new file mode 100755
index 000000000000..408b03eebb4f
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McParticleUtils.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# This just selects the directory to run cmt commands
+# But with project builds and as this package (McParticleUtils) can be moved
+# from some project to another, we wildcard the AtlasXyzRelease directory
+cd ${NIGHTLYAREA}/Atlas*Release/cmt
+#
+cmt broadcast -select=McParticleUtils make CppUnit
+stat=$?
+if [ "$stat" != "0"  ]; then
+       echo " -------------------------------- "
+       echo " FAILURE : test McParticleUtils   "
+       echo " -------------------------------- "
+fi
diff --git a/PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McVtxFilterTest_CppUnit.cxx b/PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McVtxFilterTest_CppUnit.cxx
new file mode 100755
index 000000000000..3a7853fb446d
--- /dev/null
+++ b/PhysicsAnalysis/TruthParticleID/McParticleUtils/test/McVtxFilterTest_CppUnit.cxx
@@ -0,0 +1,752 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// Class to test the McVtxFilter class
+/// Author : S.Binet<binet@cern.ch>
+
+// CppUnit includes
+#include<cppunit/extensions/HelperMacros.h>
+#include<cppunit/Exception.h>
+
+// STL includes
+#include <iostream>
+#include <stdexcept>
+#include <cmath>
+
+// Framework includes
+#include "StoreGate/StoreGateSvc.h"
+
+// CLHEP includes
+#include "CLHEP/Vector/LorentzVector.h"
+#include "CLHEP/Units/SystemOfUnits.h"
+
+// HepMC includes
+#include "HepMC/GenEvent.h"
+#include "HepMC/GenVertex.h"
+#include "HepMC/GenParticle.h"
+
+// DataModel includes
+#include "DataModel/DataVector.h"
+
+// McParticleUtils includes
+#include "McParticleUtils/McVtxFilter.h"
+
+class McVtxFilterTest : public CppUnit::TestFixture
+{
+  /// Definition of the unit test suite "FilterTest"
+  CPPUNIT_TEST_SUITE( McVtxFilterTest );
+
+  CPPUNIT_TEST( testConstructor );
+  CPPUNIT_TEST( testCopyMcVtxFilter );
+  CPPUNIT_TEST( testSettersAndGetters );
+  
+  CPPUNIT_TEST( testFullVtx );
+
+  CPPUNIT_TEST( testZeeVertices );
+  CPPUNIT_TEST( testTopWbggVertices );
+  CPPUNIT_TEST( testWlnuVertices );
+  CPPUNIT_TEST( testQuarkVertices );
+
+  CPPUNIT_TEST( testStableParticle );
+  
+  /// end the definition test suite
+  CPPUNIT_TEST_SUITE_END();
+
+private:
+  HepMC::GenEvent * m_evt;
+  int               m_bcZee;
+  int               m_bcZgee;
+  int               m_bcTopWbgg;
+  typedef HepLorentzVector HLV_t;
+  
+public:
+
+  /// Set up the data members
+  void setUp()
+  {
+    const int signalProcessId = 1000082;
+    const int evtNbr = 1;
+    m_evt = new HepMC::GenEvent( signalProcessId, evtNbr );
+    m_evt->set_event_scale( -1 );
+    m_evt->set_alphaQCD( -1 );
+    m_evt->set_alphaQED( -1 );
+    std::vector<double> weights(3);
+    weights[0] = 1;
+    weights[1] = 1;
+    weights[2] = 1;
+    std::vector<long> rdmStates(2);
+    rdmStates[0] = 85909879;
+    rdmStates[1] = 9707499;
+    m_evt->weights() = weights;
+    m_evt->set_random_states( rdmStates );
+    
+    // Add 2 vertices
+    HepMC::GenVertex * v1 = new HepMC::GenVertex();
+    m_evt->add_vertex( v1 );
+    v1->add_particle_in( new HepMC::GenParticle( HLV_t(0,0,
+						       7000*GeV,
+						       7000*GeV),
+						 2212, 3 ) );
+    HepMC::GenVertex* v2 = new HepMC::GenVertex;
+    m_evt->add_vertex( v2 );
+    v2->add_particle_in( new HepMC::GenParticle( HLV_t(0,0,
+						       -7000*GeV,
+						       +7000*GeV),
+						 2212, 3 ) );
+
+    //
+    // create the outgoing particles of v1 and v2
+    HepMC::GenParticle* p3 = 
+      new HepMC::GenParticle( HLV_t(.750*GeV,
+				    -1.569*GeV,
+				    32.191*GeV,
+				    32.238*GeV), 1, 3 );
+    v1->add_particle_out( p3 );
+    HepMC::GenParticle* p4 = 
+      new HepMC::GenParticle( HLV_t( -3.047*GeV,
+				     -19.*GeV,
+				     -54.629*GeV,
+				     57.920*GeV), -2, 3 );
+    v2->add_particle_out( p4 );
+    
+    //
+    // create v3
+    HepMC::GenVertex* v3 = new HepMC::GenVertex();
+    m_evt->add_vertex( v3 );
+    v3->add_particle_in( p3 );
+    v3->add_particle_in( p4 );
+    v3->add_particle_out( 
+	new HepMC::GenParticle( HLV_t(-3.813,0.113,-1.833,4.233 ), 
+				22, 1 )
+	);
+    HepMC::GenParticle* p5 = 
+	new HepMC::GenParticle( HLV_t(1.517,-20.68,-20.605,85.925), 
+				-24,3);
+    v3->add_particle_out( p5 );
+
+    //    
+    // tell the event which vertex is the signal process vertex
+    m_evt->set_signal_process_vertex( v3 );
+
+    //
+    // add a Z0->4gammas,e+,e-
+    //
+    HepMC::GenVertex * vZgee = new HepMC::GenVertex;
+    m_evt->add_vertex( vZgee );
+    // Z0
+    vZgee->add_particle_in( new HepMC::GenParticle( HLV_t( -4.49e+04,
+							   +8.36e+03,
+							   -2.70e+05,
+							   +2.89e+05 ),
+						    23, 2 ) );
+    // Gammas
+    vZgee->add_particle_out( new HepMC::GenParticle( HLV_t( -1.28e+03,
+							    +1.03e+03,
+							    -5.47e+03,
+							    +5.71e+03 ),
+						     22, 1 ) );
+    vZgee->add_particle_out( new HepMC::GenParticle( HLV_t( +3.89e+02,
+							    -3.16e+02,
+							    -6.69e+03,
+							    +6.70e+03 ),
+						     22, 1 ) );
+    vZgee->add_particle_out( new HepMC::GenParticle( HLV_t( +7.34e+00,
+							    -2.71e+01,
+							    -4.12e+01,
+							    +4.98e+01 ),
+						     22, 1 ) );
+    vZgee->add_particle_out( new HepMC::GenParticle( HLV_t( -1.36e+02,
+							    +9.38e+01,
+							    -5.62e+02,
+							    +5.86e+02 ),
+						     22, 1 ) );
+    // Electrons
+    vZgee->add_particle_out( new HepMC::GenParticle( HLV_t( +8.01e+03,
+							    -2.96e+04,
+							    -4.50e+04,
+							    +5.44e+04 ),
+						     -11, 1 ) );
+    vZgee->add_particle_out( new HepMC::GenParticle( HLV_t( -5.19e+04,
+							    +3.72e+04,
+							    -2.13e+05,
+							    +2.22e+05 ),
+						     11, 1 ) );
+    // store its barcode for later use
+    m_bcZgee = vZgee->barcode();
+
+    //
+    // Add a Z->e+e-
+    HepMC::GenVertex * vZee = new HepMC::GenVertex;
+    m_evt->add_vertex( vZee );
+    vZee->add_particle_in( new HepMC::GenParticle( HLV_t( +7.29e+03,
+							  +2.34e+04,
+							  +2.81e+05,
+							  +2.96e+05 ),
+						   23, 2 ) );
+    
+    vZee->add_particle_out( new HepMC::GenParticle( HLV_t( +2.74e+04,
+							   -1.83e+04,
+							   +4.70e+04,
+							   +5.74e+04 ),
+						    11, 1 ) );
+    vZee->add_particle_out( new HepMC::GenParticle( HLV_t( -2.01e+04,
+							   +4.17e+04,
+							   +2.34e+05,
+							   +2.38e+05 ),
+						    -11, 1 ) );
+    m_bcZee = vZee->barcode();
+   
+    //
+    // Add a t->W+bgg
+    HepMC::GenVertex * vtWbgg = new HepMC::GenVertex;
+    m_evt->add_vertex( vtWbgg );
+    // top
+    vtWbgg->add_particle_in(new HepMC::GenParticle(HLV_t(-2.35e+05,
+							 +7.34e+04,
+							 +3.60e+04,
+							 +3.04e+05),
+						   6, 3 ) );
+
+    // Wbgg
+    vtWbgg->add_particle_out(new HepMC::GenParticle(HLV_t(-1.09e+05,
+							  +6.99e+04,
+							  -3.86e+04,
+							  +1.57e+05),
+						    24, 2 ) );
+    vtWbgg->add_particle_out(new HepMC::GenParticle(HLV_t(-9.23e+04,
+							  +2.54e+03,
+							  +5.32e+04,
+							  +1.07e+05),
+						    5, 2 ) );
+    vtWbgg->add_particle_out(new HepMC::GenParticle(HLV_t(-4.76e+03,
+							  +6.72e+02,
+							  +2.90e+03,
+							  +5.62e+03),
+						    21, 2 ) );
+    vtWbgg->add_particle_out(new HepMC::GenParticle(HLV_t(-2.93e+04,
+							  +2.13e+02,
+							  +1.85e+04,
+							  +3.46e+04),
+						    21, 2 ) );
+    m_bcTopWbgg = vtWbgg->barcode();
+
+  }
+
+  /// destroy any on-the-heap-created data member
+  void tearDown()
+  {
+    if ( m_evt ) {
+      delete m_evt;
+      m_evt = 0;
+    }
+  }
+
+  /// Test the McVtxFilter constructors
+  void testConstructor()
+  {
+    const unsigned int defaultCand = 0;
+    McVtxFilter filter;
+    CPPUNIT_ASSERT( false == filter.matchSign() );
+    CPPUNIT_ASSERT( false == filter.matchBranches() );
+    CPPUNIT_ASSERT( ""    == filter.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == filter.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == filter.childList().size()  );
+
+    McVtxFilter f2( false, false );
+    CPPUNIT_ASSERT( false == f2.matchSign() );
+    CPPUNIT_ASSERT( false == f2.matchBranches() );
+    CPPUNIT_ASSERT( ""    == f2.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == f2.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == f2.childList().size()  );
+
+    McVtxFilter f3( true, false );
+    CPPUNIT_ASSERT( true  == f3.matchSign() );
+    CPPUNIT_ASSERT( false == f3.matchBranches() );
+    CPPUNIT_ASSERT( ""    == f3.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == f3.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == f3.childList().size()  );
+
+    McVtxFilter f4( false, true );
+    CPPUNIT_ASSERT( false == f4.matchSign() );
+    CPPUNIT_ASSERT( true  == f4.matchBranches() );
+    CPPUNIT_ASSERT( ""    == f4.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == f4.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == f4.childList().size()  );
+
+    McVtxFilter f5( f4 );
+    CPPUNIT_ASSERT( false == f5.matchSign() );
+    CPPUNIT_ASSERT( true  == f5.matchBranches() );
+    CPPUNIT_ASSERT( ""    == f5.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == f5.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == f5.childList().size()  );
+
+    McVtxFilter f6 = f4;
+    CPPUNIT_ASSERT( false == f6.matchSign() );
+    CPPUNIT_ASSERT( true  == f6.matchBranches() );
+    CPPUNIT_ASSERT( ""    == f6.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == f6.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == f6.childList().size()  );
+
+    {
+      McVtxFilter f( "6 -> 24 + -5" );
+      CPPUNIT_ASSERT( false == f.matchSign() );
+      CPPUNIT_ASSERT( false == f.matchBranches() );
+      CPPUNIT_ASSERT( "6 -> 24 + -5" == f.decayPattern() );
+      CPPUNIT_ASSERT( 1 == f.parentList().size() );
+      CPPUNIT_ASSERT( 2 == f.childList().size()  );
+      CPPUNIT_ASSERT( f.isFullVtx() );
+    }
+    {
+      McVtxFilter f( "6 -> 24 + -5", true, false );
+      CPPUNIT_ASSERT( true  == f.matchSign() );
+      CPPUNIT_ASSERT( false == f.matchBranches() );
+      CPPUNIT_ASSERT( "6 -> 24 + -5" == f.decayPattern() );
+      CPPUNIT_ASSERT( 1 == f.parentList().size() );
+      CPPUNIT_ASSERT( 2 == f.childList().size()  );
+      CPPUNIT_ASSERT( f.isFullVtx() );
+    }
+    {
+      McVtxFilter f( "6 -> 24 + -5", false, true );
+      CPPUNIT_ASSERT( false == f.matchSign() );
+      CPPUNIT_ASSERT( true  == f.matchBranches() );
+      CPPUNIT_ASSERT( "6 -> 24 + -5" == f.decayPattern() );
+      CPPUNIT_ASSERT( 1 == f.parentList().size() );
+      CPPUNIT_ASSERT( 2 == f.childList().size()  );
+      CPPUNIT_ASSERT( f.isFullVtx() );
+    }
+    {
+      McVtxFilter f( "6 -> ", false, true );
+      CPPUNIT_ASSERT( false == f.matchSign() );
+      CPPUNIT_ASSERT( true  == f.matchBranches() );
+      CPPUNIT_ASSERT( "6 -> " == f.decayPattern() );
+      CPPUNIT_ASSERT( 1 == f.parentList().size() );
+      CPPUNIT_ASSERT( 0 == f.childList().size()  );
+      CPPUNIT_ASSERT( !f.isFullVtx() );
+    }
+  }
+
+  /// Test that McVtxFilter cuts are well copied
+  void testCopyMcVtxFilter()
+  {
+    const unsigned int defaultCand = 0;
+    McVtxFilter filter( false, true );
+    CPPUNIT_ASSERT( false == filter.matchSign() );
+    CPPUNIT_ASSERT( true  == filter.matchBranches() );
+    CPPUNIT_ASSERT( ""    == filter.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == filter.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == filter.childList().size()  );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+
+    filter.setDecayPattern( "6 -> 24 + -5" );
+    CPPUNIT_ASSERT( "6 -> 24 + -5" == filter.decayPattern() );
+    CPPUNIT_ASSERT(         1  == filter.parentList().size() );
+    CPPUNIT_ASSERT(         2  == filter.childList().size()  );
+    CPPUNIT_ASSERT( true == filter.isFullVtx() );
+
+    McVtxFilter copy;
+    CPPUNIT_ASSERT( false == copy.matchSign() );
+    CPPUNIT_ASSERT( false == copy.matchBranches() );
+    CPPUNIT_ASSERT( ""    == copy.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == copy.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == copy.childList().size()  );
+    CPPUNIT_ASSERT( false == copy.isFullVtx() );
+
+    copy.setFilter( &filter );
+    CPPUNIT_ASSERT( false      == copy.matchSign() );
+    CPPUNIT_ASSERT( true       == copy.matchBranches() );
+    CPPUNIT_ASSERT( "6 -> 24 + -5" == copy.decayPattern() );
+    CPPUNIT_ASSERT(    1       == copy.parentList().size() );
+    CPPUNIT_ASSERT(    2       == copy.childList().size()  );
+    CPPUNIT_ASSERT( true == copy.isFullVtx() );
+  }
+
+  /// Test setters and getters
+  void testSettersAndGetters()
+  {
+    std::cout << std::endl;
+    //
+    // Print event
+    //
+    m_evt->print();
+
+    const unsigned int defaultCand = 0;
+    McVtxFilter filter( false, true );
+    CPPUNIT_ASSERT( false == filter.matchSign() );
+    CPPUNIT_ASSERT( true  == filter.matchBranches() );
+    CPPUNIT_ASSERT( ""    == filter.decayPattern() );
+    CPPUNIT_ASSERT( defaultCand == filter.parentList().size() );
+    CPPUNIT_ASSERT( defaultCand == filter.childList().size()  );
+
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT( true  == filter.matchSign() );
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT( false == filter.matchSign() );
+
+    filter.setMatchBranches( false );
+    CPPUNIT_ASSERT( false  == filter.matchBranches() );
+    filter.setMatchBranches( true );
+    CPPUNIT_ASSERT( true   == filter.matchBranches() );
+
+    filter.setDecayPattern( "6 -> 24 + -5" );
+    CPPUNIT_ASSERT( "6 -> 24 + -5" == filter.decayPattern() );
+    CPPUNIT_ASSERT(             1  == filter.parentList().size() );
+    CPPUNIT_ASSERT(             2  == filter.childList().size()  );
+
+    filter.setDecayPattern( "23 -> -11|-13 + 11|13|15" );
+    CPPUNIT_ASSERT( "23 -> -11|-13 + 11|13|15" == filter.decayPattern() );
+    CPPUNIT_ASSERT(                         1  == filter.parentList().size() );
+    CPPUNIT_ASSERT(                         2  == filter.childList().size()  );
+
+    bool matchSign = false;
+    CPPUNIT_ASSERT( (filter.parentList()[0])->hasInList(PDG::Z0, matchSign) );
+    matchSign = true;
+    CPPUNIT_ASSERT( (filter.parentList()[0])->hasInList(PDG::Z0, matchSign) );
+
+    matchSign = false;
+    CPPUNIT_ASSERT((filter.childList()[0])->hasInList(PDG::e_plus, matchSign));
+    CPPUNIT_ASSERT((filter.childList()[0])->hasInList(PDG::mu_plus,matchSign));
+    matchSign = true;
+    CPPUNIT_ASSERT((filter.childList()[0])->hasInList(PDG::e_plus, matchSign));
+    CPPUNIT_ASSERT((filter.childList()[0])->hasInList(PDG::mu_plus,matchSign));
+
+    matchSign = true;
+    CPPUNIT_ASSERT(!(filter.childList()[0])->hasInList( PDG::e_minus, 
+							matchSign));
+    CPPUNIT_ASSERT(!(filter.childList()[0])->hasInList( PDG::mu_minus,
+							matchSign));
+    
+    CPPUNIT_ASSERT( 1 == (filter.parentList()[0])->size() );
+    std::vector<PDG::pidType> parent;
+    for ( std::list<PDG::pidType>::const_iterator itr = 
+	    (filter.parentList()[0])->begin();
+	  itr != (filter.parentList()[0])->end();
+	  ++itr ) {
+      parent.push_back( *itr );
+    }
+    
+    CPPUNIT_ASSERT( PDG::Z0 == parent[0] );
+
+    CPPUNIT_ASSERT( 2 == (filter.childList()[0])->size() );
+    std::vector<PDG::pidType> child1;
+    for ( std::list<PDG::pidType>::const_iterator itr = 
+	    (filter.childList()[0])->begin();
+	  itr != (filter.childList()[0])->end();
+	  ++itr ) {
+      child1.push_back( *itr );
+    }
+    CPPUNIT_ASSERT( 2 == child1.size() );
+    CPPUNIT_ASSERT( PDG::e_plus  == child1[0] );
+    CPPUNIT_ASSERT( PDG::mu_plus == child1[1] );
+
+    CPPUNIT_ASSERT( 3 == (filter.childList()[1])->size() );
+    std::vector<PDG::pidType> child2;
+    for ( std::list<PDG::pidType>::const_iterator itr = 
+	    (filter.childList()[1])->begin();
+	  itr != (filter.childList()[1])->end();
+	  ++itr ) {
+      child2.push_back( *itr );
+    }
+    CPPUNIT_ASSERT( 3 == child2.size() );
+
+    CPPUNIT_ASSERT( PDG::e_minus   == child2[0] );
+    CPPUNIT_ASSERT( PDG::mu_minus  == child2[1] );
+    CPPUNIT_ASSERT( PDG::tau_minus == child2[2] );
+
+  }
+
+  /// Test full vtx
+  void testFullVtx()
+  {
+    HepMC::GenVertex * vtx = new HepMC::GenVertex;
+    m_evt->add_vertex( vtx );
+    vtx->add_particle_in( new HepMC::GenParticle( HLV_t( -2.45e+04,
+							 +1.88e+04,
+							 -8.65e+05,
+							 +8.65e+05 ),
+						  22, 3 ) );
+    vtx->add_particle_out( new HepMC::GenParticle( HLV_t( -2.45e+04,
+							  +1.88e+04,
+							  -8.65e+05,
+							  +8.65e+05 ),
+						   22, 1 ) );
+    
+    McVtxFilter filter;
+    filter.setDecayPattern( "22->" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "->22" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "24->-24" );
+    CPPUNIT_ASSERT( true == filter.isFullVtx() );
+    CPPUNIT_ASSERT( !filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "-> -24 + 24" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( !filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "22 -> -11 + 11" );
+    CPPUNIT_ASSERT( true == filter.isFullVtx() );
+    CPPUNIT_ASSERT( !filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "22 -> -11" );
+    CPPUNIT_ASSERT( true == filter.isFullVtx() );
+    CPPUNIT_ASSERT( !filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "22 -> 22" );
+    CPPUNIT_ASSERT( true == filter.isFullVtx() );
+    CPPUNIT_ASSERT( filter.isAccepted( vtx ) );
+
+  }
+
+  /// Test some vertices
+  void testZeeVertices()
+  {
+    McVtxFilter filter;
+    filter.setDecayPattern( "23 -> -11 + 11" );
+
+    const HepMC::GenVertex * vtx = m_evt->barcode_to_vertex(m_bcZee);
+    CPPUNIT_ASSERT( 0 != vtx );
+
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+
+    filter.setMatchBranches( true );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+    
+    // Retrieve a vtx with FSR showering
+    vtx = m_evt->barcode_to_vertex(m_bcZgee);
+    CPPUNIT_ASSERT( 0 != vtx );
+
+    filter.setMatchSign( false );
+    filter.setMatchBranches( true );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtx) );
+
+    filter.setMatchSign( false );
+    filter.setMatchBranches( false );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+
+    filter.setMatchSign( true );
+    filter.setMatchBranches( false );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+
+    filter.setMatchSign( true );
+    filter.setMatchBranches( true );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtx) );
+  }
+
+  /// Test some vertices
+  void testTopWbggVertices()
+  {
+    const HepMC::GenVertex * vtx = m_evt->barcode_to_vertex(m_bcTopWbgg);
+    CPPUNIT_ASSERT( 0 != vtx );
+
+    {
+      McVtxFilter filter;
+      filter.setDecayPattern( "6 -> 24 + 5" );
+      
+      filter.setMatchSign( true );
+      filter.setMatchBranches( false );
+      CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+      
+      filter.setMatchSign( true );
+      filter.setMatchBranches( true );
+      CPPUNIT_ASSERT( !filter.isAccepted(vtx) );
+      
+      filter.setMatchSign( false );
+      filter.setMatchBranches( false );
+      CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+      
+      filter.setMatchSign( false );
+      filter.setMatchBranches( true );
+      CPPUNIT_ASSERT( !filter.isAccepted(vtx) );
+    }
+  }
+
+  /// Test some vertices
+  void testWlnuVertices()
+  {
+    
+    HepMC::GenVertex * vtx = new HepMC::GenVertex;
+    m_evt->add_vertex( vtx );
+    vtx->add_particle_in( new HepMC::GenParticle( HLV_t( -6.76e+04,
+							 +4.85e+03,
+							 -1.46e+03,
+							 +9.51e+04 ),
+						  -24, 2 ) );
+    vtx->add_particle_out( new HepMC::GenParticle( HLV_t( -7.14e+04,
+							  -6.17e+03,
+							  +1.67e+04,
+							  +7.36e+04 ),
+						   13, 1 ) );
+    
+    vtx->add_particle_out( new HepMC::GenParticle( HLV_t( +3.75e+03,
+							  +1.10e+04,
+							  -1.81e+04,
+							  +2.15e+04 ),
+						   -14, 1 ) );
+    //const int bcWlnu = vtx->barcode();
+
+    McVtxFilter filter;
+    filter.setDecayPattern( "24 -> -13 + -14" );
+
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtx) );
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+
+    filter.setDecayPattern( "-24 -> 13 + -14" );
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT( filter.isAccepted(vtx) );
+    
+  }
+
+  /*
+GenVertex:       -5 ID:    0 (X,cT):0
+ I: 2         5       21 +1.23e+04,-4.79e+03,+6.52e+04,+6.65e+04   3        -5
+              6       21 +1.14e+02,+1.35e+04,-6.42e+04,+6.56e+04   3        -5
+ O: 2         7        5 +4.56e+04,+1.53e+04,-5.08e+04,+7.01e+04   3        -8
+              8       -5 -3.32e+04,-6.56e+03,+5.18e+04,+6.20e+04   3        -9
+GenVertex:      -39 ID:    0 (X,cT):0
+ I: 1       158       -5 -3.21e+04,-6.19e+03,+5.05e+04,+6.38e+04   2       -39
+ O: 2       159       -5 -1.29e+04,+1.12e+03,+3.50e+04,+3.77e+04   2       -92
+            160       21 -1.92e+04,-7.30e+03,+1.55e+04,+2.61e+04   2       -40
+   */
+
+  /// Test some vertices
+  void testQuarkVertices()
+  {
+    // create a b->g+b vertex
+    HepMC::GenVertex * vtxgb = new HepMC::GenVertex;
+    m_evt->add_vertex( vtxgb );
+    vtxgb->add_particle_in( new HepMC::GenParticle( HLV_t( -3.21e+04,
+							   -6.19e+03,
+							   +5.05e+04,
+							   +6.38e+04 ),
+						    -5, 2 ) );
+    vtxgb->add_particle_out( new HepMC::GenParticle( HLV_t( -1.29e+04,
+							    +1.12e+03,
+							    +3.50e+04,
+							    +3.77e+04 ),
+						     -5, 2 ) );
+    
+    vtxgb->add_particle_out( new HepMC::GenParticle( HLV_t( -1.92e+04,
+							    -7.30e+03,
+							    +1.55e+04,
+							    +2.61e+04 ),
+						     21, 2 ) );
+
+    // create a gg->b+bbar vertex
+    HepMC::GenVertex * vtxbb = new HepMC::GenVertex;
+    m_evt->add_vertex( vtxbb );
+    vtxbb->add_particle_in( new HepMC::GenParticle( HLV_t( +1.23e+04,
+							   -4.79e+03,
+							   +6.52e+04,
+							   +6.65e+04 ),
+						    21, 3 ) );
+    vtxbb->add_particle_in( new HepMC::GenParticle( HLV_t( +1.14e+02,
+							   +1.35e+04,
+							   -6.42e+04,
+							   +6.56e+04 ),
+						    21, 3 ) );
+    vtxbb->add_particle_out( new HepMC::GenParticle( HLV_t( +4.56e+04,
+							    +1.53e+04,
+							    -5.08e+04,
+							    +7.01e+04 ),
+						     5, 3 ) );
+    
+    vtxbb->add_particle_out( new HepMC::GenParticle( HLV_t( -3.32e+04,
+							    -6.56e+03,
+							    +5.18e+04,
+							    +6.20e+04 ),
+						     -5, 3 ) );
+
+    McVtxFilter filter;
+    filter.setDecayPattern( " -> -5 + 5" );
+    
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT(  filter.isAccepted(vtxbb) );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtxgb) );
+    
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT(  filter.isAccepted(vtxbb) );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtxgb) );
+
+    filter.setDecayPattern( "21+21 -> -5 + 5" );
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT(  filter.isAccepted(vtxbb) );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtxgb) );
+    
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT(  filter.isAccepted(vtxbb) );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtxgb) );
+
+    filter.setMatchBranches(true);
+    filter.setMatchSign( false );
+    CPPUNIT_ASSERT(  filter.isAccepted(vtxbb) );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtxgb) );
+    
+    filter.setMatchSign( true );
+    CPPUNIT_ASSERT(  filter.isAccepted(vtxbb) );
+    CPPUNIT_ASSERT( !filter.isAccepted(vtxgb) );
+
+  }
+
+  /// Test Stable particles (no end_vertex)
+  void testStableParticle()
+  {
+    HepMC::GenVertex * vtx = new HepMC::GenVertex;
+    m_evt->add_vertex( vtx );
+    vtx->add_particle_in( new HepMC::GenParticle( HLV_t( -2.45e+04,
+							 +1.88e+04,
+							 -8.65e+05,
+							 +8.65e+05 ),
+						  23, 3 ) );
+    HepMC::GenParticle * photon = 0;
+    photon = new HepMC::GenParticle( HLV_t( -2.45e+04,
+					    +1.88e+04,
+					    -8.65e+05,
+					    +8.65e+05 ),
+				     22, 1 );
+    vtx->add_particle_out( photon );
+
+    // no end_vertex so : particle is stable
+    CPPUNIT_ASSERT( photon->end_vertex() == 0 );
+
+    McVtxFilter filter;
+    filter.setDecayPattern( "23->" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "22->" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( !filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "->23" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( !filter.isAccepted( vtx ) );
+
+    filter.setDecayPattern( "->22" );
+    CPPUNIT_ASSERT( false == filter.isFullVtx() );
+    CPPUNIT_ASSERT( filter.isAccepted( vtx ) );
+
+  }
+
+};
+
+/// Registration of the test suite "McVtxFilterTest" so it will be 
+/// recognized by the main test program which drives the different tests
+CPPUNIT_TEST_SUITE_REGISTRATION( McVtxFilterTest );
+
+/// CppUnit test-driver common for all the cppunit test classes.
+/// In ATLAS sw release it is located in TestPolicy package
+#include <TestPolicy/CppUnit_SGtestdriver.cxx>
-- 
GitLab