Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:17:34

0001 #include "RawClusterBuilderTopo.h"
0002 
0003 #include <calobase/RawCluster.h>
0004 #include <calobase/RawClusterContainer.h>
0005 #include <calobase/RawClusterv1.h>
0006 #include <calobase/RawTowerDefs.h>  // for encode_towerid, Calorime...
0007 #include <calobase/RawTowerGeom.h>
0008 #include <calobase/RawTowerGeomContainer.h>
0009 #include <calobase/TowerInfo.h>           // for TowerInfo
0010 #include <calobase/TowerInfoContainer.h>  // for TowerInfoContainer
0011 
0012 #include <fun4all/Fun4AllReturnCodes.h>
0013 #include <fun4all/SubsysReco.h>
0014 
0015 #include <phool/PHCompositeNode.h>
0016 #include <phool/PHIODataNode.h>
0017 #include <phool/PHNode.h>
0018 #include <phool/PHNodeIterator.h>
0019 #include <phool/PHObject.h>
0020 #include <phool/getClass.h>
0021 #include <phool/phool.h>
0022 
0023 #include <algorithm>
0024 #include <cmath>
0025 #include <cstdlib>  // for abs
0026 #include <exception>
0027 #include <iostream>
0028 #include <iterator>  // for begin, end
0029 #include <list>
0030 #include <memory>  // for allocator_traits<>::valu...
0031 #include <stdexcept>
0032 #include <utility>
0033 #include <vector>
0034 
0035 namespace
0036 {
0037 bool sort_by_pair_second(const std::pair<int, float> &a, const std::pair<int, float> &b)
0038 {
0039   return (a.second > b.second);
0040 }
0041 }
0042 
0043 int RawClusterBuilderTopo::RawClusterBuilderTopo_constants_EMCal_eta_start_given_IHCal[24] =
0044     {2, 6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44,
0045      48, 52, 55, 59, 63, 66, 70, 74, 78, 82, 86, 90};
0046 
0047 int RawClusterBuilderTopo::RawClusterBuilderTopo_constants_EMCal_eta_end_given_IHCal[24] =
0048     {5, 9, 13, 17, 21, 25, 29, 32, 36, 40, 43, 47,
0049      51, 54, 58, 62, 65, 69, 73, 77, 81, 85, 89, 93};
0050 
0051 int RawClusterBuilderTopo::RawClusterBuilderTopo_constants_IHCal_eta_given_EMCal[96] = {
0052     -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
0053     2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
0054     5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8,
0055     8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 11,
0056     12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 14, 15,
0057     15, 15, 15, 16, 16, 16, 17, 17, 17, 17, 18, 18,
0058     18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21,
0059     21, 21, 22, 22, 22, 22, 23, 23, 23, 23, -1, -1};
0060 
0061 float RawClusterBuilderTopo::calculate_dR(float eta1, float eta2, float phi1, float phi2)
0062 {
0063   float deta = eta1 - eta2;
0064   float dphi = phi1 - phi2;
0065   while (dphi > M_PI)
0066   {
0067     dphi -= 2 * M_PI;
0068   }
0069   while (dphi < -M_PI)
0070   {
0071     dphi += 2 * M_PI;
0072   }
0073   return sqrt(pow(deta, 2) + pow(dphi, 2));
0074 }
0075 
0076 std::vector<int> RawClusterBuilderTopo::get_adjacent_towers_by_ID(int ID)
0077 {
0078   int this_layer = get_ilayer_from_ID(ID);
0079   int this_eta = get_ieta_from_ID(ID);
0080   int this_phi = get_iphi_from_ID(ID);
0081 
0082   std::vector<int> adjacent_towers;
0083 
0084   // for both IHCal and OHCal, add adjacent layers in the HCal
0085   if (this_layer == 0 || this_layer == 1)
0086   {
0087     for (int delta_layer = 0; delta_layer <= 1; delta_layer++)
0088     {
0089       for (int delta_eta = -1; delta_eta <= 1; delta_eta++)
0090       {
0091         for (int delta_phi = -1; delta_phi <= 1; delta_phi++)
0092         {
0093           if (delta_layer == 0 && delta_eta == 0 && delta_phi == 0)
0094           {
0095             continue;  // this is the same tower
0096           }
0097 
0098           int test_eta = this_eta + delta_eta;
0099           if (test_eta < 0 || test_eta >= _HCAL_NETA)
0100           {
0101             continue;
0102           }  // ignore if at the (eta) edge of calorimeter
0103 
0104           int test_layer = (this_layer + delta_layer) % 2;                  // wrap around in layer
0105           int test_phi = (this_phi + delta_phi + _HCAL_NPHI) % _HCAL_NPHI;  // wrap around in phi (add 64 to avoid -1)
0106 
0107           // disallow "corner" adjacency (diagonal in eta/phi plane and in different layer) if this option not enabled
0108           if (!_allow_corner_neighbor && delta_layer == 1 && abs(delta_phi) == 1 && abs(delta_eta) == 1)
0109           {
0110             if (Verbosity() > 20)
0111             {
0112               std::cout << "RawClusterBuilderTopo::get_adjacent_towers_by_ID : corner growth not allowed " << std::endl;
0113             }
0114             continue;
0115           }
0116 
0117           // add to list of adjacent towers
0118           adjacent_towers.push_back(get_ID(test_layer, test_eta, test_phi));
0119         }
0120       }
0121     }
0122   }
0123 
0124   // for IHCal only, also add 4x4 group of EMCal towers
0125   if (this_layer == 0 && _enable_EMCal)
0126   {
0127     int EMCal_phi_start = get_first_matching_EMCal_phi_from_IHCal(this_phi);
0128     int EMCal_eta_start = RawClusterBuilderTopo_constants_EMCal_eta_start_given_IHCal[this_eta];
0129     int EMCal_eta_end = RawClusterBuilderTopo_constants_EMCal_eta_end_given_IHCal[this_eta];
0130 
0131     for (int new_eta = EMCal_eta_start; new_eta <= EMCal_eta_end; new_eta++)
0132     {
0133       for (int delta_phi = 0; delta_phi < 4; delta_phi++)
0134       {
0135         int new_phi = (EMCal_phi_start + delta_phi + _EMCAL_NPHI) % _EMCAL_NPHI;
0136 
0137         int EMCal_tower = get_ID(2, new_eta, new_phi);
0138         if (Verbosity() > 20)
0139         {
0140           std::cout << "RawClusterBuilderTopo::get_adjacent_towers_by_ID : HCal tower with eta / phi = " << this_eta << " / " << this_phi << ", adding EMCal tower with eta / phi = " << new_eta << " / " << new_phi << std::endl;
0141         }
0142         adjacent_towers.push_back(EMCal_tower);
0143       }
0144     }
0145   }
0146 
0147   // for EMCal, add adjacent EMCal towers and (usually) one IHCal tower
0148   if (this_layer == 2)
0149   {
0150     // EMCal towers first
0151     for (int delta_eta = -1; delta_eta <= 1; delta_eta++)
0152     {
0153       for (int delta_phi = -1; delta_phi <= 1; delta_phi++)
0154       {
0155         if (delta_eta == 0 && delta_phi == 0)
0156         {
0157           continue;  // this is the same tower
0158         }
0159 
0160         int test_eta = this_eta + delta_eta;
0161         if (test_eta < 0 || test_eta >= _EMCAL_NETA)
0162         {
0163           continue;
0164         }  // ignore if at the (eta) edge of calorimeter
0165 
0166         int test_phi = (this_phi + delta_phi + _EMCAL_NPHI) % _EMCAL_NPHI;  // wrap around in phi (add 256 to avoid -1)
0167 
0168         // add to list of adjacent towers
0169         adjacent_towers.push_back(get_ID(this_layer, test_eta, test_phi));
0170       }
0171     }
0172 
0173     // now add IHCal towers
0174     if (_enable_HCal)
0175     {
0176       int HCal_eta = RawClusterBuilderTopo_constants_IHCal_eta_given_EMCal[this_eta];
0177       int HCal_phi = get_matching_HCal_phi_from_EMCal(this_phi);
0178 
0179       if (HCal_eta >= 0)
0180       {
0181         int IHCal_tower = get_ID(0, HCal_eta, HCal_phi);
0182         if (Verbosity() > 20)
0183         {
0184           std::cout << "RawClusterBuilderTopo::get_adjacent_towers_by_ID : EMCal tower with eta / phi = " << this_eta << " / " << this_phi << ", adding IHCal tower with eta / phi = " << HCal_eta << " / " << HCal_phi << std::endl;
0185         }
0186         adjacent_towers.push_back(IHCal_tower);
0187       }
0188       else
0189       {
0190         if (Verbosity() > 20)
0191         {
0192           std::cout << "RawClusterBuilderTopo::get_adjacent_towers_by_ID : EMCal tower with eta / phi = " << this_eta << " / " << this_phi << ", does not have matching IHCal due to large eta " << std::endl;
0193         }
0194       }
0195     }
0196   }
0197 
0198   return adjacent_towers;
0199 }
0200 
0201 void RawClusterBuilderTopo::export_single_cluster(const std::vector<int> &original_towers)
0202 {
0203   if (Verbosity() > 2)
0204   {
0205     std::cout << "RawClusterBuilderTopo::export_single_cluster called " << std::endl;
0206   }
0207 
0208   std::map<int, std::pair<int, int> > tower_ownership;
0209   for (const int &original_tower : original_towers)
0210   {
0211     tower_ownership[original_tower] = std::pair<int, int>(0, -1);  // all towers owned by cluster 0
0212   }
0213   export_clusters(original_towers, tower_ownership, 1, std::vector<float>(), std::vector<float>(), std::vector<float>());
0214 
0215   return;
0216 }
0217 
0218 void RawClusterBuilderTopo::export_clusters(const std::vector<int> &original_towers, std::map<int, std::pair<int, int> > tower_ownership, unsigned int n_clusters, const std::vector<float> &pseudocluster_sumE, const std::vector<float> &pseudocluster_eta, const std::vector<float> &pseudocluster_phi)
0219 {
0220   if (n_clusters != 1)  // if we didn't just pass down from export_single_cluster
0221   {
0222     if (Verbosity() > 2)
0223     {
0224       std::cout << "RawClusterBuilderTopo::export_clusters called on an initial cluster with " << n_clusters << " final clusters " << std::endl;
0225     }
0226   }
0227   // build a RawCluster for output
0228   std::vector<RawCluster *> clusters;
0229   std::vector<float> clusters_E;
0230   std::vector<float> clusters_absE;
0231   std::vector<float> clusters_x;
0232   std::vector<float> clusters_y;
0233   std::vector<float> clusters_z;
0234 
0235   for (unsigned int pc = 0; pc < n_clusters; pc++)
0236   {
0237     clusters.push_back(new RawClusterv1());
0238     clusters_E.push_back(0);
0239     clusters_absE.push_back(0);
0240     clusters_x.push_back(0);
0241     clusters_y.push_back(0);
0242     clusters_z.push_back(0);
0243   }
0244 
0245   for (int original_tower : original_towers)
0246   {
0247     int this_ID = original_tower;
0248     std::pair<int, int> the_pair = tower_ownership[this_ID];
0249 
0250     if (Verbosity() > 5)
0251     {
0252       std::cout << "RawClusterBuilderTopo::export_clusters -> assigning tower " << original_tower << " with ownership ( " << the_pair.first << ", " << the_pair.second << " ) " << std::endl;
0253     }
0254     int this_ieta = get_ieta_from_ID(this_ID);
0255     int this_iphi = get_iphi_from_ID(this_ID);
0256     int this_layer = get_ilayer_from_ID(this_ID);
0257     float this_E = get_E_from_ID(this_ID);
0258 
0259     int this_key = 0;
0260     if (this_layer == 2)
0261     {
0262       this_key = _EMTOWERMAP_KEY_ETA_PHI[this_ieta][this_iphi];
0263     }
0264     else
0265     {
0266       this_key = _TOWERMAP_KEY_LAYER_ETA_PHI[this_layer][this_ieta][this_iphi];
0267     }
0268 
0269     RawTowerGeom *tower_geom = _geom_containers[this_layer]->get_tower_geometry(this_key);
0270 
0271     if (the_pair.second == -1)
0272     {
0273       // assigned only to one cluster, easy
0274       clusters[the_pair.first]->addTower(this_key, this_E);
0275       clusters_E[the_pair.first] = clusters_E[the_pair.first] + this_E;
0276       clusters_absE[the_pair.first] = clusters_absE[the_pair.first] + std::fabs(this_E);
0277       // calculate position mean using absolute energy as weights
0278       clusters_x[the_pair.first] = clusters_x[the_pair.first] + std::fabs(this_E) * tower_geom->get_center_x();
0279       clusters_y[the_pair.first] = clusters_y[the_pair.first] + std::fabs(this_E) * tower_geom->get_center_y();
0280       clusters_z[the_pair.first] = clusters_z[the_pair.first] + std::fabs(this_E) * tower_geom->get_center_z();
0281 
0282       if (Verbosity() > 5)
0283       {
0284         std::cout << " -> tower ID " << this_ID << " fully assigned to pseudocluster " << the_pair.first << std::endl;
0285       }
0286     }
0287     else
0288     {
0289       // assigned to two clusters! get energy sharing fraction ...
0290       float dR1 = calculate_dR(tower_geom->get_eta(), pseudocluster_eta[the_pair.first], tower_geom->get_phi(), pseudocluster_phi[the_pair.first]) / _R_shower;
0291       float dR2 = calculate_dR(tower_geom->get_eta(), pseudocluster_eta[the_pair.second], tower_geom->get_phi(), pseudocluster_phi[the_pair.second]) / _R_shower;
0292       float r = std::exp(dR1 - dR2);
0293       float frac1 = fabs(pseudocluster_sumE[the_pair.first]) / (fabs(pseudocluster_sumE[the_pair.first]) + r * fabs(pseudocluster_sumE[the_pair.second]));
0294 
0295       if (Verbosity() > 5)
0296       {
0297         std::cout << " tower ID " << this_ID << " has dR1 = " << dR1 << " to pseudocluster " << the_pair.first << " , and dR2 = " << dR2 << " to pseudocluster " << the_pair.second << ", so frac1 = " << frac1 << std::endl;
0298       }
0299       clusters[the_pair.first]->addTower(this_key, this_E * frac1);
0300       clusters_E[the_pair.first] = clusters_E[the_pair.first] + this_E * frac1;
0301       clusters_absE[the_pair.first] = clusters_absE[the_pair.first] + std::fabs(this_E) * frac1;
0302       clusters_x[the_pair.first] = clusters_x[the_pair.first] + std::fabs(this_E) * tower_geom->get_center_x() * frac1;
0303       clusters_y[the_pair.first] = clusters_y[the_pair.first] + std::fabs(this_E) * tower_geom->get_center_y() * frac1;
0304       clusters_z[the_pair.first] = clusters_z[the_pair.first] + std::fabs(this_E) * tower_geom->get_center_z() * frac1;
0305 
0306       clusters[the_pair.second]->addTower(this_key, this_E * (1 - frac1));
0307       clusters_E[the_pair.second] = clusters_E[the_pair.second] + this_E * (1 - frac1);
0308       clusters_absE[the_pair.second] = clusters_absE[the_pair.second] + std::fabs(this_E) * (1 - frac1);
0309       clusters_x[the_pair.second] = clusters_x[the_pair.second] + std::fabs(this_E) * tower_geom->get_center_x() * (1 - frac1);
0310       clusters_y[the_pair.second] = clusters_y[the_pair.second] + std::fabs(this_E) * tower_geom->get_center_y() * (1 - frac1);
0311       clusters_z[the_pair.second] = clusters_z[the_pair.second] + std::fabs(this_E) * tower_geom->get_center_z() * (1 - frac1);
0312     }
0313   }
0314 
0315   // iterate through and add to official container
0316 
0317   for (unsigned int cl = 0; cl < n_clusters; cl++)
0318   {
0319     if (clusters_absE[cl] < _min_cluster_E)
0320     {
0321       if (Verbosity() > 2)
0322       {
0323         std::cout << "RawClusterBuilderTopo::export_clusters: skipping cluster with E = " << clusters_E[cl] << " and absE = " << clusters_absE[cl] << " due to low energy " << std::endl;
0324       }
0325       continue;
0326     }
0327     clusters[cl]->set_energy(clusters_E[cl]);
0328 
0329     float mean_x = clusters_x[cl] / clusters_absE[cl];
0330     float mean_y = clusters_y[cl] / clusters_absE[cl];
0331     float mean_z = clusters_z[cl] / clusters_absE[cl];
0332 
0333     clusters[cl]->set_r(std::sqrt((mean_y * mean_y) + (mean_x * mean_x)));
0334     clusters[cl]->set_phi(std::atan2(mean_y, mean_x));
0335     clusters[cl]->set_z(mean_z);
0336 
0337     _clusters->AddCluster(clusters[cl]);
0338 
0339     if (Verbosity() > 1)
0340     {
0341       std::cout << "RawClusterBuilderTopo::export_clusters: added cluster with E = " << clusters_E[cl] << ", eta = " << -1 * log(tan(std::atan2(std::sqrt((mean_y * mean_y) + (mean_x * mean_x)), mean_z) / 2.0)) << ", phi = " << std::atan2(mean_y, mean_x) << std::endl;
0342     }
0343   }
0344 
0345   return;
0346 }
0347 
0348 RawClusterBuilderTopo::RawClusterBuilderTopo(const std::string &name)
0349   : SubsysReco(name)
0350 {
0351   // geometry defined at run-time
0352 
0353   std::fill(std::begin(_geom_containers), std::end(_geom_containers), nullptr);
0354   _noise_LAYER[0] = 0.0025;
0355   _noise_LAYER[1] = 0.006;
0356   _noise_LAYER[2] = 0.03;  // EM
0357 
0358   _local_max_minE_LAYER[0] = 1;
0359   _local_max_minE_LAYER[1] = 1;
0360   _local_max_minE_LAYER[2] = 1;
0361 }
0362 
0363 int RawClusterBuilderTopo::InitRun(PHCompositeNode *topNode)
0364 {
0365   try
0366   {
0367     CreateNodes(topNode);
0368   }
0369   catch (std::exception &e)
0370   {
0371     std::cout << PHWHERE << ": " << e.what() << std::endl;
0372     throw;
0373   }
0374 
0375   if (Verbosity() > 0)
0376   {
0377     std::cout << "RawClusterBuilderTopo::InitRun: initialized with EMCal enable = " << _enable_EMCal << " and I+OHCal enable = " << _enable_HCal << std::endl;
0378     std::cout << "RawClusterBuilderTopo::InitRun: initialized with sigma_noise in EMCal / IHCal / OHCal = " << _noise_LAYER[2] << " / " << _noise_LAYER[0] << " / " << _noise_LAYER[1] << std::endl;
0379     std::cout << "RawClusterBuilderTopo::InitRun: initialized with noise multiples for seeding / growth / perimeter ( S / N / P ) = " << _sigma_seed << " / " << _sigma_grow << " / " << _sigma_peri << std::endl;
0380     std::cout << "RawClusterBuilderTopo::InitRun: initialized with allow_corner_neighbor = " << _allow_corner_neighbor << " (in HCal)" << std::endl;
0381     std::cout << "RawClusterBuilderTopo::InitRun: initialized with use_absE = " << _use_absE << std::endl;
0382     std::cout << "RawClusterBuilderTopo::InitRun: initialized with do_split = " << _do_split << " , R_shower = " << _R_shower << " (angular units) " << std::endl;
0383     std::cout << "RawClusterBuilderTopo::InitRun: initialized with minE for local max in EMCal / IHCal / OHCal = " << _local_max_minE_LAYER[2] << " / " << _local_max_minE_LAYER[0] << " / " << _local_max_minE_LAYER[1] << std::endl;
0384   }
0385 
0386   return Fun4AllReturnCodes::EVENT_OK;
0387 }
0388 
0389 int RawClusterBuilderTopo::process_event(PHCompositeNode *topNode)
0390 {
0391   TowerInfoContainer *towerinfosEM = findNode::getClass<TowerInfoContainer>(topNode, "TOWERINFO_CALIB_CEMC");
0392   TowerInfoContainer *towerinfosIH = findNode::getClass<TowerInfoContainer>(topNode, "TOWERINFO_CALIB_HCALIN");
0393   TowerInfoContainer *towerinfosOH = findNode::getClass<TowerInfoContainer>(topNode, "TOWERINFO_CALIB_HCALOUT");
0394 
0395   if (!towerinfosEM)
0396   {
0397     std::cout << " RawClusterBuilderTopo::process_event : container TOWERINFO_CALIB_CEMC does not exist, aborting " << std::endl;
0398     return Fun4AllReturnCodes::ABORTEVENT;
0399   }
0400   if (!towerinfosIH)
0401   {
0402     std::cout << " RawClusterBuilderTopo::process_event : container TOWERINFO_CALIB_HCALIN does not exist, aborting " << std::endl;
0403     return Fun4AllReturnCodes::ABORTEVENT;
0404   }
0405   if (!towerinfosOH)
0406   {
0407     std::cout << " RawClusterBuilderTopo::process_event : container TOWERINFO_CALIB_HCALOUT does not exist, aborting " << std::endl;
0408     return Fun4AllReturnCodes::ABORTEVENT;
0409   }
0410 
0411   _geom_containers[0] = findNode::getClass<RawTowerGeomContainer>(topNode, "TOWERGEOM_HCALIN");
0412   _geom_containers[1] = findNode::getClass<RawTowerGeomContainer>(topNode, "TOWERGEOM_HCALOUT");
0413   _geom_containers[2] = findNode::getClass<RawTowerGeomContainer>(topNode, "TOWERGEOM_CEMC");
0414 
0415   if (!_geom_containers[0])
0416   {
0417     std::cout << " RawClusterBuilderTopo::process_event : container TOWERGEOM_HCALIN does not exist, aborting " << std::endl;
0418     return Fun4AllReturnCodes::ABORTEVENT;
0419   }
0420   if (!_geom_containers[1])
0421   {
0422     std::cout << " RawClusterBuilderTopo::process_event : container TOWERGEOM_HCALOUT does not exist, aborting " << std::endl;
0423     return Fun4AllReturnCodes::ABORTEVENT;
0424   }
0425   if (!_geom_containers[2])
0426   {
0427     std::cout << " RawClusterBuilderTopo::process_event : container TOWERGEOM_CEMC does not exist, aborting " << std::endl;
0428     return Fun4AllReturnCodes::ABORTEVENT;
0429   }
0430 
0431   if (Verbosity() > 10)
0432   {
0433     std::cout << "RawClusterBuilderTopo::process_event: " << towerinfosEM->size() << " TOWERINFO_CALIB_CEMC towers" << std::endl;
0434     std::cout << "RawClusterBuilderTopo::process_event: " << towerinfosIH->size() << " TOWERINFO_CALIB_HCALIN towers" << std::endl;
0435     std::cout << "RawClusterBuilderTopo::process_event: " << towerinfosOH->size() << " TOWERINFO_CALIB_HCALOUT towers" << std::endl;
0436 
0437     std::cout << "RawClusterBuilderTopo::process_event: pointer to TOWERGEOM_CEMC: " << _geom_containers[2] << std::endl;
0438     std::cout << "RawClusterBuilderTopo::process_event: pointer to TOWERGEOM_HCALIN: " << _geom_containers[0] << std::endl;
0439     std::cout << "RawClusterBuilderTopo::process_event: pointer to TOWERGEOM_HCALOUT: " << _geom_containers[1] << std::endl;
0440   }
0441 
0442   if (_EMCAL_NETA < 0)
0443   {
0444     // define geometry only once if it has not been yet
0445     _EMCAL_NETA = _geom_containers[2]->get_etabins();
0446     _EMCAL_NPHI = _geom_containers[2]->get_phibins();
0447 
0448     _EMTOWERMAP_STATUS_ETA_PHI.resize(_EMCAL_NETA, std::vector<int>(_EMCAL_NPHI, -2));
0449     _EMTOWERMAP_KEY_ETA_PHI.resize(_EMCAL_NETA, std::vector<int>(_EMCAL_NPHI, 0));
0450     _EMTOWERMAP_E_ETA_PHI.resize(_EMCAL_NETA, std::vector<float>(_EMCAL_NPHI, 0));
0451   }
0452 
0453   if (_HCAL_NETA < 0)
0454   {
0455     // define geometry only once if it has not been yet
0456     _HCAL_NETA = _geom_containers[1]->get_etabins();
0457     _HCAL_NPHI = _geom_containers[1]->get_phibins();
0458 
0459     _TOWERMAP_STATUS_LAYER_ETA_PHI.resize(2, std::vector<std::vector<int> >(_HCAL_NETA, std::vector<int>(_HCAL_NPHI, -2)));
0460     _TOWERMAP_KEY_LAYER_ETA_PHI.resize(2, std::vector<std::vector<int> >(_HCAL_NETA, std::vector<int>(_HCAL_NPHI, 0)));
0461     _TOWERMAP_E_LAYER_ETA_PHI.resize(2, std::vector<std::vector<float> >(_HCAL_NETA, std::vector<float>(_HCAL_NPHI, 0)));
0462   }
0463 
0464   // reset maps
0465   // but note -- do not reset keys!
0466   for (int ieta = 0; ieta < _EMCAL_NETA; ieta++)
0467   {
0468     for (int iphi = 0; iphi < _EMCAL_NPHI; iphi++)
0469     {
0470       _EMTOWERMAP_STATUS_ETA_PHI[ieta][iphi] = -2;  // set tower does not exist
0471       _EMTOWERMAP_E_ETA_PHI[ieta][iphi] = 0;        // set zero energy
0472     }
0473   }
0474   for (int ilayer = 0; ilayer < 2; ilayer++)
0475   {
0476     for (int ieta = 0; ieta < _HCAL_NETA; ieta++)
0477     {
0478       for (int iphi = 0; iphi < _HCAL_NPHI; iphi++)
0479       {
0480         _TOWERMAP_STATUS_LAYER_ETA_PHI[ilayer][ieta][iphi] = -2;  // set tower does not exist
0481         _TOWERMAP_E_LAYER_ETA_PHI[ilayer][ieta][iphi] = 0;        // set zero energy
0482       }
0483     }
0484   }
0485 
0486   // setup
0487   std::vector<std::pair<int, float> > list_of_seeds;
0488 
0489   // translate towers to our internal representation
0490   if (_enable_EMCal)
0491   {
0492     TowerInfo *towerInfo = nullptr;
0493     unsigned int n_EM_towers = towerinfosEM->size();
0494     for (unsigned int iEM = 0; iEM < n_EM_towers; iEM++)
0495     {
0496       towerInfo = towerinfosEM->get_tower_at_channel(iEM);
0497       if (_only_good_towers && (!towerInfo->get_isGood()))
0498       {
0499         continue;
0500       }
0501       unsigned int towerinfo_key = towerinfosEM->encode_key(iEM);
0502       int ti_ieta = towerinfosEM->getTowerEtaBin(towerinfo_key);
0503       int ti_iphi = towerinfosEM->getTowerPhiBin(towerinfo_key);
0504       const RawTowerDefs::keytype key = RawTowerDefs::encode_towerid(RawTowerDefs::CalorimeterId::CEMC, ti_ieta, ti_iphi);
0505 
0506       // RawTowerGeom *tower_geom = _geom_containers[2]->get_tower_geometry(key);
0507 
0508       // int ieta = _geom_containers[2]->get_etabin(tower_geom->get_eta());
0509       // int iphi = _geom_containers[2]->get_phibin(tower_geom->get_phi());
0510 
0511       int ieta = ti_ieta;
0512       int iphi = ti_iphi;
0513 
0514       float this_E = towerInfo->get_energy();
0515 
0516       // if not using abs E, short circuit all negative towers right here (same for IHCal, OHCal below)
0517       if (!_use_absE && this_E < 1.E-10)
0518       {
0519         continue;
0520       }
0521 
0522       _EMTOWERMAP_STATUS_ETA_PHI[ieta][iphi] = -1;  // change status to unknown
0523       _EMTOWERMAP_E_ETA_PHI[ieta][iphi] = this_E;
0524       _EMTOWERMAP_KEY_ETA_PHI[ieta][iphi] = key;
0525 
0526       // use fabs() here for simplicity - if we're not using abs E, negative towers are already excluded
0527       if (std::fabs(this_E) >= _sigma_seed * _noise_LAYER[2])
0528       {
0529         int ID = get_ID(2, ieta, iphi);
0530         list_of_seeds.emplace_back(ID, this_E);
0531         if (Verbosity() > 10)
0532         {
0533           std::cout << "RawClusterBuilderTopo::process_event: adding EMCal tower at ieta / iphi = " << ieta << " / " << iphi << " with E = " << this_E << std::endl;
0534           std::cout << " --> ID = " << ID << " , check ilayer / ieta / iphi = " << get_ilayer_from_ID(ID) << " / " << get_ieta_from_ID(ID) << " / " << get_iphi_from_ID(ID) << std::endl;
0535         };
0536       }
0537     }
0538   }
0539 
0540   // translate towers to our internal representation
0541   if (_enable_HCal)
0542   {
0543     TowerInfo *towerInfo = nullptr;
0544     unsigned int n_IH_towers = towerinfosIH->size();
0545     for (unsigned int iIH = 0; iIH < n_IH_towers; iIH++)
0546     {
0547       towerInfo = towerinfosIH->get_tower_at_channel(iIH);
0548       if (_only_good_towers && (!towerInfo->get_isGood()))
0549       {
0550         continue;
0551       }
0552       unsigned int towerinfo_key = towerinfosIH->encode_key(iIH);
0553       int ti_ieta = towerinfosIH->getTowerEtaBin(towerinfo_key);
0554       int ti_iphi = towerinfosIH->getTowerPhiBin(towerinfo_key);
0555       const RawTowerDefs::keytype key = RawTowerDefs::encode_towerid(RawTowerDefs::CalorimeterId::HCALIN, ti_ieta, ti_iphi);
0556 
0557       RawTowerGeom *tower_geom = _geom_containers[0]->get_tower_geometry(key);
0558 
0559       int ieta = _geom_containers[0]->get_etabin(tower_geom->get_eta());
0560       int iphi = _geom_containers[0]->get_phibin(tower_geom->get_phi());
0561       float this_E = towerInfo->get_energy();
0562 
0563       if (!_use_absE && this_E < 1.E-10)
0564       {
0565         continue;
0566       }
0567 
0568       _TOWERMAP_STATUS_LAYER_ETA_PHI[0][ieta][iphi] = -1;  // change status to unknown
0569       _TOWERMAP_E_LAYER_ETA_PHI[0][ieta][iphi] = this_E;
0570       _TOWERMAP_KEY_LAYER_ETA_PHI[0][ieta][iphi] = key;
0571 
0572       if (std::fabs(this_E) >= _sigma_seed * _noise_LAYER[0])
0573       {
0574         int ID = get_ID(0, ieta, iphi);
0575         list_of_seeds.emplace_back(ID, this_E);
0576         if (Verbosity() > 10)
0577         {
0578           std::cout << "RawClusterBuilderTopo::process_event: adding IHCal tower at ieta / iphi = " << ieta << " / " << iphi << " with E = " << this_E << std::endl;
0579           std::cout << " --> ID = " << ID << " , check ilayer / ieta / iphi = " << get_ilayer_from_ID(ID) << " / " << get_ieta_from_ID(ID) << " / " << get_iphi_from_ID(ID) << std::endl;
0580         };
0581       }
0582     }
0583     unsigned int n_OH_towers = towerinfosOH->size();
0584     for (unsigned int iOH = 0; iOH < n_OH_towers; iOH++)
0585     {
0586       towerInfo = towerinfosOH->get_tower_at_channel(iOH);
0587       if (_only_good_towers && (!towerInfo->get_isGood()))
0588       {
0589         continue;
0590       }
0591       unsigned int towerinfo_key = towerinfosOH->encode_key(iOH);
0592       int ti_ieta = towerinfosOH->getTowerEtaBin(towerinfo_key);
0593       int ti_iphi = towerinfosOH->getTowerPhiBin(towerinfo_key);
0594       const RawTowerDefs::keytype key = RawTowerDefs::encode_towerid(RawTowerDefs::CalorimeterId::HCALOUT, ti_ieta, ti_iphi);
0595 
0596       RawTowerGeom *tower_geom = _geom_containers[1]->get_tower_geometry(key);
0597 
0598       int ieta = _geom_containers[1]->get_etabin(tower_geom->get_eta());
0599       int iphi = _geom_containers[1]->get_phibin(tower_geom->get_phi());
0600       float this_E = towerInfo->get_energy();
0601 
0602       if (!_use_absE && this_E < 1.E-10)
0603       {
0604         continue;
0605       }
0606 
0607       _TOWERMAP_STATUS_LAYER_ETA_PHI[1][ieta][iphi] = -1;  // change status to unknown
0608       _TOWERMAP_E_LAYER_ETA_PHI[1][ieta][iphi] = this_E;
0609       _TOWERMAP_KEY_LAYER_ETA_PHI[1][ieta][iphi] = key;
0610 
0611       if (std::fabs(this_E) >= _sigma_seed * _noise_LAYER[1])
0612       {
0613         int ID = get_ID(1, ieta, iphi);
0614         list_of_seeds.emplace_back(ID, this_E);
0615         if (Verbosity() > 10)
0616         {
0617           std::cout << "RawClusterBuilderTopo::process_event: adding OHCal tower at ieta / iphi = " << ieta << " / " << iphi << " with E = " << this_E << std::endl;
0618           std::cout << " --> ID = " << ID << " , check ilayer / ieta / iphi = " << get_ilayer_from_ID(ID) << " / " << get_ieta_from_ID(ID) << " / " << get_iphi_from_ID(ID) << std::endl;
0619         };
0620       }
0621     }
0622   }
0623 
0624   if (Verbosity() > 10)
0625   {
0626     for (unsigned int n = 0; n < list_of_seeds.size(); n++)
0627     {
0628       std::cout << "RawClusterBuilderTopo::process_event: unsorted seed element n = " << n << " , ID / E = " << list_of_seeds.at(n).first << " / " << list_of_seeds.at(n).second << std::endl;
0629     }
0630   }
0631 
0632   std::sort(list_of_seeds.begin(), list_of_seeds.end(), sort_by_pair_second);
0633 
0634   if (Verbosity() > 10)
0635   {
0636     for (unsigned int n = 0; n < list_of_seeds.size(); n++)
0637     {
0638       std::cout << "RawClusterBuilderTopo::process_event: sorted seed element n = " << n << " , ID / E = " << list_of_seeds.at(n).first << " / " << list_of_seeds.at(n).second << std::endl;
0639     }
0640   }
0641 
0642   if (Verbosity() > 0)
0643   {
0644     std::cout << "RawClusterBuilderTopo::process_event: initialized with " << list_of_seeds.size() << " seeds with E > 4*sigma " << std::endl;
0645   }
0646 
0647   int cluster_index = 0;  // begin counting clusters
0648 
0649   std::vector<std::vector<int> > all_cluster_towers;  // store final cluster tower lists here
0650 
0651   while (!list_of_seeds.empty())
0652   {
0653     int seed_ID = list_of_seeds.at(0).first;
0654     list_of_seeds.erase(list_of_seeds.begin());
0655 
0656     if (Verbosity() > 5)
0657     {
0658       std::cout << " RawClusterBuilderTopo::process_event: in seeded loop, current seed has ID = " << seed_ID << " , length of remaining seed vector = " << list_of_seeds.size() << std::endl;
0659     }
0660 
0661     // if this seed was already claimed by some other seed during its growth, remove it and do nothing
0662     int seed_status = get_status_from_ID(seed_ID);
0663     if (seed_status > -1)
0664     {
0665       if (Verbosity() > 10)
0666       {
0667         std::cout << " --> already owned by cluster # " << seed_status << std::endl;
0668       }
0669       continue;  // go onto the next iteration of the loop
0670     }
0671 
0672     // this seed tower now owned by new cluster
0673     set_status_by_ID(seed_ID, cluster_index);
0674 
0675     std::vector<int> cluster_tower_ID;
0676     cluster_tower_ID.push_back(seed_ID);
0677 
0678     std::vector<int> grow_tower_ID;
0679     grow_tower_ID.push_back(seed_ID);
0680 
0681     // iteratively process growth towers, adding > 2 * sigma neighbors to the list for further checking
0682 
0683     if (Verbosity() > 5)
0684     {
0685       std::cout << " RawClusterBuilderTopo::process_event: Entering Growth stage for cluster " << cluster_index << std::endl;
0686     }
0687 
0688     while (!grow_tower_ID.empty())
0689     {
0690       int grow_ID = grow_tower_ID.at(0);
0691       grow_tower_ID.erase(grow_tower_ID.begin());
0692 
0693       if (Verbosity() > 5)
0694       {
0695         std::cout << " --> cluster " << cluster_index << ", growth stage, examining neighbors of ID " << grow_ID << ", " << grow_tower_ID.size() << " grow towers left" << std::endl;
0696       }
0697 
0698       std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(grow_ID);
0699 
0700       for (int this_adjacent_tower_ID : adjacent_tower_IDs)
0701       {
0702         if (Verbosity() > 10)
0703         {
0704           std::cout << " --> --> --> checking possible adjacent tower with ID " << this_adjacent_tower_ID << " : ";
0705         }
0706         int test_layer = get_ilayer_from_ID(this_adjacent_tower_ID);
0707 
0708         // if tower does not exist, continue
0709         if (get_status_from_ID(this_adjacent_tower_ID) == -2)
0710         {
0711           if (Verbosity() > 10)
0712           {
0713             std::cout << "does not exist " << std::endl;
0714           }
0715           continue;
0716         }
0717 
0718         // if tower is owned by THIS cluster already, continue
0719         if (get_status_from_ID(this_adjacent_tower_ID) == cluster_index)
0720         {
0721           if (Verbosity() > 10)
0722           {
0723             std::cout << "already owned by this cluster index " << cluster_index << std::endl;
0724           }
0725           continue;
0726         }
0727 
0728         // if tower has < 2*sigma energy, continue
0729         if (std::fabs(get_E_from_ID(this_adjacent_tower_ID)) < _sigma_grow * _noise_LAYER[test_layer])
0730         {
0731           if (Verbosity() > 10)
0732           {
0733             std::cout << "E = " << get_E_from_ID(this_adjacent_tower_ID) << " under 2*sigma threshold " << std::endl;
0734           }
0735           continue;
0736         }
0737 
0738         // if tower is owned by somebody else, continue (although should this really happen?)
0739         if (get_status_from_ID(this_adjacent_tower_ID) > -1)
0740         {
0741           if (Verbosity() > 10)
0742           {
0743             std::cout << "ERROR! in growth stage, encountered >2sigma tower which is already owned?!" << std::endl;
0744           }
0745           continue;
0746         }
0747 
0748         // tower good to be added to cluster and to list of grow towers
0749         grow_tower_ID.push_back(this_adjacent_tower_ID);
0750         cluster_tower_ID.push_back(this_adjacent_tower_ID);
0751         set_status_by_ID(this_adjacent_tower_ID, cluster_index);
0752         if (Verbosity() > 10)
0753         {
0754           std::cout << "add this tower ( ID " << this_adjacent_tower_ID << " ) to grow list " << std::endl;
0755         }
0756       }
0757 
0758       if (Verbosity() > 5)
0759       {
0760         std::cout << " --> after examining neighbors, grow list is now " << grow_tower_ID.size() << ", # of towers in cluster = " << cluster_tower_ID.size() << std::endl;
0761       }
0762     }
0763 
0764     // done growing cluster, now add on perimeter towers with E > 0 * sigma
0765     if (Verbosity() > 5)
0766     {
0767       std::cout << " RawClusterBuilderTopo::process_event: Entering Perimeter stage for cluster " << cluster_index << std::endl;
0768     }
0769     // we'll be adding on to the cluster list, so get the # of core towers first
0770     int n_core_towers = cluster_tower_ID.size();
0771 
0772     for (int ic = 0; ic < n_core_towers; ic++)
0773     {
0774       int core_ID = cluster_tower_ID.at(ic);
0775 
0776       if (Verbosity() > 5)
0777       {
0778         std::cout << " --> cluster " << cluster_index << ", perimeter stage, examining neighbors of ID " << core_ID << ", core cluster # " << ic << " of " << n_core_towers << " total " << std::endl;
0779       }
0780       std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(core_ID);
0781 
0782       for (int this_adjacent_tower_ID : adjacent_tower_IDs)
0783       {
0784         if (Verbosity() > 10)
0785         {
0786           std::cout << " --> --> --> checking possible adjacent tower with ID " << this_adjacent_tower_ID << " : ";
0787         }
0788 
0789         int test_layer = get_ilayer_from_ID(this_adjacent_tower_ID);
0790 
0791         // if tower does not exist, continue
0792         if (get_status_from_ID(this_adjacent_tower_ID) == -2)
0793         {
0794           if (Verbosity() > 10)
0795           {
0796             std::cout << "does not exist " << std::endl;
0797           }
0798           continue;
0799         }
0800 
0801         // if tower is owned by somebody else (including current cluster), continue. ( allowed during perimeter fixing state )
0802         if (get_status_from_ID(this_adjacent_tower_ID) > -1)
0803         {
0804           if (Verbosity() > 10)
0805           {
0806             std::cout << "already owned by other cluster index " << get_status_from_ID(this_adjacent_tower_ID) << std::endl;
0807           }
0808           continue;
0809         }
0810 
0811         // if tower has < 0*sigma energy, continue
0812         if (std::fabs(get_E_from_ID(this_adjacent_tower_ID)) < _sigma_peri * _noise_LAYER[test_layer])
0813         {
0814           if (Verbosity() > 10)
0815           {
0816             std::cout << "E = " << get_E_from_ID(this_adjacent_tower_ID) << " under 0*sigma threshold " << std::endl;
0817           }
0818           continue;
0819         }
0820 
0821         // perimeter tower good to be added to cluster
0822         cluster_tower_ID.push_back(this_adjacent_tower_ID);
0823         set_status_by_ID(this_adjacent_tower_ID, cluster_index);
0824         if (Verbosity() > 10)
0825         {
0826           std::cout << "add this tower ( ID " << this_adjacent_tower_ID << " ) to cluster " << std::endl;
0827         }
0828       }
0829 
0830       if (Verbosity() > 5)
0831       {
0832         std::cout << " --> after examining perimeter neighbors, # of towers in cluster is now = " << cluster_tower_ID.size() << std::endl;
0833       }
0834     }
0835 
0836     // keep track of these
0837     all_cluster_towers.push_back(cluster_tower_ID);
0838 
0839     // increment cluster index for next one
0840     cluster_index++;
0841   }
0842 
0843   if (Verbosity() > 0)
0844   {
0845     std::cout << "RawClusterBuilderTopo::process_event: " << cluster_index << " topo-clusters initially reconstructed, entering splitting step" << std::endl;
0846   }
0847 
0848   int original_cluster_index = cluster_index;  // since it may be updated
0849 
0850   // now entering cluster splitting stage
0851 
0852   for (int cl = 0; cl < original_cluster_index; cl++)
0853   {
0854     std::vector<int> original_towers = all_cluster_towers.at(cl);
0855 
0856     if (!_do_split)
0857     {
0858       // don't run splitting, just export entire cluster as it is
0859       if (Verbosity() > 2)
0860       {
0861         std::cout << "RawClusterBuilderTopo::process_event: splitting step disabled, cluster " << cluster_index << " is final" << std::endl;
0862       }
0863       export_single_cluster(original_towers);
0864       continue;
0865     }
0866 
0867     std::vector<std::pair<int, float> > local_maxima_ID;
0868 
0869     // iterate through each tower, looking for maxima
0870     for (int tower_ID : original_towers)
0871     {
0872       if (Verbosity() > 10)
0873       {
0874         std::cout << " -> examining tower ID " << tower_ID << " for possible local maximum " << std::endl;
0875       }
0876 
0877       // check minimum energy
0878       if (get_E_from_ID(tower_ID) < _local_max_minE_LAYER[get_ilayer_from_ID(tower_ID)])
0879       {
0880         if (Verbosity() > 10)
0881         {
0882           std::cout << " -> -> energy E = " << get_E_from_ID(tower_ID) << " < " << _local_max_minE_LAYER[get_ilayer_from_ID(tower_ID)] << " too low" << std::endl;
0883         }
0884         continue;
0885       }
0886 
0887       // examine neighbors
0888       std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(tower_ID);
0889       int neighbors_in_cluster = 0;
0890 
0891       // check for higher neighbor
0892       bool has_higher_neighbor = false;
0893       for (int this_adjacent_tower_ID : adjacent_tower_IDs)
0894       {
0895         if (get_status_from_ID(this_adjacent_tower_ID) != cl)
0896         {
0897           continue;  // only consider neighbors in cluster, obviously
0898         }
0899 
0900         neighbors_in_cluster++;
0901 
0902         if (get_E_from_ID(this_adjacent_tower_ID) > get_E_from_ID(tower_ID))
0903         {
0904           if (Verbosity() > 10)
0905           {
0906             std::cout << " -> -> has higher-energy neighbor ID / E = " << this_adjacent_tower_ID << " / " << get_E_from_ID(this_adjacent_tower_ID) << std::endl;
0907           }
0908           has_higher_neighbor = true;  // at this point we can break -- we won't need to count the number of good neighbors, since we won't even pass the E_neighbor test
0909           break;
0910         }
0911       }
0912 
0913       if (has_higher_neighbor)
0914       {
0915         continue;  // if we broke out, now continue
0916       }
0917 
0918       // check number of neighbors
0919       if (neighbors_in_cluster < 4)
0920       {
0921         if (Verbosity() > 10)
0922         {
0923           std::cout << " -> -> too few neighbors N = " << neighbors_in_cluster << std::endl;
0924         }
0925         continue;
0926       }
0927 
0928       local_maxima_ID.emplace_back(tower_ID, get_E_from_ID(tower_ID));
0929     }
0930 
0931     // check for possible EMCal-OHCal seed overlaps
0932     for (unsigned int n = 0; n < local_maxima_ID.size(); n++)
0933     {
0934       // only look at I/OHCal local maxima
0935       std::pair<int, float> this_LM = local_maxima_ID.at(n);
0936       if (get_ilayer_from_ID(this_LM.first) == 2)
0937       {
0938         continue;
0939       }
0940 
0941       float this_phi = _geom_containers[get_ilayer_from_ID(this_LM.first)]->get_phicenter(get_iphi_from_ID(this_LM.first));
0942       if (this_phi > M_PI)
0943       {
0944         this_phi -= 2 * M_PI;
0945       }
0946       float this_eta = _geom_containers[get_ilayer_from_ID(this_LM.first)]->get_etacenter(get_ieta_from_ID(this_LM.first));
0947 
0948       bool has_EM_overlap = false;
0949 
0950       // check all other local maxima for overlaps
0951       for (unsigned int n2 = 0; n2 < local_maxima_ID.size(); n2++)
0952       {
0953         if (n == n2)
0954         {
0955           continue;  // don't check the same one
0956         }
0957 
0958         // only look at EMCal local mazima
0959         std::pair<int, float> this_LM2 = local_maxima_ID.at(n2);
0960         if (get_ilayer_from_ID(this_LM2.first) != 2)
0961         {
0962           continue;
0963         }
0964 
0965         float this_phi2 = _geom_containers[get_ilayer_from_ID(this_LM2.first)]->get_phicenter(get_iphi_from_ID(this_LM2.first));
0966         if (this_phi2 > M_PI)
0967         {
0968           this_phi -= 2 * M_PI;
0969         }
0970         float this_eta2 = _geom_containers[get_ilayer_from_ID(this_LM2.first)]->get_etacenter(get_ieta_from_ID(this_LM2.first));
0971 
0972         // calculate geometric dR
0973         float dR = calculate_dR(this_eta, this_eta2, this_phi, this_phi2);
0974 
0975         // check for and report overlaps
0976         if (dR < 0.15)
0977         {
0978           has_EM_overlap = true;
0979           if (Verbosity() > 2)
0980           {
0981             std::cout << "RawClusterBuilderTopo::process_event : removing I/OHal local maximum (ID,E,phi,eta = " << this_LM.first << ", " << this_LM.second << ", " << this_phi << ", " << this_eta << "), ";
0982             std::cout << "due to EM overlap (ID,E,phi,eta = " << this_LM2.first << ", " << this_LM2.second << ", " << this_phi2 << ", " << this_eta2 << "), dR = " << dR << std::endl;
0983           }
0984           break;
0985         }
0986       }
0987 
0988       if (has_EM_overlap)
0989       {
0990         // remove the I/OHCal local maximum from the list
0991         local_maxima_ID.erase(local_maxima_ID.begin() + n);
0992         // make sure to back up one index...
0993         n = n - 1;
0994       }  // otherwise, keep this local maximum
0995     }
0996 
0997     // only now print out full set of local maxima
0998     if (Verbosity() > 2)
0999     {
1000       for (auto this_LM : local_maxima_ID)
1001       {
1002         int tower_ID = this_LM.first;
1003         std::cout << "RawClusterBuilderTopo::process_event in cluster " << cl << ", tower ID " << tower_ID << " is LOCAL MAXIMUM with layer / E = " << get_ilayer_from_ID(tower_ID) << " / " << get_E_from_ID(tower_ID) << ", ";
1004         float this_phi = _geom_containers[get_ilayer_from_ID(tower_ID)]->get_phicenter(get_iphi_from_ID(tower_ID));
1005         if (this_phi > M_PI)
1006         {
1007           this_phi -= 2 * M_PI;
1008         }
1009         std::cout << " eta / phi = " << _geom_containers[get_ilayer_from_ID(tower_ID)]->get_etacenter(get_ieta_from_ID(tower_ID)) << " / " << this_phi << std::endl;
1010       }
1011     }
1012 
1013     // do we have only 1 or 0 local maxima?
1014     if (local_maxima_ID.size() <= 1)
1015     {
1016       if (Verbosity() > 2)
1017       {
1018         std::cout << "RawClusterBuilderTopo::process_event cluster " << cl << " has only " << local_maxima_ID.size() << " local maxima, not splitting " << std::endl;
1019       }
1020       export_single_cluster(original_towers);
1021 
1022       continue;
1023     }
1024 
1025     // engage splitting procedure!
1026 
1027     if (Verbosity() > 2)
1028     {
1029       std::cout << "RawClusterBuilderTopo::process_event splitting cluster " << cl << " into " << local_maxima_ID.size() << " according to local maxima!" << std::endl;
1030     }
1031     // translate all cluster towers to a map which keeps track of their ownership
1032     // -1 means unseen
1033     // -2 means seen and in the seed list now (e.g. don't add it to the seed list again)
1034     // -3 shared tower, ignore going forward...
1035     std::map<int, std::pair<int, int> > tower_ownership;
1036     for (int &original_tower : original_towers)
1037     {
1038       tower_ownership[original_tower] = std::pair<int, int>(-1, -1);  // initialize all towers as un-seen
1039     }
1040     std::vector<int> seed_list;
1041     std::vector<int> neighbor_list;
1042     std::vector<int> shared_list;
1043 
1044     // sort maxima before populating seed list
1045     std::sort(local_maxima_ID.begin(), local_maxima_ID.end(), sort_by_pair_second);
1046 
1047     // initialize neighbor list
1048     for (unsigned int s = 0; s < local_maxima_ID.size(); s++)
1049     {
1050       tower_ownership[local_maxima_ID.at(s).first] = std::pair<int, int>(s, -1);
1051       neighbor_list.push_back(local_maxima_ID.at(s).first);
1052     }
1053 
1054     if (Verbosity() > 100)
1055     {
1056       for (int &original_tower : original_towers)
1057       {
1058         std::pair<int, int> the_pair = tower_ownership[original_tower];
1059         std::cout << " Debug Pre-Split: tower_ownership[ " << original_tower << " ] = ( " << the_pair.first << ", " << the_pair.second << " ) ";
1060         std::cout << " , layer / ieta / iphi = " << get_ilayer_from_ID(original_tower) << " / " << get_ieta_from_ID(original_tower) << " / " << get_iphi_from_ID(original_tower);
1061         std::cout << std::endl;
1062       }
1063     }
1064 
1065     bool first_pass = true;
1066 
1067     do
1068     {
1069       if (Verbosity() > 5)
1070       {
1071         std::cout << " -> starting split loop with " << seed_list.size() << " seed, " << neighbor_list.size() << " neighbor, and " << shared_list.size() << " shared towers " << std::endl;
1072       }
1073       // go through neighbor list, assigning ownership only via the seed list
1074       std::vector<int> new_ownerships;
1075 
1076       for (unsigned int n = 0; n < neighbor_list.size(); n++)
1077       {
1078         int neighbor_ID = neighbor_list.at(n);
1079 
1080         if (Verbosity() > 10)
1081         {
1082           std::cout << " -> -> looking at neighbor " << n << " (tower ID " << neighbor_ID << " ) of " << neighbor_list.size() << " total" << std::endl;
1083         }
1084         if (first_pass)
1085         {
1086           if (Verbosity() > 10)
1087           {
1088             std::cout << " -> -> -> special first pass rules, this tower already owned by pseudocluster " << tower_ownership[neighbor_ID].first << std::endl;
1089           }
1090           new_ownerships.push_back(tower_ownership[neighbor_ID].first);
1091         }
1092         else
1093         {
1094           std::map<int, bool> pseudocluster_adjacency;
1095           for (unsigned int s = 0; s < local_maxima_ID.size(); s++)
1096           {
1097             pseudocluster_adjacency[s] = false;
1098           }
1099           // look over all towers THIS one is adjacent to, and count up...
1100           std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(neighbor_ID);
1101 
1102           for (int this_adjacent_tower_ID : adjacent_tower_IDs)
1103           {
1104             if (get_status_from_ID(this_adjacent_tower_ID) != cl)
1105             {
1106               continue;
1107             }
1108 
1109             if (tower_ownership[this_adjacent_tower_ID].first > -1)
1110             {
1111               if (Verbosity() > 20)
1112               {
1113                 std::cout << " -> -> -> adjacent tower to this one, with ID " << this_adjacent_tower_ID << " , is owned by pseudocluster " << tower_ownership[this_adjacent_tower_ID].first << std::endl;
1114               }
1115               pseudocluster_adjacency[tower_ownership[this_adjacent_tower_ID].first] = true;
1116             }
1117           }
1118           int n_pseudocluster_adjacent = 0;
1119           int last_adjacent_pseudocluster = -1;
1120           for (unsigned int s = 0; s < local_maxima_ID.size(); s++)
1121           {
1122             if (pseudocluster_adjacency[s])
1123             {
1124               last_adjacent_pseudocluster = s;
1125               n_pseudocluster_adjacent++;
1126               if (Verbosity() > 20)
1127               {
1128                 std::cout << " -> -> adjacent to pseudocluster " << s << std::endl;
1129               }
1130             }
1131           }
1132 
1133           if (n_pseudocluster_adjacent == 0)
1134           {
1135             std::cout << " -> -> ERROR! How can a neighbor tower at this stage be adjacent to no pseudoclusters?? " << std::endl;
1136             new_ownerships.push_back(9999);
1137           }
1138           else if (n_pseudocluster_adjacent == 1)
1139           {
1140             if (Verbosity() > 10)
1141             {
1142               std::cout << " -> -> neighbor tower " << neighbor_ID << " is ONLY adjacent to one pseudocluster # " << last_adjacent_pseudocluster << std::endl;
1143             }
1144             new_ownerships.push_back(last_adjacent_pseudocluster);
1145           }
1146           else
1147           {
1148             if (Verbosity() > 10)
1149             {
1150               std::cout << " -> -> neighbor tower " << neighbor_ID << " is adjacent to " << n_pseudocluster_adjacent << " pseudoclusters, move to shared list " << std::endl;
1151             }
1152             new_ownerships.push_back(-3);
1153           }
1154         }
1155       }
1156 
1157       if (Verbosity() > 5)
1158       {
1159         std::cout << " -> now updating status of all " << neighbor_list.size() << " original neighbors " << std::endl;
1160       }
1161       // transfer neighbor list to seed list or shared list
1162       for (unsigned int n = 0; n < neighbor_list.size(); n++)
1163       {
1164         int neighbor_ID = neighbor_list.at(n);
1165         if (new_ownerships.at(n) > -1)
1166         {
1167           tower_ownership[neighbor_ID] = std::pair<int, int>(new_ownerships.at(n), -1);
1168           seed_list.push_back(neighbor_ID);
1169           if (Verbosity() > 20)
1170           {
1171             std::cout << " -> -> neighbor ID " << neighbor_ID << " has new status " << new_ownerships.at(n) << std::endl;
1172           }
1173         }
1174         if (new_ownerships.at(n) == -3)
1175         {
1176           tower_ownership[neighbor_ID] = std::pair<int, int>(-3, -1);
1177           shared_list.push_back(neighbor_ID);
1178           if (Verbosity() > 20)
1179           {
1180             std::cout << " -> -> neighbor ID " << neighbor_ID << " has new status " << -3 << std::endl;
1181           }
1182         }
1183       }
1184 
1185       if (Verbosity() > 5)
1186       {
1187         std::cout << " producing a new neighbor list ... " << std::endl;
1188       }
1189       // populate a new neighbor list from the about-to-be-owned towers before transferring this one
1190       std::list<int> new_neighbor_list;
1191       for (unsigned int n = 0; n < neighbor_list.size(); n++)
1192       {
1193         int neighbor_ID = neighbor_list.at(n);
1194         if (new_ownerships.at(n) > -1)
1195         {
1196           std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(neighbor_ID);
1197 
1198           for (int this_adjacent_tower_ID : adjacent_tower_IDs)
1199           {
1200             if (get_status_from_ID(this_adjacent_tower_ID) != cl)
1201             {
1202               continue;
1203             }
1204             if (tower_ownership[this_adjacent_tower_ID].first == -1)
1205             {
1206               new_neighbor_list.push_back(this_adjacent_tower_ID);
1207               if (Verbosity() > 5)
1208               {
1209                 std::cout << " -> queueing up to add tower " << this_adjacent_tower_ID << " , neighbor of tower " << neighbor_ID << " to new neighbor list" << std::endl;
1210               }
1211             }
1212           }
1213         }
1214       }
1215 
1216       if (Verbosity() > 5)
1217       {
1218         std::cout << " new neighbor list has size " << new_neighbor_list.size() << ", but after removing duplicate elements: ";
1219       }
1220 
1221       new_neighbor_list.sort();
1222       new_neighbor_list.unique();
1223 
1224       if (Verbosity() > 5)
1225       {
1226         std::cout << new_neighbor_list.size() << std::endl;
1227       }
1228 
1229       // clear neighbor list!
1230       neighbor_list.clear();
1231 
1232       // now transfer over new neighbor list
1233       for (; !new_neighbor_list.empty();)
1234       {
1235         neighbor_list.push_back(new_neighbor_list.front());
1236         new_neighbor_list.pop_front();
1237       }
1238 
1239       first_pass = false;
1240 
1241     } while (!neighbor_list.empty());
1242 
1243     if (Verbosity() > 100)
1244     {
1245       for (int &original_tower : original_towers)
1246       {
1247         std::pair<int, int> the_pair = tower_ownership[original_tower];
1248         std::cout << " Debug Mid-Split: tower_ownership[ " << original_tower << " ] = ( " << the_pair.first << ", " << the_pair.second << " ) ";
1249         std::cout << " , layer / ieta / iphi = " << get_ilayer_from_ID(original_tower) << " / " << get_ieta_from_ID(original_tower) << " / " << get_iphi_from_ID(original_tower);
1250         std::cout << std::endl;
1251         if (the_pair.first == -1)
1252         {
1253           std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(original_tower);
1254 
1255           for (int this_adjacent_tower_ID : adjacent_tower_IDs)
1256           {
1257             if (get_status_from_ID(this_adjacent_tower_ID) != cl)
1258             {
1259               continue;
1260             }
1261             std::cout << "    -> adjacent to add tower " << this_adjacent_tower_ID << " , which has status " << tower_ownership[this_adjacent_tower_ID].first << std::endl;
1262           }
1263         }
1264       }
1265     }
1266 
1267     // calculate pseudocluster energies and positions
1268     std::vector<float> pseudocluster_sumeta;
1269     std::vector<float> pseudocluster_sumphi;
1270     std::vector<float> pseudocluster_sumE;
1271     std::vector<int> pseudocluster_ntower;
1272     std::vector<float> pseudocluster_eta;
1273     std::vector<float> pseudocluster_phi;
1274 
1275     pseudocluster_sumeta.resize(local_maxima_ID.size(), 0);
1276     pseudocluster_sumphi.resize(local_maxima_ID.size(), 0);
1277     pseudocluster_sumE.resize(local_maxima_ID.size(), 0);
1278     pseudocluster_ntower.resize(local_maxima_ID.size(), 0);
1279 
1280     for (int &original_tower : original_towers)
1281     {
1282       std::pair<int, int> the_pair = tower_ownership[original_tower];
1283       if (the_pair.first > -1)
1284       {
1285         float this_ID = original_tower;
1286         pseudocluster_sumE[the_pair.first] += get_E_from_ID(this_ID);
1287         float this_eta = _geom_containers[get_ilayer_from_ID(this_ID)]->get_etacenter(get_ieta_from_ID(this_ID));
1288         float this_phi = _geom_containers[get_ilayer_from_ID(this_ID)]->get_phicenter(get_iphi_from_ID(this_ID));
1289 
1290         pseudocluster_sumeta[the_pair.first] += this_eta;
1291         pseudocluster_sumphi[the_pair.first] += this_phi;
1292         pseudocluster_ntower[the_pair.first] += 1;
1293       }
1294     }
1295 
1296     for (unsigned int pc = 0; pc < local_maxima_ID.size(); pc++)
1297     {
1298       pseudocluster_eta.push_back(pseudocluster_sumeta.at(pc) / pseudocluster_ntower.at(pc));
1299       pseudocluster_phi.push_back(pseudocluster_sumphi.at(pc) / pseudocluster_ntower.at(pc));
1300 
1301       if (Verbosity() > 2)
1302       {
1303         std::cout << "RawClusterBuilderTopo::process_event pseudocluster #" << pc << ", E / eta / phi / Ntower = " << pseudocluster_sumE.at(pc) << " / " << pseudocluster_eta.at(pc) << " / " << pseudocluster_phi.at(pc) << " / " << pseudocluster_ntower.at(pc) << std::endl;
1304       }
1305     }
1306 
1307     if (Verbosity() > 2)
1308     {
1309       std::cout << "RawClusterBuilderTopo::process_event now splitting up shared clusters (including unassigned clusters), initial shared list has size " << shared_list.size() << std::endl;
1310     }
1311     // iterate through shared cells, identifying which two they belong to
1312     while (!shared_list.empty())
1313     {
1314       // pick the first cell and pop off list
1315       int shared_ID = shared_list.at(0);
1316       shared_list.erase(shared_list.begin());
1317 
1318       if (Verbosity() > 5)
1319       {
1320         std::cout << " -> looking at shared tower " << shared_ID << ", after this one there are " << shared_list.size() << " shared towers left " << std::endl;
1321       }
1322       // look through adjacent pseudoclusters, taking two with highest energies
1323       std::vector<bool> pseudocluster_adjacency;
1324       pseudocluster_adjacency.resize(local_maxima_ID.size(), false);
1325 
1326       std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(shared_ID);
1327 
1328       for (int this_adjacent_tower_ID : adjacent_tower_IDs)
1329       {
1330         if (get_status_from_ID(this_adjacent_tower_ID) != cl)
1331         {
1332           continue;
1333         }
1334         if (tower_ownership[this_adjacent_tower_ID].first > -1)
1335         {
1336           pseudocluster_adjacency[tower_ownership[this_adjacent_tower_ID].first] = true;
1337         }
1338         if (tower_ownership[this_adjacent_tower_ID].second > -1)
1339         {  // can inherit adjacency from shared cluster
1340           pseudocluster_adjacency[tower_ownership[this_adjacent_tower_ID].second] = true;
1341         }
1342         // at the same time, add unowned towers to the list for later examination
1343         if (tower_ownership[this_adjacent_tower_ID].first == -1)
1344         {
1345           shared_list.push_back(this_adjacent_tower_ID);
1346           tower_ownership[this_adjacent_tower_ID] = std::pair<int, int>(-3, -1);
1347           if (Verbosity() > 10)
1348           {
1349             std::cout << " -> while looking at neighbors, have added un-examined tower " << this_adjacent_tower_ID << " to shared list " << std::endl;
1350           }
1351         }
1352       }
1353 
1354       // now figure out which pseudoclusters this shared tower is adjacent to...
1355       int highest_pseudocluster_index = -1;
1356       int second_highest_pseudocluster_index = -1;
1357 
1358       float highest_pseudocluster_E = -999;
1359       float second_highest_pseudocluster_E = -999;
1360 
1361       for (unsigned int n = 0; n < pseudocluster_adjacency.size(); n++)
1362       {
1363         if (!pseudocluster_adjacency[n])
1364         {
1365           continue;
1366         }
1367 
1368         if (pseudocluster_sumE[n] > highest_pseudocluster_E)
1369         {
1370           second_highest_pseudocluster_E = highest_pseudocluster_E;
1371           second_highest_pseudocluster_index = highest_pseudocluster_index;
1372 
1373           highest_pseudocluster_E = pseudocluster_sumE[n];
1374           highest_pseudocluster_index = n;
1375         }
1376         else if (pseudocluster_sumE[n] > second_highest_pseudocluster_E)
1377         {
1378           second_highest_pseudocluster_E = pseudocluster_sumE[n];
1379           second_highest_pseudocluster_index = n;
1380         }
1381       }
1382 
1383       if (Verbosity() > 5)
1384       {
1385         std::cout << " -> highest pseudoclusters its adjacent to are " << highest_pseudocluster_index << " ( E = " << highest_pseudocluster_E << " ) and " << second_highest_pseudocluster_index << " ( E = " << second_highest_pseudocluster_E << " ) " << std::endl;
1386       }
1387       // assign these clusters as owners
1388       tower_ownership[shared_ID] = std::pair<int, int>(highest_pseudocluster_index, second_highest_pseudocluster_index);
1389     }
1390 
1391     if (Verbosity() > 100)
1392     {
1393       for (int &original_tower : original_towers)
1394       {
1395         std::pair<int, int> the_pair = tower_ownership[original_tower];
1396         std::cout << " Debug Post-Split: tower_ownership[ " << original_tower << " ] = ( " << the_pair.first << ", " << the_pair.second << " ) ";
1397         std::cout << " , layer / ieta / iphi = " << get_ilayer_from_ID(original_tower) << " / " << get_ieta_from_ID(original_tower) << " / " << get_iphi_from_ID(original_tower);
1398         std::cout << std::endl;
1399         if (the_pair.first == -1)
1400         {
1401           std::vector<int> adjacent_tower_IDs = get_adjacent_towers_by_ID(original_tower);
1402 
1403           for (int this_adjacent_tower_ID : adjacent_tower_IDs)
1404           {
1405             if (get_status_from_ID(this_adjacent_tower_ID) != cl)
1406             {
1407               continue;
1408             }
1409             std::cout << " -> adjacent to add tower " << this_adjacent_tower_ID << " , which has status " << tower_ownership[this_adjacent_tower_ID].first << std::endl;
1410           }
1411         }
1412       }
1413     }
1414 
1415     // call helper function
1416     export_clusters(original_towers, tower_ownership, local_maxima_ID.size(), pseudocluster_sumE, pseudocluster_eta, pseudocluster_phi);
1417   }
1418 
1419   if (Verbosity() > 1)
1420   {
1421     std::cout << "RawClusterBuilderTopo::process_event after splitting (if any) final clusters output to node are: " << std::endl;
1422     RawClusterContainer::ConstRange begin_end = _clusters->getClusters();
1423     int ncl = 0;
1424     for (RawClusterContainer::ConstIterator hiter = begin_end.first; hiter != begin_end.second; ++hiter)
1425     {
1426       std::cout << "-> #" << ncl++ << " ";
1427       hiter->second->identify();
1428       std::cout << std::endl;
1429     }
1430   }
1431 
1432   return Fun4AllReturnCodes::EVENT_OK;
1433 }
1434 
1435 int RawClusterBuilderTopo::End(PHCompositeNode * /*topNode*/)
1436 {
1437   return Fun4AllReturnCodes::EVENT_OK;
1438 }
1439 
1440 void RawClusterBuilderTopo::CreateNodes(PHCompositeNode *topNode)
1441 {
1442   PHNodeIterator iter(topNode);
1443 
1444   PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
1445   if (!dstNode)
1446   {
1447     std::cerr << PHWHERE << "DST Node missing, doing nothing." << std::endl;
1448     throw std::runtime_error("Failed to find DST node in RawClusterBuilderTopo::CreateNodes");
1449   }
1450 
1451   PHNodeIterator dstiter(dstNode);
1452   PHCompositeNode *DetNode = dynamic_cast<PHCompositeNode *>(dstiter.findFirst("PHCompositeNode", "TOPOCLUSTER"));
1453   if (!DetNode)
1454   {
1455     DetNode = new PHCompositeNode("TOPOCLUSTER");
1456     dstNode->addNode(DetNode);
1457   }
1458 
1459   _clusters = new RawClusterContainer();
1460 
1461   PHIODataNode<PHObject> *clusterNode = new PHIODataNode<PHObject>(_clusters, ClusterNodeName, "PHObject");
1462   DetNode->addNode(clusterNode);
1463 }