.. _program_listing_file_larflow_CRTMatch_CRTHitMatch.cxx: Program Listing for File CRTHitMatch.cxx ======================================== |exhale_lsh| :ref:`Return to documentation for file ` (``larflow/CRTMatch/CRTHitMatch.cxx``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "CRTHitMatch.h" #include "LArUtil/LArProperties.h" #include "larflow/Reco/cluster_functions.h" #include "TRandom2.h" namespace larflow { namespace crtmatch { void CRTHitMatch::clear() { // input containers _intime_opflash_v.clear(); _outtime_opflash_v.clear(); _crthit_v.clear(); _crttrack_v.clear(); _lfcluster_v.clear(); _pcaxis_v.clear(); _hit2track_rank_v.clear(); _all_rank_v.clear(); // output containers _matched_hitidx.clear(); _flash_v.clear(); _match_hit_v.clear(); _matched_cluster.clear(); _matched_cluster_t.clear(); _matched_opflash_v.clear(); _used_tracks_v.clear(); } void CRTHitMatch::addIntimeOpFlashes( const larlite::event_opflash& opflash_v ) { for ( auto const& opf : opflash_v ) _intime_opflash_v.push_back( opf ); } void CRTHitMatch::addCosmicOpFlashes( const larlite::event_opflash& opflash_v ) { for ( auto const& opf : opflash_v ) _outtime_opflash_v.push_back( opf ); } void CRTHitMatch::addCRThits( const larlite::event_crthit& crthit_v ) { for ( auto const& hit : crthit_v ) _crthit_v.push_back( hit ); } void CRTHitMatch::addCRTtracks( const larlite::event_crttrack& crttrack_v ) { for ( auto const& track : _crttrack_v ) _crttrack_v.push_back( track ); } void CRTHitMatch::addLArFlowClusters( const larlite::event_larflowcluster& lfcluster_v, const larlite::event_pcaxis& pcaxis_v ) { for ( auto const& cluster : lfcluster_v ) { _lfcluster_v.push_back( &cluster ); } for ( auto const& pca : pcaxis_v ) { _pcaxis_v.push_back( &pca ); } } bool CRTHitMatch::process( larcv::IOManager& iolcv, larlite::storage_manager& llio ) { clear(); larlite::event_opflash* beamflash = (larlite::event_opflash*)llio.get_data( larlite::data::kOpFlash, "simpleFlashBeam" ); larlite::event_opflash* cosmicflash = (larlite::event_opflash*)llio.get_data( larlite::data::kOpFlash, "simpleFlashCosmic" ); addIntimeOpFlashes( *beamflash ); addCosmicOpFlashes( *cosmicflash ); // get crt hits larlite::event_crthit* crthit_v = (larlite::event_crthit*)llio.get_data( larlite::data::kCRTHit, "crthitcorr" ); addCRThits( *crthit_v ); // get clusters larlite::event_larflowcluster* lfclusters_v = (larlite::event_larflowcluster*)llio.get_data( larlite::data::kLArFlowCluster, _input_cluster_treename ); larlite::event_pcaxis* pcaxis_v = (larlite::event_pcaxis*)llio.get_data( larlite::data::kPCAxis, _input_pcaxis_treename ); addLArFlowClusters( *lfclusters_v, *pcaxis_v ); std::vector< larflow::reco::cluster_t > test_v; int icluster = 0; for ( auto const& plfcluster : _lfcluster_v ) { std::cout << "[convert cluster " << icluster << "]" << std::endl; larflow::reco::cluster_t c = larflow::reco::cluster_from_larflowcluster( *plfcluster ); test_v.emplace_back( std::move(c) ); icluster++; } LARCV_INFO() << "passed conversion test" << std::endl; return makeMatches(); } bool CRTHitMatch::makeMatches() { // compile matches compilematches(); _used_tracks_v.resize( _lfcluster_v.size(), 0 ); // process matches in greedy fashion for ( auto& m : _all_rank_v ) { // get index of crt hit of this match int crthitidx = m.hitidx; auto& hit_matches_v = _hit2track_rank_v[ crthitidx ]; if ( hit_matches_v.size()==0 ) continue; // shouldn't happen if ( hit_matches_v.size()==1 ) { // if track already used. skip if ( _used_tracks_v[ m.trackidx ]==1 ) continue; // if not, make the match LARCV_INFO() << " store single crt-hit to cluster match products" << std::endl; _matched_hitidx.push_back( crthitidx ); // create a copy larlite::larflowcluster lfc = *_lfcluster_v[ m.trackidx ]; // create a copy _matched_cluster.emplace_back( std::move(lfc) ); _used_tracks_v[ m.trackidx ] = 1; } else { // more than 1, attempt a merge // set the usage vector for these matches bool has_unused = false; std::vector used_track_v( hit_matches_v.size(), 0 ); for ( size_t i=0; iDriftVelocity()*( crthit.ts2_ns*0.001 ); // we need to t0 subtract the points for ( auto& lfhit : lfc ) { lfhit[0] -= xoffset; } // store cluster_t for modified larflow cluster larflow::reco::cluster_t clout = larflow::reco::cluster_from_larflowcluster( lfc ); _matched_cluster_t.emplace_back( std::move(clout) ); } // now match CRT hits with tracks to opflashes // make list of flashes for ( auto const& flash : _intime_opflash_v ) _flash_v.push_back( &flash ); for ( auto const& flash : _outtime_opflash_v ) _flash_v.push_back( &flash ); // make list of crt hits with matches for ( auto const& hitidx : _matched_hitidx ) { _match_hit_v.push_back( &_crthit_v[ hitidx ] ); } _matchOpflashes( _flash_v, _match_hit_v, _matched_cluster, _matched_opflash_v ); return true; } void CRTHitMatch::compilematches() { _hit2track_rank_v.resize( _crthit_v.size() ); // plane positions float crt_plane_pos[4] = { -261.606, -142.484, 393.016, 618.25 }; // bottom, side, side, top //larlite::event_pcaxis* ev_pcaout = (larlite::event_pcaxis*)outio.get_data( larlite::data::kPCAxis, "crtmatch" ); // loop over larflow tracks, assign it to the best hit in each CRT plane // we are filling out _hit2track_rank_v[ hit index ] -> list of track indices LARCV_INFO() << "==============================" << std::endl; LARCV_INFO() << " Start match function" << std::endl; if ( _pcaxis_v.size()!=_lfcluster_v.size() ) { throw std::runtime_error( "number of clusters and pc axis do not match" ); } for (size_t itrack=0; itrack<_pcaxis_v.size(); itrack++ ) { // represent track and its direction with its pc axis auto const& pca = *_pcaxis_v[itrack]; // enforce minimum length float len = getLength(pca); if ( len<5.0 ) continue; // allocate vars for best intersection for each CRT plane float min_dist[4] = { 1.0e9, 1.0e9, 1.0e9, 1.0e9 }; int best_hitidx[4] = {-1, -1, -1, -1}; std::vector best_panel_pos[4]; for (int i=0; i<4; i++) best_panel_pos[i].resize(3,0); // loop over crt hits for (size_t jhit=0; jhit<_crthit_v.size(); jhit++) { auto const& crthit = _crthit_v[jhit]; int crtplane = crthit.plane; // calculate distance to crt hit and position of intersection on the panel std::vector panel_pos; float dist = makeOneMatch( pca, crthit, panel_pos ); // update closest hit on the plane if ( dist>0 && dist=0 ) { auto const& besthit = _crthit_v[best_hitidx[p]]; LARCV_DEBUG() << " crt_hit=(" << besthit.x_pos << ", " << besthit.y_pos << "," << besthit.z_pos << ") " << " panel_pos=(" << best_panel_pos[p][0] << "," << best_panel_pos[p][1] << "," << best_panel_pos[p][2] << ") " << std::endl; if ( min_dist[p]>=0 && min_dist[p]<50.0 ) { float line[3] = {0}; float dist = 0.; for (int i=0; i<3; i++ ) { line[i] = best_panel_pos[p][i]-pca.getAvePosition()[i]; dist += line[i]*line[i]; } dist = sqrt(dist); for (int i=0; i<3; i++ ) line[i] /= dist; // store a match match_t m; m.hitidx = best_hitidx[p]; m.trackidx = itrack; m.tracklen = len; m.dist2hit = min_dist[p]; _hit2track_rank_v[ best_hitidx[p] ].push_back( m ); _all_rank_v.push_back( m ); // store for visualization purposes // larlite::pcaxis::EigenVectors e_v; // 3-axis + 2-endpoints // for ( size_t p=0; p<3; p++ ) { // std::vector da_v = { line[0], line[1], line[2] }; // e_v.push_back( da_v ); // } // std::vector centroid_v = { pca.getAvePosition()[0], pca.getAvePosition()[1], pca.getAvePosition()[2] }; // std::vector panel_v = { best_panel_pos[p][0], best_panel_pos[p][1], best_panel_pos[p][2] }; // e_v.push_back( centroid_v ); // e_v.push_back( panel_v ); // double eigenval[3] = { min_dist[p], 0, 0 }; // larlite::pcaxis llpca( true, 1, eigenval, e_v, centroid_v.data(), 0, itrack ); // //ev_pcaout->emplace_back( std::move(llpca) ); } // close enough to a hit to register a match }//if valid hit found for plane }//end of loop over CRT planes }//end of loop over tracks // sort the all rank list std::sort( _all_rank_v.begin(), _all_rank_v.end() ); // sort each hit list for ( auto& hitmatches : _hit2track_rank_v ) { std::sort( hitmatches.begin(), hitmatches.end() ); } printHitInfo(); return; } void CRTHitMatch::printHitInfo() { LARCV_NORMAL() << "===============================================" << std::endl; LARCV_NORMAL() << "[ CRT HIT INFO ]" << std::endl; for ( size_t idx=0; idx<_crthit_v.size(); idx++ ) { auto const& hit = _crthit_v.at(idx); std::stringstream ss; ss << " [hit " << idx << " p=" << hit.plane << " pos=(" << hit.x_pos << ", " << hit.y_pos << ", " << hit.z_pos << ") " << " t=" << hit.ts2_ns*1.0e-3 << " usec ]"; ss << " matches["; for ( auto const& hidx : _hit2track_rank_v[idx] ) ss << " (" << hidx.dist2hit << "," << hidx.tracklen << ")"; ss << " ] "; LARCV_NORMAL() << ss.str() << std::endl; } LARCV_NORMAL() << "===============================================" << std::endl; } float CRTHitMatch::makeOneMatch( const larlite::pcaxis& lfcluster_axis, const larlite::crthit& hit, std::vector& panel_pos ) { int crt_plane_dim[4] = { 1, 0, 0, 1 }; // Y-axis, X-axis, X-axis, Y-axis float endpts[2][3]; float center[3]; float dir[3]; float crtpos[3] = { hit.x_pos, hit.y_pos, hit.z_pos }; float len = 0; for ( int i=0; i<3; i++ ) { endpts[0][i] = lfcluster_axis.getEigenVectors()[3][i]; endpts[1][i] = lfcluster_axis.getEigenVectors()[4][i]; center[i] = lfcluster_axis.getAvePosition()[i]; dir[i] = endpts[0][i]-center[i]; len += dir[i]*dir[i]; } if (len>0) { len = sqrt(len); for (int i=0; i<3; i++) dir[i] /= len; } float max_x = 0.; float min_x = 1.0e9; for (int i=0; i<2; i++ ) { if ( endpts[i][0] > max_x ) max_x = endpts[i][0]; if ( endpts[i][0] < min_x ) min_x = endpts[i][0]; } // check if parallel to the plane if ( dir[ crt_plane_dim[ hit.plane ] ]==0 ) { return -1; } // check if in time float x_offset = hit.ts2_ns*0.001*larutil::LArProperties::GetME()->DriftVelocity(); if ( max_x-x_offset >= 260.0 || max_x-x_offset<-10.0 ) { return -1; } if ( min_x-x_offset >= 260.0 || min_x-x_offset<-10.0 ) { return -1; } // in time, so let's calculte the position onto the plane // remove the time offset center[0] -= x_offset; // project onto crt plane float s = (crtpos[ crt_plane_dim[ hit.plane ] ] - center[ crt_plane_dim[hit.plane] ])/dir[ crt_plane_dim[hit.plane] ]; panel_pos.resize(3,0); for ( int i=0; i<3; i++ ) { panel_pos[i] = center[i] + s*dir[i]; } // calculate distance to the hit float dist = 0.; for (int i=0; i<3; i++) { dist += ( crtpos[i]-panel_pos[i] )*( crtpos[i]-panel_pos[i] ); } dist = sqrt( dist ); panel_pos[0] += x_offset; return dist; } float CRTHitMatch::getLength( const larlite::pcaxis& pca ) { float dist = 0.; for (int i=0; i<3; i++ ) { float dx = ( pca.getEigenVectors()[3][i]-pca.getEigenVectors()[4][i] ); dist += dx*dx; } return sqrt(dist); } void CRTHitMatch::_matchOpflashes( const std::vector< const larlite::opflash* >& flash_v, const std::vector< const larlite::crthit* >& hit_v, const std::vector< larlite::larflowcluster >& cluster_v, std::vector< larlite::opflash >& matched_opflash_v ) { std::vector< int > used_flash_v( flash_v.size(), 0 ); const float _max_dt_flash_crt = 2.0; for ( size_t ihit=0; ihit matched_in_time_v; std::vector dt_usec_v; std::vector< int > matched_index_v; float closest_time = 10e9; for ( size_t i=0; i0 ) continue; float dt_usec = crt.ts2_ns*1.0e-3 - flash_v[i]->Time(); if ( fabs(dt_usec) < closest_time ) closest_time = fabs(dt_usec); if ( fabs(dt_usec)<_max_dt_flash_crt ) { matched_in_time_v.push_back( flash_v[i] ); dt_usec_v.push_back( dt_usec ); matched_index_v.push_back( i ); LARCV_INFO() << " ... crt-track and opflash matched. dt_usec=" << dt_usec << std::endl; } }//end of flash loop LARCV_NORMAL() << "crt-hit has " << matched_index_v.size() << " flash matches. closest time=" << closest_time << std::endl; if ( matched_in_time_v.size()==0 ) { // make empty opflash at the time of the crttrack std::vector< double > PEperOpDet(32,0.0); larlite::opflash blank_flash( crt.ts2_ns*1.0e-3, 0.0, crt.ts2_ns*1.0e-3, 0, PEperOpDet ); matched_opflash_v.emplace_back( std::move(blank_flash) ); } else if ( matched_in_time_v.size()==1 ) { // store a copy of the single matched opflash matched_opflash_v.push_back( *matched_in_time_v[0] ); used_flash_v[ matched_index_v[0] ] = 1; } else { // choose closest using distance from center of flash and track // need mean of cluster std::vector cluster_mean(3,0); for ( auto const& hit : cluster_v[ ihit ] ) { for (int i=0; i<3; i++) cluster_mean[i] += hit[i]; } for (int i=0; i<3; i++ ) cluster_mean[i] /= (float)cluster_v[ihit].size(); float smallest_dist = 1.0e9; const larlite::opflash* closest_opflash = nullptr; // we have a loose initial standard. if more than one, we use a tighter standard bool have_tight_match = false; for ( auto& dt_usec : dt_usec_v ) { if ( fabs(dt_usec)<1.5 ) have_tight_match = true; } for ( size_t i=0; i1.5 ) continue; // get mean of opflash std::vector flashcenter = { 0.0, (float)matched_in_time_v[i]->YCenter(), (float)matched_in_time_v[i]->ZCenter() }; float dist = 0.; for (int v=1; v<3; v++ ) { dist += ( flashcenter[v]-cluster_mean[v] )*( flashcenter[v]-cluster_mean[v] ); } dist = sqrt(dist); if ( dist& hit_match_v, std::vector& used_in_merge, bool& merged ) { merged = false; larlite::larflowcluster lfcluster; // if one or zero matches, nothing to do here. if ( hit_match_v.size()<=1 ) return lfcluster; // we use cluster tools, so start by converting best track larflow::reco::cluster_t mergecluster; int nmerged = 0; for ( size_t idx=0; idx > closestpts_vv; float endptdist = larflow::reco::cluster_closest_endpt_dist( mergecluster, c, closestpts_vv ); float cospca = cluster_cospca( mergecluster, c ); cospca = 1.0 - fabs(cospca); if ( float(cospca)*180.0/3.14159 < 20.0 && endptdist < 30.0 ) { LARCV_INFO() << "merge test passed" << std::endl; larflow::reco::cluster_t testmerge = larflow::reco::cluster_merge( mergecluster, c ); if ( testmerge.pca_eigenvalues[1] < 30.0 ) { merged = true; used_in_merge[idx] = 1; nmerged++; LARCV_INFO() << "swap for merged cluster" << std::endl; std::swap( mergecluster, testmerge ); } } } for ( size_t idx=0; idx