void PHG4SvtxClusterizer::ClusterLadderCells(PHCompositeNode *topNode) {

  //----------
  // Get Nodes
  //----------

  // get the SVX geometry object
  PHG4CylinderGeomContainer* geom_container = findNode::getClass<PHG4CylinderGeomContainer>(topNode,"CYLINDERGEOM_SILICON_TRACKER");
  if (!geom_container) return;
  
  PHG4HitContainer* g4hits =  findNode::getClass<PHG4HitContainer>(topNode,"G4HIT_SILICON_TRACKER");
  if (!g4hits) return;
  
  PHG4CylinderCellContainer* cells =  findNode::getClass<PHG4CylinderCellContainer>(topNode,"G4CELL_SILICON_TRACKER");
  if (!cells) return; 
 
  //-----------
  // Clustering
  //-----------

  // sort hits layer by layer
  std::multimap<int,SvtxHit*> layer_hits_mmap;
  for (SvtxHitMap::Iter iter = _hits->begin();
       iter != _hits->end();
       ++iter) {
    SvtxHit* hit = &iter->second;
    layer_hits_mmap.insert(make_pair(hit->get_layer(),hit));
  }
  
  PHG4CylinderGeomContainer::ConstRange layerrange = geom_container->get_begin_end();
  for(PHG4CylinderGeomContainer::ConstIterator layeriter = layerrange.first;
      layeriter != layerrange.second;
      ++layeriter) {

    int layer = layeriter->second->get_layer();

    std::map<PHG4CylinderCell*,SvtxHit*> cell_hit_map;
    vector<PHG4CylinderCell*> cell_list;
    for (std::multimap<int,SvtxHit*>::iterator hiter = layer_hits_mmap.lower_bound(layer);
	 hiter != layer_hits_mmap.upper_bound(layer);
	 ++hiter) {
      SvtxHit* hit = hiter->second;
      PHG4CylinderCell* cell = cells->findCylinderCell(hit->get_cellid());
      cell_list.push_back(cell);
      cell_hit_map.insert(make_pair(cell,hit));
    }
    
    if (cell_list.size() == 0) continue; // if no cells, go to the next layer
    
    // i'm not sure this sorting is ever really used
    sort(cell_list.begin(), cell_list.end(), PHG4SvtxClusterizer::ladder_lessthan);

    typedef adjacency_list <vecS, vecS, undirectedS> Graph;
    typedef graph_traits<Graph>::vertex_descriptor Vertex;
    Graph G;

    for(unsigned int i=0; i<cell_list.size(); i++) {
      for(unsigned int j=i+1; j<cell_list.size(); j++) {
        if(ladder_are_adjacent(cell_list[i], cell_list[j]) )
          add_edge(i,j,G);
      }
      
      add_edge(i,i,G);
    }

    // Find the connections between the vertices of the graph (vertices are the rawhits, 
    // connections are made when they are adjacent to one another)
    vector<int> component(num_vertices(G));
    
    // this is the actual clustering, performed by boost
    connected_components(G, &component[0]); 

    // Loop over the components(hit cells) compiling a list of the
    // unique connected groups (ie. clusters).
    set<int> cluster_ids; // unique components
    multimap<int, PHG4CylinderCell*> clusters;
    for (unsigned int i=0; i<component.size(); i++) {
      cluster_ids.insert( component[i] );
      clusters.insert( make_pair(component[i], cell_list[i]) );
    }
    
    // 
    for (set<int>::iterator clusiter = cluster_ids.begin(); 
	 clusiter != cluster_ids.end(); 
	 clusiter++ ) {
      
      int clusid = *clusiter;
      pair<multimap<int, PHG4CylinderCell*>::iterator,
	   multimap<int, PHG4CylinderCell*>::iterator> clusrange = clusters.equal_range(clusid);
      
      multimap<int, PHG4CylinderCell*>::iterator mapiter = clusrange.first;
      
      int layer = mapiter->second->get_layer();
      PHG4CylinderGeom* geom = geom_container->GetLayerGeom(layer);
      
      SvtxCluster clus;
      clus.set_layer( layer );
      float clus_energy = 0.0;
      unsigned int clus_adc = 0;

      // determine the size of the cluster in phi and z
      // useful for track fitting the cluster

      set<int> phibins;
      set<int> zbins;
      for (mapiter = clusrange.first; mapiter != clusrange.second; mapiter++ ) {
	PHG4CylinderCell* cell = mapiter->second;     
	
	phibins.insert(cell->get_binphi());
	zbins.insert(cell->get_binz());
      }

      float thickness = geom->get_thickness();
      float pitch = geom->get_strip_y_spacing();
      float length = geom->get_strip_z_spacing();
      float phisize = phibins.size()*pitch;
      float zsize = zbins.size()*length;
      float tilt = geom->get_strip_tilt();

      // determine the cluster position...
      double xsum = 0.0;
      double ysum = 0.0;
      double zsum = 0.0;
      unsigned nhits = 0;

      int ladder_z_index = -1;
      int ladder_phi_index = -1;
      
      for(mapiter = clusrange.first; mapiter != clusrange.second; mapiter++ ) {
        PHG4CylinderCell* cell = mapiter->second;
	SvtxHit* hit = cell_hit_map[cell];
	
	clus.insert_hit(hit->get_id());
	
        clus_energy += hit->get_e();
	clus_adc    += hit->get_adc();

	double hit_location[3] = {0.0,0.0,0.0};
	geom->find_strip_center(cell->get_ladder_z_index(),
				cell->get_ladder_phi_index(),
				cell->get_binz(),
				cell->get_binphi(),
				hit_location);

	ladder_z_index = cell->get_ladder_z_index();
	ladder_phi_index = cell->get_ladder_phi_index();

	if (_make_e_weights[layer]) {
	  xsum += hit_location[0] * hit->get_adc();
	  ysum += hit_location[1] * hit->get_adc();
	  zsum += hit_location[2] * hit->get_adc();  
	} else {
	  xsum += hit_location[0];
	  ysum += hit_location[1];
	  zsum += hit_location[2];
	}
	++nhits;
      }

      double clusx = NAN;
      double clusy = NAN;
      double clusz = NAN;

      if (_make_e_weights[layer]) {
	clusx = xsum / clus_adc;
	clusy = ysum / clus_adc;
	clusz = zsum / clus_adc;	
      } else {
	clusx = xsum / nhits;
	clusy = ysum / nhits;
	clusz = zsum / nhits;
      }
      
      double ladder_location[3] = {0.0,0.0,0.0};
      geom->find_segment_center(ladder_z_index,
				ladder_phi_index,
				ladder_location);
      double ladderphi = atan2( ladder_location[1], ladder_location[0] );
      
      clus.set_position(0, clusx);
      clus.set_position(1, clusy);
      clus.set_position(2, clusz);

      clus.set_e(clus_energy);
      clus.set_adc(clus_adc);

      float invsqrt12 = 1.0/sqrt(12.0);
      
      TMatrixF DIM(3,3);
      DIM[0][0] = pow(0.5*thickness,2);
      DIM[0][1] = 0.0;
      DIM[0][2] = 0.0;
      DIM[1][0] = 0.0;
      DIM[1][1] = pow(0.5*phisize,2);
      DIM[1][2] = 0.0;
      DIM[2][0] = 0.0;
      DIM[2][1] = 0.0;
      DIM[2][2] = pow(0.5*zsize,2);

      TMatrixF ERR(3,3);
      ERR[0][0] = pow(0.5*thickness*invsqrt12,2);
      ERR[0][1] = 0.0;
      ERR[0][2] = 0.0;
      ERR[1][0] = 0.0;
      ERR[1][1] = pow(0.5*phisize*invsqrt12,2);
      ERR[1][2] = 0.0;
      ERR[2][0] = 0.0;
      ERR[2][1] = 0.0;
      ERR[2][2] = pow(0.5*zsize*invsqrt12,2);
      
      TMatrixF ROT(3,3);
      ROT[0][0] = cos(ladderphi);
      ROT[0][1] = -1.0*sin(ladderphi);
      ROT[0][2] = 0.0;
      ROT[1][0] = sin(ladderphi);
      ROT[1][1] = cos(ladderphi);
      ROT[1][2] = 0.0;
      ROT[2][0] = 0.0;
      ROT[2][1] = 0.0;
      ROT[2][2] = 1.0;

      TMatrixF TILT(3,3);
      TILT[0][0] = 1.0;
      TILT[0][1] = 0.0;
      TILT[0][2] = 0.0;
      TILT[1][0] = 0.0;
      TILT[1][1] = cos(tilt);
      TILT[1][2] = -1.0*sin(tilt);
      TILT[2][0] = 0.0;
      TILT[2][1] = sin(tilt);
      TILT[2][2] = cos(tilt);

      TMatrixF R(3,3);
      R = ROT * TILT;
      
      TMatrixF R_T(3,3);
      R_T.Transpose(R);
          
      TMatrixF COVAR_DIM(3,3);
      COVAR_DIM = R * DIM * R_T;
      
      clus.set_size( 0 , 0 , COVAR_DIM[0][0] );
      clus.set_size( 0 , 1 , COVAR_DIM[0][1] );
      clus.set_size( 0 , 2 , COVAR_DIM[0][2] );
      clus.set_size( 1 , 0 , COVAR_DIM[1][0] );
      clus.set_size( 1 , 1 , COVAR_DIM[1][1] );
      clus.set_size( 1 , 2 , COVAR_DIM[1][2] );
      clus.set_size( 2 , 0 , COVAR_DIM[2][0] );
      clus.set_size( 2 , 1 , COVAR_DIM[2][1] );
      clus.set_size( 2 , 2 , COVAR_DIM[2][2] );

      TMatrixF COVAR_ERR(3,3);
      COVAR_ERR = R * ERR * R_T;
      
      clus.set_error( 0 , 0 , COVAR_ERR[0][0] );
      clus.set_error( 0 , 1 , COVAR_ERR[0][1] );
      clus.set_error( 0 , 2 , COVAR_ERR[0][2] );
      clus.set_error( 1 , 0 , COVAR_ERR[1][0] );
      clus.set_error( 1 , 1 , COVAR_ERR[1][1] );
      clus.set_error( 1 , 2 , COVAR_ERR[1][2] );
      clus.set_error( 2 , 0 , COVAR_ERR[2][0] );
      clus.set_error( 2 , 1 , COVAR_ERR[2][1] );
      clus.set_error( 2 , 2 , COVAR_ERR[2][2] );
      
      if (clus_energy > get_threshold_by_layer(layer)) {
	SvtxCluster* ptr = _clusterlist->insert(clus);
	if (!ptr->IsValid()) {
	  static bool first = true;
	  if (first) {
	    cout << PHWHERE << "ERROR: Invalid SvtxClusters are being produced" << endl;
	    ptr->identify();
	    first = false;
	  }
	}
	
	if (verbosity>1) {
	  double radius = sqrt(clusx*clusx+clusy*clusy);
	  double clusphi = atan2(clusy,clusx);
	  cout << "r=" << radius << " phi=" << clusphi << " z=" << clusz << endl;
	  cout << "pos=(" << clus.get_position(0) << ", " << clus.get_position(1)
	       << ", " << clus.get_position(2) << ")" << endl;
	  cout << endl;
	}
      }	else if (verbosity>1) {
	double radius = sqrt(clusx*clusx+clusy*clusy);
	double clusphi = atan2(clusy,clusx);
	cout << "removed r=" << radius << " phi=" << clusphi << " z=" << clusz << endl;
	cout << "pos=(" << clus.get_position(0) << ", " << clus.get_position(1)
	     << ", " << clus.get_position(2) << ")" << endl;
	cout << endl;
      } 
    }
  }
  
  return;
}
int PHG4TPCClusterizer::process_event(PHCompositeNode* topNode) {

  PHNodeIterator iter(topNode);

  PHCompositeNode* dstNode =
      static_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
  if (!dstNode) {
    cout << PHWHERE << "DST Node missing, doing nothing." << endl;
    return Fun4AllReturnCodes::ABORTRUN;
  }
  PHNodeIterator iter_dst(dstNode);

  SvtxHitMap* hits = findNode::getClass<SvtxHitMap>(dstNode, "SvtxHitMap");
  if (!hits) {
    cout << PHWHERE << "ERROR: Can't find node SvtxHitMap" << endl;
    return Fun4AllReturnCodes::ABORTRUN;
  }

  PHCompositeNode* svxNode =
      dynamic_cast<PHCompositeNode*>(iter_dst.findFirst("PHCompositeNode", "SVTX"));
  if (!svxNode) {
    svxNode = new PHCompositeNode("SVTX");
    dstNode->addNode(svxNode);
  }

  SvtxClusterMap* svxclusters =
      findNode::getClass<SvtxClusterMap>(dstNode, "SvtxClusterMap");
  if (!svxclusters) {
    svxclusters = new SvtxClusterMap_v1();
    PHIODataNode<PHObject>* SvtxClusterMapNode =
        new PHIODataNode<PHObject>(svxclusters, "SvtxClusterMap", "PHObject");
    svxNode->addNode(SvtxClusterMapNode);
  }

  PHG4CylinderCellGeomContainer* geom_container =
    findNode::getClass<PHG4CylinderCellGeomContainer>(topNode,"CYLINDERCELLGEOM_SVTX");
  if (!geom_container) return Fun4AllReturnCodes::ABORTRUN;

  PHG4CylinderCellContainer* cells =  findNode::getClass<PHG4CylinderCellContainer>(dstNode,"G4CELL_SVTX");
  if (!cells) return Fun4AllReturnCodes::ABORTRUN;

  std::vector<std::vector<const SvtxHit*> > layer_sorted;
  PHG4CylinderCellGeomContainer::ConstRange layerrange = geom_container->get_begin_end();
  for (PHG4CylinderCellGeomContainer::ConstIterator layeriter = layerrange.first;
       layeriter != layerrange.second;
       ++layeriter) {
    // We only need TPC layers here, so skip the layers below _min_layer
    // This if statement is needed because although the maps ladder layers are not included in the cylinder cell geom container, 
    // the cylinder Svx layers are, so they have to be dropped here if they are present
    if( (unsigned int) layeriter->second->get_layer() < _min_layer)
      continue;
    layer_sorted.push_back(std::vector<const SvtxHit*>());
  }
  for (SvtxHitMap::Iter iter = hits->begin(); iter != hits->end(); ++iter) {
    SvtxHit* hit = iter->second;
    if( (unsigned int) hit->get_layer() < _min_layer)
      continue;
    layer_sorted[hit->get_layer() - _min_layer].push_back(hit);
  }
  
  for (PHG4CylinderCellGeomContainer::ConstIterator layeriter =
           layerrange.first;
       layeriter != layerrange.second; ++layeriter) {

    unsigned int layer = (unsigned int)layeriter->second->get_layer();
    
    // exit on the MAPS layers...
    // needed in case cylinder svtx layers are present      
    if (layer < _min_layer) continue;
    if (layer > _max_layer) continue;
    
    PHG4CylinderCellGeom* geo = geom_container->GetLayerCellGeom(layer);
    const int nphibins = layeriter->second->get_phibins();
    const int nzbins = layeriter->second->get_zbins();

    nhits.clear();
    nhits.assign(nzbins, 0);
    amps.clear();
    amps.assign(nphibins * nzbins, 0.);
    cellids.clear();
    cellids.assign(nphibins * nzbins, 0);

    for (unsigned int i = 0; i < layer_sorted[layer - _min_layer].size(); ++i) {

      const SvtxHit* hit = layer_sorted[layer - _min_layer][i];
      if (hit->get_e() <= 0.) continue;
      
      PHG4CylinderCell* cell = cells->findCylinderCell(hit->get_cellid());
      int phibin = cell->get_binphi();
      int zbin = cell->get_binz();
      nhits[zbin] += 1;
      amps[zbin * nphibins + phibin] += hit->get_e();
      cellids[zbin * nphibins + phibin] = hit->get_id();
    }

    int nhits_tot = 0;
    for (int zbin = 0; zbin < nzbins; ++zbin) {
      nhits_tot += nhits[zbin];
    }

    while (nhits_tot > 0) {

      for (int zbin = 0; zbin < nzbins; ++zbin) {

        if (nhits[zbin] <= 0) continue;

        for (int phibin = 0; phibin < nphibins; ++phibin) {

          if (is_local_maximum(amps, nphibins, nzbins, phibin, zbin) == false) {
            continue;
          }

          float phi = 0.;
          float z = 0.;
          float e = 0.;

          fit_cluster(amps, nphibins, nzbins, nhits_tot, nhits, phibin, zbin,
                      geo, phi, z, e);

          if ((layer > 2) && (e < energy_cut)) {
            continue;
          }

          SvtxCluster_v1 clus;
          clus.set_layer(layer);
          clus.set_e(e);
          double radius = geo->get_radius() + 0.5*geo->get_thickness();
          clus.set_position(0, radius * cos(phi));
          clus.set_position(1, radius * sin(phi));
          clus.set_position(2, z);
	  
          clus.insert_hit(cellids[zbin * nphibins + phibin]);

	  float invsqrt12 = 1.0/sqrt(12.);
      
	  TMatrixF DIM(3,3);
	  DIM[0][0] = 0.0;//pow(0.0*0.5*thickness,2);
	  DIM[0][1] = 0.0;
	  DIM[0][2] = 0.0;
	  DIM[1][0] = 0.0;
	  DIM[1][1] = pow(0.5*0.011,2);
	  DIM[1][2] = 0.0;
	  DIM[2][0] = 0.0;
	  DIM[2][1] = 0.0;
	  DIM[2][2] = pow(0.5*0.03,2);

	  TMatrixF ERR(3,3);
	  ERR[0][0] = 0.0;//pow(0.0*0.5*thickness*invsqrt12,2);
	  ERR[0][1] = 0.0;
	  ERR[0][2] = 0.0;
	  ERR[1][0] = 0.0;
	  ERR[1][1] = pow(0.5*0.011*invsqrt12,2);
	  ERR[1][2] = 0.0;
	  ERR[2][0] = 0.0;
	  ERR[2][1] = 0.0;
	  ERR[2][2] = pow(0.5*0.03*invsqrt12,2);

	  TMatrixF ROT(3,3);
	  ROT[0][0] = cos(phi);
	  ROT[0][1] = -sin(phi);
	  ROT[0][2] = 0.0;
	  ROT[1][0] = sin(phi);
	  ROT[1][1] = cos(phi);
	  ROT[1][2] = 0.0;
	  ROT[2][0] = 0.0;
	  ROT[2][1] = 0.0;
	  ROT[2][2] = 1.0;

	  TMatrixF ROT_T(3,3);
	  ROT_T.Transpose(ROT);
      
	  TMatrixF COVAR_DIM(3,3);
	  COVAR_DIM = ROT * DIM * ROT_T;
	  
	  clus.set_size( 0 , 0 , COVAR_DIM[0][0] );
	  clus.set_size( 0 , 1 , COVAR_DIM[0][1] );
	  clus.set_size( 0 , 2 , COVAR_DIM[0][2] );
	  clus.set_size( 1 , 0 , COVAR_DIM[1][0] );
	  clus.set_size( 1 , 1 , COVAR_DIM[1][1] );
	  clus.set_size( 1 , 2 , COVAR_DIM[1][2] );
	  clus.set_size( 2 , 0 , COVAR_DIM[2][0] );
	  clus.set_size( 2 , 1 , COVAR_DIM[2][1] );
	  clus.set_size( 2 , 2 , COVAR_DIM[2][2] );

	  TMatrixF COVAR_ERR(3,3);
	  COVAR_ERR = ROT * ERR * ROT_T;
	  
	  clus.set_error( 0 , 0 , COVAR_ERR[0][0] );
	  clus.set_error( 0 , 1 , COVAR_ERR[0][1] );
	  clus.set_error( 0 , 2 , COVAR_ERR[0][2] );
	  clus.set_error( 1 , 0 , COVAR_ERR[1][0] );
	  clus.set_error( 1 , 1 , COVAR_ERR[1][1] );
	  clus.set_error( 1 , 2 , COVAR_ERR[1][2] );
	  clus.set_error( 2 , 0 , COVAR_ERR[2][0] );
	  clus.set_error( 2 , 1 , COVAR_ERR[2][1] );
	  clus.set_error( 2 , 2 , COVAR_ERR[2][2] );
      
          svxclusters->insert(&clus);
        }
      }
    }
  }

  reset();
  return Fun4AllReturnCodes::EVENT_OK;
}