diff --git a/DDCore/include/DD4hep/Segmentations.h b/DDCore/include/DD4hep/Segmentations.h
index 245af1ca227e3cfaedc5ceea4b5aa7642d664a17..c775e39b420104671612f265ef965de7c5ba0c92 100644
--- a/DDCore/include/DD4hep/Segmentations.h
+++ b/DDCore/include/DD4hep/Segmentations.h
@@ -93,6 +93,11 @@ namespace dd4hep {
      *   \return vector<double> in natural order of dimensions, e.g., dx/dy/dz, or dr/r*dPhi
      */
     std::vector<double> cellDimensions(const CellID& cellID) const;
+    /// Return true if this segmentation can have cells that span multiple
+    /// volumes.  That is, points from multiple distinct volumes may
+    /// be assigned to the same cell.
+    /// In that case, a working volumeID() implementation is required.
+    bool cellsSpanVolumes() const;
       
     /// Access to the base DDSegmentation object. WARNING: Deprecated call!
     DDSegmentation::Segmentation* segmentation() const;
diff --git a/DDCore/include/DDSegmentation/Segmentation.h b/DDCore/include/DDSegmentation/Segmentation.h
index b2da572ba3a3dbdb656df6fcc4a4583929e02ce6..e4a39a7dcf9af4dc568446f3127e2eed2d3e0b6a 100644
--- a/DDCore/include/DDSegmentation/Segmentation.h
+++ b/DDCore/include/DDSegmentation/Segmentation.h
@@ -127,6 +127,14 @@ namespace dd4hep {
           \return vector<double> in natural order of dimensions, e.g., dx/dy/dz, or dr/r*dPhi
       */
       virtual std::vector<double> cellDimensions(const CellID& cellID) const;
+      /// Return true if this segmentation can have cells that span multiple
+      /// volumes.  That is, points from multiple distinct volumes may
+      /// be assigned to the same cell.
+      /// In that case, a working volumeID() implementation is required.
+      virtual bool cellsSpanVolumes() const
+      {
+        return false;
+      }
 
     protected:
       /// Default constructor used by derived classes passing the encoding string
diff --git a/DDCore/src/Segmentations.cpp b/DDCore/src/Segmentations.cpp
index 4d36775dee6185840eacf08e3e9fde17d09526a4..7b1de46fef8f9f949b6a78b77e49b58b439e20b1 100644
--- a/DDCore/src/Segmentations.cpp
+++ b/DDCore/src/Segmentations.cpp
@@ -95,6 +95,14 @@ std::vector<double> Segmentation::cellDimensions(const CellID& cell) const  {
   return access()->segmentation->cellDimensions(cell);
 }
 
+/// Return true if this segmentation can have cells that span multiple
+/// volumes.  That is, points from multiple distinct volumes may
+/// be assigned to the same cell.
+bool Segmentation::cellsSpanVolumes() const
+{
+  return access()->segmentation->cellsSpanVolumes();
+}
+
 /// Access to the base DDSegmentation object. WARNING: Deprecated call!
 DDSegmentation::Segmentation* Segmentation::segmentation() const  {
   return access()->segmentation;
diff --git a/DDG4/plugins/Geant4SDActions.cpp b/DDG4/plugins/Geant4SDActions.cpp
index c3b734e55c4b79018a173e001c0cb48afc07e5be..0338fb9b15a76529fb1e9eb51e3332afabb57e5a 100644
--- a/DDG4/plugins/Geant4SDActions.cpp
+++ b/DDG4/plugins/Geant4SDActions.cpp
@@ -27,7 +27,58 @@ namespace dd4hep {
 
     namespace {
       struct Geant4VoidSensitive {};
+
+      /// Common code to handle the creation of a calorimeter hit.
+      template <class HANDLER>
+      void handleCalorimeterHit (VolumeID cell,
+                                 const HitContribution& contrib,
+                                 Geant4HitCollection& coll,
+                                 const HANDLER& h,
+                                 const Geant4Sensitive& sd,
+                                 const Segmentation& segmentation)
+      {
+        typedef Geant4Calorimeter::Hit Hit;
+        Hit* hit = coll.findByKey<Hit>(cell);
+        if ( !hit ) {
+          DDSegmentation::Vector3D pos = segmentation.position(cell);
+          Position global;
+
+          // Convert the position relative to the local readout volume
+          // to a global position.
+          if (!segmentation.cellsSpanVolumes()) {
+            global = h.localToGlobal(pos);
+          }
+          else {
+            // The segmentation can gang together multiple volumes.
+            // In this case, we can't use the transformation we get from
+            // the step --- the volume that actually contains the hit
+            // may not be the same volume that the segmentation uses
+            // for the local coordinate system.  We need to get the
+            // actual volID used from the segmentation and then look
+            // it up the volume manager to get the proper transformation.
+            VolumeID volID = segmentation.volumeID(cell);
+            VolumeManager vman = VolumeManager::getVolumeManager(sd.detectorDescription());
+            VolumeManagerContext* vc = vman.lookupContext(volID);
+            // explicit unit conversion; h.localToGlobal does it internally already
+            global = vc->localToWorld(Position(pos)) / dd4hep::mm;
+          }
+          hit = new Hit(global);
+          hit->cellID = cell;
+          coll.add(cell, hit);
+          Geant4TouchableHandler handler(h.touchable());
+          sd.printM2("%s> CREATE hit with deposit:%e MeV  Pos:%8.2f %8.2f %8.2f  %s  [%s]",
+                     sd.c_name(),contrib.deposit,pos.X,pos.Y,pos.Z,handler.path().c_str(),
+                     coll.GetName().c_str());
+          if ( 0 == hit->cellID )  { // for debugging only!
+            hit->cellID = cell;
+            sd.except("+++ Invalid CELL ID for hit!");
+          }
+        }
+        hit->truth.emplace_back(contrib);
+        hit->energyDeposit += contrib.deposit;
+      }
     }
+
     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     //               Geant4SensitiveAction<Geant4VoidSensitive>
     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -250,25 +301,7 @@ namespace dd4hep {
         return true;
       }
 
-      //Hit* hit = coll->find<Hit>(CellIDCompare<Hit>(cell));
-      Hit* hit = coll->findByKey<Hit>(cell);
-      if ( !hit ) {
-        Geant4TouchableHandler handler(step);
-        DDSegmentation::Vector3D pos = m_segmentation.position(cell);
-        Position global = h.localToGlobal(pos);
-        hit = new Hit(global);
-        hit->cellID = cell;
-        coll->add(cell, hit);
-        printM2("%s> CREATE hit with deposit:%e MeV  Pos:%8.2f %8.2f %8.2f  %s  [%s]",
-                c_name(),contrib.deposit,pos.X,pos.Y,pos.Z,handler.path().c_str(),
-                coll->GetName().c_str());
-        if ( 0 == hit->cellID )  { // for debugging only!
-          hit->cellID = cellID(step);
-          except("+++ Invalid CELL ID for hit!");
-        }
-      }
-      hit->truth.emplace_back(contrib);
-      hit->energyDeposit += contrib.deposit;
+      handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
       mark(h.track);
       return true;
     }
@@ -294,24 +327,7 @@ namespace dd4hep {
         std::cout << out.str();
         return true;
       }
-      Hit* hit = coll->findByKey<Hit>(cell);
-      if ( !hit ) {
-        Geant4TouchableHandler   handler(h.touchable());
-        DDSegmentation::Vector3D pos = m_segmentation.position(cell);
-        Position global = h.localToGlobal(pos);
-        hit = new Hit(global);
-        hit->cellID = cell;
-        coll->add(cell, hit);
-        printM2("%s> CREATE hit with deposit:%e MeV  Pos:%8.2f %8.2f %8.2f  %s  [%s]",
-                c_name(),contrib.deposit,pos.X,pos.Y,pos.Z,handler.path().c_str(),
-                coll->GetName().c_str());
-        if ( 0 == hit->cellID )  { // for debugging only!
-          hit->cellID = cellID(h.touchable(), h.avgPositionG4());
-          except("+++ Invalid CELL ID for hit!");
-        }
-      }
-      hit->truth.emplace_back(contrib);
-      hit->energyDeposit += contrib.deposit;
+      handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
       mark(h.track);
       return true;
     }
@@ -462,23 +478,7 @@ namespace dd4hep {
         std::cout << out.str();
         return true;
       }
-      Hit* hit = coll->findByKey<Hit>(cell);
-      if ( !hit ) {
-        Geant4TouchableHandler handler(step);
-        DDSegmentation::Vector3D pos = m_segmentation.position(cell);
-        Position global = h.localToGlobal(pos);
-        hit = new Hit(global);
-        hit->cellID = cell;
-        coll->add(cell, hit);
-        printM2("CREATE hit with deposit:%e MeV  Pos:%8.2f %8.2f %8.2f  %s",
-                contrib.deposit,pos.X,pos.Y,pos.Z,handler.path().c_str());
-        if ( 0 == hit->cellID )  { // for debugging only!
-          hit->cellID = cellID(step);
-          except("+++ Invalid CELL ID for hit!");
-        }
-      }
-      hit->truth.emplace_back(contrib);
-      hit->energyDeposit += contrib.deposit;
+      handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
       mark(h.track);
       return true;
     }
@@ -505,23 +505,7 @@ namespace dd4hep {
         std::cout << out.str();
         return true;
       }
-      Hit* hit = coll->findByKey<Hit>(cell);
-      if ( !hit ) {
-        Geant4TouchableHandler   handler(h.touchable());
-        DDSegmentation::Vector3D pos = m_segmentation.position(cell);
-        Position global = h.localToGlobal(pos);
-        hit = new Hit(global);
-        hit->cellID = cell;
-        coll->add(cell, hit);
-        printM2("CREATE hit with deposit:%e MeV  Pos:%8.2f %8.2f %8.2f  %s",
-                contrib.deposit,pos.X,pos.Y,pos.Z,handler.path().c_str());
-        if ( 0 == hit->cellID )  { // for debugging only!
-          hit->cellID = cellID(h.touchable(), h.avgPositionG4());
-          except("+++ Invalid CELL ID for hit!");
-        }
-      }
-      hit->truth.emplace_back(contrib);
-      hit->energyDeposit += contrib.deposit;
+      handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
       mark(h.track);
       return true;
     }