Program Listing for File FlowTriples.cxx

Return to documentation for file (larflow/PrepFlowMatchData/FlowTriples.cxx)

#include "FlowTriples.h"
#include "WireOverlap.h"

#include <ctime>
#include <sstream>

namespace larflow {
namespace prep {

  FlowTriples::FlowTriples( int source, int target,
                            const std::vector<larcv::Image2D>& adc_v,
                            const std::vector<larcv::Image2D>& badch_v,
                            float threshold, bool save_index ) {

    _sparseimg_vv = make_initial_sparse_image( adc_v, threshold );
    _makeTriples( source, target, adc_v, badch_v, _sparseimg_vv, threshold, save_index );

  }

  FlowTriples::FlowTriples( int source, int target,
                            const std::vector<larcv::Image2D>& adc_v,
                            const std::vector<larcv::Image2D>& badch_v,
                            const std::vector< std::vector<PixData_t> >& sparseimg_vv,
                            float threshold, bool save_index ) {

    _makeTriples( source, target, adc_v, badch_v, sparseimg_vv, threshold, save_index );

  }

  void FlowTriples::_makeTriples( int source, int target,
                                  const std::vector<larcv::Image2D>& adc_v,
                                  const std::vector<larcv::Image2D>& badch_v,
                                  const std::vector< std::vector<PixData_t> >& sparseimg_vv,
                                  float threshold, bool save_index ) {


    std::clock_t start_ = std::clock();

    _source_plane = source;
    _target_plane = target;
    _other_plane  = larflow::LArFlowConstants::getOtherPlane( source, target );

    // allocate space for triples
    _triple_v.reserve( sparseimg_vv[_source_plane].size() );

    int ndeadch_added = 0;
    std::map< std::pair<int,int>, int > _added_dead_channel_pixels[3];
    _deadch_to_add.resize( adc_v.size() );

    // turn this on for a full dump
    bool for_debug = false;

    // make possible triples based on going from source pixel to target pixels
    for ( auto& srcpix : sparseimg_vv[_source_plane] ) {

      // activate conditional debug
      // we puke for pixels above threshold, but sitting in dead channel.
      // this is to trace effect of INFILL
      // float badchval = badch_v[_source_plane].pixel( srcpix.row, srcpix.col );
      // if ( badchval>0 )
      //   for_debug = true;
      // else
      //   for_debug = false;

      // we get all the 'target' and 'other' plane wires this pixel overlaps with
      std::vector< std::vector<int> > overlap = larflow::prep::WireOverlap::getOverlappingWires( _source_plane, _target_plane, srcpix.col );

      if ( for_debug ) {
        std::cout << "FlowTriples[" << _source_plane << "," << _target_plane << "," << _other_plane << "] "
                  << " srcwire=" << srcpix.col << " row=" << srcpix.row
                  << " tar-overlap=[" << overlap[0].front() << "," << overlap[0].back() << "] "
                  << " oth-overlap=[" << overlap[1].front() << "," << overlap[1].back() << "] "
                  << std::endl;
      }

      if ( overlap[0].size()==0 ) {
        if ( for_debug )
          std::cout << " -- target no overlap" << std::endl;
        continue;
      }

      // get iterator to overlap[0] vector: contains pixels in the target wire, the source wire overlaps with
      auto it_overlap0 = overlap[0].begin();

      // get the lowerbound pixel for (row,col) in target sparse image container (assumes sorted properly sort worked)
      PixData_t lb( srcpix.row, (int)overlap[0][0], 0.0 );
      auto it_target = std::lower_bound( sparseimg_vv[_target_plane].begin(), sparseimg_vv[_target_plane].end(), lb );

      if ( for_debug ) {
        if ( it_target!=sparseimg_vv[_target_plane].end() )
          std::cout << " target_pix_lb(row,col)=(" << it_target->row << "," << it_target->col << ") ";
        std::cout << " other=[" << overlap[1].front() << "," << overlap[1].back() << "]"
                  << std::endl;
      }

      // iterator forward in target wire with charge, until we go to another row (different time)
      if (for_debug) std::cout << " ... start of target pixel loop ..." << std::endl;
      while ( it_target!=sparseimg_vv[_target_plane].end() && it_target->row==srcpix.row ) {

        // break if we out of range over the target wire
        if ( it_target->col>overlap[0].back() || it_target->row!=srcpix.row ) break;

        // find position in overlap[0] in order to get overlap[1] element, i.e. the other wire the source+target wires intersect
        it_overlap0 = std::lower_bound( it_overlap0, overlap[0].end(), it_target->col );
        if ( it_overlap0==overlap[0].end() ) {
          if ( for_debug ) std::cout << " target col not in list: break" << std::endl;
          break; // didnt find the target column in the list
        }

        // scan up until matches
        int ivec = -1;
        while ( *it_overlap0<=it_target->col && it_overlap0!=overlap[0].end()) {
          ivec = it_overlap0-overlap[0].begin();
          it_overlap0++;
        }
        if ( for_debug )
          std::cout << " .. target pixel: row=" << it_target->row << " col=" << it_target->col << " ivec=" << ivec << std::endl;

        int otherwire = overlap[1][ivec];
        if ( for_debug )
          std::cout << "  ... search for otherwire=" << otherwire << " ivec=" << ivec << std::endl;

        // now find the other plane pixel in the sparse matrix.
        // we allow for a little slop
        // first search for pixel in other plane sparseimg vector (that is above threshold)
        auto it_other = std::lower_bound( sparseimg_vv[_other_plane].begin(),
                                          sparseimg_vv[_other_plane].end(),
                                          PixData_t( srcpix.row, otherwire-2, 0.0) );

        if ( for_debug ) {
          if ( it_other!=sparseimg_vv[_other_plane].end() ) {
            std::cout << " ... otherlb(r,c)=" << it_other->row << "," << it_other->col << ")" << std::endl;
          }
        }

        // now we scan through the pixels in the other plane
        bool found = false;
        if ( it_other!=sparseimg_vv[_other_plane].end() && it_other->row==srcpix.row && it_other->col<=otherwire ) {
          // pixels to search
          while ( it_other!=sparseimg_vv[_other_plane].end() && it_other->row==srcpix.row ) {
            // if a pixel in the other plane is found close to the one we searched for,
            //  we know it has charge, so we store the triple
            //valid triple
            if ( for_debug ) {
              std::cout << " .... looking for row=" << srcpix.row << " otherwire=" << otherwire
                        << " cols=(" << srcpix.col << "," << it_target->col << "," << it_other->col << ")"
                        << std::endl;
            }

            if ( abs(it_other->col-otherwire)<=1 ) {
              if (for_debug) {
                std::cout << "  ... found triple with charge!!!" << std::endl;
              }
              if (save_index) {
                std::vector<int> trip = { srcpix.idx, it_target->idx, it_other->idx, srcpix.row }; // store positions in sparsematrix
                _triple_v.push_back( trip );
              }
              else {
                std::vector<int> trip = { srcpix.col, it_target->col, it_other->col, srcpix.row }; // store positions in sparsematrix
                _triple_v.push_back( trip );
              }
              found = true;
              break;
            }
            it_other++;
          }
        }

        // if we did not find a pixel in the other plane, then we check if it is in a bad region
        if ( !found && badch_v[ _other_plane ].pixel( srcpix.row, otherwire ) > 0 ) {
          // badchannel, other wire lands in dead channel

          // check we have this pixel
          if ( for_debug ) {
            std::cout << " ... looking for badch row=" << srcpix.row
                      << " cols=(" << srcpix.col << "," << it_target->col << "," << otherwire << ")"
                      << std::endl;
            std::cout << "  ... found triple with dead channel" << std::endl;
          }

          // store this dead pixel to add to the sparsematrix after we complete the search method
          auto it_dead_other = _added_dead_channel_pixels[_other_plane].find( std::pair<int,int>(srcpix.row,otherwire) );
          int otherplane_index = 0;

          if ( it_dead_other==_added_dead_channel_pixels[_other_plane].end() ) {
            // was not in the creation set, create this pixel in the other plane sparse image
            otherplane_index = sparseimg_vv[_other_plane].size() + _deadch_to_add[_other_plane].size();
            PixData_t badpix( srcpix.row, otherwire, 0.0 );
            badpix.idx = otherplane_index;
            // this screws up the search, but at the end, so won't matter?
            _deadch_to_add[_other_plane].push_back( badpix );
            _added_dead_channel_pixels[_other_plane][ std::pair<int,int>(srcpix.row,otherwire) ]  = otherplane_index;
            ndeadch_added++;
          }
          else {
            otherplane_index = it_dead_other->second;
          }

          if (save_index) {
            // store position in sparsematrix, omit last plane
            std::vector<int> trip = { srcpix.idx, it_target->idx, otherplane_index, srcpix.row };
            _triple_v.push_back( trip );
          }
          else {
            // store position in image
            std::vector<int> trip = { srcpix.col, it_target->col, otherwire, srcpix.row };
            _triple_v.push_back( trip );
          }
        }
        else {

          if ( for_debug ) {
            std::cout << "looking for row=" << srcpix.row
                      << " cols=(" << srcpix.col << "," << it_target->col << "," << otherwire << ")"
                      << std::endl;

            if ( it_other==sparseimg_vv[_other_plane].end() )
              std::cout << "  ... no triple" << std::endl;
            else
              std::cout << "  ... lowerbound found, but no match? lowerbound otherpix=(" <<it_other->row << "," << it_other->col << ")" <<  std::endl;
          }

        }

        // iterate the target pixel
        it_target++;
      }//target while loop
    }

    // for ( auto& deadpix : _deadch_to_add[_other_plane] ) {
    //   sparseimg_vv[_other_plane].push_back( deadpix );
    // }

    _triple_v.shrink_to_fit();

    std::clock_t end_ = std::clock();

    std::cout << "[FlowTriples] for flow source[" << _source_plane << "] "
              << "to target[" << _target_plane << "] planes "
              << "found " << _triple_v.size() << " triples "
              << "ndeadch-added=" << ndeadch_added << " "
              << "elasped=" << float(end_-start_)/float(CLOCKS_PER_SEC)
              << std::endl;


  }


