.. _program_listing_file_larflow_CRTMatch_CRTTrackMatch.cxx: Program Listing for File CRTTrackMatch.cxx ========================================== |exhale_lsh| :ref:`Return to documentation for file ` (``larflow/CRTMatch/CRTTrackMatch.cxx``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "CRTTrackMatch.h" #include "larlite/core/LArUtil/LArProperties.h" #include "larlite/core/LArUtil/Geometry.h" #include "larcv/core/DataFormat/EventImage2D.h" #include "larcv/core/ROOTUtil/ROOTUtils.h" #include "ublarcvapp/UBWireTool/UBWireTool.h" #include "TH2D.h" #include "TCanvas.h" #include "TGraph.h" #include "TStyle.h" namespace larflow { namespace crtmatch { CRTTrackMatch::CRTTrackMatch() : larcv::larcv_base("CRTTrackMatch"), _max_iters(25), _col_neighborhood(100), _max_fit_step_size(1.0), _max_last_step_size(0.1), _max_dt_flash_crt(2.0), _adc_producer("wire"), _crttrack_producer("crttrack"), _opflash_producer_v( {"simpleFlashBeam","simpleFlashCosmic"} ), _make_debug_images(false) { _sce = new larutil::SpaceChargeMicroBooNE( larutil::SpaceChargeMicroBooNE::kMCC9_Forward ); _reverse_sce = new larutil::SpaceChargeMicroBooNE( larutil::SpaceChargeMicroBooNE::kMCC9_Backward ); } CRTTrackMatch::~CRTTrackMatch() { delete _sce; delete _reverse_sce; } void CRTTrackMatch::process( larcv::IOManager& iolcv, larlite::storage_manager& ioll ) { // clear containers storing output of algorithm clear_output_containers(); // collect input // -------------- larcv::EventImage2D* ev_adc = (larcv::EventImage2D*)iolcv.get_data( larcv::kProductImage2D, _adc_producer ); auto const& adc_v = ev_adc->Image2DArray(); larlite::event_crttrack* ev_crttrack = (larlite::event_crttrack*)ioll.get_data( larlite::data::kCRTTrack, _crttrack_producer ); std::vector< const larlite::event_opflash* > opflash_v; for ( auto& flashname : _opflash_producer_v ) { larlite::event_opflash* ev_opflash = (larlite::event_opflash*)ioll.get_data( larlite::data::kOpFlash, flashname ); opflash_v.push_back( ev_opflash ); } // vizualize for debug std::vector h2d_v; // for adc image std::vector< std::vector< TGraph* > > graph_vv( adc_v.size() ); // for projected crttrack path in image planes if ( _make_debug_images ) { h2d_v = larcv::rootutils::as_th2d_v( ev_adc->Image2DArray(), "crttrack_adc" ); } // try to fit crt track path for ( size_t i=0; isize(); i++ ) { crttrack_t fit = _find_optimal_track( ev_crttrack->at(i), ev_adc->Image2DArray() ); // save, only if there are points inside the TPC and in the image if ( fit.pixelpos_vv.size()>0 ) { LARCV_NORMAL() << "found fitted CRT track: " << _str(fit) << std::endl; _fitted_crttrack_v.emplace_back( std::move(fit) ); }//if fitted track has image path with charge }//end of crttrack loop // match opflashes _matchOpflashes( opflash_v, _fitted_crttrack_v, _matched_opflash_v ); for (int i=0; i<_fitted_crttrack_v.size(); i++ ) { auto& fitdata = _fitted_crttrack_v[i]; larlite::crttrack outcrt( *fitdata.pcrttrack ); // record shift outcrt.x1_err = fitdata.hit_pos_vv[0][0] - outcrt.x1_pos; outcrt.y1_err = fitdata.hit_pos_vv[0][1] - outcrt.y1_pos; outcrt.z1_err = fitdata.hit_pos_vv[0][2] - outcrt.z1_pos; outcrt.x2_err = fitdata.hit_pos_vv[1][0] - outcrt.x2_pos; outcrt.y2_err = fitdata.hit_pos_vv[1][1] - outcrt.y2_pos; outcrt.z2_err = fitdata.hit_pos_vv[1][2] - outcrt.z2_pos; // record new hit positions outcrt.x1_pos = fitdata.hit_pos_vv[0][0]; outcrt.y1_pos = fitdata.hit_pos_vv[0][1]; outcrt.z1_pos = fitdata.hit_pos_vv[0][2]; outcrt.x2_pos = fitdata.hit_pos_vv[1][0]; outcrt.y2_pos = fitdata.hit_pos_vv[1][1]; outcrt.z2_pos = fitdata.hit_pos_vv[1][2]; _modified_crttrack_v.emplace_back( std::move(outcrt) ); larlite::larflowcluster cluster = _crttrack2larflowcluster( fitdata ); _cluster_v.emplace_back( std::move(cluster) ); } if ( _make_debug_images ) { gStyle->SetOptStat(0); int nplanes = (int)adc_v.size(); TCanvas c("c","c",1000,400*nplanes); c.Divide(1,nplanes); for ( int p=0; pSetRangeUser(0,2400); for ( auto const& fit : _fitted_crttrack_v ) { TGraph* g = new TGraph( fit.pixelcoord_vv[p].size() ); for ( size_t n=0; nSetPoint( n, fit.pixelcoord_vv[n][p], fit.pixelcoord_vv[n][3] ); } g->SetLineWidth(1); if ( fit.pixelpos_vv.front()[0]<80.0 ) g->SetLineColor(kMagenta); else if ( fit.pixelpos_vv.front()[0]>160.0 ) g->SetLineColor(kCyan); else g->SetLineColor(kOrange); g->Draw("L"); graph_vv[p].push_back( g ); }// fitted loop std::cout << "graphs in plane[" << p << "]: " << graph_vv[p].size() << std::endl; }//end of plane loop c.Update(); // save canvas char name[100]; sprintf( name, "crttrackmatch_debug_run%d_subrun%d_event%d.png", (int)iolcv.event_id().run(), (int)iolcv.event_id().subrun(), (int)iolcv.event_id().event() ); c.SaveAs(name); // clean up for ( int p=0; p& adc_v ) { LARCV_DEBUG() << " hit1 pos w/ error: (" << " " << crt.x1_pos << " +/- " << crt.x1_err << "," << " " << crt.y1_pos << " +/- " << crt.y1_err << "," << " " << crt.z1_pos << " +/- " << crt.z1_err << ")" << std::endl; LARCV_DEBUG() << " hit2 pos w/ error: (" << " " << crt.x2_pos << " +/- " << crt.x2_err << "," << " " << crt.y2_pos << " +/- " << crt.y2_err << "," << " " << crt.z2_pos << " +/- " << crt.z2_err << ")" << std::endl; // walk the point in a 5 cm range std::vector< double > hit1_center = { crt.x1_pos, crt.y1_pos, crt.z1_pos }; std::vector< double > hit2_center = { crt.x2_pos, crt.y2_pos, crt.z2_pos }; float t0_usec = 0.5*( crt.ts2_ns_h1+crt.ts2_ns_h2 )*0.001; const int ntries = _max_iters; // max iterations // first try, latest iteration of track crttrack_t first = _collect_chargepixels_for_track( hit1_center, hit2_center, t0_usec, adc_v, _max_fit_step_size, _col_neighborhood ); // provide additional info, besides projected points found first.pcrttrack = &crt; first.t0_usec = t0_usec; LARCV_INFO() << "first try: " << _str( first ) << std::endl; for (int itry=0; itry hit1_new; std::vector hit2_new; bool foundproposal = _propose_corrected_track( first, hit1_new, hit2_new ); if ( !foundproposal ) break; LARCV_DEBUG() << " proposal moves CRT hits to: " << "(" << hit1_new[0] << "," << hit1_new[1] << "," << hit1_new[2] << ") " << "(" << hit2_new[0] << "," << hit2_new[1] << "," << hit2_new[2] << ") " << std::endl; LARCV_DEBUG() << " proposal shifts: " << "(" << hit1_new[0]-hit1_center[0] << "," << hit1_new[1]-hit1_center[1] << "," << hit1_new[2]-hit1_center[2] << ") " << "(" << hit2_new[0]-hit2_center[0] << "," << hit2_new[1]-hit2_center[1] << "," << hit2_new[2]-hit2_center[2] << ") " << std::endl; // make a new track crttrack_t sample = _collect_chargepixels_for_track( hit1_new, hit2_new, t0_usec, adc_v, _max_fit_step_size, _col_neighborhood ); sample.pcrttrack = &crt; sample.t0_usec = t0_usec; sample.hit_pos_vv.clear(); sample.hit_pos_vv.push_back( hit1_new ); sample.hit_pos_vv.push_back( hit2_new ); LARCV_DEBUG() << " ... [try " << itry << "] " << _str(sample) << std::endl; float err_diff = 0.; for (size_t p=0; p0.0 ) { LARCV_DEBUG() << " ... error improved by " << err_diff << ", replace old" << std::endl; // replace the old std::swap( sample, first ); hit1_center = hit1_new; hit2_center = hit2_new; } else { LARCV_INFO() << " ... no improvement after " << itry << " iterations" << std::endl; break; } }//end over try loop crttrack_t bestfit_data = _collect_chargepixels_for_track( hit1_center, hit2_center, t0_usec, adc_v, _max_last_step_size, 10 ); bestfit_data.pcrttrack = &crt; bestfit_data.t0_usec = t0_usec; bestfit_data.hit_pos_vv.push_back( hit1_center ); bestfit_data.hit_pos_vv.push_back( hit2_center ); LARCV_INFO() << " track after fit: " << _str(bestfit_data) << std::endl; return bestfit_data; } CRTTrackMatch::crttrack_t CRTTrackMatch::_collect_chargepixels_for_track( const std::vector& hit1_pos, const std::vector& hit2_pos, const float t0_usec, const std::vector& adc_v, const float max_step_size, const int col_neighborhood ) { // create struct for this path crttrack_t data( -1, nullptr ); // determine direction from one crt hit position to the other std::vector dir(3,0); float len = 0.; for (int i=0; i<3; i++ ) { dir[i] = hit2_pos[i]-hit1_pos[i]; len += dir[i]*dir[i]; } len = sqrt( len ); if ( len==0 ) return data; for ( size_t p=0; p<3; p++ ) dir[p] /= len; int nsteps = len/max_step_size+1; float stepsize = len/float(nsteps); // create image to maker where we have stepped in the image std::vector< larcv::Image2D > pix_visited_v; for ( auto const& img : adc_v ) { larcv::Image2D visited(img.meta()); visited.paint(0.0); pix_visited_v.emplace_back( std::move(visited) ); } // step through the path std::vector last_pos; for (int istep=0; istep pos(3,0.0); for (int i=0; i<3; i++ ) pos[i] = hit1_pos[i] + istep*stepsize*dir[i]; // do not evaluate outside TPC if ( pos[0]<1.0 || pos[0]>255.0 ) continue; if ( pos[1]<-116.0 || pos[1]>116.0 ) continue; if ( pos[2]<0.5 || pos[2]>1035.0 ) continue; //std::cout << " [" << istep << "] pos=(" << pos[0] << "," << pos[1] << "," << pos[2] << ")" << std::endl; // space charge correct the straight-line path position std::vector offset = _sce->GetPosOffsets( pos[0], pos[1], pos[2] ); std::vector pos_sce(3,0); pos_sce[0] = pos[0] - offset[0] + 0.6; pos_sce[1] = pos[1] + offset[1]; pos_sce[2] = pos[2] + offset[2]; // get image coordinates bool inimage = true; std::vector imgcoord_v(4); for (size_t p=0; pWireCoordinate( pos_sce, (UInt_t)p )+0.5); if ( imgcoord_v[p]<0 || imgcoord_v[p]>=larutil::Geometry::GetME()->Nwires(p) ) { inimage = false; } } imgcoord_v[3] = 3200 + ( pos_sce[0]/larutil::LArProperties::GetME()->DriftVelocity() + t0_usec )/0.5; //imgcoord_v[3] = 3200 + ( pos[0]/larutil::LArProperties::GetME()->DriftVelocity() )/0.5; if ( imgcoord_v[3]<=adc_v[0].meta().min_y() || imgcoord_v[3]>=adc_v[0].meta().max_y() ) inimage = false; //std::cout << " [" << istep << "] imgcoord=(" << imgcoord_v[0] << "," << imgcoord_v[1] << "," << imgcoord_v[2] << ", tick=" << imgcoord_v[3] << ")" << std::endl; if ( !inimage ) continue; data.pixelcoord_vv.push_back( imgcoord_v ); pos.resize(6); for (int i=0; i<3; i++ ) { pos[3+i] = pos[i]; // move original to end pos[i] = pos_sce[i]; // replace with sce-corrected } data.pixelpos_vv.push_back( pos ); int row = adc_v[0].meta().row( imgcoord_v[3], __FILE__, __LINE__ ); for (int dr=0; dr<=0; dr++ ) { // no row variation for now int r = row+dr; if ( r<0 || r>=(int)adc_v[0].meta().rows() ) continue; for (int p=0; p<3; p++ ) { int col = adc_v[p].meta().col( imgcoord_v[p], __FILE__, __LINE__ ); // for one 3D point right now // we move through a neighborhood float minrad = col_neighborhood; float minrad_pixval = 0.; std::vector minradpix = { row, col }; for (int dc=-col_neighborhood; dc<=col_neighborhood; dc++) { int c = col+dc; if ( c<0 || c>=(int)adc_v[p].meta().cols() ) continue; if ( pix_visited_v[p].pixel( r, c )>0 ) continue; float pixval = adc_v[p].pixel( r, c ); if ( pixval<10.0 ) continue; // store pixel std::vector pix = { r, c}; float rad = sqrt( dr*dr + dc*dc ); if ( rad < minrad ) { minrad = rad; minradpix = pix; minrad_pixval = pixval; } pix_visited_v[p].set_pixel( r, c, 10.0 ); }//end of dc loop // store pixel for same 3D point data.pixellist_vv[p].push_back( minradpix ); data.pixelrad_vv[p].push_back( minrad ); data.totalq_v[p] += minrad_pixval; data.toterr_v[p] += minrad; }//end of plane loop }//end of dr neighborhood if ( last_pos.size()>0 ) { double step_len = 0.; for (int i=0; i<3; i++ ) step_len += (last_pos[i]-pos_sce[i])*(last_pos[i]-pos_sce[i]); step_len = sqrt(step_len); data.len_intpc_sce += step_len; } last_pos = pos_sce; }//end of step loop for (size_t p=0; p<3; p++ ) { if ( data.pixelrad_vv[p].size()>0 ) data.toterr_v[p] /= float(data.pixelrad_vv[p].size()); } return data; } bool CRTTrackMatch::_propose_corrected_track( const CRTTrackMatch::crttrack_t& old, std::vector< double >& hit1_new, std::vector< double >& hit2_new ) { // cannot make new track if ( old.pixelpos_vv.size()<5 ) return false; // we use points found along old track, along with shifts to make space points std::vector< std::vector > candidate_points_vv; for ( size_t ipt=0; ipt > pos_vv; // bank of 3d points from wire combinatinos for (int i=0; i<3;i++ ) { for (int j=i+1; j<3; j++ ) { int otherplane = 0; int otherwire = 0; std::vector poszy; int crosses = 0; ublarcvapp::UBWireTool::getMissingWireAndPlane( i, old.pixellist_vv[i][ipt][1], j, old.pixellist_vv[j][ipt][1], otherplane, otherwire, poszy, crosses ); if ( crosses==1 ) { // if valid point, bank this space point std::vector newpos = { old.pixelpos_vv[ipt][3], poszy[1], poszy[0] }; // reverse the space-charge std::vector offset_v = _reverse_sce->GetPosOffsets( newpos[0], newpos[1], newpos[2] ); // std::vector pos_sce(3,0); // pos_sce[0] = newpos[0] - offset_v[0] + 0.6; // pos_sce[1] = newpos[1] + offset_v[1]; // pos_sce[2] = newpos[2] + offset_v[2]; // pos_vv.push_back( pos_sce ); pos_vv.push_back( newpos ); } }//end of plane-j loop }//end of plane-i loop // pick the one closest to the old (non-SCE applied) point if ( pos_vv.size()>1 ) { int iclosest = 0; double mindist = 1.0e9; int ii=0; for (auto it_pos : pos_vv ) { // non-SCE corrected point in pixelpos_vv[ipt][3:] float dist = 0.; for (int i=0; i<3; i++ ) dist += ( it_pos[i]-old.pixelpos_vv[ipt][3+i] )*( it_pos[i]-old.pixelpos_vv[ipt][3+i] ); if ( dist < mindist ) { mindist = dist; iclosest = ii; } ii++; } candidate_points_vv.push_back( pos_vv[iclosest] ); } else if ( pos_vv.size()==1 ) { candidate_points_vv.push_back( pos_vv[0] ); } // std::cout << " old[" << old.pixelpos_vv[ipt][3] << "," << old.pixelpos_vv[ipt][4] << "," << old.pixelpos_vv[ipt][5] << "]" // << " -> " // << " new[" << candidate_points_vv.back()[0] << "," << candidate_points_vv.back()[1] << "," << candidate_points_vv.back()[2] << "]" // << std::endl; }//end of old point loop //std::cout << " found " << candidate_points_vv.size() << " new 3D points from " << old.pixelpos_vv.size() << " points" << std::endl; // make cluster, and pca larflow::reco::cluster_t cluster; cluster.points_v.resize( candidate_points_vv.size() ); int ii=0; for ( auto const& pt : candidate_points_vv ) { std::vector fpt = { (float)pt[0], (float)pt[1], (float)pt[2] }; cluster.points_v[ii] = fpt; ii++; } larflow::reco::cluster_pca( cluster ); // now we use first pca as line-fit // we find intersections to crt std::vector< std::vector > panel_pos_v; std::vector< double > dist2_original_hits; bool ok = _crt_intersections( cluster, *old.pcrttrack, panel_pos_v, dist2_original_hits ); if ( !ok ) return false; if ( dist2_original_hits[0]>50.0 || dist2_original_hits[1]>50.0 ) return false; hit1_new = panel_pos_v[0]; hit2_new = panel_pos_v[1]; return true; } std::string CRTTrackMatch::_str( const CRTTrackMatch::crttrack_t& data ) { std::stringstream ss; ss << "[crttrack_t] "; ss << " pixellist_vv.size=(" << data.pixellist_vv[0].size() << "," << data.pixellist_vv[1].size() << "," << data.pixellist_vv[2].size() << ") "; ss << " pixelpos_vv.size=" << data.pixelpos_vv.size(); ss << " totalq=(" << data.totalq_v[0] << "," << data.totalq_v[1] << "," << data.totalq_v[2] << ") "; ss << " toterr=(" << data.toterr_v[0] << "," << data.toterr_v[1] << "," << data.toterr_v[2] << ") "; ss << " len=" << data.len_intpc_sce; return ss.str(); } bool CRTTrackMatch::_crt_intersections( larflow::reco::cluster_t& track_cluster, const larlite::crttrack& orig_crttrack, std::vector< std::vector >& panel_pos_v, std::vector< double >& dist2_original_hits ) { int crt_plane_norm_dim[4] = { 1, 0, 0, 1 }; // Y-axis, X-axis, X-axis, Y-axis panel_pos_v.resize(2); dist2_original_hits.resize(2,0.0); std::vector< int > hitplane_v = { orig_crttrack.plane1, orig_crttrack.plane2 }; std::vector< std::vector > crtpos_v(2); crtpos_v[0] = { orig_crttrack.x1_pos, orig_crttrack.y1_pos, orig_crttrack.z1_pos }; crtpos_v[1] = { orig_crttrack.x2_pos, orig_crttrack.y2_pos, orig_crttrack.z2_pos }; std::vector dir(3,0); for (int i=0; i<3; i++ ) dir[i] = track_cluster.pca_axis_v[0][i]; std::vector center(3); for (int i=0; i<3; i++ ) center[i] = track_cluster.pca_center[i]; for (int p=0; p<2; p++ ) { int hitplane = hitplane_v[p]; if ( dir[ crt_plane_norm_dim[ hitplane ] ]!=0 ) { // only evaluate if not parallel to CRT plane std::vector crtpos = crtpos_v[p]; // dist to center double s = (crtpos[ crt_plane_norm_dim[ hitplane ] ] - center[ crt_plane_norm_dim[hitplane] ])/dir[ crt_plane_norm_dim[hitplane] ]; std::vector panel_pos(3); for ( int i=0; i<3; i++ ) { panel_pos[i] = center[i] + s*dir[i]; } panel_pos_v[p] = panel_pos; float dist = 0.; for (int i=0; i<3; i++ ) { dist += (panel_pos[i]-crtpos_v[p][i])*(panel_pos[i]-crtpos_v[p][i]); } dist = sqrt(dist); } else { return false; } } return true; } void CRTTrackMatch::_matchOpflashes( std::vector< const larlite::event_opflash* > flash_vv, const std::vector& tracks_v, std::vector< larlite::opflash >& matched_opflash_v ) { std::vector< std::vector > used_flash_v( flash_vv.size() ); for ( size_t i=0; isize(), 0 ); for ( auto const& trackdata : tracks_v ) { std::vector< const larlite::opflash* > matched_in_time_v; std::vector dt_usec_v; std::vector< std::pair > matched_index_v; float closest_time = 10e9; for ( size_t i=0; isize(); j++ ) { if ( used_flash_v[i][j]>0 ) continue; float dt_usec = trackdata.t0_usec - flash_vv[i]->at(j).Time(); //std::cout <<" ... compare flash and crttrack time: " << flash_vv[i]->at(j).Time() << " vs. " << trackdata.t0_usec << " dt=" << dt_usec << std::endl; 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_vv[i]->at(j)) ); dt_usec_v.push_back( dt_usec ); matched_index_v.push_back( std::pair( i, j ) ); //std::cout << "[CRTTrackMatch] ... crt-track and opflash matched. dt_usec=" << dt_usec << std::endl; } } } LARCV_NORMAL() << "crt-track 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( trackdata.t0_usec, 0.0, trackdata.t0_usec, 0, PEperOpDet ); matched_opflash_v.emplace_back( std::move(blank_flash) ); } else if ( matched_in_time_v.size()==1 ) { // store a copy of the opflash matched_opflash_v.push_back( *matched_in_time_v[0] ); used_flash_v[ matched_index_v[0].first ][ matched_index_v[0].second ] = 1; } else { 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; } int flashindex[2] = { 0, 0 }; for ( size_t i=0; i1.5 ) continue; // get middle of 3d cluster std::vector< float > ave_pos_v(3,0); int npoints = 0; for ( auto const& pixpos : trackdata.pixelpos_vv ) { for (int v=0; v<3; v++ ) ave_pos_v[v] += pixpos[v]; npoints++; } if (npoints>0) { for (int v=0; v<3; v++ ) ave_pos_v[v] /= float(npoints); } // 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]-ave_pos_v[v] )*( flashcenter[v]-ave_pos_v[v] ); } dist = sqrt(dist); if ( dist