Program Listing for File CRTTrackMatch.cxx¶
↰ Return to documentation for file (larflow/CRTMatch/CRTTrackMatch.cxx
)
#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<TH2D> 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; i<ev_crttrack->size(); 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; p<nplanes; p++ ) {
c.cd(p+1);
h2d_v[p].Draw("colz");
if ( p<=1 )
h2d_v[p].GetXaxis()->SetRangeUser(0,2400);
for ( auto const& fit : _fitted_crttrack_v ) {
TGraph* g = new TGraph( fit.pixelcoord_vv[p].size() );
for ( size_t n=0; n<fit.pixelcoord_vv.size(); n++ ) {
g->SetPoint( 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<nplanes; p++ ) {
for ( auto& pg : graph_vv[p] )
delete pg;
}
}//end of block to make debug image
}
CRTTrackMatch::crttrack_t CRTTrackMatch::_find_optimal_track( const larlite::crttrack& crt,
const std::vector<larcv::Image2D>& 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<ntries; itry++ ) {
// next use the found points to fit a new track direction
std::vector<double> hit1_new;
std::vector<double> 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; p<adc_v.size(); p++ ) {
err_diff += (first.toterr_v[p] - sample.toterr_v[p]);
}
if ( err_diff>0.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<double>& hit1_pos,
const std::vector<double>& hit2_pos,
const float t0_usec,
const std::vector<larcv::Image2D>& 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<float> 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<double> last_pos;
for (int istep=0; istep<nsteps; istep++) {
// 3D step position
std::vector<double> 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<double> offset = _sce->GetPosOffsets( pos[0], pos[1], pos[2] );
std::vector<double> 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<int> imgcoord_v(4);
for (size_t p=0; p<adc_v.size(); p++ ) {
imgcoord_v[p] = (int)(larutil::Geometry::GetME()->WireCoordinate( 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<int> 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<int> 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<double> > candidate_points_vv;
for ( size_t ipt=0; ipt<old.pixelpos_vv.size(); ipt++ ) {
// make 3d points from the plane hits with good-ol ubwiretool
std::vector< std::vector<double> > 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<float> 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<double> newpos = { old.pixelpos_vv[ipt][3], poszy[1], poszy[0] };
// reverse the space-charge
std::vector<double> offset_v = _reverse_sce->GetPosOffsets( newpos[0], newpos[1], newpos[2] );
// std::vector<double> 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<float> 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<double> > 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<double> >& 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<double> > 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<double> dir(3,0);
for (int i=0; i<3; i++ )
dir[i] = track_cluster.pca_axis_v[0][i];
std::vector<double> 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<double> 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<double> 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<CRTTrackMatch::crttrack_t>& tracks_v,
std::vector< larlite::opflash >& matched_opflash_v ) {
std::vector< std::vector<int> > used_flash_v( flash_vv.size() );
for ( size_t i=0; i<flash_vv.size(); i++ )
used_flash_v[i].resize( flash_vv[i]->size(), 0 );
for ( auto const& trackdata : tracks_v ) {
std::vector< const larlite::opflash* > matched_in_time_v;
std::vector<float> dt_usec_v;
std::vector< std::pair<int,int> > matched_index_v;
float closest_time = 10e9;
for ( size_t i=0; i<flash_vv.size(); i++ ) {
for ( size_t j=0; j<flash_vv[i]->size(); 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<int,int>( 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; i<matched_in_time_v.size(); i++ ) {
// ignore loose match if we know we have at least one good one
if ( have_tight_match && dt_usec_v[i]>1.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<float> 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<smallest_dist ) {
smallest_dist = dist;
closest_opflash = matched_in_time_v[i];
flashindex[0] = matched_index_v[i].first;
flashindex[1] = matched_index_v[i].second;
}
std::cout << "[CRTTrackMatch] ... distance between opflash and track center: " << dist << " cm" << std::endl;
}//end of candidate opflash match
// store best match
if ( closest_opflash ) {
matched_opflash_v.push_back( *closest_opflash );
used_flash_v[ flashindex[0] ][ flashindex[1] ] = 1;
}
else {
// shouldnt get here
throw std::runtime_error( "should not get here" );
}
}//else more than 1 match
}//end of track data loop
// check we have the right amount of flashes
if ( matched_opflash_v.size()!=tracks_v.size() ) {
throw std::runtime_error( "different amount of input crttrack data and opflashes");
}
}
larlite::larflowcluster CRTTrackMatch::_crttrack2larflowcluster( const CRTTrackMatch::crttrack_t& fitdata ) {
larlite::larflowcluster cluster;
// loop over hits, store crttrack_t data in a cluster of larflow3dhits
for ( size_t i=0; i<fitdata.pixelpos_vv.size(); i++ ) {
larlite::larflow3dhit lfhit;
lfhit.resize(3,0);
lfhit.targetwire.resize(3,0);
lfhit.X_truth.resize(3,0); // store pre-sce position, CRT hit1, CRT hit2
for (int v=0; v<3; v++ ) {
lfhit[v] = fitdata.pixelpos_vv[i][v];
lfhit.targetwire[v] = fitdata.pixelcoord_vv[i][v];
lfhit.X_truth[v] = fitdata.pixelpos_vv[i][3+v];
}
lfhit.tick = fitdata.pixelcoord_vv[i][3];
lfhit.srcwire = fitdata.pixelcoord_vv[i][2];
lfhit.idxhit = i;
cluster.emplace_back( std::move(lfhit) );
}
return cluster;
}
void CRTTrackMatch::save_to_file( larlite::storage_manager& ioll, bool remove_if_no_flash ) {
// now store data
// --------------
// (1) new crt object with updated hit positions (and old hit positions as well)
// (2) matched opflash objects
// (3) larflow3dhit clusters which store 3d pos and corresponding imgcoord locations for each track
larlite::event_crttrack* out_crttrack
= (larlite::event_crttrack*)ioll.get_data( larlite::data::kCRTTrack, "fitcrttrack" );
larlite::event_opflash* out_opflash
= (larlite::event_opflash*)ioll.get_data( larlite::data::kOpFlash, "fitcrttrack" );
larlite::event_larflowcluster* out_lfcluster
= (larlite::event_larflowcluster*)ioll.get_data( larlite::data::kLArFlowCluster, "fitcrttrack" );
for (size_t i=0; i<_modified_crttrack_v.size(); i++ ) {
auto& crttrack = _modified_crttrack_v[i];
auto& opflash = _matched_opflash_v[i];
auto& cluster = _cluster_v[i];
if ( remove_if_no_flash && _matched_opflash_v[i].TotalPE()==0.0 ) {
LARCV_INFO() << "no matching flash for fitted CRT track[" << i << "], not saving" << std::endl;
continue;
}
float petot = opflash.TotalPE();
LARCV_NORMAL() << "saving track with opflash pe=" << petot << " nopdets=" << opflash.nOpDets() << std::endl;
out_crttrack->push_back(crttrack);
out_opflash->push_back(opflash);
out_lfcluster->push_back( cluster );
}
}
void CRTTrackMatch::clear_output_containers() {
_fitted_crttrack_v.clear(); //< result of crt track fitter
_matched_opflash_v.clear(); //< opflashes matched to tracks in _fitted_crttrack_v
_modified_crttrack_v.clear(); //< crttrack object with modified end points
_cluster_v.clear();
}
}
}