  std::vector<TH2D> FlowTriples::plot_triple_data( const std::vector<larcv::Image2D>& adc_v,
                                                   const std::vector< std::vector<PixData_t> >& sparseimg_vv,
                                                   std::string hist_stem_name ) {

    std::vector<TH2D> out_v;

    for ( int p=0; p<(int)adc_v.size(); p++ ) {
      std::stringstream ss;
      ss << "htriples_plane" << p << "_" << hist_stem_name;
      auto const& meta = adc_v[p].meta();
      TH2D hist( ss.str().c_str(), "",
                 meta.cols(), meta.min_x(), meta.max_x(),
                 meta.rows(), meta.min_y(), meta.max_y() );
      out_v.emplace_back(std::move(hist));
    }

    std::vector<int> pl = { _source_plane, _target_plane, _other_plane };
    for ( auto const& trip : _triple_v ) {
      for (size_t i=0; i<3; i++ ) {
        auto const& pix = sparseimg_vv[ pl[i] ][ trip[i] ];
        out_v[ pl[i] ].SetBinContent( pix.col+1, pix.row+1, pix.val+1 );
      }
    }

    return out_v;
  }

  std::vector<TH2D> FlowTriples::plot_sparse_data( const std::vector<larcv::Image2D>& adc_v,
                                                   const std::vector< std::vector<PixData_t> >& sparseimg_vv,
                                                   std::string hist_stem_name ) {

    std::vector<TH2D> out_v;

    for ( int p=0; p<(int)adc_v.size(); p++ ) {
      std::stringstream ss;
      ss << "htriples_plane" << p << "_" << hist_stem_name;
      auto const& meta = adc_v[p].meta();
      TH2D hist( ss.str().c_str(), "",
                 meta.cols(), meta.min_x(), meta.max_x(),
                 meta.rows(), meta.min_y(), meta.max_y() );

      for ( auto const& pix : sparseimg_vv[p] ) {
        if (pix.val>=10 )
          hist.SetBinContent( pix.col+1, pix.row+1, pix.val );
      }

      out_v.emplace_back(std::move(hist));
    }


    return out_v;
  }


