From e198960f36a026e7044df9765328b90afd415945 Mon Sep 17 00:00:00 2001
From: Scott Snyder <scott.snyder@cern.ch>
Date: Mon, 29 Aug 2016 03:27:10 +0200
Subject: [PATCH] 'Add dictionary for CaloGain.' (CaloIdentifier-00-10-95)

2016-08-29  scott snyder  <snyder@bnl.gov>
	* Tagging CaloIdentifier-00-10-95.
	* Add dictionary for CaloGain.

2016-07-29  scott snyder  <snyder@bnl.gov>

	* Tagging CaloIdentifier-00-10-94.
	* Disable GTower/JTower tests.

2016-07-28  scott snyder  <snyder@bnl.gov>

	* Tagging CaloIdentifier-00-10-93.
	* Tagging CaloIdentifier-00-10-92.
	* Tag previous changes.
	* src/JGTowerBase_ID.cxx: Fix compilation.
---
 Calorimeter/CaloIdentifier/CMakeLists.txt     |   14 +
 .../CaloIdentifier/CaloIdManager.h            |    8 +
 .../CaloIdentifier/CaloIdentifier/GTower_ID.h |   59 +
 .../CaloIdentifier/JGTowerBase_ID.h           |  608 +++++++++
 .../CaloIdentifier/CaloIdentifier/JTower_ID.h |   59 +
 .../CaloIdentifier/selection.xml              |    1 +
 Calorimeter/CaloIdentifier/cmt/requirements   |    2 +
 .../CaloIdentifier/src/CaloIdManager.cxx      |   30 +-
 Calorimeter/CaloIdentifier/src/GTower_ID.cxx  |   52 +
 .../CaloIdentifier/src/JGTowerBase_ID.cxx     | 1189 +++++++++++++++++
 Calorimeter/CaloIdentifier/src/JTower_ID.cxx  |   52 +
 .../CaloIdentifier/test/GTower_ID_test.cxx    |   76 ++
 .../CaloIdentifier/test/JTower_ID_test.cxx    |   77 ++
 .../test/jgtower_id_test_common.cxx           |  246 ++++
 14 files changed, 2471 insertions(+), 2 deletions(-)
 create mode 100644 Calorimeter/CaloIdentifier/CaloIdentifier/GTower_ID.h
 create mode 100644 Calorimeter/CaloIdentifier/CaloIdentifier/JGTowerBase_ID.h
 create mode 100644 Calorimeter/CaloIdentifier/CaloIdentifier/JTower_ID.h
 create mode 100644 Calorimeter/CaloIdentifier/src/GTower_ID.cxx
 create mode 100644 Calorimeter/CaloIdentifier/src/JGTowerBase_ID.cxx
 create mode 100644 Calorimeter/CaloIdentifier/src/JTower_ID.cxx
 create mode 100644 Calorimeter/CaloIdentifier/test/GTower_ID_test.cxx
 create mode 100644 Calorimeter/CaloIdentifier/test/JTower_ID_test.cxx
 create mode 100644 Calorimeter/CaloIdentifier/test/jgtower_id_test_common.cxx

diff --git a/Calorimeter/CaloIdentifier/CMakeLists.txt b/Calorimeter/CaloIdentifier/CMakeLists.txt
index 29f6ec33a8c..0211e9b186d 100644
--- a/Calorimeter/CaloIdentifier/CMakeLists.txt
+++ b/Calorimeter/CaloIdentifier/CMakeLists.txt
@@ -60,6 +60,20 @@ atlas_add_executable( test_lvl1_id
                       INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS}
                       LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} CaloGeoHelpers SGTools AtlasDetDescr IdDict Identifier GaudiKernel TestTools CxxUtils StoreGateLib SGtests IdDictParser PathResolver CaloIdentifier )
 
