void SvtxEvaluator::printOutputInfo(PHCompositeNode *topNode) {
  
  if (verbosity > 1) cout << "SvtxEvaluator::printOutputInfo() entered" << endl;

  //==========================================
  // print out some useful stuff for debugging
  //==========================================

  if (verbosity > 0) {
    
    SvtxTrackEval*     trackeval = _svtxevalstack->get_track_eval();
    SvtxClusterEval* clustereval = _svtxevalstack->get_cluster_eval();
    SvtxTruthEval*     trutheval = _svtxevalstack->get_truth_eval();
  
    // event information
    cout << endl;
    cout << PHWHERE << "   NEW OUTPUT FOR EVENT " << _ievent << endl;
    cout << endl;

    PHG4TruthInfoContainer* truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode,"G4TruthInfo");
    
    PHG4VtxPoint *gvertex = truthinfo->GetPrimaryVtx( truthinfo->GetPrimaryVertexIndex() );
    float gvx = gvertex->get_x();
    float gvy = gvertex->get_y();
    float gvz = gvertex->get_z();

    float vx = NAN;
    float vy = NAN;
    float vz = NAN;

    SvtxVertexMap* vertexmap = findNode::getClass<SvtxVertexMap>(topNode,"SvtxVertexMap");    
    if (vertexmap) {
      if (!vertexmap->empty()) {
	SvtxVertex* vertex = (vertexmap->begin()->second);
	
	vx = vertex->get_x();
	vy = vertex->get_y();
	vz = vertex->get_z();
      }
    }

    cout << "===Vertex Reconstruction=======================" << endl;
    cout << "vtrue = (" << gvx << "," << gvy << "," << gvz << ") => vreco = (" << vx << "," << vy << "," << vz << ")" << endl;
    cout << endl;

    cout << "===Tracking Summary============================" << endl;
    unsigned int ng4hits[100] = {0};
    std::set<PHG4Hit*> g4hits = trutheval->all_truth_hits();
    for (std::set<PHG4Hit*>::iterator iter = g4hits.begin();
	 iter != g4hits.end();
	 ++iter) {
      PHG4Hit *g4hit = *iter;
      ++ng4hits[g4hit->get_layer()];
    }

    SvtxHitMap* hitmap = findNode::getClass<SvtxHitMap>(topNode,"SvtxHitMap");
    unsigned int nhits[100] = {0};
    if (hitmap) {
      for (SvtxHitMap::Iter iter = hitmap->begin();
	   iter != hitmap->end();
	   ++iter) {
	SvtxHit* hit = iter->second;
	++nhits[hit->get_layer()];
      }
    }
    
    SvtxClusterMap* clustermap = findNode::getClass<SvtxClusterMap>(topNode,"SvtxClusterMap");
    unsigned int nclusters[100] = {0};
    if (clustermap) {
      for (SvtxClusterMap::Iter iter = clustermap->begin();
	   iter != clustermap->end();
	   ++iter) {
	SvtxCluster* cluster = iter->second;
	++nclusters[cluster->get_layer()];
      }
    }

    for (unsigned int ilayer = 0; ilayer < 100; ++ilayer) {
      cout << "layer " << ilayer << ": nG4hits = " << ng4hits[ilayer]
	   << " => nHits = " << nhits[ilayer]
	   << " => nClusters = " << nclusters[ilayer] << endl;
    }

    SvtxTrackMap* trackmap = findNode::getClass<SvtxTrackMap>(topNode,"SvtxTrackMap");
    
    cout << "nGtracks = " << std::distance(truthinfo->GetPrimaryParticleRange().first,
					  truthinfo->GetPrimaryParticleRange().second);
    cout << " => nTracks = ";
    if (trackmap) cout << trackmap->size() << endl;
    else cout << 0 << endl;

    // cluster wise information
    if (verbosity > 1) {
 
      for(std::set<PHG4Hit*>::iterator iter = g4hits.begin();
	  iter != g4hits.end();
	  ++iter) {
	PHG4Hit *g4hit = *iter;

	cout << endl;
        cout << "===PHG4Hit===================================" << endl;
	cout << " PHG4Hit: "; g4hit->identify();

	std::set<SvtxCluster*> clusters = clustereval->all_clusters_from(g4hit);

	for (std::set<SvtxCluster*>::iterator jter = clusters.begin();
	     jter != clusters.end();
	     ++jter) {
	  SvtxCluster *cluster = *jter;
	  cout << "===Created-SvtxCluster================" << endl;      
	  cout << "SvtxCluster: "; cluster->identify();
	}
      }

      PHG4TruthInfoContainer::ConstRange range = truthinfo->GetPrimaryParticleRange();
      for (PHG4TruthInfoContainer::ConstIterator iter = range.first;
	   iter != range.second; 
	   ++iter) {
	
	PHG4Particle *particle = iter->second;

	// track-wise information
	cout << endl;

	cout << "=== Gtrack ===================================================" << endl;
	cout << " PHG4Particle id = " << particle->get_track_id() << endl;
	particle->identify();
	cout << " ptrue = (";
	cout.width(5); cout << particle->get_px();
	cout << ",";
	cout.width(5); cout << particle->get_py();
	cout << ",";
	cout.width(5); cout << particle->get_pz();
	cout << ")" << endl;

	cout << " vtrue = (";
	cout.width(5); cout << truthinfo->GetVtx(particle->get_vtx_id())->get_x();
	cout << ",";
	cout.width(5); cout << truthinfo->GetVtx(particle->get_vtx_id())->get_y();
	cout << ",";
	cout.width(5); cout << truthinfo->GetVtx(particle->get_vtx_id())->get_z();
	cout << ")" << endl;
	  
	cout << " pt = " << sqrt(pow(particle->get_px(),2)+pow(particle->get_py(),2)) << endl;
	cout << " phi = " << atan2(particle->get_py(),particle->get_px()) << endl;
	cout << " eta = " << asinh(particle->get_pz()/sqrt(pow(particle->get_px(),2)+pow(particle->get_py(),2))) << endl;
	  
	cout << " embed flag = " << truthinfo->isEmbeded(particle->get_track_id()) << endl;

	cout << " ---Associated-PHG4Hits-----------------------------------------" << endl;
	std::set<PHG4Hit*> g4hits = trutheval->all_truth_hits(particle);
	for(std::set<PHG4Hit*>::iterator jter = g4hits.begin();
	    jter != g4hits.end();
	    ++jter) {
	  PHG4Hit *g4hit = *jter;

	  float x = 0.5*(g4hit->get_x(0)+g4hit->get_x(1));
	  float y = 0.5*(g4hit->get_y(0)+g4hit->get_y(1));
	  float z = 0.5*(g4hit->get_z(0)+g4hit->get_z(1));
	      
	  cout << " #" << g4hit->get_hit_id() << " xtrue = (";
	  cout.width(5); cout << x;
	  cout << ",";
	  cout.width(5); cout << y;
	  cout << ",";
	  cout.width(5); cout << z;
	  cout << ")";

	  std::set<SvtxCluster*> clusters = clustereval->all_clusters_from(g4hit);
	  for (std::set<SvtxCluster*>::iterator kter = clusters.begin();
	       kter != clusters.end();
	       ++kter) {
	  
	    SvtxCluster *cluster = *kter;

	    float x = cluster->get_x();
	    float y = cluster->get_y();
	    float z = cluster->get_z();
		 
	    cout << " => #" << cluster->get_id(); 
	    cout << " xreco = (";
	    cout.width(5); cout << x;
	    cout << ",";
	    cout.width(5); cout << y;
	    cout << ",";
	    cout.width(5); cout << z;
	    cout << ")";
	  }

	  cout << endl;
	}

	if (trackmap&&clustermap) {

	  std::set<SvtxTrack*> tracks = trackeval->all_tracks_from(particle);
	  for (std::set<SvtxTrack*>::iterator jter = tracks.begin();
	       jter != tracks.end();
	       ++jter) {
	  
	    SvtxTrack *track = *jter;

	    float px = track->get_px();
	    float py = track->get_py();
	    float pz = track->get_pz();

	    cout << "===Created-SvtxTrack==========================================" << endl;
	    cout << " SvtxTrack id = " << track->get_id() << endl;
	    cout << " preco = (";
	    cout.width(5); cout << px;
	    cout << ",";
	    cout.width(5); cout << py;
	    cout << ",";
	    cout.width(5); cout << pz;
	    cout << ")" << endl;
	    cout << " quality = " << track->get_quality() << endl;
	    cout << " nfromtruth = " << trackeval->get_nclusters_contribution(track,particle) << endl;

	    cout << " ---Associated-SvtxClusters-to-PHG4Hits-------------------------" << endl;    

	    for (SvtxTrack::ConstClusterIter iter = track->begin_clusters();
		 iter != track->end_clusters();
		 ++iter) {
	      unsigned int cluster_id = *iter;
	      SvtxCluster* cluster = clustermap->get(cluster_id);
	      		  
	      float x = cluster->get_x();
	      float y = cluster->get_y();
	      float z = cluster->get_z();
			  
	      cout << " #" << cluster->get_id() << " xreco = (";
	      cout.width(5); cout << x;
	      cout << ",";
	      cout.width(5); cout << y;
	      cout << ",";
	      cout.width(5); cout << z;
	      cout << ") =>";

	      PHG4Hit* g4hit = clustereval->max_truth_hit_by_energy(cluster);
	      if ((g4hit) && (g4hit->get_trkid() == particle->get_track_id())) {
			  
		x = 0.5*(g4hit->get_x(0)+g4hit->get_x(1));
		y = 0.5*(g4hit->get_y(0)+g4hit->get_y(1));
		z = 0.5*(g4hit->get_z(0)+g4hit->get_z(1));
			    
		cout << " #" << g4hit->get_hit_id()
		     << " xtrue = (";
		cout.width(5); cout << x;
		cout << ",";
		cout.width(5); cout << y;
		cout << ",";
		cout.width(5); cout << z;
		cout << ") => Gtrack id = " << g4hit->get_trkid();
	      } else {
		cout << " noise hit";
	      }
	    }
  
	    cout << endl;
	  }
	}
      }
    }
      
    cout << endl;

  } // if verbosity

  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;
}