From 38918c45bb0753a91027be8863e436ce4a4ca3a9 Mon Sep 17 00:00:00 2001 From: Chengdong Fu <fucd@ihep.ac.cn> Date: Tue, 18 Aug 2020 16:31:42 +0800 Subject: [PATCH] add ForwardTrackingAlg --- Reconstruction/SiliconTracking/CMakeLists.txt | 3 +- .../src/ForwardTrackingAlg.cpp | 975 ++++++++++++++++++ .../SiliconTracking/src/ForwardTrackingAlg.h | 346 +++++++ 3 files changed, 1323 insertions(+), 1 deletion(-) create mode 100644 Reconstruction/SiliconTracking/src/ForwardTrackingAlg.cpp create mode 100644 Reconstruction/SiliconTracking/src/ForwardTrackingAlg.h diff --git a/Reconstruction/SiliconTracking/CMakeLists.txt b/Reconstruction/SiliconTracking/CMakeLists.txt index 0f76fb01..93d860ba 100644 --- a/Reconstruction/SiliconTracking/CMakeLists.txt +++ b/Reconstruction/SiliconTracking/CMakeLists.txt @@ -10,6 +10,7 @@ gaudi_depends_on_subdirs( Service/EventSeeder Service/TrackSystemSvc Utilities/DataHelper + Utilities/KiTrack ) set(SiliconTracking_srcs src/*.cpp) @@ -17,5 +18,5 @@ set(SiliconTracking_srcs src/*.cpp) # Modules gaudi_add_module(SiliconTracking ${SiliconTracking_srcs} INCLUDE_DIRS GaudiKernel FWCore gear ${GSLx_INCLUDE_DIRS} ${LCIOx_INCLUDE_DIRS} - LINK_LIBRARIES TrackSystemSvcLib DataHelperLib GaudiKernel FWCore $ENV{GEAR}/lib/libgearsurf.so ${GSL_LIBRARIES} ${LCIO_LIBRARIES} + LINK_LIBRARIES TrackSystemSvcLib DataHelperLib KiTrackLib GaudiKernel FWCore $ENV{GEAR}/lib/libgearsurf.so ${GSL_LIBRARIES} ${LCIO_LIBRARIES} ) diff --git a/Reconstruction/SiliconTracking/src/ForwardTrackingAlg.cpp b/Reconstruction/SiliconTracking/src/ForwardTrackingAlg.cpp new file mode 100644 index 00000000..57868b94 --- /dev/null +++ b/Reconstruction/SiliconTracking/src/ForwardTrackingAlg.cpp @@ -0,0 +1,975 @@ +#include "ForwardTrackingAlg.h" +#include "GearSvc/IGearSvc.h" +#include "TrackSystemSvc/ITrackSystemSvc.h" +#include "DataHelper/Navigation.h" + +#include "edm4hep/TrackerHit.h" +#include "edm4hep/TrackerHitConst.h" +#include "edm4hep/Track.h" + +#include "UTIL/ILDConf.h" + +//#include "MarlinCED.h" + +#include "gear/GEAR.h" +#include "gear/GearParameters.h" +#include "gear/BField.h" +#include "gear/FTDParameters.h" +#include "gear/FTDLayerLayout.h" + +//----From KiTrack----------------------------- +#include "KiTrack/SubsetHopfieldNN.h" +#include "KiTrack/SubsetSimple.h" +#include "KiTrack/SegmentBuilder.h" +#include "KiTrack/Automaton.h" + +//----From KiTrackMarlin----------------------- +#include "ILDImpl/FTDTrack.h" +#include "ILDImpl/FTDHit01.h" +#include "ILDImpl/FTDNeighborPetalSecCon.h" +#include "ILDImpl/FTDSectorConnector.h" +#include "Tools/KiTrackMarlinTools.h" +//#include "Tools/KiTrackMarlinCEDTools.h" +#include "Tools/FTDHelixFitter.h" + +using namespace MarlinTrk ; + +// Used to fedine the quality of the track output collection +const int ForwardTrackingAlg::_output_track_col_quality_GOOD = 1; +const int ForwardTrackingAlg::_output_track_col_quality_FAIR = 2; +const int ForwardTrackingAlg::_output_track_col_quality_POOR = 3; + +DECLARE_COMPONENT( ForwardTrackingAlg ) + +ForwardTrackingAlg::ForwardTrackingAlg(const std::string& name, ISvcLocator* svcLoc) +: GaudiAlgorithm(name, svcLoc) { + //_description = "ForwardTracking reconstructs tracks through the FTD" ; + + declareProperty("FTDPixelHitCollection", _inFTDPixelColHdl, "Handle of the Input FTD TrackerHits collection"); + declareProperty("FTDSpacePointCollection", _inFTDSpacePointColHdl, "Handle of the Input FTD SpacePoints collection"); + declareProperty("FTDRawHitCollection", _inFTDRawColHdl, "Handle of the FTD SpacePoints raw hit collection collection"); + //std::vector< std::string > collections; + //collections.push_back( "FTDTrackerHits" ); + //collections.push_back( "FTDSpacePoints" ); + declareProperty("ForwardTrackCollection", _outColHdl, "Handle of the ForwarTrack output collection"); +} + +StatusCode ForwardTrackingAlg::initialize(){ + debug() << " init called " << endmsg; + + _nRun = 0 ; + _nEvt = 0 ; + + _useCED = false; // Setting this to on will initialise CED in the processor and tracks or segments (from the CA) + // can be printed. As this is mainly used for debugging it is not a steerable parameter. + //if( _useCED )MarlinCED::init(this) ; //CED + + // Now set min and max values for all the criteria + for( unsigned i=0; i < _criteriaNames.size(); i++ ){ + std::vector< float > emptyVec; + _critMinima[_criteriaNames[i]] = emptyVec; + _critMaxima[_criteriaNames[i]] = emptyVec; + } + if(_critMinimaInit.size()!=_critMaximaInit.size()){ + warning() << "number of Criteria min values != max values, will be filled as 0 for less" << endmsg; + } + unsigned nCritValues = std::max(_critMinimaInit.size(),_critMaximaInit.size()); + //if(nCritValues>_criteriaNames.size()){ + // warning() << "number of Criteria values > name's, will be discarded" << endmsg; + //} + //nCritValues = std::min(nCritValues, _criteriaNames.size()); + for(unsigned i=0; i<nCritValues; i++){ + int iCritName = i%_criteriaNames.size(); + std::string critName = _criteriaNames[iCritName]; + if(i<_critMinimaInit.size()) _critMinima[critName].push_back(_critMinimaInit[i]); + else _critMinima[critName].push_back(0.); + if(i<_critMaximaInit.size()) _critMaxima[critName].push_back(_critMaximaInit[i]); + else _critMaxima[critName].push_back(0.); + } + debug() << "Criteria table:" << endmsg; + for(unsigned i=0; i<_criteriaNames.size(); i++ ){ + std::string critName = _criteriaNames[i]; + debug() << "---- " << critName; + for(unsigned j=0; j<_critMinima[critName].size(); j++){ + debug() << " [" << _critMinima[critName][j] << ", " << _critMaxima[critName][j] << "]"; + } + debug() << endmsg; + } + + auto _gear = service<IGearSvc>("GearSvc"); + if ( !_gear ) { + error() << "Failed to find GearSvc ..." << endmsg; + return StatusCode::FAILURE; + } + gear::GearMgr* gearMgr = _gear->getGearMgr(); + + /**********************************************************************************************/ + /* Make a SectorSystemFTD */ + /**********************************************************************************************/ + + // The SectorSystemFTD is the object translating the sectors of the hits into layers, modules etc. and vice versa + const gear::FTDParameters& ftdParams = gearMgr->getFTDParameters() ; + const gear::FTDLayerLayout& ftdLayers = ftdParams.getFTDLayerLayout() ; + int nLayers = ftdLayers.getNLayers() + 1; // we add one layer for the IP + int nModules = ftdLayers.getNPetals(0); + int nSensors = ftdLayers.getNSensors(0); + + // make sure we take the highest number of modules / sensors available + for( int i=1; i < nLayers - 1; i++){ + + if( ftdLayers.getNPetals(i) > nModules ) nModules = ftdLayers.getNPetals(i); + if( ftdLayers.getNSensors(i) > nSensors ) nSensors = ftdLayers.getNSensors(i); + + } + + debug() << "SectorSystemFTD is using " << nLayers << " layers (including one for the IP), " << nModules << " petals and " << nSensors << " sensors." << endmsg; + + _sectorSystemFTD = new SectorSystemFTD( nLayers, nModules , nSensors ); + + // Get the B Field in z direction + _Bz = gearMgr->getBField().at( gear::Vector3D(0., 0., 0.) ).z(); //The B field in z direction + + /**********************************************************************************************/ + /* Initialise the MarlinTrkSystem, needed by the tracks for fitting */ + /**********************************************************************************************/ + auto _trackSystemSvc = service<ITrackSystemSvc>("TrackSystemSvc"); + if ( !_trackSystemSvc ) { + error() << "Failed to find TrackSystemSvc ..." << endmsg; + return StatusCode::FAILURE; + } + _trkSystem = _trackSystemSvc->getTrackSystem(); + + if( _trkSystem == 0 ){ + error() << "Cannot initialize MarlinTrkSystem of Type: KalTest" <<endmsg; + return StatusCode::FAILURE; + } + + // set the options + _trkSystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::useQMS, _MSOn ) ; //multiple scattering + _trkSystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::usedEdx, _ElossOn) ; //energy loss + _trkSystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::useSmoothing, _SmoothOn) ; //smoothing + + // initialise the tracking system + //_trkSystem->init() ; + + /**********************************************************************************************/ + /* Do a few checks, if the set parameters are right */ + /**********************************************************************************************/ + + // Only use allowed methods to find subsets. + assert( ( _bestSubsetFinder == "None" ) || ( _bestSubsetFinder == "SubsetHopfieldNN" ) || ( _bestSubsetFinder == "SubsetSimple" ) ); + + // Use a sensible chi2prob cut. (chi squared probability, like any probability must range from 0 to 1) + assert( _chi2ProbCut >= 0. ); + assert( _chi2ProbCut <= 1. ); + + // Make sure, every used criterion exists and has at least one min and max set + for( unsigned i=0; i<_criteriaNames.size(); i++ ){ + + std::string critName = _criteriaNames[i]; + + ICriterion* crit = Criteria::createCriterion( critName ); //throws an exception if the criterion is non existent + delete crit; + + assert( !_critMinima[ critName ].empty() ); + assert( !_critMaxima[ critName ].empty() ); + + } + return GaudiAlgorithm::initialize(); +} + +StatusCode ForwardTrackingAlg::execute(){ + debug() << " processing event number " << _nEvt << endmsg; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // ForwardTracking // + // // + // Track Reconstruction in the FTD // + // // + // // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + //--CED (only used for debugging )--------------------------------------- + // Reset drawing buffer and START drawing collection + /* + if( _useCED ){ + MarlinCED::newEvent(this , 0) ; + CEDPickingHandler &pHandler=CEDPickingHandler::getInstance(); + pHandler.update(evt); + } + */ + //----------------------------------------------------------------------- + + // Reset the quality flag of the output track collection (we start with the assumption that our results are good. + // If anything happens along the way, we modify this value ) + _output_track_col_quality = _output_track_col_quality_GOOD; + + std::vector< IHit* > hitsTBD; //Hits to be deleted at the end + _map_sector_hits.clear(); + + /**********************************************************************************************/ + /* Read in the collections, create hits from the TrackerHits and store them in a map */ + /**********************************************************************************************/ + + debug() << "\t\t---Reading in Collections---" << endmsg; + Navigation::Instance()->Initialize(); + std::vector<const edm4hep::TrackerHitCollection*> hitFTDCollections; + + try { + auto hitFTDPixelCol = _inFTDPixelColHdl.get(); + hitFTDCollections.push_back(hitFTDPixelCol); + Navigation::Instance()->AddTrackerHitCollection(hitFTDPixelCol); + } + catch ( GaudiException &e ) { + debug() << "Collection " << _inFTDPixelColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + } + + try { + auto hitFTDSpacePointCol = _inFTDSpacePointColHdl.get(); + hitFTDCollections.push_back(hitFTDSpacePointCol); + Navigation::Instance()->AddTrackerHitCollection(hitFTDSpacePointCol); + //const edm4hep::TrackerHitCollection* rawHitCol = nullptr; + try{ + auto rawHitCol = _inFTDRawColHdl.get(); + Navigation::Instance()->AddTrackerHitCollection(rawHitCol); + } + catch ( GaudiException &e ) { + fatal() << "Collection " << _inFTDRawColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + } + } + catch ( GaudiException &e ) { + debug() << "Collection " << _inFTDSpacePointColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + } + + if(hitFTDCollections.size()==0){ + return StatusCode::SUCCESS; + } + /* + const edm4hep::TrackerHitCollection* rawHitCol = nullptr; + if(1){ + try{ + rawHitCol = _inFTDRawColHdl.get(); + } + catch ( GaudiException &e ) { + fatal() << "Collection " << _inFTDRawColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + } + } + */ + for( unsigned iCol=0; iCol < hitFTDCollections.size(); iCol++ ){ //read in all input collections + unsigned nHits = hitFTDCollections[iCol]->size(); + debug() << "Number of hits in collection " << hitFTDCollections[iCol]->getID() << ": " << nHits << endmsg; + + for(auto trackerHit : *hitFTDCollections[iCol]){ + edm4hep::ConstTrackerHit hit = trackerHit; + debug() << "hit " << trackerHit.id() << " " << KiTrackMarlin::getCellID0Info( trackerHit.getCellID() ) + << " " << KiTrackMarlin::getPositionInfo( &hit )<< endmsg; + + //Make an FTDHit01 from the TrackerHit + FTDHit01* ftdHit = new FTDHit01 ( trackerHit , _sectorSystemFTD ); + hitsTBD.push_back(ftdHit); //so we can easily delete every created hit afterwards + + _map_sector_hits[ ftdHit->getSector() ].push_back( ftdHit ); + } + } + + if( !_map_sector_hits.empty() ){ + /**********************************************************************************************/ + /* Check if no sector is overflowing with hits */ + /**********************************************************************************************/ + std::map< int , std::vector< IHit* > >::iterator it; + + for( it=_map_sector_hits.begin(); it != _map_sector_hits.end(); it++ ){ + int nHits = it->second.size(); + debug() << "Number of hits in sector " << it->first << " = " << nHits << endmsg; + + if( nHits > _maxHitsPerSector ){ + + it->second.clear(); //delete the hits in this sector, it will be dropped + + error() << " ### EVENT " << _nEvt << " :: RUN " << _nRun << " \n ### Number of Hits in FTD Sector " << it->first << ": " << nHits << " > " + << _maxHitsPerSector << " (MaxHitsPerSector)\n : This sector will be dropped from track search, and QualityCode set to \"Poor\" " << endmsg; + + _output_track_col_quality = _output_track_col_quality_POOR; // We had to drop hits, so the quality of the result is decreased + } + } + + /**********************************************************************************************/ + /* Check the possible connections of hits on overlapping petals */ + /**********************************************************************************************/ + debug() << "\t\t---Overlapping Hits---" << endmsg; + + std::map< IHit* , std::vector< IHit* > > map_hitFront_hitsBack = getOverlapConnectionMap( _map_sector_hits, _sectorSystemFTD, _overlappingHitsDistMax); + + /**********************************************************************************************/ + /* Add the IP as virtual hit for forward and backward */ + /**********************************************************************************************/ + IHit* virtualIPHitForward = createVirtualIPHit(1 , _sectorSystemFTD ); + hitsTBD.push_back( virtualIPHitForward ); + _map_sector_hits[ virtualIPHitForward->getSector() ].push_back( virtualIPHitForward ); + + IHit* virtualIPHitBackward = createVirtualIPHit(-1 , _sectorSystemFTD ); + hitsTBD.push_back( virtualIPHitBackward ); + _map_sector_hits[ virtualIPHitBackward->getSector() ].push_back( virtualIPHitBackward ); + + /**********************************************************************************************/ + /* SegmentBuilder and Cellular Automaton */ + /**********************************************************************************************/ + unsigned round = 0; // the round we are in + std::vector < RawTrack > rawTracks; + + // The following while loop ideally only runs once. (So we do round 0 and everything works) + // It will repeat as long as the Automaton creates too many connections and as long as there are new criteria + // parameters to use to cut down the problem. + // Ideally already in round 0, there is a reasonable number of connections (not more than _maxConnectionsAutomaton), + // so the loop will be left. If however there are too many connections we stay in the loop and use + // (hopefully) tighter cut offs (if provided in the steering). This should prevent combinatorial breakdown + // for very evil events. + while( setCriteria( round ) ){ + round++; // count up the round we are in + + /**********************************************************************************************/ + /* Build the segments */ + /**********************************************************************************************/ + debug() << "\t\t---SegementBuilder---" << endmsg; + + //Create a segmentbuilder + SegmentBuilder segBuilder( _map_sector_hits ); + + segBuilder.addCriteria ( _crit2Vec ); // Add the criteria on when to connect two hits. The vector has been filled by the method setCriteria + + //Also load hit connectors + unsigned layerStepMax = 1; // how many layers to go at max + unsigned petalStepMax = 1; // how many petals to go at max + unsigned lastLayerToIP = 5;// layer 1,2,3 and 4 get connected directly to the IP + FTDSectorConnector secCon( _sectorSystemFTD , layerStepMax , petalStepMax , lastLayerToIP ); + + segBuilder.addSectorConnector ( & secCon ); // Add the sector connector (so the SegmentBuilder knows what hits from different sectors it is allowed to look for connections) + + // And get out the Cellular Automaton with the 1-segments + Automaton automaton = segBuilder.get1SegAutomaton(); + + // Check if there are not too many connections + if( automaton.getNumberOfConnections() > unsigned( _maxConnectionsAutomaton ) ){ + debug() << "Redo the Automaton with different parameters, because there are too many connections:" << endmsg + << "\tconnections( " << automaton.getNumberOfConnections() << " ) > MaxConnectionsAutomaton( " << _maxConnectionsAutomaton << " )" << endmsg; + continue; + } + + /**********************************************************************************************/ + /* Automaton */ + /**********************************************************************************************/ + debug() << "\t\t---Automaton---" << endmsg; + + //if( _useCED ) KiTrackMarlin::drawAutomatonSegments( automaton ); // draws the 1-segments (i.e. hits) + + /*******************************/ + /* 2-hit segments */ + /*******************************/ + debug() << "\t\t--2-hit-Segments--" << endmsg; + + //debug() << "Automaton has " << automaton.getTracks( 3 ).size() << " track candidates" << endmsg; //should be commented out, because it takes time + + automaton.clearCriteria(); + automaton.addCriteria( _crit3Vec ); // Add the criteria for 3 hits (i.e. 2 2-hit segments ) + + // Let the automaton lengthen its 1-hit-segments to 2-hit-segments + automaton.lengthenSegments(); + + // So now we have 2-hit-segments and are ready to perform the Cellular Automaton. + + // Perform the automaton + automaton.doAutomaton(); + + // Clean segments with bad states + automaton.cleanBadStates(); + + // Reset the states of all segments + automaton.resetStates(); + + //debug() << "Automaton has " << automaton.getTracks( 3 ).size() << " track candidates" << endmsg; //should be commented out, because it takes time + + // Check if there are not too many connections + if( automaton.getNumberOfConnections() > unsigned( _maxConnectionsAutomaton ) ){ + debug() << "Redo the Automaton with different parameters, because there are too many connections:" << endmsg + << "\tconnections( " << automaton.getNumberOfConnections() << " ) > MaxConnectionsAutomaton( " << _maxConnectionsAutomaton << " )" << endmsg; + continue; + } + + /*******************************/ + /* 3-hit segments */ + /*******************************/ + debug() << "\t\t--3-hit-Segments--" << endmsg; + + automaton.clearCriteria(); + automaton.addCriteria( _crit4Vec ); + + // Lengthen the 2-hit-segments to 3-hits-segments + automaton.lengthenSegments(); + + // Perform the Cellular Automaton + automaton.doAutomaton(); + + //Clean segments with bad states + automaton.cleanBadStates(); + + //Reset the states of all segments + automaton.resetStates(); + + //debug() << "Automaton has " << automaton.getTracks( 3 ).size() << " track candidates" << endmsg; //should be commented out, because it takes time + + // Check if there are not too many connections + if( automaton.getNumberOfConnections() > unsigned( _maxConnectionsAutomaton ) ){ + debug() << "Redo the Automaton with different parameters, because there are too many connections:" << endmsg + << "\tconnections( " << automaton.getNumberOfConnections() << " ) > MaxConnectionsAutomaton( " << _maxConnectionsAutomaton << " )" << endmsg; + continue; + } + + // get the raw tracks (raw track = just a vector of hits, the most rudimentary form of a track) + rawTracks = automaton.getTracks( 3 ); + + break; // if we reached this place all went well and we don't need another round --> exit the loop + } + + debug() << "Automaton returned " << rawTracks.size() << " raw tracks " << endmsg; + + /**********************************************************************************************/ + /* Add the overlapping hits */ + /**********************************************************************************************/ + debug() << "\t\t---Add hits from overlapping petals + fit + helix and Kalman cuts---" << endmsg; + + std::vector <ITrack*> trackCandidates; + + // for all raw tracks we got from the automaton + for( unsigned i=0; i < rawTracks.size(); i++){ + RawTrack rawTrack = rawTracks[i]; + + _nTrackCandidates++; + + // get all versions of the track plus hits from overlapping petals + std::vector < RawTrack > rawTracksPlus = getRawTracksPlusOverlappingHits( rawTrack, map_hitFront_hitsBack ); + + debug() << "For raw track number " << i << " there are " << rawTracksPlus.size() << " versions" << endmsg; + + /**********************************************************************************************/ + /* Make track candidates, fit them and throw away bad ones */ + /**********************************************************************************************/ + std::vector< ITrack* > overlappingTrackCands; + + for( unsigned j=0; j < rawTracksPlus.size(); j++ ){ + _nTrackCandidatesPlus++; + + RawTrack rawTrackPlus = rawTracksPlus[j]; + + if( rawTrackPlus.size() < unsigned( _hitsPerTrackMin ) ){ + debug() << "Trackversion discarded, too few hits: only " << rawTrackPlus.size() << " < " << _hitsPerTrackMin << "(hitsPerTrackMin)" << endmsg; + continue; + } + + FTDTrack* trackCand = new FTDTrack( _trkSystem ); + + // add the hits to the track + for( unsigned k=0; k<rawTrackPlus.size(); k++ ){ + IFTDHit* ftdHit = dynamic_cast< IFTDHit* >( rawTrackPlus[k] ); // cast to IFTDHits, as needed for an FTDTrack + if( ftdHit != NULL ) trackCand->addHit( ftdHit ); + else debug() << "Hit " << rawTrackPlus[k] << " could not be casted to IFTDHit" << endmsg; + } + + std::vector< IHit* > trackCandHits = trackCand->getHits(); + debug() << "Fitting track candidate with " << trackCandHits.size() << " hits" << endmsg; + + for( unsigned k=0; k < trackCandHits.size(); k++ ) debug() << trackCandHits[k]->getPositionInfo(); + debug() << endmsg; + + /*-----------------------------------------------*/ + /* Helix Fit */ + /*-----------------------------------------------*/ + debug() << "Fitting with Helix Fit" << endmsg; + try{ + FTDHelixFitter helixFitter( trackCand->getLcioTrack() ); + float chi2OverNdf = helixFitter.getChi2() / float( helixFitter.getNdf() ); + debug() << "chi2OverNdf = " << chi2OverNdf << endmsg; + + if( chi2OverNdf > _helixFitMax ){ + debug() << "Discarding track because of bad helix fit: chi2/ndf = " << chi2OverNdf << endmsg; + delete trackCand; + continue; + } + else debug() << "Keeping track because of good helix fit: chi2/ndf = " << chi2OverNdf << endmsg; + } + catch( FTDHelixFitterException& e ){ + debug() << "Track rejected, because fit failed: " << e.what() << endmsg; + delete trackCand; + continue; + } + + /*-----------------------------------------------*/ + /* Kalman Fit */ + /*-----------------------------------------------*/ + + debug() << "Fitting with Kalman Filter" << endmsg; + try{ + trackCand->fit(); + + debug() << " Track " << trackCand + << " chi2Prob = " << trackCand->getChi2Prob() + << "( chi2=" << trackCand->getChi2() + <<", Ndf=" << trackCand->getNdf() << " )" << endmsg; + + if ( trackCand->getChi2Prob() >= _chi2ProbCut ){ + debug() << "Track accepted (chi2prob " << trackCand->getChi2Prob() << " >= " << _chi2ProbCut << endmsg; + } + else{ + debug() << "Track rejected (chi2prob " << trackCand->getChi2Prob() << " < " << _chi2ProbCut << endmsg; + delete trackCand; + + continue; + } + } + catch( FitterException& e ){ + debug() << "Track rejected, because fit failed: " << e.what() << endmsg; + delete trackCand; + continue; + } + + // If we reach this point than the track got accepted by all cuts + overlappingTrackCands.push_back( trackCand ); + } + + /**********************************************************************************************/ + /* Take the best version of the track */ + /**********************************************************************************************/ + // Now we have all versions of one track, coming from adding possible hits from overlapping petals. + if( _takeBestVersionOfTrack ){ // we want to take only the best version + debug() << "Take the version of the track with best quality from " << overlappingTrackCands.size() << " track candidates" << endmsg; + + if( !overlappingTrackCands.empty() ){ + ITrack* bestTrack = overlappingTrackCands[0]; + + for( unsigned j=1; j < overlappingTrackCands.size(); j++ ){ + if( overlappingTrackCands[j]->getChi2Prob() > bestTrack->getChi2Prob() ){ + delete bestTrack; //delete the old one, not needed anymore + bestTrack = overlappingTrackCands[j]; + } + else{ + delete overlappingTrackCands[j]; //delete this one + } + } + debug() << "Adding best track candidate with " << bestTrack->getHits().size() << " hits" << endmsg; + + trackCandidates.push_back( bestTrack ); + } + } + else{ // we take all versions + debug() << "Taking all " << overlappingTrackCands.size() << " versions of the track" << endmsg; + trackCandidates.insert( trackCandidates.end(), overlappingTrackCands.begin(), overlappingTrackCands.end() ); + } + } + + //if( _useCED ){ + //for( unsigned i=0; i < trackCandidates.size(); i++ ) KiTrackMarlin::drawTrackRandColor( trackCandidates[i] ); + //} + + /**********************************************************************************************/ + /* Get the best subset of tracks */ + /**********************************************************************************************/ + + debug() << "The track candidates so far: " << endmsg; + for( unsigned iTrack=0; iTrack < trackCandidates.size(); iTrack++ ){ + debug() << "track " << iTrack << ": " << trackCandidates[iTrack] << "\t" << KiTrackMarlin::getTrackHitInfo( trackCandidates[iTrack] ) << endmsg; + } + + debug() << "\t\t---Get best subset of tracks---" << endmsg ; + + std::vector< ITrack* > tracks; + std::vector< ITrack* > rejected; + + TrackCompatibilityShare1SP comp; + TrackQIChi2Prob trackQI; + TrackQISpecial trackQISpecial; + + if( _bestSubsetFinder == "SubsetHopfieldNN" ){ + debug() << "Use SubsetHopfieldNN for getting the best subset" << endmsg ; + + SubsetHopfieldNN< ITrack* > subset; + subset.add( trackCandidates ); + subset.calculateBestSet( comp, trackQI ); + tracks = subset.getAccepted(); + rejected = subset.getRejected(); + } + else if( _bestSubsetFinder == "SubsetSimple" ){ + debug() << "Use SubsetSimple for getting the best subset" << endmsg ; + + SubsetSimple< ITrack* > subset; + subset.add( trackCandidates ); + subset.calculateBestSet( comp, trackQISpecial ); + tracks = subset.getAccepted(); + rejected = subset.getRejected(); + } + else { // in any other case take all tracks + debug() << "Input for subset = \"" << _bestSubsetFinder << "\". All tracks are kept" << endmsg ; + + tracks = trackCandidates; + } + + if( _useCED ){ + //for( unsigned i=0; i < tracks.size(); i++ ) KiTrackMarlin::drawTrack( tracks[i] , 0x00ff00 ); + //for( unsigned i=0; i < rejected.size(); i++ ) KiTrackMarlin::drawTrack( rejected[i] , 0xff0000 ); + } + + for ( unsigned i=0; i<rejected.size(); i++){ + delete rejected[i]; + } + + /**********************************************************************************************/ + /* Finally: Finalise and save the tracks */ + /**********************************************************************************************/ + debug() << "\t\t---Save Tracks---" << endmsg ; + + auto trkCol = _outColHdl.createAndPut(); + + for (unsigned int i=0; i < tracks.size(); i++){ + FTDTrack* myTrack = dynamic_cast< FTDTrack* >( tracks[i] ); + + if( myTrack != NULL ){ + edm4hep::Track trackImpl( *(myTrack->getLcioTrack()) ); + + try{ + finaliseTrack( &trackImpl ); + //trkCol->addElement( trackImpl ); + trkCol->push_back(trackImpl); + } + catch( FitterException& e ){ + debug() << "ForwardTracking: track couldn't be finalized due to fitter error: " << e.what() << endmsg; + //delete trackImpl; + } + } + } + + // set the quality of the output collection + switch (_output_track_col_quality) { + case _output_track_col_quality_FAIR: + //trkCol->parameters().setValue( "QualityCode" , "Fair" ) ; + break; + + case _output_track_col_quality_POOR: + //trkCol->parameters().setValue( "QualityCode" , "Poor" ) ; + break; + + default: + //trkCol->parameters().setValue( "QualityCode" , "Good" ) ; + break; + } + + //evt->addCollection(trkCol,_ForwardTrackCollection.c_str()); + + debug() << "Forward Tracking found and saved " << tracks.size() << " tracks in event " << _nEvt << endmsg; + + /**********************************************************************************************/ + /* Clean up */ + /**********************************************************************************************/ + // delete all the created IHits + for ( unsigned i=0; i<hitsTBD.size(); i++ ) delete hitsTBD[i]; + + // delete the FTracks + for (unsigned int i=0; i < tracks.size(); i++){ delete tracks[i];} + } + + //if( _useCED ) MarlinCED::draw(this); + + _nEvt ++ ; + return StatusCode::SUCCESS; +} + +StatusCode ForwardTrackingAlg::finalize(){ + for ( unsigned i=0; i< _crit2Vec.size(); i++) delete _crit2Vec[i]; + for ( unsigned i=0; i< _crit3Vec.size(); i++) delete _crit3Vec[i]; + for ( unsigned i=0; i< _crit4Vec.size(); i++) delete _crit4Vec[i]; + _crit2Vec.clear(); + _crit3Vec.clear(); + _crit4Vec.clear(); + + delete _sectorSystemFTD; + _sectorSystemFTD = NULL; + + debug() << "There are " << _nTrackCandidates << "track candidates from CA and "<< _nTrackCandidatesPlus + << " track Candidates with hits from overlapping hits" << endmsg + << "The ratio is " << float( _nTrackCandidatesPlus )/_nTrackCandidates << endmsg; + + return GaudiAlgorithm::finalize(); +} + +std::map< IHit* , std::vector< IHit* > > ForwardTrackingAlg::getOverlapConnectionMap(std::map< int , std::vector< IHit* > > & map_sector_hits, + const SectorSystemFTD* secSysFTD, + float distMax){ + + unsigned nConnections=0; + + std::map< IHit* , std::vector< IHit* > > map_hitFront_hitsBack; + std::map< int , std::vector< IHit* > >::iterator it; + + //for every sector + for ( it= map_sector_hits.begin() ; it != map_sector_hits.end(); it++ ){ + std::vector< IHit* > hitVecA = it->second; + int sector = it->first; + + // get the neighbouring petals + FTDNeighborPetalSecCon secCon( secSysFTD ); + std::set< int > targetSectors = secCon.getTargetSectors( sector ); + + //for all neighbouring petals + for ( std::set<int>::iterator itTarg = targetSectors.begin(); itTarg!=targetSectors.end(); itTarg++ ){ + std::vector< IHit* > hitVecB = map_sector_hits[ *itTarg ]; + for ( unsigned j=0; j < hitVecA.size(); j++ ){ + IHit* hitA = hitVecA[j]; + for ( unsigned k=0; k < hitVecB.size(); k++ ){ + IHit* hitB = hitVecB[k]; + + float dx = hitA->getX() - hitB->getX(); + float dy = hitA->getY() - hitB->getY(); + float dz = hitA->getZ() - hitB->getZ(); + float dist = sqrt( dx*dx + dy*dy + dz*dz ); + + if (( dist < distMax )&& ( fabs( hitB->getZ() ) > fabs( hitA->getZ() ) ) ){ // if they are close enough and B is behind A + debug() << "Connected: (" << hitA->getX() << "," << hitA->getY() << "," << hitA->getZ() << ")-->(" + << hitB->getX() << "," << hitB->getY() << "," << hitB->getZ() << ")" << endmsg; + + map_hitFront_hitsBack[ hitA ].push_back( hitB ); + nConnections++; + } + } + } + } + } + debug() << "Connected " << map_hitFront_hitsBack.size() << " hits with " << nConnections << " possible overlapping hits" << endmsg; + + return map_hitFront_hitsBack; +} + +std::string ForwardTrackingAlg::getInfo_map_sector_hits(){ + std::stringstream s; + + std::map< int , std::vector< IHit* > >::iterator it; + + for( it = _map_sector_hits.begin(); it != _map_sector_hits.end(); it++ ){ + std::vector<IHit*> hits = it->second; + int sector = it->first; + + int side = _sectorSystemFTD->getSide( sector ); + unsigned layer = _sectorSystemFTD->getLayer( sector ); + unsigned module = _sectorSystemFTD->getModule( sector ); + unsigned sensor = _sectorSystemFTD->getSensor( sector ); + + s << "sector " << sector << " (si" + << side << ",la" + << layer << ",mo" + << module << "se," + << sensor << ") has " + << hits.size() << " hits\n" ; + } + + return s.str(); +} + +std::vector < RawTrack > ForwardTrackingAlg::getRawTracksPlusOverlappingHits( RawTrack rawTrack , std::map< IHit* , std::vector< IHit* > >& map_hitFront_hitsBack ){ + // So we have a raw track (a vector of hits, that is) and a map, that tells us + // for every hit, if there is another hit in the overlapping region behind it very close, + // so that it could be part of the same track. + // + // We now want to find for a given track all possible tracks, when hits from the overlapping regions are added + // + // The method is this: start with pure track. + // Make a vector of rawTracks and fill in the pure track. + // For every hit on the original track do the following: + // Check if there are overlapping hits. + // For every overlapping hit take all the created tracks so far and make another version + // with the overlapping hit added to it and add them to the vector of rawTracks. + // + // + // Let's do an example: + // the original hits in the track are calles A,B and C. + // A has one overlapping hit A1 + // and B has two overlapping hits B1 and B2. + // + // So we start with a vector containing only the original track: {(A,B,C)} + // + // We start with the first hit: A. It has one overlapping hit A1. + // We take all tracks (which is the original one so far) and make another version containing A1 as well. + // Then we add it to the vector of tracks: + // + // {(A,B,C)(A,A1,B,C)} + // + // On to the next hit from the original track: B. Here we have overlapping hits B1 and B2. + // We take all the tracks so far and add versions with B1: (A,B,B1,C) and (A,A1,B,B1,C) + // We don't immediately add them or otherwise, we would create a track containing B1 as well as B2, which is plainly wrong + // + // So instead we make the combinations with B2: (A,B,B2,C) and (A,A1,B,B2,C) + // And now having gone through all overlapping hits of B, we add all the new versions to the vector: + // + // {(A,B,C)(A,A1,B,C)(A,B,B1,C)(A,A1,B,B1,C)(A,B,B2,C)(A,A1,B,B2,C)} + // + // So now we have all possible versions of the track with overlapping hits + + std::vector < RawTrack > rawTracksPlus; + + rawTracksPlus.push_back( rawTrack ); //add the original one + + // for every hit in the original track + for( unsigned i=0; i < rawTrack.size(); i++ ){ + IHit* frontHit = rawTrack[i]; + + // get the hits that are behind frontHit + std::map< IHit* , std::vector< IHit* > >::iterator it; + it = map_hitFront_hitsBack.find( frontHit ); + if( it == map_hitFront_hitsBack.end() ) continue; // if there are no hits on the back skip this one + std::vector< IHit* > backHits = it->second; + + // Create the different versions of the tracks so far with the hits from the back + + std::vector< RawTrack > newVersions; //here we store all the versions with overlapping hits from the different back hits at this frontHit + + // for every hit in back of the frontHit + for( unsigned j=0; j<backHits.size(); j++ ){ + IHit* backHit = backHits[j]; + // for all tracks we have so far + for( unsigned k=0; k<rawTracksPlus.size(); k++ ){ + RawTrack newVersion = rawTracksPlus[k]; // exact copy of the track + newVersion.push_back( backHit ); // add the backHit to it + newVersions.push_back( newVersion ); // store it + + } + } + // Now put all the new versions of the tracks into the rawTracksPlus vector before we go on to the next + // hit of the original track + rawTracksPlus.insert( rawTracksPlus.end(), newVersions.begin(), newVersions.end() ); + } + + return rawTracksPlus; +} + +bool ForwardTrackingAlg::setCriteria( unsigned round ){ + + // delete the old ones + for ( unsigned i=0; i< _crit2Vec.size(); i++) delete _crit2Vec[i]; + for ( unsigned i=0; i< _crit3Vec.size(); i++) delete _crit3Vec[i]; + for ( unsigned i=0; i< _crit4Vec.size(); i++) delete _crit4Vec[i]; + _crit2Vec.clear(); + _crit3Vec.clear(); + _crit4Vec.clear(); + + bool newValuesGotUsed = false; // if new values are used + for( unsigned i=0; i<_criteriaNames.size(); i++ ){ + std::string critName = _criteriaNames[i]; + + float min = _critMinima[critName].back(); + float max = _critMaxima[critName].back(); + + // use the value corresponding to the round, if there are no new ones for this criterion, just do nothing (the previous value stays in place) + if( round + 1 <= _critMinima[critName].size() ){ + min = _critMinima[critName][round]; + newValuesGotUsed = true; + } + + if( round + 1 <= _critMaxima[critName].size() ){ + max = _critMaxima[critName][round]; + newValuesGotUsed = true; + } + + ICriterion* crit = Criteria::createCriterion( critName, min , max ); + + // Some debug output about the created criterion + std::string type = crit->getType(); + + debug() << "Added: Criterion " << critName << " (type = " << type + << " ). Min = " << min + << ", Max = " << max + << ", round " << round << endmsg; + + // Add the new criterion to the corresponding vector + if( type == "2Hit" ){ + _crit2Vec.push_back( crit ); + } + else if( type == "3Hit" ){ + _crit3Vec.push_back( crit ); + } + else if( type == "4Hit" ){ + _crit4Vec.push_back( crit ); + } + else delete crit; + } + + return newValuesGotUsed; +} + +void ForwardTrackingAlg::finaliseTrack( edm4hep::Track* trackImpl ){ + + Fitter fitter( trackImpl , _trkSystem ); + + //trackImpl->trackStates().clear(); + debug() << "Track has " << trackImpl->trackStates_size() << " TrackState now, should be cleared but not supported by EDM4hep" << endmsg; + + edm4hep::TrackState trkStateIP( *fitter.getTrackState( 1/*lcio::TrackState::AtIP*/ ) ) ; + trkStateIP.location = 1; + trackImpl->addToTrackStates( trkStateIP ); + + edm4hep::TrackState trkStateFirstHit( *fitter.getTrackState( 2/*TrackState::AtFirstHit*/ ) ) ; + trkStateFirstHit.location = 2; + trackImpl->addToTrackStates( trkStateFirstHit ); + + edm4hep::TrackState trkStateLastHit( *fitter.getTrackState( 3/*TrackState::AtLastHit*/ ) ) ; + trkStateLastHit.location = 3; + trackImpl->addToTrackStates( trkStateLastHit ); + + edm4hep::TrackState trkStateAtCalo( *fitter.getTrackState( 4/*TrackState::AtCalorimeter*/ ) ) ; + trkStateAtCalo.location = 4; + trackImpl->addToTrackStates( trkStateAtCalo ); + + trackImpl->setChi2( fitter.getChi2( 1 ) ); + trackImpl->setNdf( fitter.getNdf ( 1 ) ); + + const edm4hep::Vector3f& p = trkStateFirstHit.referencePoint; + trackImpl->setRadiusOfInnermostHit( sqrt( p[0]*p[0] + p[1]*p[1] + p[2]*p[2] ) ); + + std::map<int, int> hitNumbers; + + hitNumbers[UTIL::ILDDetID::VXD] = 0; + hitNumbers[UTIL::ILDDetID::SIT] = 0; + hitNumbers[UTIL::ILDDetID::FTD] = 0; + hitNumbers[UTIL::ILDDetID::TPC] = 0; + hitNumbers[UTIL::ILDDetID::SET] = 0; + hitNumbers[UTIL::ILDDetID::ETD] = 0; + + unsigned int nHits = trackImpl->trackerHits_size(); + for( unsigned j=0; j<nHits; j++ ){ + const edm4hep::ConstTrackerHit& hit = trackImpl->getTrackerHits(j); + UTIL::BitField64 encoder( UTIL::ILDCellID0::encoder_string ); + encoder.setValue( hit.getCellID() ); + int subdet = encoder[UTIL::ILDCellID0::subdet]; + + ++hitNumbers[ subdet ]; + } + + //trackImpl->subdetectorHitNumbers().resize(2 * lcio::ILDDetID::ETD); + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::VXD - 2 ] = hitNumbers[lcio::ILDDetID::VXD]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::FTD - 2 ] = hitNumbers[lcio::ILDDetID::FTD]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::SIT - 2 ] = hitNumbers[lcio::ILDDetID::SIT]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 2 ] = hitNumbers[lcio::ILDDetID::TPC]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::SET - 2 ] = hitNumbers[lcio::ILDDetID::SET]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::ETD - 2 ] = hitNumbers[lcio::ILDDetID::ETD]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::VXD - 1 ] = hitNumbers[lcio::ILDDetID::VXD]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::FTD - 1 ] = hitNumbers[lcio::ILDDetID::FTD]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::SIT - 1 ] = hitNumbers[lcio::ILDDetID::SIT]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 1 ] = hitNumbers[lcio::ILDDetID::TPC]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::SET - 1 ] = hitNumbers[lcio::ILDDetID::SET]; + //trackImpl->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::ETD - 1 ] = hitNumbers[lcio::ILDDetID::ETD]; + trackImpl->addToSubDetectorHitNumbers(hitNumbers[UTIL::ILDDetID::VXD]); + trackImpl->addToSubDetectorHitNumbers(hitNumbers[UTIL::ILDDetID::SIT]); + trackImpl->addToSubDetectorHitNumbers(hitNumbers[UTIL::ILDDetID::FTD]); + trackImpl->addToSubDetectorHitNumbers(hitNumbers[UTIL::ILDDetID::TPC]); + trackImpl->addToSubDetectorHitNumbers(hitNumbers[UTIL::ILDDetID::SET]); + trackImpl->addToSubDetectorHitNumbers(hitNumbers[UTIL::ILDDetID::ETD]); + + return; +} + + diff --git a/Reconstruction/SiliconTracking/src/ForwardTrackingAlg.h b/Reconstruction/SiliconTracking/src/ForwardTrackingAlg.h new file mode 100644 index 00000000..8e9e2229 --- /dev/null +++ b/Reconstruction/SiliconTracking/src/ForwardTrackingAlg.h @@ -0,0 +1,346 @@ +#ifndef ForwardTracking_h +#define ForwardTracking_h 1 + +#include "FWCore/DataHandle.h" +#include "GaudiAlg/GaudiAlgorithm.h" +#include "edm4hep/EventHeaderCollection.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/MCRecoTrackerAssociationCollection.h" + +#include <string> + +#include "edm4hep/TrackerHit.h" +#include "TrackSystemSvc/IMarlinTrkSystem.h" +//#include "gear/BField.h" + +#include "KiTrack/Segment.h" +#include "KiTrack/ITrack.h" +#include "Criteria/Criteria.h" +#include "ILDImpl/SectorSystemFTD.h" + +using namespace KiTrack; +using namespace KiTrackMarlin; + +/** a simple typedef, making writing shorter. And it makes sense: a track consists of hits. But as a real track + * has more information, a vector of hits can be considered as a "raw track". */ +typedef std::vector< IHit* > RawTrack; + +/** Standallone Forward Tracking Processor for Marlin.<br> + * + * Reconstructs the tracks through the FTD <br> + * + * For a summary of what happens during each event see the method processEvent + * + * <h4>Input - Prerequisites</h4> + * The hits in the Forward Tracking Detector FTD + * + * <h4>Output</h4> + * A collection of reconstructed Tracks. + * + * @param FTDHitCollections The collections containing the FTD hits <br> + * (default value "FTDTrackerHits FTDSpacePoints" (string vector) ) + * + * @param ForwardTrackCollection Name of the Forward Tracking output collection<br> + * (default value "ForwardTracks" ) + * + * @param MultipleScatteringOn Whether to take multiple scattering into account when fitting the tracks<br> + * (default value true ) + * + * @param EnergyLossOn Whether to take energy loss into account when fitting the tracks<br> + * (default value true ) + * + * @param SmoothOn Whether to smooth all measurement sites in fit<br> + * (default value false ) + * + * @param Chi2ProbCut Tracks with a chi2 probability below this will get sorted out<br> + * (default value 0.005 ) + * + * @param HelixFitMax the maximum chi2/Ndf that is allowed as result of a helix fit + * (default value 500 ) + * + * @param OverlappingHitsDistMax The maximum distance of hits from overlapping petals belonging to one track<br> + * (default value 3.5 ) + * + * @param HitsPerTrackMin The minimum number of hits to create a track<br> + * (default value 3 ) + * + * @param BestSubsetFinder The method used to find the best non overlapping subset of tracks. Available are: SubsetHopfieldNN, SubsetSimple and None. + * None means, that no final search for the best subset is done and overlapping tracks are possible. <br> + * (default value TrackSubsetHopfieldNN ) + * + * @param Criteria A vector of the criteria that are going to be used by the Cellular Automaton. <br> + * For every criterion a min and max needs to be set!!!<br> + * (default value is defined in class Criteria ) + * + * @param NameOfACriterion_min/max For every used criterion a minimum and maximum value needs to be set. <br> + * If a criterion is named "Crit_Example", then the min parameter would be: Crit_Example_min and the max parameter Crit_Example_max.<br> + * You can enter more than one value!!! So for example you could write something like \<parameter name="Crit_Example_min" type="float">30 0.8\</parameter>.<br> + * This means, that if the Cellular Automaton creates too many connections (how many is defined in "MaxConnectionsAutomaton" ) it reruns + * it with the next set of parameters. Thus allowing to tighten the cuts, if there are too many connections and so preventing + * it from getting stuck in very bad combinatorial situations. So in this example the Automaton will first run with the Crit_Example_min of 30 + * and if that generates too many connections, it will rerun it with the value 0.8.<br> + * If for a criterion no further parameters are specified, the first ones will be taken on reruns. + * + * @param MaxConnectionsAutomaton If the automaton has more connections than this it will be redone with the next cut off values for the criteria.<br> + * If there are no further new values for the criteria, the event will be skipped.<br> + * (default value 100000 ) + * + * @param MaxHitsPerSector If on any single sector there are more hits than this, all the hits in the sector get dropped. + * This is to prevent combinatorial breakdown (It is a second safety mechanism, the first one being MaxConnectionsAutomaton. + * But if there are soooo many hits, that already the first round of the Cellular Automaton would take forever, this mechanism + * prevents it) <br> + * (default value 1000) + * + * @author Robin Glattauer HEPHY, Wien + * + */ + +class ForwardTrackingAlg : public GaudiAlgorithm { + public: + + ForwardTrackingAlg(const std::string& name, ISvcLocator* svcLoc); + + /** Called at the begin of the job before anything is read. + * Use to initialize the processor, e.g. book histograms. + */ + virtual StatusCode initialize() ; + + virtual StatusCode execute() ; + + virtual StatusCode finalize() ; + + /** Called for every run. + */ + //virtual void processRunHeader( LCRunHeader* run ) ; + + /** Called for every event - the working horse. + * + * The basic procedure for reconstruction of tracks in the FTD is as follows: + * + * -# Read in all collections of hits on the FTD that are passed as steering parameters + * -# From every hit in these collections an FTDHit01 is created. This is, because the SegmentBuilder and the Automaton + * need their own hit classes. + * -# The hits are stored in the map _map_sector_hits. The keys in this map are the sectors and the values of the map are vectors + * of the hits within those sectors. Sector here means an integer somehow representing a place in the detector. + * (For using this numbers and getting things like layer or side the class SectorSystemFTD is used.) + * -# Make a safety check to ensure no single sector is overflowing with hits. This could give a combinatorial + * disaster leading to endless calculation times. + * -# Add a virtual hit in the place of the IP. It is used by the Cellular Automaton as additional information + * (almost all reconstructable tracks come from a vertex roughly around the IP) + * -# Look for hits on overlapping petals. If two petals from the FTD overlap, a track may pass through both and thus + * create two hits in close range. For pattern recognition as it is now, they are not useful. (Imagine you try to + * guess the radius of the helix formed by a track and you have 3 hits. If these 3 hits are sensibly spaced, this is + * no problem. But now imagine, that two of them are very close. Just a small deviation in the relative position + * of those two would give entirely different results.) Such short connections are therefore looked for and stored, but + * are not dealt with until later the track candidates are found. + * -# The SegmentBuilder takes the hits and a vector of criteria.These criteria tell the SegmentBuilder when two + * hits might be part of a possible track. (For example, when we look for stiff tracks, we can form a line from one hit + * to the other and demand that this line will come close to the IP. ) + * The SegmentBuilder as the name suggests builds segments from the hits. A Segment is essentially a part of a track. + * For now a segment consists of a single hit. BUT: in contrast to a hit it knows all other segments it is + * connected to. Lets take an easy example: A track crosses layer 2,3,4 and 5 creating hits A,B,C and D. + * Then, if the track is not very ugly (huge multiple sacttering or energy loss ) the SegmentBuilder will create + * corresponding segments A,B,C and D. D is connected with C. C with B, B with A and A with the IP. + * So in these connections the true track is already contained. + * For real situations we have of course many more hits and many more tracks and background and so on. So the connections + * are plenty and if we took every possible track we could create from them, we would kill the system. + * Therefore we use the Cellular Automaton to get rid of as much as possible. + * -# The Cellular Automaton: As a result from the SegmentBuilder we get an Automaton object. It has all the segments + * the SegmentBuilder created and from us it gets some criteria to work with. (These Criteria again tell us when a + * connection makes sense and when it doesn't. Only the connections now get longer and involve first 3 hits and then 4). + * It first builds longer segments (now containing 2 hits instead of 1). These longer segments are again connected with + * each other (connections are made if the criteria allow it). The Automaton then looks for connections that go all + * the way through to the innermost layer (the IP). Segments not connected all the way through to the IP get deleted. + * It might be a good idea to look into <a href="../CellularAutomaton.pdf">Introduction to the Cellular Automaton</a> + * for more details on the Cellular Automaton. The summary is, that with every step and every criterion the CA is able + * to sort out combinatorial background until at the end we extract track candidates from it. + * -# Next we iterate over every trackcandidate we got. + * -# The hits from overlapping petals are added and every possible combination of the trackcandidate and the hits + * we could add is created. The best version is then taken (if this is switched on in the steering parameters). + * -# Cuts: First we do a helix fit. If the result (chi2 / Ndf ) is too bad the track is dropped. Then we do a + * Kalman Fit. Also if the results here (chi squared probability) are bad the track is not saved. + * -# Find the best subset: the tracks we now gathered may not be all compatible with each other (i.e. share hits). + * This situation is resolved with a best subset finder like the Hopfield Neural Network. + * -# Now the tracks are all compatible and suited our different criteria. It is time to save them. At the end they + * get finalised and stored in the output collection. + * + * + */ + //virtual void processEvent( LCEvent * evt ) ; + protected: + /* + * @return a map that links hits with overlapping hits on the petals behind + * + * @param map_sector_hits a map with first= the sector number. second = the hits in the sector. + * + * @param secSysFTD the SectorSystemFTD that is used + * + * @param distMax the maximum distance of two hits. If two hits are on the right petals and their distance is smaller + * than this, the connection will be saved in the returned map. + */ + std::map< IHit* , std::vector< IHit* > > getOverlapConnectionMap( std::map< int , std::vector< IHit* > > & map_sector_hits, + const SectorSystemFTD* secSysFTD, + float distMax); + + /* Adds hits from overlapping areas to a RawTrack in every possible combination. + * + * @return all of the resulting RawTracks + * + * @param rawTrack a RawTrack (vector of IHit* ), we want to add hits from overlapping regions + * + * @param map_hitFront_hitsBack a map, where IHit* are the keys and the values are vectors of hits that + * are in an overlapping region behind them. + */ + std::vector < RawTrack > getRawTracksPlusOverlappingHits( RawTrack rawTrack , std::map< IHit* , std::vector< IHit* > >& map_hitFront_hitsBack ); + + /* Finalises the track: fits it and adds TrackStates at IP, Calorimeter Face, inner- and outermost hit. + * Sets the subdetector hit numbers and the radius of the innermost hit. + * Also sets chi2 and Ndf. + */ + void finaliseTrack( edm4hep::Track* track ); + + /* Sets the cut off values for all the criteria + * + * This method is necessary for cases where the CA just finds too much. + * Therefore it is possible to enter a whole list of cut off values for every criterion (for every min and every max to be more precise), + * that are then used one after the other. + * If the CA finds way too many connections, we can thus make the cuts tighter and rerun it. If there are still too many + * connections, just tighten them again. + * + * This method will set the according values. It will read the passed (as steering parameter) cut off values, create + * criteria from them and store them in the corresponding vectors. + * + * If there are no new cut off values for a criterion, the last one remains. + * + * @return whether any new cut off value was set. false == there are no new cutoff values anymore + * + * @param round The number of the round we are in. I.e. the nth time we run the Cellular Automaton. + */ + bool setCriteria( unsigned round ); + + /* @return Info on the content of _map_sector_hits. Says how many hits are in each sector */ + std::string getInfo_map_sector_hits(); + + /* Input collection names */ + DataHandle<edm4hep::TrackerHitCollection> _inFTDPixelColHdl{"FTDPixelTrackerHits", Gaudi::DataHandle::Reader, this}; + DataHandle<edm4hep::TrackerHitCollection> _inFTDSpacePointColHdl{"FTDSpacePoints", Gaudi::DataHandle::Reader, this}; + DataHandle<edm4hep::TrackerHitCollection> _inFTDRawColHdl{"FTDStripTrackerHits", Gaudi::DataHandle::Reader, this}; + + /** Output collection name */ + DataHandle<edm4hep::TrackCollection> _outColHdl{"ForwardTracks", Gaudi::DataHandle::Writer, this}; + + int _nRun ; + int _nEvt ; + + /** B field in z direction */ + double _Bz; + + Gaudi::Property<double> _chi2ProbCut{this, "Chi2ProbCut", 0.005}; + Gaudi::Property<double> _helixFitMax{this, "HelixFitMax", 500}; + Gaudi::Property<double> _overlappingHitsDistMax{this, "OverlappingHitsDistMax", 3.5}; + Gaudi::Property<int> _hitsPerTrackMin{this, "HitsPerTrackMin", 4}; + Gaudi::Property<std::string> _bestSubsetFinder{this, "BestSubsetFinder", "SubsetHopfieldNN"}; + //true = when adding hits from overlapping petals, store only the best track; false = store all tracksS + Gaudi::Property<bool> _takeBestVersionOfTrack{this, "TakeBestVersionOfTrack", true}; + /* the maximum number of connections that are allowed in the automaton, if this value is surpassed, rerun + * the automaton with tighter cuts or stop it entirely. */ + Gaudi::Property<int> _maxConnectionsAutomaton{this, "MaxConnectionsAutomaton", 100000}; + Gaudi::Property<int> _maxHitsPerSector{this, "MaxHitsPerSector", 1000}; + Gaudi::Property<bool> _MSOn{this, "MultipleScatteringOn", true}; + Gaudi::Property<bool> _ElossOn{this, "EnergyLossOn", true}; + Gaudi::Property<bool> _SmoothOn{this, "SmoothOn", false}; + Gaudi::Property<std::vector<std::string> > _criteriaNames{this, "Criteria", Criteria::getAllCriteriaNamesVec()}; + Gaudi::Property<std::vector<float> > _critMinimaInit{this, "CriteriaMin", {} }; + Gaudi::Property<std::vector<float> > _critMaximaInit{this, "CriteriaMax", {} }; + + std::map<std::string, std::vector<float> > _critMinima; + std::map<std::string, std::vector<float> > _critMaxima; + + /** A map to store the hits according to their sectors */ + std::map< int , std::vector< IHit* > > _map_sector_hits; + + /** Map containing the name of a criterion and a vector of the minimum cut offs for it */ + //std::map< std::string , std::vector<float> > _critMinima; + + /** Map containing the name of a criterion and a vector of the maximum cut offs for it */ + //std::map< std::string , std::vector<float> > _critMaxima; + + /** A vector of criteria for 2 hits (2 1-hit segments) */ + std::vector <ICriterion*> _crit2Vec; + + /** A vector of criteria for 3 hits (2 2-hit segments) */ + std::vector <ICriterion*> _crit3Vec; + + /** A vector of criteria for 4 hits (2 3-hit segments) */ + std::vector <ICriterion*> _crit4Vec; + + const SectorSystemFTD* _sectorSystemFTD; + + bool _useCED; + + unsigned _nTrackCandidates; + unsigned _nTrackCandidatesPlus; + + MarlinTrk::IMarlinTrkSystem* _trkSystem; + + /** The quality of the output track collection */ + int _output_track_col_quality ; + + static const int _output_track_col_quality_GOOD; + static const int _output_track_col_quality_FAIR; + static const int _output_track_col_quality_POOR; +}; + +/** A functor to return whether two tracks are compatible: The criterion is if they share a Hit or more */ +class TrackCompatibilityShare1SP{ + public: + + inline bool operator()( ITrack* trackA, ITrack* trackB ){ + + std::vector< IHit* > hitsA = trackA->getHits(); + std::vector< IHit* > hitsB = trackB->getHits(); + + + for( unsigned i=0; i < hitsA.size(); i++){ + for( unsigned j=0; j < hitsB.size(); j++){ + if ( hitsA[i] == hitsB[j] ) return false; // a hit is shared -> incompatible + } + } + + return true; + } +}; + +/** A functor to return the quality of a track, which is currently the chi2 probability. */ +class TrackQIChi2Prob{ + public: + + inline double operator()( ITrack* track ){ return track->getChi2Prob(); } +}; + +/** A functor to return the quality of a track. + For tracks with 4 hits or more the chi2prob is mapped to* 0.5-1, with x = prob/2 + 0.5. + Tracks with 3 hits get the chi2 mapped to 0-0.5 by 1/(ln( e^2 + chi2 ); + That gives 0 for an infinite chi2 and 0.5 for a chi2 of 0. + + Reason: now 3-hit-tracks can be compared as well + */ +class TrackQISpecial{ + public: + + inline double operator()( ITrack* track ){ + if( track->getHits().size() > 3 ){ + return track->getChi2Prob()/2. +0.5; + } + else{ + return 1/( log( 7.3890561 + track->getChi2() ) ); //e^2 = 7.3890561 + } + } +}; +#endif + + + -- GitLab