+#atlas_add_test( GTower_ID_test
+#                SOURCES
+#                test/GTower_ID_test.cxx
+#                INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS}
+#                LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} CaloGeoHelpers SGTools AtlasDetDescr IdDict Identifier GaudiKernel TestTools CxxUtils StoreGateLib SGtests IdDictParser PathResolver CaloIdentifier
+#                EXTRA_PATTERNS "mask/zero|Reading file|^AtlasDetectorID(Helper)?::" )
+
+#atlas_add_test( JTower_ID_test
+#                SOURCES
+#                test/JTower_ID_test.cxx
+#                INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS}
+#                LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} CaloGeoHelpers SGTools AtlasDetDescr IdDict Identifier GaudiKernel TestTools CxxUtils StoreGateLib SGtests IdDictParser PathResolver CaloIdentifier
+#                EXTRA_PATTERNS "mask/zero|Reading file|^AtlasDetectorID(Helper)?::" )
+
 atlas_add_test( LArEM_ID_test
                 SOURCES
                 test/LArEM_ID_test.cxx
diff --git a/Calorimeter/CaloIdentifier/CaloIdentifier/CaloIdManager.h b/Calorimeter/CaloIdentifier/CaloIdentifier/CaloIdManager.h
index dc979ddc996..ff9216db887 100755
--- a/Calorimeter/CaloIdentifier/CaloIdentifier/CaloIdManager.h
+++ b/Calorimeter/CaloIdentifier/CaloIdentifier/CaloIdManager.h
@@ -30,6 +30,8 @@ class LArHEC_SuperCell_ID;
 class TileID;
 class Tile_SuperCell_ID;
 class TTOnlineID;
+class JTower_ID;
+class GTower_ID;
 
 
 /**
@@ -65,6 +67,8 @@ public:
     const LArHEC_SuperCell_ID*  getHEC_SuperCell_ID      (void) const;
     const LArFCAL_SuperCell_ID* getFCAL_SuperCell_ID     (void) const;
     const Tile_SuperCell_ID*    getTile_SuperCell_ID     (void) const;
+    const JTower_ID*            getJTower_ID             (void) const;
+    const GTower_ID*            getGTower_ID             (void) const;
 
     void                        initialize               (void);
     bool                        isInitialized            (void) const;
@@ -86,6 +90,8 @@ public:
     void set_helper (const LArHEC_SuperCell_ID* idHelper);
     void set_helper (const LArFCAL_SuperCell_ID* idHelper);
     void set_helper (const Tile_SuperCell_ID* idHelper);
+    void set_helper (const JTower_ID* idHelper);
+    void set_helper (const GTower_ID* idHelper);
 
 private:
 
@@ -106,6 +112,8 @@ private:
     const LArHEC_SuperCell_ID*  m_hec_supercell_id;
     const LArFCAL_SuperCell_ID*  m_fcal_supercell_id;
     const Tile_SuperCell_ID*  m_tile_supercell_id;
+    const JTower_ID*     m_jTower_id;
+    const GTower_ID*     m_gTower_id;
 
 };
 
diff --git a/Calorimeter/CaloIdentifier/CaloIdentifier/GTower_ID.h b/Calorimeter/CaloIdentifier/CaloIdentifier/GTower_ID.h
new file mode 100644
index 00000000000..6c82891d651
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/CaloIdentifier/GTower_ID.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef GTOWER_ID_H
+#define GTOWER_ID_H
+
+#include "CLIDSvc/CLASS_DEF.h"
+#include "AtlasDetDescr/AtlasDetectorID.h"
+#include "Identifier/IdentifierHash.h"
+#include "IdDict/IdDictFieldImplementation.h"
+#include "IdDict/IdDictDefs.h"
+
+#include "CaloIdentifier/JGTowerBase_ID.h"
+#include "SGTools/BaseInfo.h"
+
+#include <vector>
+#include <algorithm>
+
+/**
+*
+* @class GTower_ID
+* @brief Helper class for jTower offline identifiers
+*  
+*  This class provides an interface to decode and generate offline identifiers
+*  for jTowers.  <p>
+* 
+* Definition and range of values for the elements of the identifier are: <p>
+* <pre>
+
+* </pre>
+* @author based on code by RD Schaffer
+* @author maintained by Walter Hopkins
+*/
+
+class Range;
+
+class GTower_ID : public JGTowerBase_ID
+{
+public:        
+
+  typedef Identifier::size_type  size_type ;
+
+  GTower_ID(void);    
+  ~GTower_ID(void);
+
+
+  
+  /** initialization from the identifier dictionary*/
+  virtual int  initialize_from_dictionary (const IdDictMgr& dict_mgr);
+};
+
+//using the macro below we can assign an identifier (and a version)
+//This is required and checked at compile time when you try to record/retrieve
+CLASS_DEF( GTower_ID , 49678914 , 1 )
+SG_BASE (GTower_ID, JGTowerBase_ID);
+
+
+#endif // GTOWER_ID_H
diff --git a/Calorimeter/CaloIdentifier/CaloIdentifier/JGTowerBase_ID.h b/Calorimeter/CaloIdentifier/CaloIdentifier/JGTowerBase_ID.h
new file mode 100644
index 00000000000..11dafba231d
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/CaloIdentifier/JGTowerBase_ID.h
@@ -0,0 +1,608 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id$
+/**
+ * @file CaloIdentifier/JGTowerBase_ID.h
+ * @author scott snyder <whopkins@uoregon.edu>
+ * @date Oct, 2014
+ * @brief Factor out code common between JTower_ID and GTower_ID.
+ */
+#ifndef JGTOWERBASE_ID_H
+#define JGTOWERBASE_ID_H
+
+#include "CLIDSvc/CLASS_DEF.h"
+#include "AtlasDetDescr/AtlasDetectorID.h"
+#include "Identifier/IdentifierHash.h"
+#include "Identifier/Range.h"
+#include "IdDict/IdDictFieldImplementation.h"
+
+#include "CaloIdentifier/CaloID_Exception.h"
+
+#include "boost/range/iterator_range.hpp"
+#include <vector>
+#include <algorithm>
+
+class IdentifierHash;
+class IdDictRegion;
+
+class JGTowerBase_ID : public AtlasDetectorID
+{
+public:        
+	enum { NOT_VALID=999999 };
+
+	typedef Identifier::size_type  size_type ;
+
+	JGTowerBase_ID(void);    
+
+	virtual ~JGTowerBase_ID();
+
+	/** build a tower identifier */
+	Identifier  tower_id   	(int pos_neg, int sampling, int region,
+		 int eta,       int phi ) const ;
+				 
+	/** build a tower identifier */
+	Identifier  tower_id   	(const Identifier regionId,
+		 int eta,       int phi )  const ;
+ 
+	/** build a region (of towers) identifier */
+	Identifier region_id  ( int pos_neg, int sampling, int region ) const;
+
+	/** access to IdContext's which define which levels of fields are contained in the id */
+	/** id for towers ("reduced" id) */
+	IdContext    region_context   (void) const;
+
+	/** access to IdContext's which define which levels of fields are contained in the id */
+	/** id for towers ("normal" id) */
+	IdContext    tower_context   (void) const;
+
+	/** create compact id from hash id (return == 0 for OK)*/
+	virtual int  get_id    (const IdentifierHash& hash_id, Identifier& id, const IdContext* context = 0 ) const;
+
+	/** create hash id from compact id (return == 0 for OK)*/
+	virtual int  get_hash  (const Identifier& id, IdentifierHash& hash_id, const IdContext* context = 0 ) const;
+
+	IdentifierHash calo_region_hash(const Identifier id) const;
+
+	/** create region id from hash id*/
+	Identifier region_id	(IdentifierHash region_hash_id) const;
+
+	Identifier region_id  ( const Identifier tower_Id ) const;
+
+	/** create tower id from hash id*/
+	Identifier tower_id	(IdentifierHash tower_hash_id) const;	
+
+	/** create hash id from tower id*/
+	IdentifierHash tower_hash (Identifier towerId) const;
+	/** create hash id from tower id  -- method NOT optimised, please use tower_hash() above */
+	IdentifierHash tower_hash_binary_search (Identifier towerId) const;
+
+	/** initialization from the identifier dictionary*/
+	virtual int  initialize_base_from_dictionary (const IdDictMgr& dict_mgr, const std::string& group_name, const std::string& t_pre);
+
+	/**  tower hash table max size */
+	size_type     tower_hash_max (void) const;
+  
+	/**  region hash table max size */
+	size_type     calo_region_hash_max (void) const;
+
+
+	/** Type for iterators over identifiers. */
+	typedef std::vector<Identifier>::const_iterator id_iterator;
+	/** Type for range over identifiers. */
+	typedef boost::iterator_range<id_iterator> id_range;
+
+
+	/** begin iterator over regions */
+	id_iterator region_begin    () const;
+	/** end iterator over regions */
+	id_iterator region_end      () const;
+	/** range over regions */
+	id_range    reg_range    () const;
+
+	/** begin iterator over towers */
+	id_iterator tower_begin    () const;
+	/** end iterator over towers */
+	id_iterator tower_end      () const;
+	/** range over towers */
+	id_range    tower_range    () const;
+
+	// /** Test wether given tower or layer is part of the Tile Calorimeter
+	//     @warning does NOT take as input a REGION identifier   */
+	// bool	has_tile	      (const Identifier id) const;
+	// /** Test wether given tower or layer is part of the EM barrel
+	//     @warning does NOT take as input a REGION identifier   
+	//     @warning excluding 'barrel end'! */
+	// bool	has_emb	      (const Identifier id) const;
+	// /** Test wether given tower or layer is part of the EM barrel END
+	//     @warning does NOT take as input a REGION identifier   */
+	// bool	has_barrel_end (const Identifier id) const;
+	// /** Test wether given tower or layer is part of the EM end-cap
+	//     @warning does NOT take as input a REGION identifier   */
+	// bool	has_emec	      (const Identifier id) const;
+	// /** Test wether given tower or layer is part of the HEC
+	//     @warning does NOT take as input a REGION identifier   */
+	// bool	has_hec	      (const Identifier id) const;
+	// /** Test wether given tower or layer is part of the FCAL
+	//     @warning does NOT take as input a REGION identifier   */
+	// bool	has_fcal	      (const Identifier id) const;
+
+	/**
+	 * return pos_neg according to : <br>
+	 * 
+	 * <pre>
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * DETZSIDE           +/-1             positive/negative
+	 * 
+	 * failure returns 0
+	 * 
+	 * </pre> 
+	 */
+	int         pos_neg       (const Identifier id)const;
+  
+	/**
+	 * return sampling according to : <br>
+	 * 
+	 * <pre>
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * LVL1sampling         0                EM
+	 * "                    1                Hadronic
+	 * 
+	 * failure returns 0
+	 * 
+	 * </pre> 
+	 */
+	int         sampling        (const Identifier id)const;  
+  
+	/**
+	 * return region according to : <br>
+	 * 
+	 * <pre>
+	 * GTowers
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * region           [0,1]            2 regions of different eta/phi granularity (FCAL and not FCAL)
+	 * 
+	 * JTowers
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * region           [0,3]            4 regions of different eta/phi granularity
+	 * 
+	 * failure returns 0
+	 * 
+	 * </pre> 
+	 */
+	int         region          (const Identifier id)const;  
+
+	/**
+	 * return eta according to : <br>
+	 * 
+	 * <pre>
+	 * GTower
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * eta              [0,15]         region 0(|eta|<3.2), granularity = 0.2
+	 * "                [0,2]          region 1(3.2<|eta|<4.9), granularity = 0.6 ***** NEEDS IMPROVEMENT *****
+	 * 
+	 * JTowers
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * eta              [0,24]         region 0(|eta|<2.5), granularity = 0.1
+	 * "                [0,2]          region 1(2.5|eta|<3.1), granularity = 0.1
+	 * "                [0]            region 2(3.1|eta|<3.2), granularity = 0.1
+	 * "                [0,2]          region 3(3.2|eta|<4.9), granularity = 0.6
+	 * 
+	 * failure returns 0
+	 * 
+	 * </pre> 
+	 */
+	int         eta             (const Identifier id)const; 
+
+	/**
+	 * return phi according to : <br>
+	 * 
+	 * <pre>
+	 * GTower
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * phi              [0,31]         region 0(|eta|<3.2), granularity = 0.2
+	 * "                [0,15]         region 1(3.2<|eta|<4.9), granularity = 0.4
+	 * 
+	 * JTower
+	 * element           range              meaning
+	 * -------           -----              -------
+	 * phi              [0,63]         region 0(|eta|<2.5), granularity = 0.1
+	 * "                [0,31]         region 1(2.5<|eta|<3.1), granularity = 0.2
+	 * "                [0,31]         region 1(3.1<|eta|<3.2), granularity = 0.2
+	 * "                [0,15]         region 1(3.2<|eta|<4.9), granularity = 0.4
+	 * 
+	 * failure returns 0
+	 * 
+	 * </pre> 
+	 */
+	int         phi             (const Identifier id)const;
+
+	/** min value of eta index (-999 == failure) 
+	    @warning input = REGION ID !! */
+	int eta_min(const Identifier regId) const;
+	/** max value of eta index (-999 == failure) 
+	    @warning input = REGION ID !! */
+	int eta_max(const Identifier regId) const;
+	/** min value of phi index (-999 == failure) 
+	    @warning input = REGION ID !! */
+	int phi_max(const Identifier regId) const;
+
+	/**
+	 * @brief Return the eta granularity of a region, or @c NOT_VALID.
+	 * @param regHash REGION ID code.
+	 */
+	float etaGranularity(const Identifier regId) const;
+   
+	/**
+	 * @brief Return the phi granularity of a region, or @c NOT_VALID.
+	 * @param regHash REGION ID code.
+	 */
+	float phiGranularity(const Identifier regId) const;
+   
+	/**
+	 * @brief Return the minimum eta of region, or @c NOT_VALID.
+	 * @param regHash REGION ID code.
+	 */
+	float eta0(const Identifier regId) const;
+    
+	/**
+	 * @brief Return the minimum phi of region, or @c NOT_VALID.
+	 * @param regHash REGION ID code.
+	 */
+	float phi0(const Identifier regId) const;
+   
+	/** access to hashes for neighbours in phi -- towers only (no extended) 
+	    return == 0 for neighbours found  */
+	int          get_prev_in_phi(const IdentifierHash& id, IdentifierHash& prev) const;
+	/** access to hashes for neighbours in phi -- towers only (no extended) 
+	    return == 0 for neighbours found  */
+	int          get_next_in_phi(const IdentifierHash& id, IdentifierHash& next) const;
+	/** access to hashes for neighbours in eta -- towers only (no extended) 
+	    return == 0 for neighbours found  */
+	int          get_prev_in_eta(const IdentifierHash& id, IdentifierHash& prev) const;
+	/** access to hashes for neighbours in eta -- towers only (no extended) 
+	    return == 0 for neighbours found  */
+	int          get_next_in_eta(const IdentifierHash& id, IdentifierHash& next) const;
+
+	int fill_vec_of_dict_regions (const std::string& group_name = "");
+
+	const std::vector<const IdDictRegion*>& dictRegions() const;
+
+private:    
+
+     
+	enum {NOT_VALID_HASH = 64000};
+
+
+	/** create expanded Identifier from Identifier (return == 0 for OK) */
+	int  get_expanded_id  (const Identifier& id, ExpandedIdentifier& exp_id, const IdContext* context) const;
+	void  tower_id_checks   	(int pos_neg, int sampling, int region,
+		 int eta,       int phi ) const throw(CaloID_Exception);
+	void  tower_id_checks   	(const Identifier regionId,
+		 int eta,       int phi )  const throw(CaloID_Exception);
+	void region_id_checks  ( int pos_neg, int sampling, int region ) const throw(CaloID_Exception);
+
+	int         initLevelsFromDict(const std::string& t_pre) ;
+
+	int         init_hashes(void) ;
+
+	int         init_neighbors(void) ;
+
+
+	size_type                     m_jgtower_region_index;
+	size_type                     m_CALO_INDEX;
+	size_type                     m_DETZSIDE_INDEX;
+	size_type                     m_SAMPLING_INDEX;
+	size_type                     m_REGION_INDEX;
+	size_type                     m_ETA_INDEX;
+	size_type                     m_PHI_INDEX;
+
+	const IdDictDictionary*       m_dict;
+
+	MultiRange                    m_full_reg_range;
+	MultiRange                    m_full_tower_range;
+	size_type                     m_tower_hash_max;
+	size_type                     m_calo_region_hash_max;
+	std::vector<Identifier>       m_tower_vec;
+	std::vector<Identifier>       m_region_vec;
+	std::vector<unsigned short>   m_prev_phi_vec;
+	std::vector<unsigned short>   m_next_phi_vec;
+	std::vector<unsigned short>   m_prev_eta_vec;
+	std::vector<unsigned short>   m_next_eta_vec;
+  
+	/// List of @c IdDictRegion objects.
+	std::vector<const IdDictRegion*> m_vecOfDictRegions;
+
+	/**
+	   @brief small class holding the starting hash value, the min eta and the number of phi bins of each region 
+
+	   used to CPU-optimize the conversion from an identifier to a hash index
+	*/
+	class HashCalc
+	{
+	public:
+		HashCalc() :
+			m_hash(0),
+			m_etamin(0),
+			m_nphi(0) {}
+		IdentifierHash m_hash;
+		size_type      m_etamin;
+		size_type      m_nphi;
+	};
+	std::vector<HashCalc>         m_hash_calcs;
+
+
+
+	IdDictFieldImplementation     m_calo_impl;
+	IdDictFieldImplementation  	m_jgtower_impl;
+	IdDictFieldImplementation  	m_sampling_impl;
+	IdDictFieldImplementation  	m_region_impl;
+	IdDictFieldImplementation  	m_eta_impl;
+	IdDictFieldImplementation  	m_phi_impl;
+
+	IdDictFieldImplementation  	m_pnz_reg_impl;
+
+};
+
+//using the macros below we can assign an identifier (and a version)
+//This is required and checked at compile time when you try to record/retrieve
+CLASS_DEF( JGTowerBase_ID , 131336095, 1 )
+
+inline Identifier JGTowerBase_ID::tower_id   ( int pos_neg, int sampling, int region,
+                                               int eta,       int phi ) const 
+{  
+	Identifier result(0);
+	// Pack fields independently
+	m_calo_impl.pack     (calo_field_value(),    result);
+	m_jgtower_impl.pack     (pos_neg,             result);
+	m_sampling_impl.pack (sampling,              result);
+	m_region_impl.pack   (region,                result);
+	m_eta_impl.pack      (eta,                   result);
+	m_phi_impl.pack      (phi,                   result);
+
+	// Do checks
+	if(m_do_checks) {
+		tower_id_checks( pos_neg, sampling, region, eta, phi );
+	}
+
+	return result;
+}
+//----------------------------------------------------------------------------
+inline Identifier JGTowerBase_ID::tower_id   ( const Identifier regionId,
+                                               int eta,  int phi ) const 
+{
+	Identifier result(regionId);
+
+	// Reset the fields and then set the values
+	m_eta_impl.reset     (result);
+	m_phi_impl.reset     (result);
+	m_eta_impl.pack      (eta, result);
+	m_phi_impl.pack      (phi, result);
+
+	// Do checks
+	if(m_do_checks) {
+		tower_id_checks( regionId, eta, phi );
+	}
+
+	return result;
+}
+
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::size_type   JGTowerBase_ID::tower_hash_max (void) const
+{
+	return m_tower_hash_max;
+}
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::size_type   JGTowerBase_ID::calo_region_hash_max (void) const
+{
+	return m_calo_region_hash_max;
+}
+
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::id_iterator JGTowerBase_ID::region_begin    (void) const
+{
+	return(m_region_vec.begin());
+}
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::id_iterator JGTowerBase_ID::region_end      (void) const
+{
+	return(m_region_vec.end());
+}
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::id_range JGTowerBase_ID::reg_range    () const
+{
+	return id_range (region_begin(), region_end());
+}
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::id_iterator JGTowerBase_ID::tower_begin    (void) const
+{
+	return(m_tower_vec.begin());
+}
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::id_iterator JGTowerBase_ID::tower_end      (void) const
+{
+	return(m_tower_vec.end());
+}
+
+//----------------------------------------------------------------------------
+inline JGTowerBase_ID::id_range JGTowerBase_ID::tower_range    () const
+{
+	return id_range (tower_begin(), tower_end());
+}
+
+//----------------------------------------------------------------------------
+inline int JGTowerBase_ID::pos_neg(const Identifier id) const
+{
+	return (m_jgtower_impl.unpack(id));
+}
+
+//----------------------------------------------------------------------------
+inline int JGTowerBase_ID::sampling(const Identifier id) const
+{
+	return (m_sampling_impl.unpack(id));
+}
+//----------------------------------------------------------------------------
+inline int JGTowerBase_ID::region(const Identifier id) const
+{
+	return (m_region_impl.unpack(id));
+}
+
+//----------------------------------------------------------------------------
+inline int JGTowerBase_ID::eta(const Identifier id) const
+{
+	return (m_eta_impl.unpack(id));
+}
+
+//----------------------------------------------------------------------------
+inline int JGTowerBase_ID::phi(const Identifier id) const
+{
+	return (m_phi_impl.unpack(id));
+}
+
+// //----------------------------------------------------------------------------
+// inline bool	JGTowerBase_ID::has_tile	(const Identifier id) const
+// {
+//   // Must first check if this is a normal tile id
+//   if (AtlasDetectorID::is_tile(id)) {
+//     return (true);
+//   }
+//   /* else { */
+//   /* 	return (has_jgtower_trig_towers(id) && region(id) == 0 && sampling(id) == 1 && eta(id) < 15); */
+//   /* } */
+// }
+
+// //----------------------------------------------------------------------------
+// inline bool	JGTowerBase_ID::has_emb	(const Identifier id) const
+// {
+//   return true;//(has_jgtower_trig_towers(id) && region(id) == 0 && sampling(id) == 0 && eta(id) < 14);
+// }
+
+// //----------------------------------------------------------------------------
+// inline bool	JGTowerBase_ID::has_barrel_end	(const Identifier id) const
+// {
+//   return true;//(has_jgtower_trig_towers(id) && region(id) == 0 && sampling(id) == 0 && eta(id) == 14);
+// }
+
+// //----------------------------------------------------------------------------
+// inline bool	JGTowerBase_ID::has_emec	(const Identifier id) const
+// {
+//   return true;//(has_jgtower_trig_towers(id) && sampling(id) == 0 &&
+//   //((region(id) == 0 &&  eta(id) > 14) || region(id) == 1 || region(id) == 2 ));
+// }
+
+// //----------------------------------------------------------------------------
+// inline bool	JGTowerBase_ID::has_hec	(const Identifier id) const
+// {
+//   return true;//(has_jgtower_trig_towers(id) && sampling(id) == 1 &&
+//   //((region(id) == 0 &&  eta(id) > 14) || region(id) == 1 || region(id) == 2 ));
+// }
+
+// //----------------------------------------------------------------------------
+// inline bool	JGTowerBase_ID::has_fcal	(const Identifier id) const
+// {
+//   return true;//(has_jgtower_trig_towers(id) && region(id) == 3 );
+// }
+
+//----------------------------------------------------------------------------
+inline Identifier JGTowerBase_ID::region_id    (IdentifierHash region_hash_id) const
+{
+	return(m_region_vec[region_hash_id]);
+}
+//----------------------------------------------------------------------------
+inline Identifier JGTowerBase_ID::region_id   ( const Identifier tower_Id ) const 
+{
+	Identifier result(tower_Id);
+	//  reset eta, phi, layer
+	m_eta_impl.reset(result);
+	m_phi_impl.reset(result);
+	return (result);
+}
+
+//----------------------------------------------------------------------------
+inline Identifier JGTowerBase_ID::region_id (int pos_neg, int sampling, int region)const 
+{
+	Identifier result(0);
+	// Pack fields independently
+	m_calo_impl.pack     (calo_field_value(),    result);
+	m_jgtower_impl.pack     (pos_neg,             result);
+	m_sampling_impl.pack (sampling,              result);
+	m_region_impl.pack   (region,                result);
+
+	// Do checks
+	if(m_do_checks) {
+		region_id_checks( pos_neg, sampling, region);
+	}
+
+	return result;
+}
+
+//----------------------------------------------------------------------------
+inline Identifier JGTowerBase_ID::tower_id    (IdentifierHash tower_hash_id) const
+{
+	return(m_tower_vec[tower_hash_id]);
+}
+
+//----------------------------------------------------------------------------
+inline IdentifierHash JGTowerBase_ID::tower_hash (Identifier towerId) const
+{
+	const HashCalc& hc = m_hash_calcs[m_pnz_reg_impl.unpack(towerId)];
+	return (hc.m_hash + (eta(towerId)-hc.m_etamin)*hc.m_nphi + phi(towerId));
+}
+
+//----------------------------------------------------------------------------
+inline IdentifierHash JGTowerBase_ID::tower_hash_binary_search  (Identifier towerId) const
+{
+	std::vector<Identifier>::const_iterator it = std::lower_bound(m_tower_vec.begin(),m_tower_vec.end(),towerId);
+	if ( it != m_tower_vec.end() ){
+		return (it - m_tower_vec.begin());
+	}
+	return (0);
+}
+
+inline
+const std::vector<const IdDictRegion*>& JGTowerBase_ID::dictRegions() const
+{     return m_vecOfDictRegions;
+}
+
+#endif // CALOJGTOWER_ID_H
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Calorimeter/CaloIdentifier/CaloIdentifier/JTower_ID.h b/Calorimeter/CaloIdentifier/CaloIdentifier/JTower_ID.h
new file mode 100644
index 00000000000..6aa9057705c
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/CaloIdentifier/JTower_ID.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef JTOWER_ID_H
+#define JTOWER_ID_H
+
+#include "CLIDSvc/CLASS_DEF.h"
+#include "AtlasDetDescr/AtlasDetectorID.h"
+#include "Identifier/IdentifierHash.h"
+#include "IdDict/IdDictFieldImplementation.h"
+#include "IdDict/IdDictDefs.h"
+
+#include "CaloIdentifier/JGTowerBase_ID.h"
+#include "SGTools/BaseInfo.h"
+
+#include <vector>
+#include <algorithm>
+
+/**
+*
+* @class JTower_ID
+* @brief Helper class for jTower offline identifiers
+*  
+*  This class provides an interface to decode and generate offline identifiers
+*  for jTowers.  <p>
+* 
+* Definition and range of values for the elements of the identifier are: <p>
+* <pre>
+
+* </pre>
+* @author based on code by RD Schaffer
+* @author maintained by Walter Hopkins
+*/
+
+class Range;
+
+class JTower_ID : public JGTowerBase_ID
+{
+public:        
+
+  typedef Identifier::size_type  size_type ;
+
+  JTower_ID(void);    
+  ~JTower_ID(void);
+
+
+  
+  /** initialization from the identifier dictionary*/
+  virtual int  initialize_from_dictionary (const IdDictMgr& dict_mgr);
+};
+
+//using the macro below we can assign an identifier (and a version)
+//This is required and checked at compile time when you try to record/retrieve
+CLASS_DEF( JTower_ID , 218674799 , 1 )
+SG_BASE (JTower_ID, JGTowerBase_ID);
+
+
+#endif // JTOWER_ID_H
diff --git a/Calorimeter/CaloIdentifier/CaloIdentifier/selection.xml b/Calorimeter/CaloIdentifier/CaloIdentifier/selection.xml
index 7bfb35e6409..0f60b5c5870 100755
--- a/Calorimeter/CaloIdentifier/CaloIdentifier/selection.xml
+++ b/Calorimeter/CaloIdentifier/CaloIdentifier/selection.xml
@@ -23,6 +23,7 @@
   <class name="TileTBID" />
   <class name="CaloIdManager" />
   <class name="CaloIDHelper" />
+  <enum name="CaloGain::CaloGain"/>
 
   <!-- should really be in DetDescrDictionary. -->
   <class name="boost::iterator_range<std::vector<Identifier>::const_iterator>"/>
diff --git a/Calorimeter/CaloIdentifier/cmt/requirements b/Calorimeter/CaloIdentifier/cmt/requirements
index c0588264a03..1d631caf5fc 100755
--- a/Calorimeter/CaloIdentifier/cmt/requirements
+++ b/Calorimeter/CaloIdentifier/cmt/requirements
@@ -46,6 +46,8 @@ macro_append test_lvl1_id_dependencies CaloIdentifier
 macro ep "mask/zero|Reading file|^AtlasDetectorID(Helper)?::"
 
 use TestTools      TestTools-*         AtlasTest 
+#apply_pattern UnitTest_run unit_test=GTower_ID extrapatterns="$(ep)"
+#apply_pattern UnitTest_run unit_test=JTower_ID extrapatterns="$(ep)"
 apply_pattern UnitTest_run unit_test=LArEM_ID extrapatterns="$(ep)"
 apply_pattern UnitTest_run unit_test=LArEM_SuperCell_ID extrapatterns="$(ep)"
 apply_pattern UnitTest_run unit_test=LArHEC_ID extrapatterns="$(ep)"
diff --git a/Calorimeter/CaloIdentifier/src/CaloIdManager.cxx b/Calorimeter/CaloIdentifier/src/CaloIdManager.cxx
index 18987fe3d08..ce4f16edc20 100755
--- a/Calorimeter/CaloIdentifier/src/CaloIdManager.cxx
+++ b/Calorimeter/CaloIdentifier/src/CaloIdManager.cxx
@@ -27,6 +27,8 @@
 #include "CaloIdentifier/LArHEC_SuperCell_ID.h"
 #include "CaloIdentifier/LArFCAL_SuperCell_ID.h"
 #include "CaloIdentifier/Tile_SuperCell_ID.h"
+#include "CaloIdentifier/JTower_ID.h"
+#include "CaloIdentifier/GTower_ID.h"
 
 // Athena/Gaudi includes
 #include "GaudiKernel/Bootstrap.h"
@@ -52,7 +54,9 @@ CaloIdManager::CaloIdManager(void)
     m_em_supercell_id(0),
     m_hec_supercell_id(0),
     m_fcal_supercell_id(0),
-    m_tile_supercell_id(0)
+    m_tile_supercell_id(0),
+    m_jTower_id(0),
+    m_gTower_id(0)
 {
 }
 
@@ -204,7 +208,16 @@ CaloIdManager::getTile_SuperCell_ID      (void) const
 {
     return (m_tile_supercell_id);
 }
-
+const JTower_ID*
+CaloIdManager::getJTower_ID              (void) const
+{
+    return (m_jTower_id);
+}
+const GTower_ID*
+CaloIdManager::getGTower_ID              (void) const
+{
+    return (m_gTower_id);
+}
 
 void
 CaloIdManager::initialize              (void)
@@ -306,3 +319,16 @@ CaloIdManager::set_helper              (const Tile_SuperCell_ID* idHelper)
 {
     m_tile_supercell_id = idHelper;
 }
+
+void
+CaloIdManager::set_helper              (const JTower_ID*     idHelper)
+{
+    m_jTower_id = idHelper;
+}
+
+
+void
+CaloIdManager::set_helper              (const GTower_ID*     idHelper)
+{
+    m_gTower_id = idHelper;
+}
diff --git a/Calorimeter/CaloIdentifier/src/GTower_ID.cxx b/Calorimeter/CaloIdentifier/src/GTower_ID.cxx
new file mode 100644
index 00000000000..7f4afa125e4
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/src/GTower_ID.cxx
@@ -0,0 +1,52 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CaloIdentifier/GTower_ID.h"
+#include "AtlasDetDescr/AtlasDetectorID.h"
+#include "Identifier/IdentifierHash.h"
+#include "IdDict/IdDictDefs.h"
+
+#include "GaudiKernel/MsgStream.h"
+
+#include <string>
+#include <set>
+#include <iostream>
+#include <math.h>
+
+
+
+GTower_ID::GTower_ID(void) :
+  JGTowerBase_ID()
+{
+}
+
+GTower_ID::~GTower_ID(void) 
+{
+}
+
+int  GTower_ID::initialize_from_dictionary (const IdDictMgr& dict_mgr)
+/*=================================================================*/
+{
+  MsgStream log(m_msgSvc, "GTower_ID" );
+
+  log << MSG::DEBUG << "initialize_from_dictionary" << endmsg;
+
+  // Check whether this helper should be reinitialized
+  if (!reinitialize(dict_mgr)) {
+    log << MSG::DEBUG << "Request to reinitialize not satisfied - tags have not changed" << endmsg;
+    return (0);
+  }
+  else {
+    if(m_msgSvc)log << MSG::DEBUG << "(Re)initialize" << endmsg;
+  }
+
+  // init base object
+  if (JGTowerBase_ID::initialize_base_from_dictionary(dict_mgr, "positive_gTower_side", "GT"))
+    return (1);
+
+
+  return 0;
+}
+
+
diff --git a/Calorimeter/CaloIdentifier/src/JGTowerBase_ID.cxx b/Calorimeter/CaloIdentifier/src/JGTowerBase_ID.cxx
new file mode 100644
index 00000000000..616fdbd335c
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/src/JGTowerBase_ID.cxx
@@ -0,0 +1,1189 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CaloIdentifier/JGTowerBase_ID.h"
+#include "AtlasDetDescr/AtlasDetectorID.h"
+#include "Identifier/IdentifierHash.h"
+#include "IdDict/IdDictDefs.h"
+#include "CxxUtils/StrFormat.h"
+#include "GaudiKernel/MsgStream.h"
+#include <algorithm>
+#include <string>
+#include <set>
+#include <stdio.h>
+#include <iostream>
+#include <math.h>
+#include <assert.h>
+#include "boost/foreach.hpp"
+
+using CxxUtils::strformat;
+
+
+JGTowerBase_ID::JGTowerBase_ID(void) : 
+  m_jgtower_region_index(0) 
+  , m_CALO_INDEX(999) 
+  , m_DETZSIDE_INDEX(999)
+  , m_SAMPLING_INDEX(999) 
+  , m_REGION_INDEX(999) 
+  , m_ETA_INDEX(999)
+  , m_PHI_INDEX(999)
+  , m_dict(0)
+  , m_tower_hash_max(0) 
+  , m_calo_region_hash_max(0) 
+
+{
+
+}
+
+JGTowerBase_ID:: ~JGTowerBase_ID(){}
+
+
+
+IdContext	
+JGTowerBase_ID::region_context 		(void) const
+{
+  ExpandedIdentifier id;
+  return (IdContext(id, 0, m_REGION_INDEX));
+}
+
+IdContext	
+JGTowerBase_ID::tower_context 		(void) const
+{
+  ExpandedIdentifier id;
+  return (IdContext(id, 0, m_PHI_INDEX));
+}
+
+
+int  JGTowerBase_ID::get_id  (const IdentifierHash& hash_id, Identifier& id, const IdContext* context) const
+{
+  MsgStream log(m_msgSvc, "JGTowerBase_ID" );
+  std::stringstream strm;
+  std::string strg;
+  int result = 1;
+  id.clear();
+
+  size_t begin = (context) ? context->begin_index(): 0;
+  // cannot get hash if end is 0:
+  size_t end   = (context) ? context->end_index()  : 0; 
+
+  if (0 == begin) { 
+    if (m_REGION_INDEX == end) {
+      if (hash_id < (unsigned int)(m_region_vec.end() - m_region_vec.begin())) 
+	{
+	  id = m_region_vec[hash_id];
+	  result = 0;
+	} 
+      else 
+	{
+	  strm << hash_id;
+	  strg = " hash_id out of range "+strm.str();
+	  if(m_msgSvc)
+	    {
+	      log << MSG::ERROR << strg << endmsg;
+	    }
+	  else
+	    {
+	      std::cout << strg << std::endl;
+	    }
+	}
+    }
+    else if (m_PHI_INDEX == end) {
+      if (hash_id < (unsigned int)(m_tower_vec.end() - m_tower_vec.begin())) {
+	id = m_tower_vec[hash_id];
+	result = 0;
+      } 
+      else 
+	{
+	  strm << hash_id;
+	  strg = " hash_id out of range "+strm.str();
+	  if(m_msgSvc)
+	    {
+	      log << MSG::ERROR << strg << endmsg;
+	    }
+	  else
+	    {
+	      std::cout << strg << std::endl;
+	    }
+	}
+    }
+  }
+  return(result);
+}
+
+int  JGTowerBase_ID::get_hash  (const Identifier& id, IdentifierHash& hash_id, const IdContext* context) const
+{
+  hash_id = 0;
+  int result = 1;
+
+  size_t begin = (context) ? context->begin_index(): 0;
+  size_t end   = (context) ? context->end_index()  : 0; 
+
+  if (0 == begin) {
+    if (m_REGION_INDEX  == end) {
+      Identifier redId = region_id (id);
+      std::vector<Identifier>::const_iterator it = std::lower_bound(m_region_vec.begin(),m_region_vec.end(),redId);
+      if ( it != m_region_vec.end() ){
+	hash_id = it - m_region_vec.begin();
+	result = 0;
+      }
+    }
+    else if (m_PHI_INDEX  == end) {
+
+      //Identifier redId = tower_id (id);
+
+      std::vector<Identifier>::const_iterator it = std::lower_bound(m_tower_vec.begin(),m_tower_vec.end(),id);
+      if ( it != m_tower_vec.end() ){
+	hash_id = it - m_tower_vec.begin();
+	result = 0;
+      }
+    }
+
+    else {
+      std::string errorMessage =
+        "Error in JGTowerBase_ID::get_hash, invalid context ";
+      throw CaloID_Exception(errorMessage , 10);
+    }
+  }
+
+  return (result);
+}
+
+
+IdentifierHash JGTowerBase_ID::calo_region_hash(const Identifier regId) const
+{
+  IdentifierHash regHash;
+  IdContext regionContext = region_context();
+  int sc = get_hash(regId, regHash, &regionContext);
+  if (sc!=0) return NOT_VALID;
+  return regHash;
+}
+
+
+int  JGTowerBase_ID::initialize_base_from_dictionary (const IdDictMgr& dict_mgr, const std::string& group_name, const std::string& t_pre)
+{
+  MsgStream log(m_msgSvc, "JGTowerBase_ID" );
+  std::string strg = "initialize_from_dictionary";
+  if(m_msgSvc) {
+    log << MSG::INFO << strg << endmsg;
+  }
+  else {
+    std::cout << strg << std::endl;
+  }
+
+  // Check whether this helper should be reinitialized
+  if (!reinitialize(dict_mgr)) {
+    if(m_msgSvc)log << MSG::DEBUG << "Request to reinitialize not satisfied - tags have not changed" << endmsg;
+    return (0);
+  }
+  else {
+    if(m_msgSvc)log << MSG::DEBUG << "(Re)initialize" << endmsg;
+  }
+
+  std::stringstream strm;
+  std::stringstream strm1;
+  std::stringstream strm2;
+  std::string strg1;
+  std::string strg2;
+  
+  // init base object
+  if(AtlasDetectorID::initialize_from_dictionary(dict_mgr)) return (1);
+
+  // Register version of the Calorimeter dictionary
+  if (register_dict_tag(dict_mgr, "Calorimeter")) return(1);
+
+  m_dict = dict_mgr.find_dictionary ("Calorimeter"); 
+  if(!m_dict) 
+    {
+      strg= " initialize_from_dict - cannot access Calorimeter dictionary ";
+      if(m_msgSvc) {
+	log << MSG::ERROR << strg << endmsg;
+      }
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}
+      return(1);
+    }
+  
+  // Initialize the field indices
+  if(initLevelsFromDict(t_pre)) return (1);
+
+  // Find values for the calo and  JGTOWER (neg) fields
+  int caloValue   = -1;
+  if (m_dict->get_label_value("subdet", "Calorimeter", caloValue)) 
+    {
+      strm << m_dict->m_name;
+      strg= "Could not get value for label 'Calorimeter' of field 'subdet' in dictionary "+strm.str(); 
+      if(m_msgSvc)
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else
+	{
+	  std::cout << strg << std::endl;
+	}
+      return (1);
+    }
+
+  int jgtowerCaloValue   = -1;
+  // negative half
+  //    if (m_dict->get_label_value("DetZside", "negative_jgtower_side", jgtowerCaloValue)) 
+  // positive half  FLG 12 Jul 07: negative side -> problem for test beam
+  if (m_dict->get_label_value("DetZside", group_name, jgtowerCaloValue)) 
+    {
+      strm << m_dict->m_name;
+      //	strg = " Could not get value for label 'negative_jgtower_side' of field 'DetZside in dictionary"+strm.str();
+      strg = " Could not get value for label "+group_name+" of field 'DetZside in dictionary"+strm.str();
+      if(m_msgSvc)
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else
+	{
+	  std::cout << strg << std::endl;
+	}
+      return (1);
+    }
+
+  // Set up id for region and range prefix
+  // NOTE: negative value is good enough to get multirange since the
+  // regions are symmetric in +/-eta
+  // FLG Jul 07: EXCEPT FOR CTB !!!!!!!!!!
+
+  ExpandedIdentifier reg_id;
+  reg_id.add(caloValue);
+  reg_id.add(jgtowerCaloValue); 
+  Range prefix;
+  m_full_reg_range = m_dict->build_multirange(reg_id, prefix, t_pre+"region");
+  m_full_tower_range = m_dict->build_multirange(reg_id, prefix, t_pre+"phi");
+  
+  // Setup the hash tables
+  if(init_hashes()) return (1);
+
+  // initialize dictionary regions
+  if (fill_vec_of_dict_regions ("Reg_"+t_pre+"ower")) return 1;
+
+  // Setup hash tables for finding neighbors
+  if(init_neighbors()) return (1);     
+
+  strm1 << (std::string)m_full_tower_range; 
+  strg  = " JGTowerBase_ID::initialize_from_dict : ";
+  strg1 = " tower range -> "+strm1.str();
+  if(m_msgSvc)
+    {
+      log << MSG::DEBUG << strg << endmsg;
+      log << MSG::DEBUG << strg1 << endmsg;
+      log << MSG::DEBUG << strg2 << endmsg;
+    }
+  else
+    {
+      std::cout << strg << std::endl;
+      std::cout << strg1 << std::endl;
+      std::cout << strg2 << std::endl;
+    }
+
+  //std::cout << " JGTowerBase_ID::initialize_from_dict : " 
+  //        << std::endl;
+  //std::cout << " tower range -> "  << (std::string)m_full_tower_range
+  //        << std::endl;
+  //std::cout << " layer range -> "  << (std::string)m_full_layer_range
+  //        << std::endl;
+
+
+  // Setup for hash calculation
+
+  // Regions have uniform eta/phi granularity 
+  // The lookup table only needs to contain the
+  // hash offset for each region, the first eta index
+  // and the number of phi cells.
+
+  // The implementation requires:
+
+  //   1) a lookup table for each region containing hash offset,
+  //      etamin and nphi
+  //   2) a decoder to access the "index" corresponding to the
+  //      pnz/samp/reg fields. These fields use 6 bits, so the
+  //      vector has a length of 64 for 16 regions.
+
+
+  // Create decoder for fields pnz to region
+  IdDictFieldImplementation::size_type bits = 
+    m_jgtower_impl.bits() +
+    m_sampling_impl.bits() +
+    m_region_impl.bits();
+  IdDictFieldImplementation::size_type bits_offset = m_jgtower_impl.bits_offset();
+  m_pnz_reg_impl.set_bits(bits, bits_offset);
+  int size = (1 << bits);
+
+  //      std::cout << "pnz_reg        "  
+  //  	    << m_pnz_reg_impl.show_to_string() << std::endl; 
+  //      std::cout << "size " << size << std::endl;
+    
+
+  //    std::cout << "pnz_reg "  << m_pnz_reg_impl.decode_index()  << " " 
+  //      << (std::string)m_pnz_reg_impl.ored_field()  << " " 
+  //      << std::hex << m_pnz_reg_impl.mask() << " " 
+  //      << m_pnz_reg_impl.zeroing_mask() << " " 
+  //      << std::dec << m_pnz_reg_impl.shift()
+  //      << " " << m_pnz_reg_impl.bits() << " " <<m_pnz_reg_impl.bits_offset()
+  //      << std::endl;
+
+
+  // Set up vector as lookup table for hash calculation. 
+  m_hash_calcs.resize(size);
+
+  for (unsigned int i = 0; i < m_calo_region_hash_max; ++i) {
+
+    Identifier regId = region_id(i) ;
+
+    HashCalc hc;
+      
+    int etamin = eta_min(regId);
+    Identifier min = tower_id ( regId, etamin, 0);
+    IdentifierHash min_hash = tower_hash_binary_search(min);
+    hc.m_hash   = min_hash;
+    hc.m_etamin = etamin;
+    hc.m_nphi   = phi_max(min)+1 ;
+    m_hash_calcs[m_pnz_reg_impl.unpack(min)] = hc;
+
+    if (m_pnz_reg_impl.unpack(min) >= size) 
+      {
+	strm << size;
+	strm1 << show_to_string(min);
+	strm2 << m_pnz_reg_impl.unpack(min);
+	strg = "Min > "+strm.str();
+	strg1= " "+strm1.str();
+	strg2= " "+strm2.str();
+	if(m_msgSvc)
+	  {
+	    log << MSG::DEBUG << strg << endmsg;
+	    log << MSG::DEBUG << strg1 << endmsg;
+	    log << MSG::DEBUG << strg2 << endmsg;
+	  }
+	else
+	  {
+	    std::cout << strg << std::endl;
+	    std::cout << strg1 << std::endl;
+	    std::cout << strg2 << std::endl;
+	  }	  
+	//std::cout << "min > " << size << " " 
+	//    << i << " "
+	//	    << show_to_string(min) << " " 
+	//	    << m_pnz_reg_impl.unpack(min) << " " 
+	//	    << std::endl;
+      }
+  }
+
+  // Check hash calculation
+  for (unsigned int i = 0; i < m_tower_hash_max; ++i) {
+    Identifier id = tower_id(i);
+    if (tower_hash(id) != i) 
+      {
+	strm << show_to_string(id);
+	strm1 << tower_hash(id);
+	strm2 << i;
+	strg = " *****  Error tower ranges, id, hash, i = "+strm.str();
+	strg1= " , "+strm1.str();
+	strg2= " , "+strm2.str();
+	if(m_msgSvc)
+	  {
+	    log << MSG::ERROR << strg << endmsg;
+	    log << MSG::ERROR << strg1 << endmsg;
+	    log << MSG::ERROR << strg2 << endmsg;
+	  }
+	else
+	  {
+	    std::cout << strg << std::endl;
+	    std::cout << strg1 << std::endl;
+	    std::cout << strg2 << std::endl;
+	  }
+	    
+	//std::cout << "tower ranges, id, hash, i = " 
+	//	      << show_to_string(id) << ", " 
+	//	      << tower_hash(id) << ", " 
+	//	      << i
+	//	      << std::endl;
+      }
+  }
+    
+  return 0;
+
+}
+
+
+
+
+int JGTowerBase_ID::eta_min(const Identifier regId) const
+{
+  ExpandedIdentifier expId;
+  IdContext region_cntxt = region_context();
+  if(!get_expanded_id(regId, expId, &region_cntxt)) {
+    int result = -999;
+    for (unsigned int i = 0; i < m_full_tower_range.size(); ++i) {
+      const Range& range = m_full_tower_range[i];
+      if (range.match(expId)) {
+	const Range::field& eta_field = range[m_ETA_INDEX];
+	if (eta_field.has_minimum()) {
+	  int etamin = eta_field.get_minimum();
+	  if (-999 == result) {
+	    result = etamin;
+	  }
+	  else {
+	    if (etamin < result) result = etamin;
+	  }
+	}
+      }
+    }
+    return (result);
+  }
+  return (-999);
+}
+
+int JGTowerBase_ID::eta_max(const Identifier regId) const
+{
+  ExpandedIdentifier expId;
+  IdContext region_cntxt = region_context();
+  if(!get_expanded_id(regId, expId, &region_cntxt)) {
+    int result = -999;
+    for (unsigned int i = 0; i < m_full_tower_range.size(); ++i) {
+      const Range& range = m_full_tower_range[i];
+      if (range.match(expId)) {
+	const Range::field& eta_field = range[m_ETA_INDEX];
+	if (eta_field.has_maximum()) {
+	  int etamax = eta_field.get_maximum();
+	  if (result < etamax) result = etamax;
+	}
+      }
+    }
+    return (result);
+  }
+  return (-999);  // default
+}
+
+int JGTowerBase_ID::phi_max(const Identifier regId) const
+{
+  ExpandedIdentifier expId;
+  IdContext region_cntxt = region_context();
+  if(!get_expanded_id(regId, expId, &region_cntxt)) {
+    int result = -999;
+    for (unsigned int i = 0; i < m_full_tower_range.size(); ++i) {
+      const Range& range = m_full_tower_range[i];
+      if (range.match(expId)) {
+	const Range::field& phi_field = range[m_PHI_INDEX];
+	if (phi_field.has_maximum()) {
+	  int phimax = phi_field.get_maximum();
+	  if (result < phimax) result = phimax;
+	}
+      }
+    }
+    return (result);
+  }
+  return (-999);  // default
+}
+
+float JGTowerBase_ID::etaGranularity(const Identifier regId) const
+{
+  IdentifierHash regHash = calo_region_hash(regId);
+  if (regHash >= m_vecOfDictRegions.size()) return NOT_VALID;
+  return m_vecOfDictRegions[regHash]->m_deta;
+}
+
+float JGTowerBase_ID::phiGranularity(const Identifier regId) const
+{
+  IdentifierHash regHash = calo_region_hash(regId);
+  if (regHash >= m_vecOfDictRegions.size()) return NOT_VALID;
+  return m_vecOfDictRegions[regHash]->m_dphi;
+}
+
+float JGTowerBase_ID::eta0(const Identifier regId) const
+{
+  IdentifierHash regHash = calo_region_hash(regId);
+    if (regHash >= m_vecOfDictRegions.size()) return NOT_VALID;
+  return m_vecOfDictRegions[regHash]->m_eta0;
+}
+
+float JGTowerBase_ID::phi0(const Identifier regId) const
+{
+  IdentifierHash regHash = calo_region_hash(regId);
+  if (regHash >= m_vecOfDictRegions.size()) return NOT_VALID;
+  return m_vecOfDictRegions[regHash]->m_phi0;
+}
+
+int             
+JGTowerBase_ID::get_prev_in_phi(const IdentifierHash& id, IdentifierHash& prev) const
+{
+  unsigned short index = id;
+  if (index < m_prev_phi_vec.size()) {
+    if (m_prev_phi_vec[index] == NOT_VALID_HASH) return (1);
+    prev =  m_prev_phi_vec[index];
+    return (0);
+  }
+  return (1);
+}
+
+int             
+JGTowerBase_ID::get_next_in_phi(const IdentifierHash& id, IdentifierHash& next) const
+{
+  unsigned short index = id;
+  if (index < m_next_phi_vec.size()) {
+    if (m_next_phi_vec[index] == NOT_VALID_HASH) return (1);
+    next =  m_next_phi_vec[index];
+    return (0);
+  }
+  return (1);
+}
+
+int             
+JGTowerBase_ID::get_prev_in_eta(const IdentifierHash& id, IdentifierHash& prev) const
+{
+  unsigned short index = id;
+  if (index < m_prev_eta_vec.size()) {
+    if (m_prev_eta_vec[index] == NOT_VALID_HASH) return (1);
+    prev =  m_prev_eta_vec[index];
+    return (0);
+  }
+  return (1);
+}
+
+int
+JGTowerBase_ID::get_next_in_eta(const IdentifierHash& id, IdentifierHash& next) const
+{
+  unsigned short index = id;
+  if (index < m_next_eta_vec.size()) {
+    if (m_next_eta_vec[index] == NOT_VALID_HASH) return (1);
+    next =  m_next_eta_vec[index];
+    return (0);
+  }
+  return (1);
+}
+
+
+int  JGTowerBase_ID::get_expanded_id  (const Identifier& id, ExpandedIdentifier& exp_id, const IdContext* context) const
+{
+  // We assume that the context is >= region
+  exp_id.clear();
+  exp_id << calo_field_value()
+	 << pos_neg(id)
+	 << sampling(id)
+	 << region(id);
+  if(context && context->end_index() >= m_ETA_INDEX) {
+    exp_id << eta(id);
+    if(context->end_index() >= m_PHI_INDEX) {
+      exp_id << phi(id);
+    }
+  }
+  return (0);
+}
+void JGTowerBase_ID::tower_id_checks   ( int pos_neg, int sampling, int region,
+					 int eta,       int phi ) const throw(CaloID_Exception)
+{  
+  // Fill expanded id
+  ExpandedIdentifier id(calo_exp());
+  id << pos_neg << sampling <<
+    region << eta << phi;
+
+  if( id.last_error () != ExpandedIdentifier::none) {
+    std::string errorMessage =
+      "Error in JGTowerBase_ID::tower_id(field values), did not build, " +
+      strformat("pos_neg: %d , sampling: %d, region: %d , eta: %d , phi: %d ",
+                pos_neg, sampling, region, eta, phi);
+    throw CaloID_Exception(errorMessage , 2);
+  }
+
+  if (!m_full_tower_range.match(id)) { 
+    std::string errorMessage = "JGTowerBase_ID::tower_id() result is not OK: ID, range = "
+      + std::string(id) + " , " + (std::string)m_full_tower_range;
+    throw CaloID_Exception(errorMessage , 2);
+  }
+}
+
+void JGTowerBase_ID::tower_id_checks   ( const Identifier regionId,
+					 int eta,  int phi ) const throw(CaloID_Exception) 
+{
+  // Fill expanded id
+  ExpandedIdentifier id; 
+
+  IdContext context = region_context();
+  if (get_expanded_id(regionId, id, &context)) {
+    std::string errorMessage = "JGTowerBase_ID::tower_id(regionId) result is not OK: ID= "
+      + show_to_string(regionId) ;
+    throw CaloID_Exception(errorMessage , 2);
+  }
+
+  id << eta << phi;
+
+  if( id.last_error () != ExpandedIdentifier::none) {
+    
+    std::string errorMessage =
+      "Error in JGTowerBase_ID::tower_id(regionId,field values), values ok but did not build, " +
+      strformat ("eta: %d , phi: %d ", 
+		 eta, phi);
+    throw CaloID_Exception(errorMessage , 2);
+  }
+
+
+  if (!m_full_tower_range.match(id)) {
+    std::string errorMessage = "JGTowerBase_ID::tower_id(regionId,field values) result is not OK: ID, range = "
+      + std::string(id) + " , " + (std::string)m_full_tower_range;
+    throw CaloID_Exception(errorMessage , 2);
+  }
+}
+
+void JGTowerBase_ID::region_id_checks (int pos_neg, int sampling, int region)const 
+  throw(CaloID_Exception)
+{
+  // Fill expanded id
+  ExpandedIdentifier id(calo_exp());
+  id << pos_neg << sampling << region ;
+
+  if( id.last_error () != ExpandedIdentifier::none) {
+    std::string errorMessage =
+      "Error in JGTowerBase_ID::region_id(field values), did not build, " +
+      strformat ("pos_neg: %d , sampling: %d, region: %d ", 
+		 pos_neg , sampling , region);
+    throw CaloID_Exception(errorMessage , 2);
+  }
+
+  if (!m_full_reg_range.match(id)) { 
+    std::string errorMessage = "JGTowerBase_ID::region_id() result is not OK: ID, range = "
+      + std::string(id) + " , " + (std::string)m_full_reg_range;
+    throw CaloID_Exception(errorMessage , 2);
+  }
+}
+
+int   JGTowerBase_ID::initLevelsFromDict(const std::string& t_pre) 
+{
+  MsgStream log(m_msgSvc, "JGTowerBase_ID" );
+  std::stringstream strm;
+  std::stringstream strm1;
+  std::stringstream strm2;
+  std::stringstream strm3;
+  std::stringstream strm4;
+  std::stringstream strm5;
+  std::stringstream strm6;
+  std::stringstream strm7;
+  std::string strg;
+  std::string strg1;
+  std::string strg2;
+  std::string strg3;
+  std::string strg4;
+  std::string strg5;
+  std::string strg6;
+  std::string strg7;
+  if(!m_dict) 
+    {
+      strg= "initLevelsFromDict - dictionary NOT initialized ";
+      if(m_msgSvc) {
+	log << MSG::ERROR << strg << endmsg;
+      }
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}
+      return (1);
+    }
+
+  // Find out which identifier field corresponds to each level.
+
+  m_CALO_INDEX       = 999 ;
+  m_DETZSIDE_INDEX   = 999 ;
+  m_SAMPLING_INDEX   = 999 ;
+  m_REGION_INDEX     = 999 ;
+  m_ETA_INDEX        = 999 ;
+  m_PHI_INDEX        = 999 ;
+
+  // Save index to a JGTOWER region for unpacking - search with region name
+  IdDictRegion* reg = m_dict->find_region(t_pre+"ower_0");
+  if (reg) 
+    {
+      m_jgtower_region_index = reg->m_index;
+    }
+  else 
+    {
+      strg = "initLevelsFromDict - unable to find jgtower region ";
+      if(m_msgSvc) {
+	log << MSG::ERROR << strg << endmsg;
+      }
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}      
+      return (1);
+    }
+  
+  // Fing a JGTOWER region
+  IdDictField* field = m_dict->find_field("subdet") ;
+  if (field) {
+    m_CALO_INDEX = field->m_index ;
+  }
+  else 
+    {
+      strg=  "initLevelsFromDict - unable to find 'subdet' field ";
+      if(m_msgSvc) 
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}	
+      return (1);
+    }
+  
+  field = m_dict->find_field("DetZside") ;
+  if (field) {
+    m_DETZSIDE_INDEX = field->m_index ;
+  }
+  else 
+    {
+      strg= "initLevelsFromDict - unable to find 'DetZside' field ";
+      if(m_msgSvc) 
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}	
+      return (1);
+    }
+
+  field = m_dict->find_field(t_pre+"sampling") ;
+  if (field) {
+    m_SAMPLING_INDEX = field->m_index ;
+  }
+  else 
+    {
+      
+      strg="initLevelsFromDict - unable to find '"+t_pre+"sampling' field ";
+      if(m_msgSvc) 
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}      
+      return (1);
+    }
+
+  field = m_dict->find_field(t_pre+"region") ;
+  if (field) {
+    m_REGION_INDEX = field->m_index ;
+  }
+  else 
+    {
+      
+      strg="initLevelsFromDict - unable to find 'region' field ";
+      if(m_msgSvc) 
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}      
+      return (1);
+    }
+
+  /* std::cout << "m_region= " << m_REGION_INDEX << std::endl; */
+
+  field = m_dict->find_field(t_pre+"eta") ;
+  if (field) {
+    m_ETA_INDEX = field->m_index ;
+  }
+  else 
+    {
+      strg= "initLevelsFromDict - unable to find 'eta' field ";
+      if(m_msgSvc) 
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}		
+      return (1);
+    }
+  
+  field = m_dict->find_field(t_pre+"phi") ;
+  if (field) {
+    m_PHI_INDEX = field->m_index ;
+  }
+  else 
+    {
+      
+      strg= "initLevelsFromDict - unable to find 'phi' field ";
+      if(m_msgSvc) 
+	{
+	  log << MSG::ERROR << strg << endmsg;
+	}
+      else 
+	{
+	  std::cout << strg << std::endl;
+	}          
+      return (1);
+    }
+  // Set the field implementations
+
+  const IdDictRegion& region = *m_dict->m_regions[m_jgtower_region_index];
+
+  m_calo_impl     = region.m_implementation[m_CALO_INDEX]; 
+  m_jgtower_impl     = region.m_implementation[m_DETZSIDE_INDEX];
+  m_sampling_impl = region.m_implementation[m_SAMPLING_INDEX]; 
+  m_region_impl   = region.m_implementation[m_REGION_INDEX]; 
+  m_eta_impl      = region.m_implementation[m_ETA_INDEX]; 
+  m_phi_impl      = region.m_implementation[m_PHI_INDEX]; 
+
+  strm1 << m_calo_impl.show_to_string();
+  strm2 << m_jgtower_impl.show_to_string();
+  strm3 << m_sampling_impl.show_to_string();
+  strm4 << m_region_impl.show_to_string();
+  strm5 << m_eta_impl.show_to_string();
+  strm6 << m_phi_impl.show_to_string();
+  strg = "decode index and bit fields for each level: ";
+  strg1= "calo  "+strm1.str();
+  strg2= "detzside  "+strm2.str();
+  strg3= "sampling  "+strm3.str();
+  strg4= "reg  "+strm4.str();
+  strg5= "eta  "+strm5.str();
+  strg6= "phi  "+strm6.str();
+  if(m_msgSvc) 
+    {
+      log << MSG::DEBUG << strg << endmsg;
+      log << MSG::DEBUG << strg1 << endmsg;
+      log << MSG::DEBUG << strg2 << endmsg;
+      log << MSG::DEBUG << strg3 << endmsg;
+      log << MSG::DEBUG << strg4 << endmsg;
+      log << MSG::DEBUG << strg5 << endmsg;
+      log << MSG::DEBUG << strg6 << endmsg;
+    }
+  else 
+    {
+      std::cout << strg << std::endl;
+      std::cout << strg1 << std::endl;
+      std::cout << strg2 << std::endl;
+      std::cout << strg3 << std::endl;
+      std::cout << strg4 << std::endl;
+      std::cout << strg5 << std::endl;
+      std::cout << strg6 << std::endl;
+    }
+  
+  return(0) ;
+}
+
+
+int   JGTowerBase_ID::init_hashes(void) 
+{
+  MsgStream log(m_msgSvc, "JGTowerBase_ID" );
+  std::stringstream strm;
+  std::stringstream strm1;
+  std::stringstream strm2;
+  std::string strg;
+  std::string strg1;
+  std::string strg2;
+  // tower hash
+  m_tower_hash_max = m_full_tower_range.cardinality();
+  m_tower_vec.resize(m_tower_hash_max);
+  unsigned int nids = 0;
+  std::set<Identifier> ids;
+  for (unsigned int i = 0; i < m_full_tower_range.size(); ++i) {
+    const Range& range = m_full_tower_range[i];
+    Range::const_identifier_factory first = range.factory_begin();
+    Range::const_identifier_factory last  = range.factory_end();
+    for (; first != last; ++first) {
+      const ExpandedIdentifier& exp_id = (*first);
+      Identifier tow_id = tower_id   ( exp_id[m_DETZSIDE_INDEX],
+				       exp_id[m_SAMPLING_INDEX], 
+				       exp_id[m_REGION_INDEX] ,
+				       exp_id[m_ETA_INDEX] ,
+				       exp_id[m_PHI_INDEX] ) ;
+      if(!(ids.insert(tow_id)).second)
+	{
+	  if(m_msgSvc)
+	    {
+	      log << MSG::ERROR << " init_hashes "
+		  << " duplicated id for J/GTower id. nids= " << nids
+		  << " compact Id  " << endmsg; 
+	    }
+	  else
+	    {
+	      std::cout << " JGTowerBase_ID::init_hashes "
+	  		<< " Error: duplicated id for J/GTower id. nids= " << nids
+	  		<< " compact Id " ;
+	      (*first).show();
+	      std::cout << " " << show_to_string(tow_id) << std::endl; 
+	    }
+	}
+      nids++;
+    }
+  }
+  if(ids.size() != m_tower_hash_max) 
+    {
+      if( m_msgSvc)
+	{
+	  log << MSG::ERROR << " init_hashes "
+	      << " set size NOT EQUAL to hash max. size " << ids.size()
+	      << " hash max " << m_tower_hash_max
+	      << endmsg;
+	}
+      else
+	{
+	  std::cout << " JGTowerBase_ID::init_hashes "
+		    << " Error: set size NOT EQUAL to hash max. size " << ids.size()
+		    << " hash max " << m_tower_hash_max
+		    << std::endl;
+	}
+      return (1);
+    }
+
+  nids=0;
+  std::set<Identifier>::const_iterator first = ids.begin();
+  std::set<Identifier>::const_iterator last  = ids.end();
+  for (;first != last && nids < m_tower_vec.size(); ++first) {
+    m_tower_vec[nids] = (*first) ;
+    nids++;
+  }
+  // region hash
+  m_calo_region_hash_max = m_full_reg_range.cardinality();
+  m_region_vec.resize(m_calo_region_hash_max);
+  nids = 0;
+  ids.clear();
+  for (unsigned int i = 0; i < m_full_reg_range.size(); ++i) {
+    const Range& range = m_full_reg_range[i];
+    Range::const_identifier_factory first = range.factory_begin();
+    Range::const_identifier_factory last  = range.factory_end();
+    for (; first != last; ++first) {
+      const ExpandedIdentifier& exp_id = (*first);
+      Identifier reg_id = region_id   ( exp_id[m_DETZSIDE_INDEX],
+					exp_id[m_SAMPLING_INDEX], 
+					exp_id[m_REGION_INDEX] );
+      if(!(ids.insert(reg_id)).second)
+	{
+	  if(m_msgSvc)
+	    {
+	      log << MSG::ERROR << " JGTowerBase_ID::init_hashes "
+		  << "  duplicated id for region id. nids= " << nids
+		  << " compact Id  " << endmsg;
+	    }
+	  else
+	    {
+	      std::cout << " JGTowerBase_ID::init_hashes "
+			<< " Error: duplicated id for region id. nids= " << nids
+			<< " compact Id  " ;
+	      (*first).show();
+	      std::cout << " " << show_to_string(reg_id) << std::endl;
+	      std::cout << std::endl;
+	    }
+	}
+      nids++;
+    }
+  }
+  if(ids.size() != m_calo_region_hash_max) 
+    {
+      if(m_msgSvc)
+	{
+	  log << MSG::ERROR << " JGTowerBase_ID::init_hashes "
+	      << "  set size NOT EQUAL to region hash max. size " << ids.size()
+	      << " region hash max " << m_calo_region_hash_max
+	      << endmsg;
+	}
+      else
+	{
+	  std::cout << " JGTowerBase_ID::init_hashes "
+		    << " Error: set size NOT EQUAL to region hash max. size " << ids.size()
+		    << " region hash max " << m_calo_region_hash_max
+		    << std::endl;
+	}
+      return (1);
+    }
+  nids=0;
+  first = ids.begin();
+  last  = ids.end();
+  for (;first != last && nids < m_region_vec.size(); ++first) {
+    m_region_vec[nids] = (*first) ;
+    nids++;
+  }
+
+  return (0);                   
+}
+
+
+
+int   JGTowerBase_ID::init_neighbors(void) 
+{
+  MsgStream log(m_msgSvc, "JGTowerBase_ID" );
+  //  std::cout << " JGTowerBase_ID::init_neighbors " << std::endl;
+  //  std::cout << " m_tower_hash_max, NOT_VALID_HASH = " << m_tower_hash_max << " " << NOT_VALID_HASH << std::endl;
+
+  m_prev_phi_vec.resize(m_tower_hash_max, NOT_VALID_HASH);
+  m_next_phi_vec.resize(m_tower_hash_max, NOT_VALID_HASH);
+  m_prev_eta_vec.resize(m_tower_hash_max, NOT_VALID_HASH);
+  m_next_eta_vec.resize(m_tower_hash_max, NOT_VALID_HASH);
+  for (unsigned int i = 0; i < m_full_tower_range.size(); ++i) {
+    const Range& range = m_full_tower_range[i];
+    const Range::field& eta_field = range[m_ETA_INDEX];
+    const Range::field& phi_field = range[m_PHI_INDEX];
+    Range::const_identifier_factory first = range.factory_begin();
+    Range::const_identifier_factory last  = range.factory_end();
+    for (; first != last; ++first) {
+      const ExpandedIdentifier& exp_id = (*first);
+      ExpandedIdentifier::element_type previous_eta;
+      ExpandedIdentifier::element_type next_eta;
+      ExpandedIdentifier::element_type previous_phi;
+      ExpandedIdentifier::element_type next_phi;
+      bool peta = eta_field.get_previous(exp_id[m_ETA_INDEX], previous_eta);
+      bool neta = eta_field.get_next    (exp_id[m_ETA_INDEX], next_eta);
+      bool pphi = phi_field.get_previous(exp_id[m_PHI_INDEX], previous_phi);
+      bool nphi = phi_field.get_next    (exp_id[m_PHI_INDEX], next_phi);
+      
+      IdContext tcontext = tower_context();
+      
+      // Get and save region id to speed things up
+      Identifier reg_id = region_id   ( exp_id[m_DETZSIDE_INDEX],
+					exp_id[m_SAMPLING_INDEX], 
+					exp_id[m_REGION_INDEX] );
+      
+      // First get primary hash id
+      IdentifierHash hash_id;
+      Identifier id = tower_id (reg_id, 
+				exp_id[m_ETA_INDEX],
+				exp_id[m_PHI_INDEX]);
+      if (get_hash(id, hash_id,&tcontext)) 
+	{
+	  if( m_msgSvc )
+	    {
+	      log << MSG::ERROR << " init_neighbors - unable to get hash, compact = " << endmsg;
+	    }
+	  else
+	    {
+	      std::cout << " JGTowerBase_ID::init_neighbors - unable to get hash, compact = ";
+	      exp_id.show();
+	      std::cout << std::endl;
+	    }
+	  return (1);
+	}
+      
+      // index for the subsequent arrays
+      unsigned short index = hash_id;
+      assert (hash_id < m_prev_phi_vec.size());
+      assert (hash_id < m_next_phi_vec.size());
+      assert (hash_id < m_prev_eta_vec.size());
+      assert (hash_id < m_next_eta_vec.size());
+
+      if (pphi) {
+	// Get previous phi hash id
+	id = tower_id (reg_id, 
+		       exp_id[m_ETA_INDEX],
+		       previous_phi);
+	// forward to compact -> hash
+	if (get_hash(id, hash_id,&tcontext)) 
+	  {
+	    if( m_msgSvc )
+	      {
+		log << MSG::ERROR << " init_neighbors - unable to get previous phi hash, exp/compact " << endmsg;
+	      }
+	    else
+	      {
+		std::cout << " JGTowerBase_ID::init_neighbors - unable to get previous phi hash, exp/compact ";
+		exp_id.show();
+		std::cout << " " 
+			  << std::endl;
+	      }
+	    return (1);
+	  }
+	m_prev_phi_vec[index] = hash_id;
+      }
+
+      if (nphi) {
+	// Get next phi hash id
+	id = tower_id (reg_id, 
+		       exp_id[m_ETA_INDEX],
+		       next_phi);
+	// forward to compact -> hash
+	if (get_hash(id, hash_id,&tcontext)) 
+	  {
+	    if(m_msgSvc)
+	      {
+		log << MSG::ERROR << " init_neighbors - unable to get next phi hash, exp/compact "<<endmsg;
+	      }
+	    else
+	      {
+		std::cout << " JGTowerBase_ID::init_neighbors - unable to get next phi hash, exp/compact ";
+		exp_id.show();
+		std::cout << " " 
+			  << std::endl;
+	      }
+	    return (1);
+	  }
+	m_next_phi_vec[index] = hash_id;
+      }
+      if (peta) {
+	// Get previous eta hash id
+	id = tower_id (reg_id, 
+		       previous_eta,
+		       exp_id[m_PHI_INDEX]);
+	// forward to compact -> hash
+	if (get_hash(id, hash_id,&tcontext)) 
+	  {
+	    if( m_msgSvc )
+	      {
+		log << MSG::ERROR << " init_neighbors - unable to get previous eta hash, exp/compact "<< endmsg;
+	      }
+	    else
+	      {
+		std::cout << " JGTowerBase_ID::init_neighbors - unable to get previous eta hash, exp/compact ";
+		exp_id.show();
+		std::cout << " " 
+			  << std::endl;
+	      }
+	    return (1);
+	  }
+	m_prev_eta_vec[index] = hash_id;
+      }
+      
+      if (neta) {
+	// Get next eta hash id
+	id = tower_id (reg_id, 
+		       next_eta,
+		       exp_id[m_PHI_INDEX]);
+	// forward to compact -> hash
+	if (get_hash(id, hash_id,&tcontext)) 
+	  {
+	    if( m_msgSvc )
+	      {
+		log << MSG::ERROR << " init_neighbors - unable to get next eta hash, exp/compact ";
+	      }
+	    else
+	      {
+		std::cout << " JGTowerBase_ID::init_neighbors - unable to get next eta hash, exp/compact ";
+		exp_id.show();
+		std::cout << " " 
+			  << std::endl;
+	      }
+	    return (1);
+	  }
+	m_next_eta_vec[index] = hash_id;
+      } // end neta cond
+    } // end loop on identifiers
+  } // end loop on ranges
+  return (0);
+}
+
+int
+JGTowerBase_ID::fill_vec_of_dict_regions (const std::string& group_name /*= ""*/)
+{
+  m_vecOfDictRegions.clear();
+  m_vecOfDictRegions.reserve (m_calo_region_hash_max);
+  IdContext region_cntxt = region_context();
+  ExpandedIdentifier expRegId;
+  for (unsigned int i = 0; i < m_calo_region_hash_max; ++i) {
+    Identifier id = region_id(i);
+    if(!get_expanded_id(id, expRegId, &region_cntxt)) {
+      m_vecOfDictRegions.push_back (m_dict->find_region(expRegId,group_name));
+    }
+  }
+  //assert (m_vecOfDictRegions.size() == regions().hash_max());
+  return 0;
+}
diff --git a/Calorimeter/CaloIdentifier/src/JTower_ID.cxx b/Calorimeter/CaloIdentifier/src/JTower_ID.cxx
new file mode 100644
index 00000000000..50b4d5a6a92
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/src/JTower_ID.cxx
@@ -0,0 +1,52 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CaloIdentifier/JTower_ID.h"
+#include "AtlasDetDescr/AtlasDetectorID.h"
+#include "Identifier/IdentifierHash.h"
+#include "IdDict/IdDictDefs.h"
+
+#include "GaudiKernel/MsgStream.h"
+
+#include <string>
+#include <set>
+#include <iostream>
+#include <math.h>
+
+
+
+JTower_ID::JTower_ID(void) :
+  JGTowerBase_ID()
+{
+}
+
+JTower_ID::~JTower_ID(void) 
+{
+}
+
+int  JTower_ID::initialize_from_dictionary (const IdDictMgr& dict_mgr)
+/*=================================================================*/
+{
+  MsgStream log(m_msgSvc, "JTower_ID" );
+
+  log << MSG::DEBUG << "initialize_from_dictionary" << endmsg;
+
+  // Check whether this helper should be reinitialized
+  if (!reinitialize(dict_mgr)) {
+    log << MSG::DEBUG << "Request to reinitialize not satisfied - tags have not changed" << endmsg;
+    return (0);
+  }
+  else {
+    if(m_msgSvc)log << MSG::DEBUG << "(Re)initialize" << endmsg;
+  }
+
+  // init base object
+  if (JGTowerBase_ID::initialize_base_from_dictionary(dict_mgr, "positive_jTower_side", "JT"))
+    return (1);
+
+
+  return 0;
+}
+
+
diff --git a/Calorimeter/CaloIdentifier/test/GTower_ID_test.cxx b/Calorimeter/CaloIdentifier/test/GTower_ID_test.cxx
new file mode 100644
index 00000000000..eca4d0f088a
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/test/GTower_ID_test.cxx
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id$
+/**
+ * @file  CaloIdentifier/test/GTower_ID_test.cxx
+ * @author Walter Hopkins
+ * @date Oct 2014
+ * @brief Unit test for GTower_ID.
+ */
+
+#undef NDEBUG
+
+#include "CaloIdentifier/GTower_ID.h"
+#include "IdDictParser/IdDictParser.h"
+#include <iostream>
+
+
+#include "jgtower_id_test_common.cxx"
+
+
+GTower_ID* make_helper (bool do_neighbours = false)
+{
+  GTower_ID* idhelper = new GTower_ID;
+  IdDictParser* parser = new IdDictParser;
+  parser->register_external_entity ("Calorimeter",
+                                    "IdDictCalorimeter_L1Onl.xml");
+  IdDictMgr& idd = parser->parse ("IdDictParser/ATLAS_IDS.xml");
+  idhelper->set_do_neighbours (do_neighbours);
+  assert (idhelper->initialize_from_dictionary (idd) == 0);
+
+  assert (!idhelper->do_checks());
+  idhelper->set_do_checks (true);
+  assert (idhelper->do_checks());
+
+  return idhelper;
+}
+
+
+void test_basic (const JGTowerBase_ID& idhelper)
+{
+  std::cout << "test_basic\n";
+  idhelper.tower_id (6, 0, 0, 0, 0);
+  // gTower positive side (detSide/subDet, sampling, regionNum, etaNum, phiNum
+  basic_print_id (idhelper, idhelper.tower_id (6, 0, 0, 0, 0));
+  basic_print_id (idhelper, idhelper.tower_id (6, 0, 0, 1, 15));
+  basic_print_id (idhelper, idhelper.tower_id (6, 0, 1, 2, 10));
+
+  // gTower negative side
+ basic_print_id (idhelper, idhelper.tower_id (-6, 0, 0, 0, 0));
+  basic_print_id (idhelper, idhelper.tower_id (-6, 0, 0, 1, 15));
+  basic_print_id (idhelper, idhelper.tower_id (-6, 0, 1, 2, 10));
+  
+  // gTower positive side, had cal (detSide/subDet, sampling, regionNum, etaNum, phiNum
+  basic_print_id (idhelper, idhelper.tower_id (6, 1, 0, 0, 0));
+  basic_print_id (idhelper, idhelper.tower_id (6, 1, 0, 1, 15));
+  basic_print_id (idhelper, idhelper.tower_id (6, 1, 1, 2, 10));
+}
+
+int main()
+{
+  GTower_ID* idhelper = make_helper();
+  GTower_ID* idhelper_n = make_helper(true);
+  try {
+    test_basic (*idhelper);
+    test_towers (*idhelper);
+    test_exceptions (*idhelper, 6);
+    test4 (*idhelper_n);
+    testRegionInfo(*idhelper);
+  }
+  catch(CaloID_Exception & except){
+    std::cout << "Unexpected exception: " << (std::string) except << std::endl ;
+  }
+  return 0;
+}
diff --git a/Calorimeter/CaloIdentifier/test/JTower_ID_test.cxx b/Calorimeter/CaloIdentifier/test/JTower_ID_test.cxx
new file mode 100644
index 00000000000..47954656791
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/test/JTower_ID_test.cxx
@@ -0,0 +1,77 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id$
+/**
+ * @file  CaloIdentifier/test/GTower_ID_test.cxx
+ * @author Walter Hopkins
+ * @date Oct 2014
+ * @brief Unit test for GTower_ID.
+ */
+
+#undef NDEBUG
+
+#include "CaloIdentifier/JTower_ID.h"
+#include "IdDictParser/IdDictParser.h"
+#include <iostream>
+
+
+#include "jgtower_id_test_common.cxx"
+
+
+JTower_ID* make_helper (bool do_neighbours = false)
+{
+  JTower_ID* idhelper = new JTower_ID;
+  IdDictParser* parser = new IdDictParser;
+  parser->register_external_entity ("Calorimeter",
+                                    "IdDictCalorimeter_DC3-05.xml");
+  IdDictMgr& idd = parser->parse ("IdDictParser/ATLAS_IDS.xml");
+  idhelper->set_do_neighbours (do_neighbours);
+  assert (idhelper->initialize_from_dictionary (idd) == 0);
+
+  assert (!idhelper->do_checks());
+  idhelper->set_do_checks (true);
+  assert (idhelper->do_checks());
+
+  return idhelper;
+}
+
+
+void test_basic (const JGTowerBase_ID& idhelper)
+{
+  std::cout << "test_basic\n";
+  idhelper.tower_id (3, 0, 0, 0, 0);
+  // jTower positive side (detSide/subDet, sampling regionNum, etaNum, phiNum
+  basic_print_id (idhelper, idhelper.tower_id (3, 0, 0, 0, 0));
+  basic_print_id (idhelper, idhelper.tower_id (3, 0, 0, 1, 15));
+  basic_print_id (idhelper, idhelper.tower_id (3, 0, 1, 2, 10));
+
+  // jTower negative side
+  basic_print_id (idhelper, idhelper.tower_id (-3, 0, 0, 0, 0));
+  basic_print_id (idhelper, idhelper.tower_id (-3, 0, 0, 1, 15));
+  basic_print_id (idhelper, idhelper.tower_id (-3, 0, 1, 2, 10));
+
+  // jTower positive side, had sampling (detSide/subDet, sampling regionNum, etaNum, phiNum
+  basic_print_id (idhelper, idhelper.tower_id (3, 1, 0, 0, 0));
+  basic_print_id (idhelper, idhelper.tower_id (3, 1, 0, 1, 15));
+  basic_print_id (idhelper, idhelper.tower_id (3, 1, 1, 2, 10));
+}
+
+
+int main()
+{
+  JTower_ID* idhelper = make_helper();
+  JTower_ID* idhelper_n = make_helper(true);
+  try {
+    test_basic (*idhelper);
+    test_towers (*idhelper);
+    test_exceptions (*idhelper, 3);
+    test4 (*idhelper_n);
+    testRegionInfo(*idhelper);
+  }
+  catch(CaloID_Exception & except){
+    std::cout << "Unexpected exception: " << (std::string) except << std::endl ;
+  }
+  return 0;
+}
diff --git a/Calorimeter/CaloIdentifier/test/jgtower_id_test_common.cxx b/Calorimeter/CaloIdentifier/test/jgtower_id_test_common.cxx
new file mode 100644
index 00000000000..0067c53df4c
--- /dev/null
+++ b/Calorimeter/CaloIdentifier/test/jgtower_id_test_common.cxx
@@ -0,0 +1,246 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id$
+/**
+ * @file  CaloIdentifier/test/larem_id_test_common.cxx
+ * @author Walter Hopkins (based on scott snyder's example)
+ * @date Oct 2014
+ * @brief Code common between GTower_ID_test and JTower_ID test.
+ *        This file is meant to be included in another file,
+ *        not compiled separately.
+ */
+
+
+#include "boost/foreach.hpp"
+
+
+// This because test differencing ignored lines containing `0x...'
+std::string munghex (const std::string& h)
+{
+  if (h.size() > 2 && h[0] == '0' && h[1] == 'x')
+    return "hex(" + h.substr (2, std::string::npos) + ")";
+  return h;
+}
+void basic_print_id (const JGTowerBase_ID& idhelper, const Identifier& id)
+{
+  std::cout << idhelper.show_to_string (id) << " "
+            << munghex(id.getString()) << "\n";
+}
+
+
+class GTower_ID_Test
+  : public JGTowerBase_ID
+{
+  // public:
+  //   using JGTowerBase_ID::lar_field_value;
+  //   using JGTowerBase_ID::lar_em_field_value;
+};
+
+
+class CellCounter
+{
+public:
+  CellCounter();
+  void count(int reg);
+  void report();
+
+
+private:
+  enum {
+    N_REG = 10
+  };
+  unsigned m_counts[N_REG];
+};
+
+CellCounter::CellCounter()
+{
+  for (unsigned i=0; i < N_REG; i++)
+    m_counts[i] = 0;
+}
+
+void CellCounter::count (int reg)
+{
+  if (reg < 0 || reg >= N_REG) std::abort();
+  ++m_counts[reg];
+}
+
+void CellCounter::report()
+{
+  int towerSum = 0;
+  for(int regI=0; regI<N_REG; ++regI){
+    printf ("Region %2d: %6d\n", regI, m_counts[regI]);
+    towerSum+=m_counts[regI];
+  }
+  printf ("Total: %6d\n", towerSum);
+}
+
+
+
+void test_towers (const JGTowerBase_ID& tower_id)
+{
+  std::cout << "test_towers\n";
+
+  // towers
+  IdContext towerContext = tower_id.tower_context();
+  std::vector<Identifier>::const_iterator itId = tower_id.tower_begin();
+  std::vector<Identifier>::const_iterator itIdEnd = tower_id.tower_end();
+  std::vector<bool> hashvec(tower_id.tower_hash_max());
+  CellCounter counts;
+
+  std::cout << "  nTowers " << itIdEnd - itId << "\n";
+
+  int hashsum = 0;
+  for(; itId!=itIdEnd; ++itId) {
+    Identifier t_id = *itId;
+  
+    IdentifierHash hashId;
+    assert (tower_id.get_hash (t_id, hashId, &towerContext) == 0);
+    Identifier t_id2;
+    assert (tower_id.get_id (hashId, t_id2, &towerContext) == 0);
+    assert (t_id == t_id2);
+    assert (t_id == tower_id.tower_id (hashId));
+    assert (hashId == tower_id.tower_hash (t_id));
+    assert (hashId == tower_id.tower_hash_binary_search (t_id));
+    assert (hashId < hashvec.size());
+    assert (!hashvec[hashId]);
+    hashvec[hashId] = true;
+
+    hashsum += hashId;
+
+    int pos_neg = tower_id.pos_neg(t_id);
+    int sampling = tower_id.sampling(t_id);
+    int reg  = tower_id.region(t_id);
+    int eta  = tower_id.eta(t_id);
+    int phi  = tower_id.phi(t_id);
+
+    t_id2 = tower_id.tower_id (pos_neg, sampling, reg, eta, phi);
+    assert (t_id == t_id2);
+    counts.count(reg);
+  }
+  for (size_t i = 0; i < hashvec.size(); i++)
+    assert (hashvec[i]);
+
+#if __cplusplus > 201100
+  for (Identifier t_id : tower_id.tower_range()) {
+#else
+    BOOST_FOREACH (Identifier t_id, tower_id.tower_range()) {
+#endif
+      hashsum -= tower_id.tower_hash (t_id);
+    }
+    assert (hashsum == 0);
+
+      counts.report();
+  }
+
+
+  void test_exceptions (const JGTowerBase_ID& tower_id, int detSide)
+{
+  std::cout << "test_exceptions\n";
+  bool caught = false;
+  try {
+    /*Identifier wrongRegionId =*/ tower_id.region_id (0,0,99); 
+  }
+  catch(CaloID_Exception & except){
+    caught = true;
+    std::cout << "Exception 1: " << (std::string)except << "\n";
+  }
+  assert (caught);
+
+  caught = false;
+  try {
+    /*Identifier wrongChannelId =*/ tower_id.tower_id (0,0,0,0,99); 
+  }
+  catch(CaloID_Exception & except){
+    caught = true;
+    std::cout << "Exception 2: " << (std::string)except << "\n";
+  }
+  assert (caught);
+
+  caught = false;
+  try {
+    Identifier goodRegionId = tower_id.region_id (detSide,0, 0); 
+    /*Identifier wrongChannelId =*/ tower_id.tower_id (goodRegionId,0,-99); 
+  }
+  catch(CaloID_Exception & except){
+    caught = true;
+    std::cout << "Exception 3: " << (std::string)except << "\n";
+  }
+  assert (caught);
+}
+
+
+// neighbors
+void test4 (const JGTowerBase_ID& idhelper)
+{
+  std::cout << "test4\n";
+
+  IdentifierHash prevPhiHash;
+  IdentifierHash nextPhiHash;
+  IdentifierHash prevEtaHash;
+  IdentifierHash nextEtaHash;
+  IdentifierHash curHash;
+  IdentifierHash tmpHash;
+
+  Identifier curId;
+
+  // towers
+  IdContext towerContext = idhelper.tower_context();
+  std::vector<Identifier>::const_iterator itId = idhelper.tower_begin();
+  std::vector<Identifier>::const_iterator itIdEnd = idhelper.tower_end();
+ 
+  for(; itId!=itIdEnd; ++itId) {
+    Identifier t_id = *itId;
+  
+    assert (idhelper.get_hash (t_id, curHash, &towerContext) == 0);
+
+    // Check that previous phi works
+    assert (idhelper.get_prev_in_phi(curHash, prevPhiHash) == 0);
+    assert (idhelper.get_next_in_phi(prevPhiHash, tmpHash) == 0);
+    Identifier t_id2;
+    assert (idhelper.get_id (tmpHash, t_id2, &towerContext) == 0);
+    assert (t_id == t_id2);
+
+    // Check next phi
+    assert (idhelper.get_next_in_phi(curHash, nextPhiHash) == 0);
+    assert (idhelper.get_prev_in_phi(nextPhiHash, tmpHash) == 0);
+    assert (idhelper.get_id (tmpHash, t_id2, &towerContext) == 0);
+    assert (t_id == t_id2);
+
+// Check that previous eta works
+    if (!idhelper.get_prev_in_eta(curHash, prevEtaHash)){
+      assert (idhelper.get_next_in_eta(prevEtaHash, tmpHash) == 0);
+      assert (idhelper.get_id (tmpHash, t_id2, &towerContext) == 0);
+      assert (t_id == t_id2);
+    }
+    else{
+      std::cout << "No previous tower in eta found for hash: " << curHash << std::endl;
+    }
+
+    // Check next eta
+    if (!idhelper.get_next_in_eta(curHash, nextEtaHash)){;
+    assert (idhelper.get_prev_in_eta(nextEtaHash, tmpHash) == 0);
+    assert (idhelper.get_id (tmpHash, t_id2, &towerContext) == 0);
+    assert (t_id == t_id2);
+    }
+else{
+  std::cout << "No next tower in eta found for hash: " << curHash << std::endl;
+    }
+  }
+}
+
+ void testRegionInfo (const JGTowerBase_ID& idhelper)
+ {
+   IdContext regionContext = idhelper.region_context();
+
+   BOOST_FOREACH (Identifier r_id, idhelper.reg_range()) {
+  
+     //IdentifierHash hashId;
+     // assert (idhelper.get_hash (r_id, hashId, &regionContext) == 0);
+       std::cout << "(deta,dphi) and (eta0,phi0) is: (" << idhelper.etaGranularity(r_id) 
+		 << "," << idhelper.phiGranularity(r_id) << "), (" 
+		 << idhelper.eta0(r_id) << "," << idhelper.phi0(r_id) 
+		 << ")" << std::endl;
+   }
+ }
-- 
GitLab