  std::vector< std::vector<FlowTriples::PixData_t> >
  FlowTriples::make_initial_sparse_image( const std::vector<larcv::Image2D>& adc_v,
                                          float threshold ) {

    // sparsify planes: pixels must be above threshold
    std::vector< std::vector<FlowTriples::PixData_t> > sparseimg_vv(adc_v.size());

    for ( size_t p=0; p<adc_v.size(); p++ ) {
      sparseimg_vv[p].reserve( (int)( 0.1 * adc_v[p].as_vector().size() ) );

      for ( size_t r=0; r<adc_v[p].meta().rows(); r++ ) {
        for ( size_t c=0; c<adc_v[p].meta().cols(); c++ ) {
          float val = adc_v[p].pixel(r,c);
          if ( val>=threshold ) {
            sparseimg_vv[p].push_back( PixData_t((int)r,(int)c, val) );
          }
        }
      }
      // should be sorted in (r,c). do i pull the trigger and sort?
      // std::sort( sparseimg_vv[p].begin(), sparseimg_vv[p].end() );
      int idx=0;
      for ( auto& pix : sparseimg_vv[p] ) {
        pix.idx = idx;
        idx++;
      }
      std::cout << "[FlowTriples] plane[" << p << "] has " << sparseimg_vv[p].size() << " (above threshold) pixels" << std::endl;
    }

    return sparseimg_vv;
  }

}
}