From 264f5c81679126cbc8311ee5a991d5a33ae2d179 Mon Sep 17 00:00:00 2001 From: Andrea Coccaro <Andrea.Coccaro@cern.ch> Date: Tue, 16 Sep 2014 13:46:14 +0200 Subject: [PATCH] removing the interface method in the tool header files (MSVertexTools-00-01-08) --- .../MSVertexTools/cmt/requirements | 39 + .../MSVertexTools/src/MSVertexRecoTool.cxx | 1148 +++++++++++++++ .../MSVertexTools/src/MSVertexRecoTool.h | 104 ++ .../src/MSVertexTrackletTool.cxx | 1295 +++++++++++++++++ .../MSVertexTools/src/MSVertexTrackletTool.h | 88 ++ .../src/components/MSVertexTools_entries.cxx | 19 + .../src/components/MSVertexTools_load.cxx | 5 + 7 files changed, 2698 insertions(+) create mode 100644 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/cmt/requirements create mode 100755 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.cxx create mode 100755 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.h create mode 100755 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.cxx create mode 100755 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.h create mode 100644 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_entries.cxx create mode 100644 MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_load.cxx diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/cmt/requirements b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/cmt/requirements new file mode 100644 index 0000000000000..fa7d7598bf4a7 --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/cmt/requirements @@ -0,0 +1,39 @@ +package MSVertexTools + +author Daniel Ventura <ventura@cern.ch> + +# ============================================================================================ +public + +use AtlasPolicy AtlasPolicy-* + +# ============================================================================================ +private + +use AthenaKernel AthenaKernel-* Control +use AthenaBaseComps AthenaBaseComps-* Control +use DataModel DataModel-* Control +use StoreGate StoreGate-* Control +use xAODTracking xAODTracking-* Event/xAOD +use GaudiInterface GaudiInterface-* External +use AtlasCLHEP AtlasCLHEP-* External +use Identifier Identifier-* DetectorDescription +use GeoAdaptors GeoAdaptors-* DetectorDescription/GeoModel +use GeoPrimitives GeoPrimitives-* DetectorDescription +use EventPrimitives EventPrimitives-* Event +use MuonIdHelpers MuonIdHelpers-* MuonSpectrometer +use MuonReadoutGeometry MuonReadoutGeometry-* MuonSpectrometer/MuonDetDescr +use MSVertexUtils MSVertexUtils-* MuonSpectrometer/MSVertexReconstruction +use MSVertexToolInterfaces MSVertexToolInterfaces-* MuonSpectrometer/MSVertexReconstruction +use TrkParameters TrkParameters-* Tracking/TrkEvent +use TrkExInterfaces TrkExInterfaces-* Tracking/TrkExtrapolation + +# ============================================================================================ +public + +apply_pattern component_library +library MSVertexTools *.cxx components/*.cxx + +#private +#macro cppdebugflags '$(cppdebugflags_s)' +#macro_remove componentshr_linkopts "-Wl,-s" \ No newline at end of file diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.cxx b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.cxx new file mode 100755 index 0000000000000..c19c5ed07a703 --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.cxx @@ -0,0 +1,1148 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "MSVertexRecoTool.h" +#include "GaudiKernel/MsgStream.h" +#include "EventPrimitives/EventPrimitivesHelpers.h" +#include "xAODTracking/Vertex.h" +#include "CLHEP/Random/RandFlat.h" +#include "xAODTracking/VertexContainer.h" +#include "xAODTracking/VertexAuxContainer.h" +#include "xAODTracking/TrackParticle.h" + +#define MAXPLANES 100 + +/* + MS vertex reconstruction routine + See documentation at https://cds.cern.ch/record/1455664 and https://cds.cern.ch/record/1520894 + Takes Tracklets as input and creates vertices in the MS volume +*/ + +namespace Muon { + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + MSVertexRecoTool::MSVertexRecoTool (const std::string& type, const std::string& name, + const IInterface* parent) + : + AthAlgTool(type, name, parent), + m_extrapolator("Trk::Extrapolator/AtlasExtrapolator") { + declareInterface<IMSVertexRecoTool>(this); + + declareProperty("xAODVertexContainer",xAODContainerName="MSDisplacedVertex");//name of SG container + + // nominal phi angle for tracklets + declareProperty("TrackPhiAngle",m_TrackPhiAngle = 0.0); + // chi^2 probability cut + declareProperty("VxChi2Probability",VxChi2ProbCUT = 0.05); + // distance between two adjacent planes + declareProperty("VertexPlaneDist",m_VxPlaneDist = 200.); + // position of last radial plane + declareProperty("VertexMaxRadialPlane",m_VertexMaxRadialPlane = 7000.); + // position of first radial plane + declareProperty("VertexMinRadialPlane",m_VertexMinRadialPlane = 3500.); + // minimum occupancy to be considered "high occupancy" + declareProperty("MinimumHighOccupancy",m_ChamberOccupancyMin = 0.25); + // number of high occupancy chambers required to be signal like + declareProperty("MinimumNumberOfHighOccupancy",m_minHighOccupancyChambers = 2); + declareProperty("UseOldMSVxEndcapMethod",m_useOldMSVxEndcapMethod = false); + declareProperty("MaxTollDist",m_MaxTollDist = 300); + + // options to calculate vertex systematic uncertainty from the tracklet reco uncertainty + declareProperty("DoSystematicUncertainty",m_doSystematics = false); + declareProperty("BarrelTrackletUncertainty",m_BarrelTrackletUncert = 0.1); + declareProperty("EndcapTrackletUncertainty",m_EndcapTrackletUncert = 0.1); + declareProperty("RndmEngine",m_rndmEngineName,"Random track killing parameter: engine used"); + + //extrapolator + declareProperty("MyExtrapolator", m_extrapolator ); + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + StatusCode MSVertexRecoTool::initialize() { + + if( AthAlgTool::initialize().isFailure() ) { + ATH_MSG_ERROR( "Failed to initialize AthAlgTool" ); + return StatusCode::FAILURE; + } + + if(detStore()->retrieve(m_mdtIdHelper,"MDTIDHELPER").isFailure()) { + ATH_MSG_ERROR( "Failed to retrieve the mdtIdHelper" ); + return StatusCode::FAILURE; + } + + if(detStore()->retrieve(m_rpcIdHelper,"RPCIDHELPER").isFailure()) { + ATH_MSG_ERROR( "Failed to retrieve the rpcIdHelper" ); + return StatusCode::FAILURE; + } + + if(detStore()->retrieve(m_tgcIdHelper,"TGCIDHELPER").isFailure()) { + ATH_MSG_ERROR( "Failed to retrieve the tgcIdHelper" ); + return StatusCode::FAILURE; + } + + if(m_doSystematics) { + m_rndmEngine = m_rndmSvc->GetEngine("TrackletKiller"); + if(m_rndmEngine==0) { + ATH_MSG_FATAL( "Could not get RndmEngine" ); + return StatusCode::FAILURE; + } + } + + if(m_extrapolator.retrieve().isFailure()) { + ATH_MSG_FATAL( "Extrapolator could not be retrieved" ); + return StatusCode::FAILURE; + } + + PI = 3.1415927; + + return StatusCode::SUCCESS; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + MSVertexRecoTool::~MSVertexRecoTool() {} + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + StatusCode MSVertexRecoTool::findMSvertices(std::vector<Tracklet>& tracklets) { + + //if there are less than 2 tracks, return (vertexing not possible) + if(tracklets.size() < 3 || tracklets.size() > 40) return StatusCode::SUCCESS; + + std::vector<MSVertex*> vertices; + + //group the tracks + std::vector<Tracklet> BarrelTracklets; + std::vector<Tracklet> EndcapTracklets; + for(unsigned int i=0; i<tracklets.size(); ++i) { + if(tracklets.at(i).mdtChamber() <= 11 || tracklets.at(i).mdtChamber() == 52) BarrelTracklets.push_back(tracklets.at(i)); + else EndcapTracklets.push_back(tracklets.at(i)); + } + + //find any clusters of tracks & decide if tracks are from single muon + std::vector<Muon::MSVertexRecoTool::TrkCluster> BarrelClusters = findTrackClusters(BarrelTracklets); + std::vector<Muon::MSVertexRecoTool::TrkCluster> EndcapClusters = findTrackClusters(EndcapTracklets); + + //if doSystematics, remove tracklets according to the tracklet reco uncertainty and rerun the cluster finder + if(m_doSystematics) { + std::vector<Tracklet> BarrelSystTracklets,EndcapSystTracklets; + for(unsigned int i=0; i<BarrelTracklets.size(); ++i) { + float prob = CLHEP::RandFlat::shoot( m_rndmEngine, 0, 1 ); + if(prob > m_BarrelTrackletUncert) BarrelSystTracklets.push_back(BarrelTracklets.at(i)); + } + if(BarrelSystTracklets.size() >= 3) { + std::vector<Muon::MSVertexRecoTool::TrkCluster> BarrelSystClusters = findTrackClusters(BarrelSystTracklets); + for(unsigned int i=0; i<BarrelSystClusters.size(); ++i) { + BarrelSystClusters.at(i).isSystematic = true; + BarrelClusters.push_back(BarrelSystClusters.at(i)); + } + } + for(unsigned int i=0; i<EndcapTracklets.size(); ++i) { + float prob = CLHEP::RandFlat::shoot( m_rndmEngine, 0, 1 ); + if(prob > m_EndcapTrackletUncert) EndcapSystTracklets.push_back(EndcapTracklets.at(i)); + } + if(EndcapSystTracklets.size() >= 3) { + std::vector<Muon::MSVertexRecoTool::TrkCluster> EndcapSystClusters = findTrackClusters(EndcapSystTracklets); + for(unsigned int i=0; i<EndcapSystClusters.size(); ++i) { + EndcapSystClusters.at(i).isSystematic = true; + EndcapClusters.push_back(EndcapSystClusters.at(i)); + } + } + } + + ///////////////////////////////////////////VERTEX ROUTINES///////////////////////////////////////// + //find vertices in the barrel MS (vertices using barrel tracklets) + for(unsigned int i=0; i<BarrelClusters.size(); ++i) { + if(BarrelClusters[i].ntrks < 3) continue; + ATH_MSG_DEBUG( "Attempting to build vertex from " << BarrelClusters[i].ntrks << " tracklets in the barrel" ); + MSVertex* barvertex = MSVxFinder(BarrelClusters[i].tracks); + if(!barvertex) continue; + if(barvertex->getChi2Probability() > 0.05) { + HitCounter(barvertex); + if(barvertex->getNMDT() > 250 && (barvertex->getNRPC()+barvertex->getNTGC()) > 200) { + ATH_MSG_DEBUG( "Vertex found in the barrel with n_trk = " << barvertex->getNTracks() << " located at (eta,phi) = (" + << barvertex->getPosition().eta() << ", " << barvertex->getPosition().phi() << ")" ); + if(BarrelClusters[i].isSystematic) barvertex->setAuthor(3); + vertices.push_back(barvertex); + }//end minimum good vertex criteria + } + }//end loop on barrel tracklet clusters + + //find vertices in the endcap MS (vertices using endcap tracklets) + for(unsigned int i=0; i<EndcapClusters.size(); ++i) { + if(EndcapClusters[i].ntrks < 3) continue; + ATH_MSG_DEBUG( "Attempting to build vertex from " << EndcapClusters[i].ntrks << " tracklets in the endcap" ); + + MSVertex* endvertex; + if(m_useOldMSVxEndcapMethod) + endvertex = MSStraightLineVx_oldMethod(EndcapClusters[i].tracks); + else + endvertex = MSStraightLineVx(EndcapClusters[i].tracks); + + if(!endvertex) continue; + if(endvertex->getPosition().perp() < 10000 && fabs(endvertex->getPosition().z()) < 14000 && + fabs(endvertex->getPosition().z()) > 8000 && endvertex->getNTracks() >= 3) { + HitCounter(endvertex); + if(endvertex->getNMDT() > 250 && (endvertex->getNRPC()+endvertex->getNTGC()) > 200) { + ATH_MSG_DEBUG( "Vertex found in the endcap with n_trk = " << endvertex->getNTracks() << " located at (eta,phi) = (" + << endvertex->getPosition().eta() << ", " << endvertex->getPosition().phi() << ")" ); + if(EndcapClusters[i].isSystematic) endvertex->setAuthor(4); + vertices.push_back(endvertex); + }//end minimum good vertex criteria + } + }//end loop on endcap tracklet clusters + + if(vertices.size() == 0) return StatusCode::SUCCESS; + + //convert to xAOD::vertex & cleanup + xAOD::VertexContainer* xAODVxContainer = new xAOD::VertexContainer(); + xAOD::VertexAuxContainer* aux = new xAOD::VertexAuxContainer(); + xAODVxContainer->setStore( aux ); + //store the new vertices! + CHECK( evtStore()->record( xAODVxContainer, xAODContainerName ) ); + CHECK( evtStore()->record( aux, xAODContainerName + "Aux" ) ); + //fill the containers + for(std::vector<MSVertex*>::const_iterator vxIt=vertices.begin(); vxIt!=vertices.end(); ++vxIt) { + xAOD::Vertex* xAODVx = new xAOD::Vertex(); + xAODVx->setVertexType( xAOD::VxType::SecVtx ); + xAODVx->setPosition( (*vxIt)->getPosition() ); + xAODVx->setFitQuality( (*vxIt)->getChi2(), (*vxIt)->getNTracks()-1 ); + + //dress the vertex with the hit counts + xAODVx->auxdata< int >( "nMDT" ) = (*vxIt)->getNMDT(); + xAODVx->auxdata< int >( "nRPC" ) = (*vxIt)->getNRPC(); + xAODVx->auxdata< int >( "nTGC" ) = (*vxIt)->getNTGC(); + + //TODO: Add links to the tracks -- need to think how to proceed .... example code below + //unsigned int VTAVsize = vxVertex.vxTrackAtVertex()->size(); + //for (unsigned int i = 0 ; i < VTAVsize ; ++i) { + // Trk::VxTrackAtVertex* VTAV = vxVertex.vxTrackAtVertex()->at(i); + // Trk::ITrackLink* trklink = VTAV->trackOrParticleLink(); + // //do we need really to check this? or can we assume xAOD::TrackParticle objects to be there? + // // definitely we don't want those to be Rec::TrackParticle do avoid mixing up things + // Trk::LinkToXAODTrackParticle* linkToXAODTP = dynamic_cast<Trk::LinkToXAODTrackParticle*>(trklink); + // if (!linkToXAODTP) { + // ATH_MSG_WARNING ("Skipping track. Trying to convert to xAOD::Vertex a Trk::VxCandidate with links to something else than xAOD::TrackParticle."); + // } else { + // //Now set the newlink to the new xAOD vertex + // xAODVx->addTrackAtVertex(*linkToXAODTP, VTAV->vtxCompatibility()); + // } + //} + + //store the new xAOD vertex + xAODVxContainer->push_back(xAODVx); + + //delete the internal EDM vertex + delete (*vxIt); + } + + + return StatusCode::SUCCESS; + }//end find vertices + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + StatusCode MSVertexRecoTool::finalize() { + return StatusCode::SUCCESS; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + Muon::MSVertexRecoTool::TrkCluster MSVertexRecoTool::ClusterizeTracks(std::vector<Tracklet>& tracks) { + + if(tracks.size() > 40) { + ATH_MSG_DEBUG( "Too many tracks found, results probably not reliable! returning empty cluster" ); + TrkCluster emptycluster; + emptycluster.ntrks=0; + emptycluster.eta=-99999.; + emptycluster.phi=-99999.; + return emptycluster; + } + TrkCluster trkClu[50]; + TrkCluster trkClu0[50]; + trkClu[0].ntrks = 0; + trkClu[0].eta = -10; + trkClu[0].phi = -10; + int ncluster(0); + //use each tracklet as a seed for the clusters + for(std::vector<Tracklet>::iterator trkItr=tracks.begin(); trkItr!=tracks.end(); ++trkItr) { + TrkCluster clu; + clu.eta = trkItr->globalPosition().eta(); + clu.phi = trkItr->globalPosition().phi(); + clu.ntrks = 0; + for(unsigned int i=0; i<tracks.size(); ++i) clu.trks[i]=0; + trkClu[ncluster] = clu; + trkClu0[ncluster] = clu; + ++ncluster; + if(ncluster >= 49) { + TrkCluster emptycluster; + return emptycluster; + } + } + //loop on the clusters and let the center move to find the optimal cluster centers + for(int icl=0; icl<ncluster; ++icl) { + bool improvement = true; + int nitr(0); + while(improvement) { + if(nitr > 6) break; + int ntracks(0); + for(int jcl=0; jcl<ncluster; ++jcl) { + float dEta = trkClu[icl].eta - trkClu0[jcl].eta; + float dPhi = trkClu[icl].phi - trkClu0[jcl].phi; + while(fabs(dPhi) > PI) { + if(dPhi < 0) dPhi += 2*PI; + else dPhi -= 2*PI; + } + if(fabs(dEta) < 0.7 && fabs(dPhi) < PI/3.) { + ntracks++; + trkClu[icl].eta = trkClu[icl].eta - dEta/ntracks; + trkClu[icl].phi = trkClu[icl].phi - dPhi/ntracks; + while(fabs(trkClu[icl].phi) > PI) { + if(trkClu[icl].phi > 0) trkClu[icl].phi -= 2*PI; + else trkClu[icl].phi += 2*PI; + } + } + }//end jcl loop + //find the number of tracks in the new cluster + int itracks[100]; + for(int k=0; k<ncluster; ++k) itracks[k]=0; + int ntracks2(0); + for(int jcl=0; jcl<ncluster; ++jcl) { + float dEta = fabs(trkClu[icl].eta - trkClu0[jcl].eta); + float dPhi = trkClu[icl].phi - trkClu0[jcl].phi; + while(fabs(dPhi) > PI) { + if(dPhi < 0) dPhi += 2*PI; + else dPhi -= 2*PI; + } + if(dEta < 0.7 && fabs(dPhi) < PI/3.) { + ntracks2++; + itracks[jcl] = 1; + } + }//end jcl loop + if(ntracks2 > trkClu[icl].ntrks) { + trkClu[icl].ntrks = ntracks2; + for(int k=0; k<ncluster; ++k) { + trkClu[icl].trks[k] = itracks[k]; + } + } + else improvement = false; + nitr++; + }//end while + }//end icl loop + //find the best cluster + TrkCluster BestCluster = trkClu[0]; + for(int icl=1; icl<ncluster; ++icl) { + if(trkClu[icl].ntrks > BestCluster.ntrks) BestCluster = trkClu[icl]; + } + //store the tracks inside the cluster + std::vector<Tracklet> unusedTracks; + for(std::vector<Tracklet>::iterator trkItr=tracks.begin(); trkItr!=tracks.end(); ++trkItr) { + float dEta = fabs(BestCluster.eta - trkItr->globalPosition().eta()); + float dPhi = BestCluster.phi - trkItr->globalPosition().phi(); + if(dPhi > PI) dPhi -= 2*PI; + else if(dPhi < -PI) dPhi += 2*PI; + if(dEta < 0.7 && fabs(dPhi) < PI/3.) BestCluster.tracks.push_back( (*trkItr) ); + else unusedTracks.push_back( (*trkItr) ); + } + //return the best cluster and the unused tracklets + tracks = unusedTracks; + return BestCluster; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<Muon::MSVertexRecoTool::TrkCluster> MSVertexRecoTool::findTrackClusters(std::vector<Tracklet>& tracks) { + std::vector<Tracklet> trks = tracks; + std::vector<TrkCluster> clusters; + //keep making clusters until there are no more possible + while(true) { + TrkCluster clust = ClusterizeTracks(trks); + if(clust.ntrks >= 3) clusters.push_back(clust); + else break; + if(trks.size() < 3) break; + } + + if(clusters.size() == 0) { + TrkCluster clust; + clusters.push_back(clust); + } + + return clusters; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + MSVertex* MSVertexRecoTool::MSVxFinder(std::vector<Tracklet>& tracklets) { + int nTrkToVertex(0); + float NominalAngle(m_TrackPhiAngle),MaxOpenAngle(0.20+m_TrackPhiAngle); + float aveX(0),aveY(0),aveR(0),aveZ(0); + float maxError(200);//maximum allowed uncertainty for tracklets [mm] (above this cut tracklets are discarded) + + //find the average position of the tracklets + for(std::vector<Tracklet>::iterator trkItr=tracklets.begin(); trkItr!=tracklets.end(); ++trkItr) { + aveR += trkItr->globalPosition().perp(); + aveX += trkItr->globalPosition().x(); + aveY += trkItr->globalPosition().y(); + aveZ += trkItr->globalPosition().z(); + } + aveX = aveX/(float)tracklets.size(); + aveY = aveY/(float)tracklets.size(); + aveZ = aveZ/(float)tracklets.size(); + aveR = aveR/(float)tracklets.size(); + float avePhi = atan2(aveY,aveX); + while(fabs(avePhi) > PI) { + if(avePhi < 0) avePhi += 2*PI; + else avePhi -= 2*PI; + } + //calculate the two angles (theta & phi) + float LoF = atan2(aveR,aveZ);//Line of Flight (theta) + avePhi = vxPhiFinder(LoF,avePhi); + + //find the positions of the radial planes + float Rpos[MAXPLANES]; + float RadialDist = m_VertexMaxRadialPlane - m_VertexMinRadialPlane; + float LoFdist = fabs(RadialDist/sin(LoF)); + int nplanes = LoFdist/m_VxPlaneDist; + float PlaneSpacing = 3500/(nplanes-1.0); + for(int k=0; k<nplanes; ++k) Rpos[k] = m_VertexMinRadialPlane + PlaneSpacing*k; + + //loop on tracklets and create two types of track parameters -- nominal and phi shifted tracklets + std::vector<const Trk::TrackParameters*> TracksForVertexing[MAXPLANES];//vector of tracklets to be used at each vertex plane + std::vector<const Trk::TrackParameters*> TracksForErrors[MAXPLANES];//vector of tracklets to be used for uncertainty at each vertex plane + std::vector<bool> isNeutralTrack[MAXPLANES]; + for(unsigned int i=0; i<tracklets.size(); ++i) { + if(tracklets.at(i).mdtChamber() <= 11 || tracklets.at(i).mdtChamber() == 52) {//only barrel tracklets + nTrkToVertex++; + //coordinate transform variables + Amg::Vector3D trkgpos(tracklets.at(i).globalPosition().perp()*cos(avePhi), + tracklets.at(i).globalPosition().perp()*sin(avePhi), + tracklets.at(i).globalPosition().z()); + float x0 = trkgpos.x(); + float y0 = trkgpos.y(); + float r0 = trkgpos.perp(); + + ////////Tracks for computing the error////// + //////coordinate transform mess + //decide which way the tracklet gets rotated -- positive or negative phi + float anglesign = 1.0; + if((tracklets.at(i).globalPosition().phi() - avePhi) < 0) anglesign = -1.0; + float NominalTrkAng = anglesign*NominalAngle;//in case there is a nominal tracklet angle + float MaxTrkAng = anglesign*MaxOpenAngle;//the rotated tracklet phi position + + //loop over the planes + for(int k=0; k<nplanes; ++k) { + if(Rpos[k] > tracklets.at(i).globalPosition().perp()) break; //only use tracklets that start AFTER the vertex plane + //nominal tracks for vertexing + float Xp = Rpos[k]*cos(avePhi); + float Yp = Rpos[k]*sin(avePhi); + //in case there is a nominal opening angle, calculate tracklet direction + //the tracklet must cross the candidate vertex plane at the correct phi + float DelR = sqrt(sq(x0-Xp)+sq(y0-Yp))/cos(NominalAngle); + float X1 = DelR*cos(NominalTrkAng+avePhi) + Xp; + float Y1 = DelR*sin(NominalTrkAng+avePhi) + Yp; + float R1 = sqrt(sq(X1)+sq(Y1)); + float Norm = r0/R1; + X1 = X1*Norm; + Y1 = Y1*Norm; + float Dirmag = sqrt(sq(X1-Xp)+sq(Y1-Yp)); + float Xdir = (X1-Xp)/Dirmag; + float Ydir = (Y1-Yp)/Dirmag; + float trkpx = Xdir*tracklets.at(i).momentum().perp(); + float trkpy = Ydir*tracklets.at(i).momentum().perp(); + float trkpz = tracklets.at(i).momentum().z(); + //check if the tracklet has a charge & momentum measurement -- if not, set charge=1 so extrapolator will work + float charge = tracklets.at(i).charge(); + if(fabs(charge) < 0.1) { + charge = 1; //for "straight" tracks, set charge = 1 + isNeutralTrack[k].push_back(true); + } + else isNeutralTrack[k].push_back(false); + + //store the tracklet as a Trk::Perigee + Amg::Vector3D trkmomentum(trkpx,trkpy,trkpz); + Amg::Vector3D trkgpos(X1,Y1,tracklets.at(i).globalPosition().z()); + AmgSymMatrix(5) * covariance = new AmgSymMatrix(5)(tracklets.at(i).errorMatrix()); + Trk::Perigee * myPerigee = new Trk::Perigee(0.,0.,trkmomentum.phi(),trkmomentum.theta(),charge/trkmomentum.mag(),Trk::PerigeeSurface(trkgpos),covariance); + TracksForVertexing[k].push_back(myPerigee); + + //tracks for errors -- rotate the plane & recalculate the tracklet parameters + float xp = Rpos[k]*cos(avePhi); + float yp = Rpos[k]*sin(avePhi); + float delR = sqrt(sq(x0-xp)+sq(y0-yp))/cos(MaxOpenAngle); + float x1 = delR*cos(MaxTrkAng+avePhi) + xp; + float y1 = delR*sin(MaxTrkAng+avePhi) + yp; + float r1 = sqrt(sq(x1)+sq(y1)); + float norm = r0/r1; + x1 = x1*norm; + y1 = y1*norm; + float dirmag = sqrt(sq(x1-xp)+sq(y1-yp)); + float xdir = (x1-xp)/dirmag; + float ydir = (y1-yp)/dirmag; + float errpx = xdir*tracklets.at(i).momentum().perp(); + float errpy = ydir*tracklets.at(i).momentum().perp(); + float errpz = tracklets.at(i).momentum().z(); + + //store the tracklet as a Trk::Perigee + AmgSymMatrix(5) * covariance2 = new AmgSymMatrix(5)(tracklets.at(i).errorMatrix()); + Amg::Vector3D trkerrmom(errpx,errpy,errpz); + Amg::Vector3D trkerrpos(x1,y1,tracklets.at(i).globalPosition().z()); + Trk::Perigee * errPerigee = new Trk::Perigee(0.,0.,trkerrmom.phi(),trkerrmom.theta(),charge/trkerrmom.mag(),Trk::PerigeeSurface(trkerrpos),covariance2); + TracksForErrors[k].push_back(errPerigee); + }//end loop on vertex planes + }//end selection of barrel tracks + }//end loop on tracks + + //return if there are not enough tracklets + if(nTrkToVertex < 3) return NULL; + + //calculate the tracklet positions on each surface + bool boundaryCheck = true; + std::vector<float> ExtrapZ[MAXPLANES], dlength[MAXPLANES];//extrapolated position & uncertainty + std::vector<std::pair<unsigned int, unsigned int> > UsedTracks[MAXPLANES]; + std::vector<bool> ExtrapSuc[MAXPLANES];//did the extrapolation succeed? + std::vector<MSVertex*> m_vertices; + m_vertices.reserve(nplanes); + + std::vector<float> sigmaZ[MAXPLANES];//total uncertainty at each plane + std::vector<Amg::Vector3D> pAtVx[MAXPLANES];//tracklet momentum expressed at the plane + for(int k=0; k<nplanes; ++k) { + float rpos = Rpos[k]; + for(unsigned int i=0; i<TracksForVertexing[k].size(); ++i) { + Amg::Transform3D* surfaceTransformMatrix = new Amg::Transform3D; + surfaceTransformMatrix->setIdentity(); + Trk::CylinderSurface cyl(surfaceTransformMatrix, rpos, 10000.);//create the surface + //extrapolate to the surface + const Trk::AtaCylinder* extrap = dynamic_cast<const Trk::AtaCylinder*>(m_extrapolator->extrapolate(*TracksForVertexing[k].at(i), cyl,Trk::anyDirection,boundaryCheck,Trk::muon)); + if(extrap) { + //if the track is neutral just store the uncertainty due to angular uncertainty of the orignal tracklet + if(isNeutralTrack[k].at(i)) { + float pTot = sqrt(sq(TracksForVertexing[k].at(i)->momentum().perp())+sq(TracksForVertexing[k].at(i)->momentum().z())); + float dirErr = Amg::error(*TracksForVertexing[k].at(i)->covariance(),Trk::theta); + float extrapRdist = TracksForVertexing[k].at(i)->position().perp() - Rpos[k]; + float sz = fabs(20*dirErr*extrapRdist*sq(pTot)/sq(TracksForVertexing[k].at(i)->momentum().perp())); + float ExtrapErr = sz; + if(ExtrapErr > maxError) ExtrapSuc[k].push_back(false); + else { + ExtrapSuc[k].push_back(true); + std::pair<unsigned int,unsigned int> trkmap(ExtrapZ[k].size(),i); + UsedTracks[k].push_back(trkmap); + ExtrapZ[k].push_back( extrap->localPosition().y() ); + sigmaZ[k].push_back(sz); + pAtVx[k].push_back(extrap->momentum()); + dlength[k].push_back(0); + } + }//end neutral tracklets + //if the tracklet has a momentum measurement + else { + //now extrapolate taking into account the extra path length & differing magnetic field + Amg::Transform3D* srfTransMat2 = new Amg::Transform3D; + srfTransMat2->setIdentity(); + Trk::CylinderSurface cyl2(srfTransMat2, rpos, 10000.); + const Trk::AtaCylinder* extrap2 = dynamic_cast<const Trk::AtaCylinder*>(m_extrapolator->extrapolate(*TracksForErrors[k].at(i), cyl2,Trk::anyDirection,boundaryCheck,Trk::muon)); + if(extrap2) { + float sz = Amg::error(*extrap->covariance(),Trk::locY); + float zdiff = extrap->localPosition().y() - extrap2->localPosition().y(); + float ExtrapErr = sqrt(sq(sz)+sq(zdiff)); + if(ExtrapErr > maxError) ExtrapSuc[k].push_back(false); + else { + //iff both extrapolations succeed && error is acceptable, store the information + ExtrapSuc[k].push_back(true); + std::pair<unsigned int,unsigned int> trkmap(ExtrapZ[k].size(),i); + UsedTracks[k].push_back(trkmap); + ExtrapZ[k].push_back( extrap->localPosition().y() ); + sigmaZ[k].push_back(sz); + pAtVx[k].push_back(extrap->momentum()); + dlength[k].push_back(zdiff); + } + } + else ExtrapSuc[k].push_back(false);//not possible to calculate the uncertainty -- do not use tracklet in vertex + delete extrap2; + } + }//fi extrap + else ExtrapSuc[k].push_back(false);//not possible to extrapolate the tracklet + delete extrap; + }//end loop on perigeebase + }//end loop on radial planes + //vertex routine + std::vector<Amg::Vector3D> trkp[MAXPLANES]; + //loop on planes + for(int k=0; k<nplanes; ++k) { + if(ExtrapZ[k].size() < 3) continue;//require at least 3 tracklets to build a vertex + //initialize the variables used in the routine + float zLoF = Rpos[k]/tan(LoF); + float dzLoF(10); + float aveZpos(0),posWeight(0); + for(unsigned int i=0; i<ExtrapZ[k].size(); ++i) { + float ExtrapErr = sqrt(sq(sigmaZ[k][i])+sq(dlength[k][i])+sq(dzLoF)); + if(isNeutralTrack[k][i]) ExtrapErr = sqrt(sq(sigmaZ[k][i]) + sq(dzLoF)); + aveZpos += ExtrapZ[k][i]/sq(ExtrapErr); + posWeight += 1./sq(ExtrapErr); + } + //calculate the weighted average position of the tracklets + zLoF = aveZpos/posWeight; + float zpossigma(dzLoF),Chi2(0),Chi2Prob(-1); + int Nitr(0); + std::vector<unsigned int> vxtracks;//tracklets to be used in the vertex routine + std::vector<bool> blacklist;//tracklets that do not belong to the vertex + for(unsigned int i=0; i<ExtrapZ[k].size(); ++i) blacklist.push_back(false); + //minimum chi^2 iterative fit + while(true) { + vxtracks.clear(); + trkp[k].clear(); + int tmpnTrks(0); + float tmpzLoF(0),tmpzpossigma(0),tmpchi2(0),posWeight(0); + float worstdelz(0); + unsigned int iworst(0xC0FFEE); + //loop on the tracklets, find the chi^2 contribution from each tracklet + for(unsigned int i=0; i<ExtrapZ[k].size(); ++i) { + if(blacklist[i]) continue; + trkp[k].push_back(pAtVx[k][i]); + float delz = zLoF - ExtrapZ[k][i]; + float ExtrapErr = sqrt(sq(sigmaZ[k][i])+sq(dlength[k][i])+sq(dzLoF)); + float trkchi2 = sq(delz)/sq(ExtrapErr); + if(trkchi2 > worstdelz) { + iworst = i; + worstdelz = trkchi2; + } + tmpzLoF += ExtrapZ[k][i]/sq(ExtrapErr); + posWeight += 1./sq(ExtrapErr); + tmpzpossigma += sq(delz); + tmpchi2 += trkchi2; + tmpnTrks++; + }//end loop on tracks + if(tmpnTrks < 3) break;//stop searching for a vertex at this plane + tmpzpossigma = sqrt(tmpzpossigma/(float)tmpnTrks); + zLoF = tmpzLoF/posWeight; + zpossigma = tmpzpossigma; + float testChi2 = TMath::Prob(tmpchi2,tmpnTrks-1); + if(testChi2 < VxChi2ProbCUT) blacklist[iworst] = true; + else { + Chi2 = tmpchi2; + Chi2Prob = testChi2; + //loop on the tracklets and find all that belong to the vertex + for(unsigned int i=0; i<ExtrapZ[k].size(); ++i) { + float delz = zLoF - ExtrapZ[k][i]; + float ExtrapErr = sqrt(sq(sigmaZ[k][i])+sq(dlength[k][i])+sq(dzLoF)); + float trkErr = sqrt(sq(ExtrapErr)+sq(zpossigma)) + 0.001; + float trkNsigma = fabs(delz/trkErr); + if(trkNsigma < 3) vxtracks.push_back(i); + } + break;//found a vertex, stop removing tracklets! + } + if( Nitr >= ((int)ExtrapZ[k].size()-3) ) break;//stop searching for a vertex at this plane + Nitr++; + }//end while + if(vxtracks.size() < 3) continue; + std::vector<xAOD::TrackParticle*> vxTrackParticles; + //create TrackParticle vector for all tracklets used in the vertex fit + vxTrackParticles.reserve(vxtracks.size()); + for(std::vector<unsigned int>::iterator vxtrk=vxtracks.begin(); vxtrk!=vxtracks.end(); ++vxtrk) { + for(unsigned int i=0; i<UsedTracks[k].size(); ++i) { + if( (*vxtrk) == UsedTracks[k].at(i).first ) { + Tracklet trklt = tracklets.at(UsedTracks[k].at(i).second); + AmgSymMatrix(5) * covariance = new AmgSymMatrix(5)(trklt.errorMatrix()); + Trk::Perigee * myPerigee = new Trk::Perigee(0.,0.,trklt.momentum().phi(),trklt.momentum().theta(),trklt.charge()/trklt.momentum().mag(), + Trk::PerigeeSurface(trklt.globalPosition()),covariance); + xAOD::TrackParticle* trackparticle = new xAOD::TrackParticle(); + trackparticle->setFitQuality(1.,(int)trklt.mdtHitsOnTrack().size()); + trackparticle->setTrackProperties(xAOD::TrackProperties::LowPtTrack); + + trackparticle->setDefiningParameters(myPerigee->parameters()[Trk::d0], + myPerigee->parameters()[Trk::z0], + myPerigee->parameters()[Trk::phi0], + myPerigee->parameters()[Trk::theta], + myPerigee->parameters()[Trk::qOverP]); + std::vector<float> covMatrixVec; + Amg::compress(*covariance,covMatrixVec); + trackparticle->setDefiningParametersCovMatrixVec(covMatrixVec); + + vxTrackParticles.push_back(trackparticle); + + delete myPerigee; + break; + } + } + } + Amg::Vector3D m_position(Rpos[k]*cos(avePhi),Rpos[k]*sin(avePhi),zLoF); + m_vertices.push_back( new MSVertex(1,m_position,vxTrackParticles,Chi2Prob,Chi2,0,0,0) ); + }//end loop on Radial planes + + //delete the perigeebase + for(int k=0; k<nplanes; ++k) { + for(unsigned int i=0; i<TracksForVertexing[k].size(); ++i) delete TracksForVertexing[k].at(i); + for(unsigned int i=0; i<TracksForErrors[k].size(); ++i) delete TracksForErrors[k].at(i); + } + + //return an empty vertex in case none were reconstructed + if(m_vertices.size() == 0) { + return NULL; + } + + //loop on the vertex candidates and select the best based on max n(tracks) and max chi^2 probability + unsigned int bestVx(0); + for(unsigned int k=1; k<m_vertices.size(); ++k) { + if(m_vertices[k]->getChi2Probability() < VxChi2ProbCUT || m_vertices[k]->getNTracks() < 3) continue; + if(m_vertices[k]->getNTracks() < m_vertices[bestVx]->getNTracks()) continue; + if(m_vertices[k]->getNTracks() == m_vertices[bestVx]->getNTracks() && m_vertices[k]->getChi2Probability() < m_vertices[bestVx]->getChi2Probability()) continue; + bestVx = k; + } + MSVertex* myVertex = m_vertices[bestVx]->clone(); + //cleanup + for(std::vector<MSVertex*>::iterator it=m_vertices.begin(); it!=m_vertices.end(); ++it) { + delete (*it); + (*it) = 0; + } + m_vertices.clear(); + + return myVertex; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + MSVertex* MSVertexRecoTool::MSStraightLineVx(std::vector<Tracklet> trks) { + // Running set of all vertices found. The inner set is the indices of trks that are used to make the vertex + std::set<std::set<int> > prelim_vx; + + // Make a list of all 3-tracklet combinations that make vertices + for(unsigned int i = 0; i < trks.size() - 2; i++) { + for(unsigned int j = i+1; j < trks.size() - 1; j++) { + for(unsigned int k = j+1; k < trks.size(); k++) { + std::set<int> tmpTracks; + tmpTracks.insert(i); + tmpTracks.insert(j); + tmpTracks.insert(k); + + Amg::Vector3D MyVx; + MyVx = VxMinQuad(getTracklets(trks, tmpTracks)); + if(MyVx.perp() < 10000 && fabs(MyVx.z()) > 7000 && fabs(MyVx.z()) < 15000 && !EndcapHasBadTrack(getTracklets(trks, tmpTracks), MyVx)) + prelim_vx.insert(tmpTracks); + } + } + } + + // If no preliminary vertices were found from 3 tracklets, then there is no vertex and we are done. + if(prelim_vx.size() == 0) + return NULL; + + // The remaining algorithm is very time consuming for large numbers of tracklets. To control this, + // we run the old algorithm when there are too many tracklets and a vertex is found. + if(trks.size() <= 20) { + std::set<std::set<int> > new_prelim_vx = prelim_vx; + std::set<std::set<int> > old_prelim_vx; + + int foundNewVx = true; + while(foundNewVx) { + foundNewVx = false; + + old_prelim_vx = new_prelim_vx; + new_prelim_vx.clear(); + + for(std::set<std::set<int> >::iterator itr = old_prelim_vx.begin(); itr != old_prelim_vx.end(); itr++) { + for(unsigned int i_trks = 0; i_trks < trks.size(); i_trks++) { + std::set<int> tempCluster = *itr; + if(tempCluster.insert(i_trks).second) { + Amg::Vector3D MyVx = VxMinQuad(getTracklets(trks, tempCluster)); + if(MyVx.perp() < 10000 && fabs(MyVx.z()) > 7000 && fabs(MyVx.z()) < 15000 && !EndcapHasBadTrack(getTracklets(trks, tempCluster), MyVx)) { + new_prelim_vx.insert(tempCluster); + prelim_vx.insert(tempCluster); + foundNewVx = true; + } + } + } + } + } + } + else { + // Since there are 20 or more tracklets, we're going to use the old MSVx finding method. Note that + // if the old method fails, we do not return here; in this case a 3-tracklet vertex that was found + // earlier in this algorithm will be returned + MSVertex* tempVertex = MSStraightLineVx_oldMethod(trks); + if(tempVertex) + return tempVertex; + } + + // Find the preliminary vertex with the maximum number of tracklets - that is the final vertex. If + // multiple preliminary vertices with same number of tracklets, the first one found is returned + std::set<std::set<int> >::iterator prelim_vx_max = prelim_vx.begin(); + for(std::set<std::set<int> >::iterator itr = prelim_vx.begin(); itr != prelim_vx.end(); itr++) { + if((*itr).size() > (*prelim_vx_max).size()) + prelim_vx_max = itr; + } + + std::vector<Tracklet> tracklets = getTracklets(trks, *prelim_vx_max); + // use tracklets to estimate the line of flight of decaying particle + float aveX(0),aveY(0); + for(std::vector<Tracklet>::iterator trkItr=tracklets.begin(); trkItr!=tracklets.end(); ++trkItr) { + aveX += ((Tracklet)*trkItr).globalPosition().x(); + aveY += ((Tracklet)*trkItr).globalPosition().y(); + } + float tracklet_vxphi = atan2(aveY,aveX); + Amg::Vector3D MyVx = VxMinQuad(tracklets); + float vxtheta = atan2(MyVx.x(),MyVx.z()); + float vxphi = vxPhiFinder(vxtheta,tracklet_vxphi); + + Amg::Vector3D vxpos(MyVx.x()*cos(vxphi),MyVx.x()*sin(vxphi),MyVx.z()); + std::vector<xAOD::TrackParticle*> vxTrkTracks; + for(std::vector<Tracklet>::iterator tracklet = tracklets.begin(); tracklet != tracklets.end(); tracklet++) { + AmgSymMatrix(5)* covariance = new AmgSymMatrix(5)(((Tracklet)*tracklet).errorMatrix()); + Trk::Perigee* myPerigee = new Trk::Perigee(vxpos,((Tracklet)*tracklet).momentum(),0,vxpos,covariance); + const Trk::TrackParameters * trkpar = dynamic_cast<const Trk::TrackParameters*>(myPerigee); + DataVector<const Trk::TrackStateOnSurface>* trackStateOnSurfaces = new DataVector<const Trk::TrackStateOnSurface>; + trackStateOnSurfaces->push_back( new Trk::TrackStateOnSurface(0,trkpar) ); + Trk::TrackInfo info( Trk::TrackInfo::Unknown, Trk::undefined); + + xAOD::TrackParticle* myTrack = new xAOD::TrackParticle(); + myTrack->setFitQuality(1.,(int)((Tracklet)*tracklet).mdtHitsOnTrack().size()); + myTrack->setDefiningParameters(myPerigee->parameters()[Trk::d0], + myPerigee->parameters()[Trk::z0], + myPerigee->parameters()[Trk::phi0], + myPerigee->parameters()[Trk::theta], + myPerigee->parameters()[Trk::qOverP]); + + vxTrkTracks.push_back(myTrack); + } + + MSVertex* vertex = new MSVertex(2,vxpos,vxTrkTracks,1,0,0,0,0); + return vertex; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + //vertex finding routine for the endcap + MSVertex* MSVertexRecoTool::MSStraightLineVx_oldMethod(std::vector<Tracklet> trks) { + //find the line of flight of the vpion + float aveX(0),aveY(0),aveR(0),aveZ(0); + for(std::vector<Tracklet>::iterator trkItr=trks.begin(); trkItr!=trks.end(); ++trkItr) { + aveX += trkItr->globalPosition().x(); + aveY += trkItr->globalPosition().y(); + aveR += trkItr->globalPosition().perp(); + aveZ += trkItr->globalPosition().z(); + } + float vxphi = atan2(aveY,aveX); + + Amg::Vector3D MyVx(0,0,0); + std::vector<Tracklet> tracks = RemoveBadTrk(trks,MyVx); + if(tracks.size() < 2) return NULL; + + while(true) { + MyVx = VxMinQuad(tracks); + std::vector<Tracklet> Tracks = RemoveBadTrk(tracks,MyVx); + if(tracks.size() == Tracks.size()) break; + tracks = Tracks; + } + if(tracks.size() >= 3 && MyVx.x() > 0) { + float vxtheta = atan2(MyVx.x(),MyVx.z()); + vxphi = vxPhiFinder(vxtheta,vxphi); + Amg::Vector3D vxpos(MyVx.x()*cos(vxphi),MyVx.x()*sin(vxphi),MyVx.z()); + //make Trk::Track for each tracklet used in the vertex fit + std::vector<xAOD::TrackParticle*> vxTrackParticles; + for(std::vector<Tracklet>::iterator trklt=tracks.begin(); trklt!=tracks.end(); ++trklt) { + AmgSymMatrix(5) * covariance = new AmgSymMatrix(5)(trklt->errorMatrix()); + Trk::Perigee* myPerigee = new Trk::Perigee(0.,0.,trklt->momentum().phi(),trklt->momentum().theta(),trklt->charge()/trklt->momentum().mag(),Trk::PerigeeSurface(trklt->globalPosition()),covariance); + xAOD::TrackParticle* trackparticle = new xAOD::TrackParticle(); + trackparticle->setFitQuality(1.,(int)trklt->mdtHitsOnTrack().size()); + trackparticle->setTrackProperties(xAOD::TrackProperties::LowPtTrack); + + trackparticle->setDefiningParameters(myPerigee->parameters()[Trk::d0], + myPerigee->parameters()[Trk::z0], + myPerigee->parameters()[Trk::phi0], + myPerigee->parameters()[Trk::theta], + myPerigee->parameters()[Trk::qOverP]); + std::vector<float> covMatrixVec; + Amg::compress(*covariance,covMatrixVec); + trackparticle->setDefiningParametersCovMatrixVec(covMatrixVec); + + vxTrackParticles.push_back(trackparticle); + + delete myPerigee; + + } + + MSVertex* vertex = new MSVertex(2,vxpos,vxTrackParticles,1,(float)vxTrackParticles.size(),0,0,0); + return vertex; + } + return NULL; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<Tracklet> MSVertexRecoTool::RemoveBadTrk(std::vector<Tracklet> tracks, Amg::Vector3D Vx) { + float MaxTollDist = 300;//max distance between the vertex and tracklet [mm] + std::vector<Tracklet> Tracks; + if(Vx.x() == 0 && Vx.z() == 0) return tracks; + //loop on all tracks and find the worst + float WorstTrkDist = MaxTollDist; + unsigned int iWorstTrk = 0xC0FFEE; + for(unsigned int i=0; i<tracks.size(); ++i) { + float TrkSlope = tan(tracks.at(i).getML1seg().alpha()); + float TrkInter = tracks.at(i).getML1seg().globalPosition().perp() - tracks.at(i).getML1seg().globalPosition().z()*TrkSlope; + float dist = fabs((TrkSlope*Vx.z() - Vx.x() + TrkInter)/sqrt(sq(TrkSlope) + 1)); + if(dist > MaxTollDist && dist > WorstTrkDist) { + iWorstTrk = i; + WorstTrkDist = dist; + } + } + + //Remove the worst track from the list + for(unsigned int i=0; i<tracks.size(); ++i) { + if(i != iWorstTrk) Tracks.push_back(tracks.at(i)); + } + return Tracks; + } + + std::vector<Tracklet> MSVertexRecoTool::getTracklets(std::vector<Tracklet> trks, std::set<int> tracklet_subset) { + std::vector<Tracklet> returnVal; + for(std::set<int>::iterator itr = tracklet_subset.begin(); itr != tracklet_subset.end(); itr++) { + if((unsigned int) *itr > trks.size()) + ATH_MSG_ERROR( "ERROR - Index out of bounds in getTracklets"); + returnVal.push_back(trks.at(*itr)); + } + + return returnVal; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + bool MSVertexRecoTool::EndcapHasBadTrack(std::vector<Tracklet> tracks, Amg::Vector3D Vx) { + float MaxTollDist = m_MaxTollDist; + if(Vx.x() == 0 && Vx.z() == 0) return true; + //loop on all tracks and find the worst + float WorstTrkDist = MaxTollDist; + for(std::vector<Tracklet>::iterator track = tracks.begin(); track != tracks.end(); track++) { + float TrkSlope = tan(((Tracklet)*track).getML1seg().alpha()); + float TrkInter = ((Tracklet)*track).getML1seg().globalPosition().perp() - ((Tracklet)*track).getML1seg().globalPosition().z()*TrkSlope; + float dist = fabs((TrkSlope*Vx.z() - Vx.x() + TrkInter)/sqrt(sq(TrkSlope) + 1)); + if(dist > MaxTollDist && dist > WorstTrkDist) { + return true; + } + } + + // No tracks found that are too far, so it is okay. + return false; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + //core algorithm for endcap vertex reconstruction + Amg::Vector3D MSVertexRecoTool::VxMinQuad(std::vector<Tracklet> tracks) { + float s(0.),sx(0.),sy(0.),sxy(0.),sxx(0.),d(0.); + float sigma = 1.; + for(unsigned int i=0; i<tracks.size(); ++i) { + float TrkSlope = tan(tracks.at(i).getML1seg().alpha()); + float TrkInter = tracks.at(i).getML1seg().globalPosition().perp() - tracks.at(i).getML1seg().globalPosition().z()*TrkSlope; + s += 1./(sq(sigma)); + sx += TrkSlope/(sq(sigma)); + sxx += sq(TrkSlope)/sq(sigma); + sy += TrkInter/sq(sigma); + sxy += (TrkSlope*TrkInter)/sq(sigma); + } + d = s*sxx - sq(sx); + float Rpos = (sxx*sy - sx*sxy)/d; + float Zpos = (sx*sy - s*sxy)/d; + + Amg::Vector3D MyVx(Rpos,0,Zpos); + + return MyVx; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + //vertex phi location -- determined from the RPC/TGC hits + float MSVertexRecoTool::vxPhiFinder(float theta,float phi) { + float nmeas(0); + float sinphi(0),cosphi(0); + float eta = -1*log(tan(0.5*theta)); + if(fabs(eta) < 1.5) { + const Muon::RpcPrepDataContainer* rpcTES = 0; + if(evtStore()->retrieve(rpcTES,"RPC_Measurements").isFailure() || !rpcTES) { + ATH_MSG_WARNING( "No RpcPrepDataContainer found in SG!" ); + return 0; + } + Muon::RpcPrepDataContainer::const_iterator RpcItr = rpcTES->begin(); + Muon::RpcPrepDataContainer::const_iterator RpcItrE = rpcTES->end(); + for(; RpcItr != RpcItrE; ++RpcItr) { + Muon::RpcPrepDataCollection::const_iterator rpcItr = (*RpcItr)->begin(); + Muon::RpcPrepDataCollection::const_iterator rpcItrE = (*RpcItr)->end(); + for(; rpcItr != rpcItrE; ++rpcItr) { + if(m_rpcIdHelper->measuresPhi((*rpcItr)->identify())) { + float rpcEta = (*rpcItr)->globalPosition().eta(); + float rpcPhi = (*rpcItr)->globalPosition().phi(); + float dphi = phi - rpcPhi; + if(dphi > PI) dphi -= 2*PI; + else if(dphi < -PI) dphi += 2*PI; + float deta = eta - rpcEta; + float DR = sqrt(sq(deta)+sq(dphi)); + if(DR < 0.6) { + nmeas++; + sinphi += sin(rpcPhi); + cosphi += cos(rpcPhi); + } + } + } + } + } + if(fabs(eta) > 0.5) { + const Muon::TgcPrepDataContainer* tgcTES = 0; + if(evtStore()->retrieve(tgcTES,"TGC_Measurements").isFailure()) { + ATH_MSG_WARNING( "No TgcPrepDataContainer found in SG!" ); + return 0; + } + Muon::TgcPrepDataContainer::const_iterator TgcItr = tgcTES->begin(); + Muon::TgcPrepDataContainer::const_iterator TgcItrE = tgcTES->end(); + for(; TgcItr != TgcItrE; ++TgcItr) { + Muon::TgcPrepDataCollection::const_iterator tgcItr = (*TgcItr)->begin(); + Muon::TgcPrepDataCollection::const_iterator tgcItrE = (*TgcItr)->end(); + for(; tgcItr != tgcItrE; ++tgcItr) { + if(m_tgcIdHelper->isStrip((*tgcItr)->identify())) { + float tgcEta = (*tgcItr)->globalPosition().eta(); + float tgcPhi = (*tgcItr)->globalPosition().phi(); + float dphi = phi - tgcPhi; + if(dphi > PI) dphi -= 2*PI; + else if(dphi < -PI) dphi += 2*PI; + float deta = eta - tgcEta; + float DR = sqrt(sq(deta)+sq(dphi)); + if(DR < 0.6) { + nmeas++; + sinphi += sin(tgcPhi); + cosphi += cos(tgcPhi); + } + } + } + } + } + float vxphi = phi; + if(nmeas > 0) vxphi = atan2(sinphi/nmeas,cosphi/nmeas); + return vxphi; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + //count the hits (MDT, RPC & TGC) around the vertex + void MSVertexRecoTool::HitCounter(MSVertex* MSRecoVx) { + int nHighOccupancy(0); + const Muon::MdtPrepDataContainer* mdtTES = 0; + if(evtStore()->retrieve(mdtTES,"MDT_DriftCircles").isFailure()) { + ATH_MSG_ERROR( "Unable to retrieve the MDT hits" ); + } + const Muon::RpcPrepDataContainer* rpcTES = 0; + if(evtStore()->retrieve(rpcTES,"RPC_Measurements").isFailure()) { + ATH_MSG_ERROR( "Unable to retrieve the RPC hits" ); + } + const Muon::TgcPrepDataContainer* tgcTES = 0; + if(evtStore()->retrieve(tgcTES,"TGC_Measurements").isFailure()) { + ATH_MSG_ERROR( "Unable to retrieve the MDT hits" ); + } + + //MDTs -- count the number around the vertex + int nmdt(0); + Muon::MdtPrepDataContainer::const_iterator MDTItr = mdtTES->begin(); + Muon::MdtPrepDataContainer::const_iterator MDTItrE = mdtTES->end(); + for(; MDTItr != MDTItrE; ++MDTItr) { + if( (*MDTItr)->size() == 0) continue; + Muon::MdtPrepDataCollection::const_iterator mdt = (*MDTItr)->begin(); + Muon::MdtPrepDataCollection::const_iterator mdtE = (*MDTItr)->end(); + Amg::Vector3D ChamberCenter = (*mdt)->detectorElement()->center(); + float deta = fabs(MSRecoVx->getPosition().eta() - ChamberCenter.eta()); + if(deta > 0.6) continue; + float dphi = MSRecoVx->getPosition().phi() - ChamberCenter.phi(); + if(dphi > PI) dphi -= 2*PI; + else if(dphi < -PI) dphi += 2*PI; + if( fabs(dphi) > 0.6 ) continue; + int nChHits(0); + Identifier id = (*mdt)->identify(); + float nTubes = (m_mdtIdHelper->tubeLayerMax(id) - m_mdtIdHelper->tubeLayerMin(id) + 1)*(m_mdtIdHelper->tubeMax(id) - m_mdtIdHelper->tubeMin(id) + 1); + for(; mdt != mdtE; ++mdt) { + if((*mdt)->adc() < 50) continue; + if((*mdt)->status() != 1) continue; + if((*mdt)->localPosition()[Trk::locR] == 0.) continue; + nChHits++; + } + nmdt += nChHits; + double ChamberOccupancy = nChHits/nTubes; + if(ChamberOccupancy > m_ChamberOccupancyMin) nHighOccupancy++; + } + + ATH_MSG_DEBUG( "Found " << nHighOccupancy << " chambers near the MS vertex with occupancy greater than " + << m_ChamberOccupancyMin ); + if(nHighOccupancy < m_minHighOccupancyChambers) return; + + //RPC -- count the number around the vertex + int nrpc(0); + Muon::RpcPrepDataContainer::const_iterator RpcItr = rpcTES->begin(); + Muon::RpcPrepDataContainer::const_iterator RpcItrE = rpcTES->end(); + for(; RpcItr != RpcItrE; ++RpcItr) { + Muon::RpcPrepDataCollection::const_iterator rpcItr = (*RpcItr)->begin(); + Muon::RpcPrepDataCollection::const_iterator rpcItrE = (*RpcItr)->end(); + for(; rpcItr != rpcItrE; ++rpcItr) { + float rpcEta = (*rpcItr)->globalPosition().eta(); + float rpcPhi = (*rpcItr)->globalPosition().phi(); + float dphi = MSRecoVx->getPosition().phi() - rpcPhi; + if(dphi > PI) dphi -= 2*PI; + else if(dphi < -PI) dphi += 2*PI; + float deta = MSRecoVx->getPosition().eta() - rpcEta; + float DR = sqrt(sq(deta)+sq(dphi)); + if(DR < 0.6) nrpc++; + if(DR > 1.2) break; + } + } + //TGC -- count the number around the vertex + int ntgc(0); + Muon::TgcPrepDataContainer::const_iterator TgcItr = tgcTES->begin(); + Muon::TgcPrepDataContainer::const_iterator TgcItrE = tgcTES->end(); + for(; TgcItr != TgcItrE; ++TgcItr) { + Muon::TgcPrepDataCollection::const_iterator tgcItr = (*TgcItr)->begin(); + Muon::TgcPrepDataCollection::const_iterator tgcItrE = (*TgcItr)->end(); + for(; tgcItr != tgcItrE; ++tgcItr) { + float tgcEta = (*tgcItr)->globalPosition().eta(); + float tgcPhi = (*tgcItr)->globalPosition().phi(); + float dphi = MSRecoVx->getPosition().phi() - tgcPhi; + if(dphi > PI) dphi -= 2*PI; + else if(dphi < -PI) dphi += 2*PI; + float deta = MSRecoVx->getPosition().eta() - tgcEta; + float DR = sqrt(sq(deta)+sq(dphi)); + if(DR < 0.6) ntgc++; + if(DR > 1.2) break; + } + } + + MSRecoVx->setNMDT(nmdt); + MSRecoVx->setNRPC(nrpc); + MSRecoVx->setNTGC(ntgc); + + return; + } + +}//end namespace diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.h b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.h new file mode 100755 index 0000000000000..72e4519717a9c --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexRecoTool.h @@ -0,0 +1,104 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef MSVVERTEXRECOTOOL_H +#define MSVVERTEXRECOTOOL_H + +#include "GaudiKernel/AlgTool.h" +#include "StoreGate/StoreGateSvc.h" +#include "AthenaBaseComps/AthAlgTool.h" +#include "GaudiKernel/ToolHandle.h" +#include "GeoPrimitives/GeoPrimitives.h" +#include "EventPrimitives/EventPrimitives.h" +#include "MuonReadoutGeometry/MdtReadoutElement.h" +#include "GeoAdaptors/GeoMuonHits.h" +#include "Identifier/Identifier.h" +#include "TrkExInterfaces/IExtrapolator.h" +#include "TrkParameters/TrackParameters.h" +#include "MSVertexToolInterfaces/IMSVertexRecoTool.h" +#include "MSVertexUtils/Tracklet.h" +#include "MSVertexUtils/MSVertex.h" +#include "AthenaKernel/IAtRndmGenSvc.h" + + +#include <utility> +#include <vector> + +class StoreGate; +class MsgStream; +class Identifier; + +namespace Muon { + + class MSVertexRecoTool : virtual public IMSVertexRecoTool, public AthAlgTool + { + + public : + /** default constructor */ + MSVertexRecoTool (const std::string& type, const std::string& name, const IInterface* parent); + /** destructor */ + virtual ~MSVertexRecoTool(); + + virtual StatusCode initialize(void); + virtual StatusCode finalize(void); + + struct TrkCluster { + float eta; + float phi; + int ntrks; + int trks[100]; + bool isSystematic; + std::vector<Tracklet> tracks; + }; + + + private: + //add tool handles, private variables, etc here + ToolHandle <Trk::IExtrapolator> m_extrapolator; + std::string xAODContainerName; + const MdtIdHelper* m_mdtIdHelper; + const RpcIdHelper* m_rpcIdHelper; + const TgcIdHelper* m_tgcIdHelper; + float m_BarrelTrackletUncert; + float m_EndcapTrackletUncert; + float m_TrackPhiAngle; + float VxChi2ProbCUT; + float m_VxPlaneDist; + float m_VertexMaxRadialPlane; + float m_VertexMinRadialPlane; + int m_minHighOccupancyChambers; + int m_ChamberOccupancyMin; + int m_useOldMSVxEndcapMethod; + float m_MaxTollDist; + float PI; + bool m_doSystematics; + + CLHEP::HepRandomEngine* m_rndmEngine; + std::string m_rndmEngineName; + IAtRndmGenSvc* m_rndmSvc; + + + public: + + StatusCode findMSvertices(std::vector<Tracklet>& traklets); + + private: + MSVertex* MSVxFinder(std::vector<Tracklet>& tracklets);//barrel vertex reco algorithm + MSVertex* MSStraightLineVx(std::vector<Tracklet> trks);//endcap vertex reco algorithm + MSVertex* MSStraightLineVx_oldMethod(std::vector<Tracklet> trks); + float vxPhiFinder(float theta,float phi);//vertex phi location reco algorithm + void HitCounter(MSVertex* MSRecoVx);//counts MDT, RPC & TGC around a reco'd vertex + std::vector<TrkCluster> findTrackClusters(std::vector<Tracklet>& tracklets);//group tracklets into clusters -- vertex reco runs on each cluster of tracklets + TrkCluster ClusterizeTracks(std::vector<Tracklet>& tracks);//core algorithm for creating the clusters + Amg::Vector3D VxMinQuad(std::vector<Tracklet> tracks);//endcap vertex reco core + std::vector<Tracklet> RemoveBadTrk(std::vector<Tracklet> tracklets, Amg::Vector3D Vx);//endcap vertex algo track selector + bool EndcapHasBadTrack(std::vector<Tracklet> tracklets, Amg::Vector3D Vx); + std::vector<Tracklet> getTracklets(std::vector<Tracklet> trks, std::set<int> tracklet_subset); + float sq(float x) { return (x)*(x); } + + }; + + +} +#endif //MSVERTEXRECOTOOL_H diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.cxx b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.cxx new file mode 100755 index 0000000000000..15b4109456213 --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.cxx @@ -0,0 +1,1295 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "MSVertexTrackletTool.h" +#include "DataModel/DataVector.h" +#include "EventPrimitives/EventPrimitivesHelpers.h" +#include "TrkParameters/TrackParameters.h" +#include "xAODTracking/TrackParticle.h" +#include "xAODTracking/TrackParticleContainer.h" +#include "xAODTracking/TrackParticleAuxContainer.h" + +/* + Tracklet reconstruction tool + See documentation at https://cds.cern.ch/record/1455664 and https://cds.cern.ch/record/1520894 + + station name reference: + Barrel | Endcap + 0 == BIL 6 == BEE | 13 == EIL 21 == EOS + 1 == BIS 7 == BIR | 14 == EEL 49 == EIS + 2 == BML 8 == BMF | 15 == EES + 3 == BMS 9 == BOF | 17 == EML + 4 == BOL 10 == BOG | 18 == EMS + 5 == BOS 52 == BIM | 20 == EOL + +*/ + + +namespace Muon { + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + const MdtIdHelper* MSVertexTrackletTool::mdtCompareIdHelper; + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + MSVertexTrackletTool::MSVertexTrackletTool (const std::string& type, const std::string& name, + const IInterface* parent) + : + AthAlgTool(type, name, parent) { + declareInterface<IMSVertexTrackletTool>(this); + + declareProperty("xAODTrackParticleContainer",m_TPContainer = "MSonlyTracklets"); + // max residual for tracklet seeds + declareProperty("SeedResidual",m_SeedResidual = 5); + // segment fitter chi^2 cut + declareProperty("MinSegFinderChi2Prob",m_minSegFinderChi2 = 0.05); + // maximum delta_alpha allowed in barrel MS chambers + declareProperty("BarrelDeltaAlpha",m_BarrelDeltaAlphaCut = 0.100); + // maximum delta_b allowed + declareProperty("maxDeltab",m_maxDeltabCut = 3); + // maximum delta_alpha allowed in the endcap MS chambers + declareProperty("EndcapDeltaAlpha",m_EndcapDeltaAlphaCut = 0.015); + // tight tracklet requirement (affects efficiency - disabled by default) + declareProperty("TightTrackletRequirement",m_tightTrackletRequirement = false); + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + StatusCode MSVertexTrackletTool::initialize() { + + if( AthAlgTool::initialize().isFailure() ) { + ATH_MSG_ERROR("Failed to initialize AthAlgTool " ); + return StatusCode::FAILURE; + } + + if(detStore()->retrieve(m_mdtIdHelper,"MDTIDHELPER").isFailure()) { + ATH_MSG_ERROR("Failed to retrieve the mdtIdHelper"); + return StatusCode::FAILURE; + } + mdtCompareIdHelper = m_mdtIdHelper; + + //CONSTANTS + PI = 3.1415927; + //Delta Alpha Constants -- p = k/(delta_alpha) + k_BIL = 28.4366;//MeV*mrad + k_BML = 62.8267;//MeV*mrad + k_BMS = 53.1259;//MeV*mrad + k_BOL = 29.7554;//MeV*mrad + + return StatusCode::SUCCESS; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + MSVertexTrackletTool::~MSVertexTrackletTool() { + + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + StatusCode MSVertexTrackletTool::finalize() { + return StatusCode::SUCCESS; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + StatusCode MSVertexTrackletTool::findTracklets(std::vector<Tracklet>& tracklets) { + + //sort the MDT hits into chambers & MLs + std::vector<std::vector<Muon::MdtPrepData*> > SortedMdt; + nMDT = SortMDThits(SortedMdt); + + //loop over the MDT hits and find segments + //select the tube combinations to be fit + /*Select hits in at least 2 layers and require hits be ordered by increasing tube number (see diagrams below). + ( )( )(3)( ) ( )(3)( )( ) ( )( )( )( ) ( )( )(3)( ) ( )(2)(3)( ) + ( )(2)( )( ) ( )(2)( )( ) ( )(2)(3)( ) ( )(1)(2)( ) ( )(1)( )( ) + (1)( )( )( ) (1)( )( )( ) (1)( )( )( ) ( )( )( )( ) ( )( )( )( ) + Barrel selection criteria: |z_mdt1 - z_mdt2| < 100 mm, |z_mdt1 - z_mdt3| < 160 mm + Endcap selection criteria: |r_mdt1 - r_mdt2| < 100 mm, |r_mdt1 - r_mdt3| < 160 mm + */ + + std::vector<TrackletSegment> segs[6][2][16];//single ML segment array (indicies [station type][ML][sector]) + std::vector<std::vector<Muon::MdtPrepData*> >::const_iterator ChamberItr = SortedMdt.begin(); + for(; ChamberItr != SortedMdt.end(); ++ChamberItr) { + std::vector<TrackletSegment> mlsegments; + //loop on hits inside the chamber + std::vector<Muon::MdtPrepData*>::const_iterator mdt1 = ChamberItr->begin(); + std::vector<Muon::MdtPrepData*>::const_iterator mdtEnd = ChamberItr->end(); + int stName = m_mdtIdHelper->stationName((*mdt1)->identify()); + int stEta = m_mdtIdHelper->stationEta((*mdt1)->identify()); + if(stName == 6 || stName == 14 || stName == 15) continue; //ignore hits from BEE, EEL and EES + if(stName == 1 && fabs(stEta) >= 7) continue;//ignore hits from BIS7/8 + //convert to the hardware sector [1-16] + int sector = 2*(m_mdtIdHelper->stationPhi((*mdt1)->identify())); + if(stName == 0 || stName == 2 || stName == 4 || stName == 13 || stName == 17 || stName == 20) sector -= 1; + //information about the chamber we are looking at + int maxLayer = m_mdtIdHelper->tubeLayerMax((*mdt1)->identify()); + int ML = m_mdtIdHelper->multilayer((*mdt1)->identify()); + for(; mdt1 != mdtEnd; ++mdt1) { + int tl1 = m_mdtIdHelper->tubeLayer((*mdt1)->identify()); + if(tl1 == maxLayer) break;//require hits in at least 2 layers + std::vector<Muon::MdtPrepData*>::const_iterator mdt2 = (mdt1+1); + for(; mdt2 != mdtEnd; ++mdt2) { + //select the correct combinations + int tl2 = m_mdtIdHelper->tubeLayer((*mdt2)->identify()); + if(mdt1 == mdt2 || (tl2 - tl1) > 1 || (tl2 - tl1) < 0 ) continue; + if(fabs((*mdt2)->globalPosition().z() - (*mdt1)->globalPosition().z()) > 100 && (stName <= 11 || stName == 52)) continue; + //if chamber is endcap, use distance in r + if( (stName > 11 && stName <=21) || stName == 49 ) { + float mdt1R = (*mdt1)->globalPosition().perp(); + float mdt2R = (*mdt2)->globalPosition().perp(); + if(fabs(mdt1R-mdt2R) > 100) continue; + } + if( (tl2-tl1) == 0 && (m_mdtIdHelper->tube((*mdt2)->identify()) - m_mdtIdHelper->tube((*mdt1)->identify())) < 0) continue; + //find the third hit + std::vector<Muon::MdtPrepData*>::const_iterator mdt3 = (mdt2+1); + for(; mdt3 != mdtEnd; ++mdt3) { + //reject the bad tube combinations + int tl3 = m_mdtIdHelper->tubeLayer((*mdt3)->identify()); + if(mdt1 == mdt3 || mdt2 == mdt3) continue; + if( (tl3-tl2) > 1 || (tl3-tl2) < 0 || (tl3-tl1) <= 0) continue; + if( (tl3-tl2) == 0 && (m_mdtIdHelper->tube((*mdt3)->identify()) - m_mdtIdHelper->tube((*mdt2)->identify())) < 0) continue; + if( fabs((*mdt3)->globalPosition().z() - (*mdt1)->globalPosition().z()) > 160 && (stName <= 11 || stName == 52) ) continue; + //if chamber is endcap, use distance in r + if( (stName > 11 && stName <=21) || stName == 49 ) { + float mdt1R = (*mdt1)->globalPosition().perp(); + float mdt3R = (*mdt3)->globalPosition().perp(); + if(fabs(mdt1R-mdt3R) > 160) continue; + } + //store and fit the good combinations + std::vector<Muon::MdtPrepData*> mdts; + mdts.push_back((*mdt1)); + mdts.push_back((*mdt2)); + mdts.push_back((*mdt3)); + std::vector<TrackletSegment> tmpSegs = TrackletSegmentFitter(mdts); + for(unsigned int i=0; i<tmpSegs.size(); ++i) mlsegments.push_back(tmpSegs.at(i)); + }//end loop on mdt3 + }//end loop on mdt2 + }//end loop on mdt1 + + //store the reconstructed segments according to station, ML and sector + //station identifiers: + // 0 == BI 1 == BM 2 == BO + // 3 == EI 4 == EM 5 == EO + if(stName == 0 || stName == 1 || stName == 7 || stName == 52) for(unsigned int k=0; k<mlsegments.size(); ++k) segs[0][ML-1][sector-1].push_back(mlsegments.at(k)); + else if(stName == 2 || stName == 3 || stName == 8) for(unsigned int k=0; k<mlsegments.size(); ++k) segs[1][ML-1][sector-1].push_back(mlsegments.at(k)); + else if(stName == 4 || stName == 5 || stName == 9 || stName == 10) for(unsigned int k=0; k<mlsegments.size(); ++k) segs[2][ML-1][sector-1].push_back(mlsegments.at(k)); + else if(stName == 13 || stName == 49) for(unsigned int k=0; k<mlsegments.size(); ++k) segs[3][ML-1][sector-1].push_back(mlsegments.at(k)); + else if(stName == 17 || stName == 18) for(unsigned int k=0; k<mlsegments.size(); ++k) segs[4][ML-1][sector-1].push_back(mlsegments.at(k)); + else if(stName == 20 || stName == 21) for(unsigned int k=0; k<mlsegments.size(); ++k) segs[5][ML-1][sector-1].push_back(mlsegments.at(k)); + else ATH_MSG_WARNING( "Found segments belonging to chamber " << stName << " that have not been stored" ); + }//end loop on mdt chambers + + //Combine/remove duplicate segments + std::vector<TrackletSegment> CleanSegs[6][2][16]; + int nSegs(0); + for(int st=0; st<6; ++st) { + for(int ml=0; ml<2; ++ml) { + for(int sector=0; sector<16; ++sector) { + if(segs[st][ml][sector].size() > 0) { + CleanSegs[st][ml][sector] = CleanSegments(segs[st][ml][sector]); + nSegs += CleanSegs[st][ml][sector].size(); + } + } + } + } + + for(int st=0; st<6; ++st) { + m_DeltaAlphaCut = m_BarrelDeltaAlphaCut; + if(st >= 3) m_DeltaAlphaCut = m_EndcapDeltaAlphaCut; + for(int sector=0; sector<16; ++sector) { + for(unsigned int i1=0; i1<CleanSegs[st][0][sector].size(); ++i1) { + //Set the delta alpha cut depending on station type + int stName = CleanSegs[st][0][sector].at(i1).mdtChamber(); + if( stName == 0) m_DeltaAlphaCut = k_BIL/750.0; + else if(stName == 2) m_DeltaAlphaCut = k_BML/750.0; + else if(stName == 3) m_DeltaAlphaCut = k_BMS/750.0; + else if(stName == 4) m_DeltaAlphaCut = k_BOL/750.0; + else if(stName == 7) m_DeltaAlphaCut = k_BIL/750.0; + else if(stName == 8) m_DeltaAlphaCut = k_BML/750.0; + else if(stName == 9) m_DeltaAlphaCut = k_BOL/750.0; + else if(stName == 10) m_DeltaAlphaCut = k_BOL/750.0; + else if(stName == 52) m_DeltaAlphaCut = k_BIL/750.0; + else if(stName <= 11) m_DeltaAlphaCut = 0.02; + else m_DeltaAlphaCut = m_EndcapDeltaAlphaCut; + //loop on ML2 segments from same sector + for(unsigned int i2=0; i2<CleanSegs[st][1][sector].size(); ++i2) { + if(CleanSegs[st][0][sector].at(i1).mdtChamber()!=CleanSegs[st][1][sector].at(i2).mdtChamber() || + CleanSegs[st][0][sector].at(i1).mdtChEta()!=CleanSegs[st][1][sector].at(i2).mdtChEta()) continue; + float deltaAlpha = CleanSegs[st][0][sector].at(i1).alpha()-CleanSegs[st][1][sector].at(i2).alpha(); + bool goodDeltab = DeltabCalc(CleanSegs[st][0][sector].at(i1),CleanSegs[st][1][sector].at(i2)); + //select the good combinations + if(fabs(deltaAlpha) < m_DeltaAlphaCut && goodDeltab) { + if(st < 3) {//barrel chambers + float charge = 1; + if( deltaAlpha*CleanSegs[st][0][sector].at(i1).globalPosition().z()*tan(CleanSegs[st][0][sector].at(i1).alpha()) < 0 ) charge = -1; + float pTot = TrackMomentum(CleanSegs[st][0][sector].at(i1).mdtChamber(),deltaAlpha); + if(pTot < 800.) continue; + if(pTot >= 9999.) { + //if we find a straight track, try to do a global refit to minimize the number of duplicates + charge = 0; + std::vector<Muon::MdtPrepData*> mdts = CleanSegs[st][0][sector].at(i1).mdtHitsOnTrack(); + std::vector<Muon::MdtPrepData*> mdts2 = CleanSegs[st][1][sector].at(i2).mdtHitsOnTrack(); + for(unsigned int k=0; k<mdts2.size(); ++k) mdts.push_back(mdts2.at(k)); + std::vector<TrackletSegment> CombinedSeg = TrackletSegmentFitter(mdts); + if(CombinedSeg.size() > 0) { + //calculate momentum components & uncertainty + float Trk1overPErr = TrackMomentumError(CombinedSeg[0]); + float pT = pTot*sin(CombinedSeg[0].alpha()); + float pz = pTot*cos(CombinedSeg[0].alpha()); + Amg::Vector3D momentum(pT*cos(CombinedSeg[0].globalPosition().phi()),pT*sin(CombinedSeg[0].globalPosition().phi()),pz); + //create the error matrix + AmgSymMatrix(5) matrix; + matrix.setIdentity(); + matrix(0,0) = sq(CombinedSeg[0].rError());//delta locR + matrix(1,1) = sq(CombinedSeg[0].zError());//delta locz + matrix(2,2) = sq(0.00000000001);//delta phi (~0 because we explicitly rotate all tracklets into the middle of the chamber) + matrix(3,3) = sq(CombinedSeg[0].alphaError());//delta theta + matrix(4,4) = sq(Trk1overPErr);//delta 1/p + Tracklet tmpTrk(CombinedSeg[0],momentum,matrix,charge); + ATH_MSG_DEBUG( "Track " << tracklets.size() <<" found with p = (" << momentum.x() << ", " << momentum.y() << ", " << momentum.z() + << ") and |p| = " << tmpTrk.momentum().mag() << " MeV" ); + tracklets.push_back(tmpTrk); + } + } + else { + //tracklet has a measurable momentum + float Trk1overPErr = TrackMomentumError(CleanSegs[st][0][sector].at(i1),CleanSegs[st][1][sector].at(i2)); + float pT = pTot*sin(CleanSegs[st][0][sector].at(i1).alpha()); + float pz = pTot*cos(CleanSegs[st][0][sector].at(i1).alpha()); + Amg::Vector3D momentum(pT*cos(CleanSegs[st][0][sector].at(i1).globalPosition().phi()), + pT*sin(CleanSegs[st][0][sector].at(i1).globalPosition().phi()),pz); + //create the error matrix + AmgSymMatrix(5) matrix; + matrix.setIdentity(); + matrix(0,0) = sq(CleanSegs[st][0][sector].at(i1).rError());//delta locR + matrix(1,1) = sq(CleanSegs[st][0][sector].at(i1).zError());//delta locz + matrix(2,2) = sq(0.00000000001);//delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber) + matrix(3,3) = sq(CleanSegs[st][0][sector].at(i1).alphaError());//delta theta + matrix(4,4) = sq(Trk1overPErr);//delta 1/p + Tracklet tmpTrk(CleanSegs[st][0][sector].at(i1),CleanSegs[st][1][sector].at(i2),momentum,matrix,charge); + ATH_MSG_DEBUG( "Track " << tracklets.size() <<" found with p = (" << momentum.x() << ", " << momentum.y() << ", " << momentum.z() + << ") and |p| = " << tmpTrk.momentum().mag() << " MeV" ); + tracklets.push_back(tmpTrk); + } + }//end barrel chamber selection + else if(st >= 3) {//endcap tracklets + //always straight tracklets (no momentum measurement possible) + std::vector<Muon::MdtPrepData*> mdts = CleanSegs[st][0][sector].at(i1).mdtHitsOnTrack(); + std::vector<Muon::MdtPrepData*> mdts2 = CleanSegs[st][1][sector].at(i2).mdtHitsOnTrack(); + for(unsigned int k=0; k<mdts2.size(); ++k) mdts.push_back(mdts2.at(k)); + std::vector<TrackletSegment> CombinedSeg = TrackletSegmentFitter(mdts); + if(CombinedSeg.size() > 0) { + float charge = 0; + float pT = 100000.0*sin(CombinedSeg[0].alpha()); + float pz = 100000.0*cos(CombinedSeg[0].alpha()); + //create the error matrix + AmgSymMatrix(5) matrix; + matrix.setIdentity(); + matrix(0,0) = sq(CombinedSeg[0].rError());//delta locR + matrix(1,1) = sq(CombinedSeg[0].zError());//delta locz + matrix(2,2) = sq(0.0000001);//delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber) + matrix(3,3) = sq(CombinedSeg[0].alphaError());//delta theta + matrix(4,4) = sq(0.00005);//delta 1/p (endcap tracks are straight lines with no momentum that we can measure ...) + Amg::Vector3D momentum(pT*cos(CombinedSeg[0].globalPosition().phi()), + pT*sin(CombinedSeg[0].globalPosition().phi()),pz); + Tracklet tmpTrk(CombinedSeg[0],momentum,matrix,charge); + tracklets.push_back(tmpTrk); + } + }//end endcap tracklet selection + + }//end tracklet selection (delta alpha & delta b) + + }//end loop on ML2 segments + }//end loop on ML1 segments + }//end loop on sectors + }//end loop on stations + + //Resolve any ambiguous tracklets + tracklets = ResolveAmbiguousTracklets(tracklets); + + //convert from tracklets to Trk::Tracks + convertToTrackParticles(tracklets); + + return StatusCode::SUCCESS; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + //convert tracklets to Trk::Track and store in a TrackCollection + void MSVertexTrackletTool::convertToTrackParticles(std::vector<Tracklet>& tracklets) { + + xAOD::TrackParticleContainer* container = new xAOD::TrackParticleContainer(); + if(evtStore()->record( container, m_TPContainer ).isFailure()) { + ATH_MSG_WARNING("Failed to record the xAOD::TrackParticleContainer with key " << m_TPContainer); + return; + } + + xAOD::TrackParticleAuxContainer* aux = new xAOD::TrackParticleAuxContainer(); + if(evtStore()->record( aux, m_TPContainer + "Aux" ).isFailure()) { + ATH_MSG_WARNING("Failed to record the xAOD::TrackParticleAuxContainer with key " << m_TPContainer << "Aux"); + return; + } + container->setStore(aux); + + for(std::vector<Tracklet>::iterator trkItr=tracklets.begin(); trkItr!=tracklets.end(); ++trkItr) { + + //create the Trk::Perigee for the tracklet + AmgSymMatrix(5) * covariance = new AmgSymMatrix(5)(trkItr->errorMatrix()); + Trk::Perigee * myPerigee = new Trk::Perigee(0.,0.,trkItr->momentum().phi(),trkItr->momentum().theta(), + trkItr->charge()/trkItr->momentum().mag(), + Trk::PerigeeSurface(trkItr->globalPosition()),covariance); + + //create, store & define the xAOD::TrackParticle + xAOD::TrackParticle* trackparticle = new xAOD::TrackParticle(); + + container->push_back( trackparticle ); + + trackparticle->setFitQuality(1.,(float)trkItr->mdtHitsOnTrack().size()); + trackparticle->setTrackProperties(xAOD::TrackProperties::LowPtTrack); + trackparticle->setDefiningParameters(myPerigee->parameters()[Trk::d0], + myPerigee->parameters()[Trk::z0], + myPerigee->parameters()[Trk::phi0], + myPerigee->parameters()[Trk::theta], + myPerigee->parameters()[Trk::qOverP]); + + std::vector<float> covMatrixVec; + Amg::compress(*covariance,covMatrixVec); + trackparticle->setDefiningParametersCovMatrixVec(covMatrixVec); + + //cleanup memory + delete myPerigee; + } + return; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + int MSVertexTrackletTool::SortMDThits(std::vector<std::vector<Muon::MdtPrepData*> >& SortedMdt) { + + SortedMdt.clear(); + int nMDT(0); + const Muon::MdtPrepDataContainer* mdtTES = 0; + if(evtStore()->retrieve(mdtTES,"MDT_DriftCircles").isFailure()) { + ATH_MSG_FATAL( "No MdtPrepDataContainer found!!" ); + return 0; + } + Muon::MdtPrepDataContainer::const_iterator MDTItr = mdtTES->begin(); + Muon::MdtPrepDataContainer::const_iterator MDTItrE = mdtTES->end(); + + for(; MDTItr != MDTItrE; ++MDTItr) { + + Muon::MdtPrepDataCollection::const_iterator mpdc = (*MDTItr)->begin(); + Muon::MdtPrepDataCollection::const_iterator mpdcE = (*MDTItr)->end(); + + int stName = m_mdtIdHelper->stationName((*mpdc)->identify()); + + // Doesn't consider hits belonging to chambers BEE, EEL and EES + if(stName == 6 || stName == 14 || stName == 15) continue; + + // Doesn't consider hits belonging to chambers BIS7 and BIS8 + if(stName == 1 && fabs(m_mdtIdHelper->stationEta((*mpdc)->identify())) >= 7) continue; + + for(; mpdc != mpdcE; ++mpdc) { + + // Removes noisy hits + if((*mpdc)->adc() < 50) continue; + + // Removes dead modules or out of time hits + if((*mpdc)->status() != 1) continue; + + // Removes tubes out of readout during drift time + if((*mpdc)->localPosition()[Trk::locR] == 0.) continue; + + // Should not happen but is seen in the barrel + if((*mpdc)->tdc() < 300 || (*mpdc)->tdc() > 2100) continue; + + // Doesn't include hits on tubes that have multiple, good hits + Identifier mpdc_id = (*mpdc)->identify(); + int hitIsUnique = 1; + Muon::MdtPrepDataContainer::const_iterator MDTItr2= mdtTES->begin(); + for(;MDTItr2 != mdtTES->end(); MDTItr2++) { + Muon::MdtPrepDataCollection::const_iterator mpdc2 = (*MDTItr)->begin(); + Muon::MdtPrepDataCollection::const_iterator mpdcE2 = (*MDTItr)->end(); + for(;mpdc2 != mpdcE2; mpdc2++) { + if(mpdc == mpdc2) continue; + if((*mpdc2)->adc() < 50) continue; + if((*mpdc2)->status() != 1) continue; + if((*mpdc2)->localPosition()[Trk::locR] == 0.) continue; + + if((*mpdc2)->identify() == mpdc_id) { + hitIsUnique = 0; + } + } + } + if(!hitIsUnique) continue; + + nMDT++; + + int foundChamberML = 0; + for(unsigned int i = 0; i < SortedMdt.size(); i++) { + if(SortedMdt.size() == 0) continue; + + Identifier id1 = SortedMdt.at(i).at(0)->identify(); + Identifier id2 = (*mpdc)->identify(); + if(SortMDT(id1, id2)) { + foundChamberML = 1; + SortedMdt.at(i).push_back(*mpdc); + } + } + if(!foundChamberML) { + std::vector<Muon::MdtPrepData*> tempMdt; + tempMdt.push_back((*mpdc)); + SortedMdt.push_back(tempMdt); + } + + }//end MdtPrepDataCollection + }//end MdtPrepDataContaier + //loop over ML, remove any with occupancy > 75% + for(std::vector<std::vector<Muon::MdtPrepData*> >::iterator mlIt=SortedMdt.begin(); mlIt!=SortedMdt.end(); ++mlIt) { + Identifier id = (*mlIt)[0]->identify(); + float nTubes = (m_mdtIdHelper->tubeMax(id) - m_mdtIdHelper->tubeMin(id))*(m_mdtIdHelper->tubeLayerMax(id))*(m_mdtIdHelper->multilayerMax(id)); + float frac = (*mlIt).size()/nTubes; + if(frac > 0.75) (*mlIt).clear(); + else std::sort(mlIt->begin(),mlIt->end(),MSVertexTrackletTool::mdtComp);//sort the MDTs by layer and tube number + } + + return nMDT; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + bool MSVertexTrackletTool::SortMDT(Identifier& i1, Identifier& i2) { + if(m_mdtIdHelper->stationName(i1) != m_mdtIdHelper->stationName(i2)) return false; + if(m_mdtIdHelper->stationEta(i1) != m_mdtIdHelper->stationEta(i2)) return false; + if(m_mdtIdHelper->stationPhi(i1) != m_mdtIdHelper->stationPhi(i2)) return false; + if(m_mdtIdHelper->multilayer(i1) != m_mdtIdHelper->multilayer(i2)) return false; + return true; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + bool MSVertexTrackletTool::mdtComp(const Muon::MdtPrepData* mprd1, const Muon::MdtPrepData* mprd2) { + //mdts sorted by layer and tube number + if(mdtCompareIdHelper->tubeLayer(mprd1->identify()) > mdtCompareIdHelper->tubeLayer(mprd2->identify())) return false; + if(mdtCompareIdHelper->tubeLayer(mprd1->identify()) < mdtCompareIdHelper->tubeLayer(mprd2->identify())) return true; + if(mdtCompareIdHelper->tube(mprd1->identify()) < mdtCompareIdHelper->tube(mprd2->identify())) return true; + return false; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<TrackletSegment> MSVertexTrackletTool::TrackletSegmentFitter(std::vector<Muon::MdtPrepData*>& mdts) { + + //create the segment seeds + std::vector<std::pair<float,float> > SeedParams = SegSeeds(mdts); + //fit the segments + std::vector<TrackletSegment> segs = TrackletSegmentFitterCore(mdts,SeedParams); + + return segs; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<std::pair<float,float> > MSVertexTrackletTool::SegSeeds(std::vector<Muon::MdtPrepData*>& mdts) { + + std::vector<std::pair<float,float> > SeedParams; + //create seeds by drawing the 4 possible lines tangent to the two outermost drift circles + //see http://cds.cern.ch/record/620198 (section 4.3) for description of the algorithm + //keep all seeds which satisfy the criterion: residual(mdt 2) < m_SeedResidual + //NOTE: here there is an assumption that each MDT has a radius of 30mm + // -- needs to be revisited when the small tubes in sectors 12 & 14 are installed + float x1 = mdts.front()->globalPosition().z(); + float y1 = mdts.front()->globalPosition().perp(); + float r1 = fabs(mdts.front()->localPosition()[Trk::locR]); + + float x2 = mdts.back()->globalPosition().z(); + float y2 = mdts.back()->globalPosition().perp(); + float r2 = fabs(mdts.back()->localPosition()[Trk::locR]); + + float DeltaX = x2 - x1; + float DeltaY = y2 - y1; + float DistanceOfCenters = sqrt(DeltaX*DeltaX + DeltaY*DeltaY); + if(DistanceOfCenters < 30) return SeedParams; + float Alpha0 = acos(DeltaX/DistanceOfCenters); + + //First seed + float phi = mdts.front()->globalPosition().phi(); + float RSum = r1 + r2; + if(RSum > DistanceOfCenters) return SeedParams; + float Alpha1 = asin(RSum/DistanceOfCenters); + float line_theta = Alpha0 + Alpha1; + float z_line = x1 + r1*sin(line_theta); + float rho_line = y1 - r1*cos(line_theta); + + Amg::Vector3D gPos1(rho_line*cos(phi), rho_line*sin(phi), z_line); + Amg::Vector3D gDir(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + Amg::Vector3D globalDir1(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + float gSlope1 = (globalDir1.perp()/globalDir1.z()); + float gInter1 = gPos1.perp() - gSlope1*gPos1.z(); + float resid = SeedResiduals(mdts,gSlope1,gInter1); + if(resid < m_SeedResidual) SeedParams.push_back( std::pair<float,float>(gSlope1,gInter1) ); + //Second seed + line_theta = Alpha0 - Alpha1; + z_line = x1 - r1*sin(line_theta); + rho_line = y1 + r1*cos(line_theta); + Amg::Vector3D gPos2(rho_line*cos(phi), rho_line*sin(phi), z_line); + Amg::Vector3D globalDir2(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + float gSlope2= (globalDir2.perp()/globalDir2.z()); + float gInter2 = gPos2.perp() - gSlope2*gPos2.z(); + resid = SeedResiduals(mdts,gSlope2,gInter2); + if(resid < m_SeedResidual) SeedParams.push_back( std::pair<float,float>(gSlope2,gInter2) ); + + float Alpha2 = asin(fabs(r2-r1)/DistanceOfCenters); + if(r1 < r2) { + //Third seed + line_theta = Alpha0 +Alpha2; + z_line = x1 - r1*sin(line_theta); + rho_line = y1 + r1*cos(line_theta); + + Amg::Vector3D gPos3(rho_line*cos(phi), rho_line*sin(phi), z_line); + Amg::Vector3D globalDir3(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + float gSlope3= (globalDir3.perp()/globalDir3.z()); + float gInter3 = gPos3.perp() - gSlope3*gPos3.z(); + resid = SeedResiduals(mdts,gSlope3,gInter3); + if(resid < m_SeedResidual) SeedParams.push_back( std::pair<float,float>(gSlope3,gInter3) ); + + //Fourth seed + line_theta = Alpha0 - Alpha2; + z_line = x1 + r1*sin(line_theta); + rho_line = y1 - r1*cos(line_theta); + + Amg::Vector3D gPos4(rho_line*cos(phi), rho_line*sin(phi), z_line); + Amg::Vector3D globalDir4(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + float gSlope4= (globalDir4.perp()/globalDir4.z()); + float gInter4 = gPos4.perp() - gSlope4*gPos4.z(); + resid = SeedResiduals(mdts,gSlope4,gInter4); + if(resid < m_SeedResidual) SeedParams.push_back( std::pair<float,float>(gSlope4,gInter4) ); + } + else { + //Third seed + line_theta = Alpha0 +Alpha2; + z_line = x1 + r1*sin(line_theta); + rho_line = y1 - r1*cos(line_theta); + + Amg::Vector3D gPos3(rho_line*cos(phi), rho_line*sin(phi), z_line); + Amg::Vector3D globalDir3(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + float gSlope3= (globalDir3.perp()/globalDir3.z()); + float gInter3 = gPos3.perp() - gSlope3*gPos3.z(); + resid = SeedResiduals(mdts,gSlope3,gInter3); + if(resid < m_SeedResidual) SeedParams.push_back( std::pair<float,float>(gSlope3,gInter3) ); + + //Fourth seed + line_theta = Alpha0 - Alpha2; + z_line = x1 - r1*sin(line_theta); + rho_line = y1 + r1*cos(line_theta); + + Amg::Vector3D gPos4(rho_line*cos(phi), rho_line*sin(phi), z_line); + Amg::Vector3D globalDir4(cos(phi)*sin(line_theta), sin(phi)*sin(line_theta), cos(line_theta)); + float gSlope4= (globalDir4.perp()/globalDir4.z()); + float gInter4 = gPos4.perp() - gSlope4*gPos4.z(); + resid = SeedResiduals(mdts,gSlope4,gInter4); + if(resid < m_SeedResidual) SeedParams.push_back( std::pair<float,float>(gSlope4,gInter4) ); + } + + return SeedParams; + } + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + float MSVertexTrackletTool::SeedResiduals(std::vector<Muon::MdtPrepData*>& mdts, float slope, float inter) { + //calculate the residual of the MDTs not used to create the seed + float resid = 0; + for(unsigned int i=1; i<(mdts.size()-1); ++i) { + float mdtR = mdts.at(i)->globalPosition().perp(); + float mdtZ = mdts.at(i)->globalPosition().z(); + float res = fabs( (mdts.at(i)->localPosition()[Trk::locR] - fabs((mdtR-inter-slope*mdtZ)/sqrt(sq(slope)+1)))/( Amg::error(mdts.at(i)->localCovariance(),Trk::locR) ) ); + if(res > resid) resid = res; + } + return resid; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<TrackletSegment> MSVertexTrackletTool::TrackletSegmentFitterCore(std::vector<Muon::MdtPrepData*>& mdts,std::vector<std::pair<float,float> >& SeedParams){ + std::vector<TrackletSegment> segs; + int stName = m_mdtIdHelper->stationName(mdts.at(0)->identify()); + float mlmidpt = 0; + if(stName <= 11 || stName == 52) mlmidpt = sqrt(sq(mdts.at(0)->detectorElement()->center().x())+sq(mdts.at(0)->detectorElement()->center().y())); + else if(stName <= 21 || stName == 49) mlmidpt = mdts.at(0)->detectorElement()->center().z(); + else return segs; + for(unsigned int i_p=0; i_p<SeedParams.size(); ++i_p) { + //Min chi^2 fit from "Precision of the ATLAS Muon Spectrometer" -- M. Woudstra + //http://cds.cern.ch/record/620198?ln=en (section 4.3) + float chi2(0); + float s(0),sz(0),sy(0); + //loop on the mdt hits, find the weighted center + for(unsigned int i=0; i<mdts.size(); ++i) { + float mdt_y = sqrt(sq(mdts.at(i)->globalPosition().x())+sq(mdts.at(i)->globalPosition().y())); + float mdt_z = mdts.at(i)->globalPosition().z(); + float sigma2 = sq(Amg::error(mdts.at(i)->localCovariance(),Trk::locR)); + s += 1/sigma2; + sz += mdt_z/sigma2; + sy += mdt_y/sigma2; + } + float yc = sy/s; + float zc = sz/s; + + //Find the initial parameters of the fit + float alpha = atan2(SeedParams.at(i_p).first,1.0); + if(alpha < 0) alpha += PI; + float dalpha = 0; + float d = (SeedParams.at(i_p).second - yc + zc*SeedParams.at(i_p).first)*cos(alpha); + float dd = 0; + + //require segments to point to the second ML + if( fabs(cos(alpha)) > 0.97 && (stName <= 11 || stName == 52) ) continue; + if( fabs(cos(alpha)) < 0.03 && ((stName > 11 && stName < 22) || stName == 49) )continue; + + //calculate constants used in the fit + float sPz(0),sPy(0),sPyy(0),sPzz(0),sPyz(0),sPyyzz(0); + for(unsigned int i=0; i<mdts.size(); ++i) { + float mdt_y = sqrt(sq(mdts.at(i)->globalPosition().x())+sq(mdts.at(i)->globalPosition().y())); + float mdt_z = mdts.at(i)->globalPosition().z(); + float sigma2 = sq(Amg::error(mdts.at(i)->localCovariance(),Trk::locR)); + sPz += (mdt_z-zc)/sigma2; + sPy += (mdt_y-yc)/sigma2; + sPyy += sq(mdt_y-yc)/sigma2; + sPzz += sq(mdt_z-zc)/sigma2; + sPyz += (mdt_y-yc)*(mdt_z-zc)/sigma2; + sPyyzz += ((mdt_y-yc)-(mdt_z-zc))*((mdt_y-yc)+(mdt_z-zc))/sigma2; + } + + //iterative fit + int Nitr = 0; + float deltaAlpha = 0; + float deltad = 0; + while(true) { + float sumRyi(0),sumRzi(0),sumRi(0); + chi2 = 0; + Nitr++; + for(unsigned int i=0; i<mdts.size(); ++i) { + float mdt_y = sqrt(sq(mdts.at(i)->globalPosition().x())+sq(mdts.at(i)->globalPosition().y())); + float mdt_z = mdts.at(i)->globalPosition().z(); + float yPi = -(mdt_z-zc)*sin(alpha) + (mdt_y-yc)*cos(alpha) - d; + float signR = -1*yPi/fabs(yPi); + float sigma2 = sq(Amg::error(mdts.at(i)->localCovariance(),Trk::locR)); + float ri = signR*mdts.at(i)->localPosition()[Trk::locR]; + //// + sumRyi += ri*(mdt_y-yc)/sigma2; + sumRzi += ri*(mdt_z-zc)/sigma2; + sumRi += ri/sigma2; + // + chi2 += sq(yPi+ri)/sigma2; + } + float bAlpha = -1*sPyz + cos(alpha)*(sin(alpha)*sPyyzz +2*cos(alpha)*sPyz + sumRzi) + sin(alpha)*sumRyi; + float AThTh = sPyy + cos(alpha)*(2*sin(alpha)*sPyz - cos(alpha)*sPyyzz); + //the new alpha & d parameters + float alphaNew = alpha + bAlpha/AThTh; + + float dNew = sumRi/s; + //the errors + dalpha = sqrt(1/AThTh); + dd = sqrt(1/s); + deltaAlpha = fabs(alphaNew-alpha); + deltad = fabs(d-dNew); + //test if the new segment is different than the previous + if(deltaAlpha < 0.0000005 && deltad < 0.000005) break; + alpha = alphaNew; + d = dNew; + //Guard against infinite loops + if(Nitr > 10) break; + }//end while loop + + //find the chi^2 probability of the segment + float chi2Prob = TMath::Prob(chi2,mdts.size()-2); + //keep only "good" segments + if(chi2Prob > m_minSegFinderChi2) { + float z0 = zc - d*sin(alpha); + float dz0 = sqrt(sq(dd*sin(alpha))+sq(d*dalpha*cos(alpha))); + float y0 = yc + d*cos(alpha); + float dy0 = sqrt(sq(dd*cos(alpha))+sq(d*dalpha*sin(alpha))); + //find the hit pattern, which side of the wire did the particle pass? (1==Left, 2==Right) + /* + ( )(/O)( ) + (./)( )( ) == RRL == 221 + (O/)( )( ) + */ + int pattern(0); + if(mdts.size() > 8) pattern = -1;//with more then 8 MDTs the pattern is unique + else { + for(unsigned int k=0; k<mdts.size(); ++k) { + int base = pow(10,k); + float mdtR = sqrt(sq(mdts.at(k)->globalPosition().x())+sq(mdts.at(k)->globalPosition().y())); + float mdtZ = mdts.at(k)->globalPosition().z(); + float zTest = (mdtR-y0)/tan(alpha) + z0 - mdtZ; + if(zTest > 0) pattern += 2*base; + else pattern += base; + } + } + + //information about the MDT chamber containing the tracklet + int chEta = m_mdtIdHelper->stationEta(mdts.at(0)->identify()); + int chPhi = m_mdtIdHelper->stationPhi(mdts.at(0)->identify()); + + //find the position of the tracklet in the global frame + float mdtPhi = mdts.at(0)->globalPosition().phi(); + Amg::Vector3D segpos(y0*cos(mdtPhi),y0*sin(mdtPhi),z0); + //create the tracklet + TrackletSegment MyTrackletSegment(stName,chEta,chPhi,mlmidpt,alpha,dalpha,segpos,dy0,dz0,mdts,pattern); + segs.push_back(MyTrackletSegment); + if(pattern == -1) break;//stop if we find a segment with more than 8 hits (guaranteed to be unique!) + } + }//end loop on segment seeds + + //in case more than 1 segment is reconstructed, check if there are duplicates using the hit patterns + if(segs.size() > 1) { + std::vector<TrackletSegment> tmpSegs; + for(unsigned int i1=0; i1<segs.size(); ++i1) { + bool isUnique = true; + int pattern1 = segs.at(i1).getHitPattern(); + for(unsigned int i2=(i1+1); i2<segs.size(); ++i2) { + if(pattern1 == -1) break; + int pattern2 = segs.at(i2).getHitPattern(); + if(pattern1 == pattern2) isUnique = false; + } + if(isUnique) tmpSegs.push_back(segs.at(i1)); + } + segs = tmpSegs; + } + + //return the unique segments + return segs; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<TrackletSegment> MSVertexTrackletTool::CleanSegments(std::vector<TrackletSegment>& Segs) { + std::vector<TrackletSegment> CleanSegs; + std::vector<TrackletSegment> segs = Segs; + bool keepCleaning(true); + int nItr(0); + while(keepCleaning) { + nItr++; + keepCleaning = false; + for(std::vector<TrackletSegment>::iterator it=segs.begin(); it!=segs.end(); ++it) { + if(it->isCombined()) continue; + std::vector<TrackletSegment> segsToCombine; + float tanTh1 = tan(it->alpha()); + float r1 = it->globalPosition().perp(); + float zi1 = it->globalPosition().z() - r1/tanTh1; + //find all segments with similar parameters & attempt to combine + for(std::vector<TrackletSegment>::iterator sit=(it+1); sit!=segs.end(); ++sit) { + if(sit->isCombined()) continue; + if(it->mdtChamber() != sit->mdtChamber()) continue;//require the segments are in the same chamber + if( (it->mdtChEta())*(sit->mdtChEta()) < 0 ) continue;//check both segments are on the same side of the detector + if( it->mdtChPhi() != sit->mdtChPhi() ) continue;//in the same sector + if(fabs(it->alpha() - sit->alpha()) > 0.005) continue;//same trajectory + float tanTh2 = tan(sit->alpha()); + float r2 = sit->globalPosition().perp(); + float zi2 = sit->globalPosition().z() - r2/tanTh2; + //find the distance at the midpoint between the two segments + float rmid = (r1+r2)/2.; + float z1 = rmid/tanTh1 + zi1; + float z2 = rmid/tanTh2 + zi2; + float zdist = fabs(z1 - z2); + if(zdist < 0.5) { + segsToCombine.push_back( *sit ); + sit->isCombined(true); + } + }//end sit loop + + //if the segment is unique, keep it + if(segsToCombine.size() == 0) { + CleanSegs.push_back( *it ); + } + //else, combine all like segments & refit + else if(segsToCombine.size() >= 1 ) { + //create a vector of all unique MDT hits in the segments + std::vector<Muon::MdtPrepData*> mdts = it->mdtHitsOnTrack(); + for(unsigned int i=0; i<segsToCombine.size(); ++i) { + std::vector<Muon::MdtPrepData*> tmpmdts = segsToCombine[i].mdtHitsOnTrack(); + for(std::vector<Muon::MdtPrepData*>::iterator mit=tmpmdts.begin(); mit!=tmpmdts.end(); ++mit) { + bool isNewHit(true); + for(std::vector<Muon::MdtPrepData*>::iterator msit=mdts.begin(); msit!=mdts.end(); ++msit) { + if((*mit)->identify() == (*msit)->identify()) { + isNewHit = false; + break; + } + } + if(isNewHit) mdts.push_back( *mit ); + } + }//end segsToCombine loop + + //only need to combine if there are extra hits added to the first segment + if(mdts.size() > it->mdtHitsOnTrack().size()) { + std::vector<TrackletSegment> refitsegs = TrackletSegmentFitter(mdts); + //if the refit fails, what to do? + if(refitsegs.size() == 0) { + if(segsToCombine.size() == 1) { + segsToCombine[0].isCombined(false); + CleanSegs.push_back( *it ); + CleanSegs.push_back(segsToCombine[0]); + } + else { + //loop on the mdts and count the number of segments that share that hit + std::vector<int> nSeg; + for(unsigned int i=0; i<mdts.size(); ++i) { + nSeg.push_back(0); + //hit belongs to the first segment + for(unsigned int k=0; k<it->mdtHitsOnTrack().size(); ++k) { + if(it->mdtHitsOnTrack()[k]->identify() == mdts[i]->identify()) { + nSeg[i]++; + break; + } + } + //hit belongs to one of the duplicate segments + for(unsigned int k=0; k<segsToCombine.size(); ++k) { + for(unsigned int m=0; m<segsToCombine[k].mdtHitsOnTrack().size(); ++m) { + if(segsToCombine[k].mdtHitsOnTrack()[m]->identify() == mdts[i]->identify()) { + nSeg[i]++; + break; + } + }//end loop on mdtHitsOnTrack + }//end loop on segsToCombine + }//end loop on mdts + + //loop over the duplicates and remove the MDT used by the fewest segments until the fit converges + bool keeprefitting(true); + int nItr2(0); + while(keeprefitting) { + nItr2++; + int nMinSeg(nSeg[0]); + Muon::MdtPrepData* minmdt = mdts[0]; + std::vector<int> nrfsegs; + std::vector<Muon::MdtPrepData*> refitmdts; + //loop on MDTs, identify the overlapping set of hits + for(unsigned int i=1; i<mdts.size(); ++i) { + if(nSeg[i] < nMinSeg) { + refitmdts.push_back(minmdt); + nrfsegs.push_back(nMinSeg); + minmdt = mdts[i]; + nMinSeg = nSeg[i]; + } + else { + refitmdts.push_back(mdts[i]); + nrfsegs.push_back(nSeg[i]); + } + } + //reset the list of MDTs & the minimum number of segments an MDT must belong to + mdts = refitmdts; + nSeg = nrfsegs; + //try to fit the new set of MDTs + refitsegs = TrackletSegmentFitter(mdts); + if(refitsegs.size() > 0) { + for(std::vector<TrackletSegment>::iterator rfit=refitsegs.begin(); rfit!=refitsegs.end(); ++rfit) { + CleanSegs.push_back( *rfit ); + } + keeprefitting = false;//stop refitting if segments are found + } + else if(mdts.size() <= 3) { + CleanSegs.push_back( *it ); + keeprefitting = false; + } + if(nItr2 > 10) break; + }//end while + } + } + else { + keepCleaning = true; + for(std::vector<TrackletSegment>::iterator rfit=refitsegs.begin(); rfit!=refitsegs.end(); ++rfit) { + CleanSegs.push_back( *rfit ); + } + } + } + //if there are no extra MDT hits, keep only the first segment as unique + else CleanSegs.push_back( *it ); + } + }//end it loop + if(keepCleaning) { + segs = CleanSegs; + CleanSegs.clear(); + } + if(nItr > 10) break; + }//end while + + return CleanSegs; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + bool MSVertexTrackletTool::DeltabCalc(TrackletSegment& ML1seg, TrackletSegment& ML2seg) { + float ChMid = (ML1seg.getChMidPoint() + ML2seg.getChMidPoint())/2.0; + //Calculate the Delta b (see http://inspirehep.net/record/1266438) + float mid1(100),mid2(1000); + float deltab(100); + if(ML1seg.mdtChamber() <= 11 || ML1seg.mdtChamber() == 52) { + //delta b in the barrel + mid1 = (ChMid - ML1seg.globalPosition().perp())/tan(ML1seg.alpha()) + ML1seg.globalPosition().z(); + mid2 = (ChMid - ML2seg.globalPosition().perp())/tan(ML2seg.alpha()) + ML2seg.globalPosition().z(); + float r01 = ML1seg.globalPosition().perp() - ML1seg.globalPosition().z()*tan(ML1seg.alpha()); + float r02 = ML2seg.globalPosition().perp() - ML2seg.globalPosition().z()*tan(ML2seg.alpha()); + deltab = (mid2*tan(ML1seg.alpha()) - ChMid + r01)/(sqrt(1+sq(tan(ML1seg.alpha())))); + float deltab2 = (mid1*tan(ML2seg.alpha()) - ChMid + r02)/(sqrt(1+sq(tan(ML2seg.alpha())))); + if(fabs(deltab2) < fabs(deltab)) deltab = deltab2; + } + else { + //delta b in the endcap + mid1 = ML1seg.globalPosition().perp() + tan(ML1seg.alpha())*(ChMid - ML1seg.globalPosition().z()); + mid2 = ML2seg.globalPosition().perp() + tan(ML2seg.alpha())*(ChMid - ML2seg.globalPosition().z()); + float z01 = ML1seg.globalPosition().z() - ML1seg.globalPosition().perp()/tan(ML1seg.alpha()); + float z02 = ML1seg.globalPosition().z() - ML1seg.globalPosition().perp()/tan(ML1seg.alpha()); + deltab = (mid2/tan(ML1seg.alpha()) - ChMid +z01)/(sqrt(1+sq(1/tan(ML1seg.alpha())))); + float deltab2 = (mid1/tan(ML2seg.alpha()) - ChMid +z02)/(sqrt(1+sq(1/tan(ML2seg.alpha())))); + if(fabs(deltab2) < fabs(deltab)) deltab = deltab2; + } + + //calculate the maximum allowed Delta b based on delta alpha uncertainties and ML spacing + double dbmax = 5*fabs(ChMid-ML1seg.getChMidPoint())*sqrt(sq(ML1seg.alphaError()) + sq(ML2seg.alphaError())); + if(dbmax > m_maxDeltabCut) dbmax = m_maxDeltabCut; + if(fabs(deltab) < dbmax) return true; + else return false; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + float MSVertexTrackletTool::TrackMomentum(int chamber,float deltaAlpha) { + float pTot = 100000.; + //p = k/delta_alpha + if(chamber == 0) pTot = k_BIL/fabs(deltaAlpha); + else if(chamber == 2) pTot = k_BML/fabs(deltaAlpha); + else if(chamber == 3) pTot = k_BMS/fabs(deltaAlpha); + else if(chamber == 4) pTot = k_BOL/fabs(deltaAlpha); + else if(chamber == 7) pTot = k_BIL/fabs(deltaAlpha); + else if(chamber == 8) pTot = k_BML/fabs(deltaAlpha); + else if(chamber == 9) pTot = k_BOL/fabs(deltaAlpha); + else if(chamber == 10) pTot = k_BOL/fabs(deltaAlpha); + else if(chamber == 52) pTot = k_BIL/fabs(deltaAlpha); + if(pTot > 10000.) pTot = 100000.; + + return pTot; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + float MSVertexTrackletTool::TrackMomentumError(TrackletSegment& ml1, TrackletSegment& ml2) { + //uncertainty on 1/p + int ChType = ml1.mdtChamber(); + float dalpha = sqrt(sq(ml1.alphaError())+sq(ml2.alphaError())); + float pErr = dalpha/k_BML; + if(ChType == 0) pErr = dalpha/k_BIL; + else if(ChType == 2) pErr = dalpha/k_BML; + else if(ChType == 3) pErr = dalpha/k_BMS; + else if(ChType == 4) pErr = dalpha/k_BOL; + else if(ChType == 7) pErr = dalpha/k_BIL; + else if(ChType == 8) pErr = dalpha/k_BML; + else if(ChType == 9) pErr = dalpha/k_BOL; + else if(ChType == 10) pErr = dalpha/k_BOL; + else if(ChType == 52) pErr = dalpha/k_BIL; + + return pErr; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + float MSVertexTrackletTool::TrackMomentumError(TrackletSegment& ml1) { + //uncertainty in 1/p + int ChType = ml1.mdtChamber(); + float dalpha = fabs(ml1.alphaError()); + float pErr = dalpha/k_BML; + if(ChType == 0) pErr = dalpha/k_BIL; + else if(ChType == 2) pErr = dalpha/k_BML; + else if(ChType == 3) pErr = dalpha/k_BMS; + else if(ChType == 4) pErr = dalpha/k_BOL; + else if(ChType == 7) pErr = dalpha/k_BIL; + else if(ChType == 8) pErr = dalpha/k_BML; + else if(ChType == 9) pErr = dalpha/k_BOL; + else if(ChType == 10) pErr = dalpha/k_BOL; + else if(ChType == 52) pErr = dalpha/k_BIL; + + return pErr; + } + + +//** ----------------------------------------------------------------------------------------------------------------- **// + + + std::vector<Tracklet> MSVertexTrackletTool::ResolveAmbiguousTracklets(std::vector<Tracklet>& tracks) { + + ATH_MSG_DEBUG( "In ResolveAmbiguousTracks" ); + + ////////////////////////////// + // considering only tracklets with the number of associated hits + // being more than 3/4 the number of layers in the MS chamber + + if (m_tightTrackletRequirement) { + + std::vector<Tracklet> myTracks = tracks; + tracks.clear(); + + for(unsigned int tk1=0; tk1<myTracks.size(); ++tk1) { + Identifier id1 = myTracks.at(tk1).getML1seg().mdtHitsOnTrack().at(0)->identify(); + Identifier id2 = myTracks.at(tk1).getML2seg().mdtHitsOnTrack().at(0)->identify(); + int nLayerML1 = m_mdtIdHelper->tubeLayerMax(id1); + int nLayerML2 = m_mdtIdHelper->tubeLayerMax(id2); + float ratio = (float)(myTracks.at(tk1).mdtHitsOnTrack().size())/(nLayerML1+nLayerML2); + if (ratio>0.75) tracks.push_back(myTracks.at(tk1)); + } + } + + std::vector<Tracklet> UniqueTracks; + std::vector<unsigned int> AmbigTrks; + int nBarrelAmbiguousTrks = 0; + int nEndcapAmbiguousTrks = 0; + for(unsigned int tk1=0; tk1<tracks.size(); ++tk1) { + int nShared = 0; + //check if any Ambiguity has been broken + bool isResolved = false; + for(unsigned int ck=0; ck<AmbigTrks.size(); ++ck) { + if(tk1 == AmbigTrks.at(ck)) { + isResolved = true; + break; + } + } + if(isResolved) continue; + std::vector<Tracklet> AmbigTracks; + AmbigTracks.push_back(tracks.at(tk1)); + //get a point on the track + float Trk1ML1R = tracks.at(tk1).getML1seg().globalPosition().perp(); + float Trk1ML1Z = tracks.at(tk1).getML1seg().globalPosition().z(); + float Trk1ML2R = tracks.at(tk1).getML2seg().globalPosition().perp(); + float Trk1ML2Z = tracks.at(tk1).getML2seg().globalPosition().z(); + + //loop over the rest of the tracks and find any amibuities + for(unsigned int tk2=(tk1+1); tk2<tracks.size(); ++tk2) { + if(tracks.at(tk1).mdtChamber() == tracks.at(tk2).mdtChamber() && + tracks.at(tk1).mdtChPhi() == tracks.at(tk2).mdtChPhi() && + (tracks.at(tk1).mdtChEta())*(tracks.at(tk2).mdtChEta()) > 0 ) { + //check if any Ambiguity has been broken + for(unsigned int ck=0; ck<AmbigTrks.size(); ++ck) { + if(tk2 == AmbigTrks.at(ck)) { + isResolved = true; + break; + } + } + if(isResolved) continue; + //get a point on the track + float Trk2ML1R = tracks.at(tk2).getML1seg().globalPosition().perp(); + float Trk2ML1Z = tracks.at(tk2).getML1seg().globalPosition().z(); + float Trk2ML2R = tracks.at(tk2).getML2seg().globalPosition().perp(); + float Trk2ML2Z = tracks.at(tk2).getML2seg().globalPosition().z(); + + //find the distance between the tracks + float DistML1(1000),DistML2(1000); + if(tracks.at(tk1).mdtChamber() <= 11 || tracks.at(tk1).mdtChamber() == 52) { + DistML1 = fabs(Trk1ML1Z - Trk2ML1Z); + DistML2 = fabs(Trk1ML2Z - Trk2ML2Z); + } + else if(tracks.at(tk1).mdtChamber() <= 21 || tracks.at(tk1).mdtChamber() == 49) { + DistML1 = fabs(Trk1ML1R - Trk2ML1R); + DistML2 = fabs(Trk1ML2R - Trk2ML2R); + } + if(DistML1 < 40 || DistML2 < 40) { + //find how many MDTs the tracks share + std::vector<Muon::MdtPrepData*> mdt1 = tracks.at(tk1).mdtHitsOnTrack(); + std::vector<Muon::MdtPrepData*> mdt2 = tracks.at(tk2).mdtHitsOnTrack(); + nShared = 0; + for(unsigned int m1=0; m1<mdt1.size(); ++m1) { + for(unsigned int m2=0; m2<mdt2.size(); ++m2) { + if(mdt1.at(m1)->identify() == mdt2.at(m2)->identify()) { + nShared++; + break; + } + } + } + + if(nShared <= 1) continue;//if the tracks share only 1 hits move to next track + //store the track as ambiguous + AmbigTracks.push_back(tracks.at(tk2)); + AmbigTrks.push_back(tk2); + } + }//end chamber match + }//end tk2 loop + + if(AmbigTracks.size() == 1) { + UniqueTracks.push_back(tracks.at(tk1)); + continue; + } + if(tracks.at(tk1).mdtChamber() <= 11 || tracks.at(tk1).mdtChamber() == 52) nBarrelAmbiguousTrks += (AmbigTracks.size() - 1) ; + else nEndcapAmbiguousTrks += (AmbigTracks.size() - 1) ; + //Deal with any ambiguities + //Barrel tracks + if(tracks.at(tk1).mdtChamber() <= 11 || tracks.at(tk1).mdtChamber() == 52) { + bool hasMomentum = true; + if(tracks.at(tk1).charge() == 0) hasMomentum = false; + float aveX(0),aveY(0),aveZ(0),aveAlpha(0); + float aveP(0),nAmbigP(0),TrkCharge(tracks.at(tk1).charge()); + bool allSameSign(true); + for(unsigned int i=0; i<AmbigTracks.size(); ++i) { + if(!hasMomentum) { + aveX += AmbigTracks.at(i).globalPosition().x(); + aveY += AmbigTracks.at(i).globalPosition().y(); + aveZ += AmbigTracks.at(i).globalPosition().z(); + aveAlpha += AmbigTracks.at(i).getML1seg().alpha(); + } + else { + //check the charge is the same + if(fabs(AmbigTracks.at(i).charge()-TrkCharge) > 0.1) allSameSign = false; + //find the average momentum + aveP += AmbigTracks.at(i).momentum().mag(); + nAmbigP++; + aveAlpha += AmbigTracks.at(i).alpha(); + aveX += AmbigTracks.at(i).globalPosition().x(); + aveY += AmbigTracks.at(i).globalPosition().y(); + aveZ += AmbigTracks.at(i).globalPosition().z(); + } + }//end loop on ambiguous tracks + if(!hasMomentum) { + aveX = aveX/(float)AmbigTracks.size(); + aveY = aveY/(float)AmbigTracks.size(); + aveZ = aveZ/(float)AmbigTracks.size(); + Amg::Vector3D gpos(aveX,aveY,aveZ); + aveAlpha = aveAlpha/(float)AmbigTracks.size(); + float alphaErr = tracks.at(tk1).getML1seg().alphaError(); + float rErr = tracks.at(tk1).getML1seg().rError(); + float zErr = tracks.at(tk1).getML1seg().zError(); + int Chamber = tracks.at(tk1).mdtChamber(); + int ChEta = tracks.at(tk1).mdtChEta(); + int ChPhi = tracks.at(tk1).mdtChPhi(); + // + TrackletSegment aveSegML1(Chamber,ChEta,ChPhi,tracks.at(tk1).getML1seg().getChMidPoint(),aveAlpha,alphaErr,gpos,rErr,zErr,tracks.at(tk1).getML1seg().mdtHitsOnTrack(),0); + float pT = 10000.0*sin(aveSegML1.alpha()); + float pz = 10000.0*cos(aveSegML1.alpha()); + Amg::Vector3D momentum(pT*cos(aveSegML1.globalPosition().phi()),pT*sin(aveSegML1.globalPosition().phi()),pz); + AmgSymMatrix(5) matrix; + matrix.setIdentity(); + matrix(0,0) = sq(tracks.at(tk1).getML1seg().rError());//delta R + matrix(1,1) = sq(tracks.at(tk1).getML1seg().zError());//delta z + matrix(2,2) = sq(0.0000001);//delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber) + matrix(3,3) = sq(tracks.at(tk1).getML1seg().alphaError());//delta theta + matrix(4,4) = sq(0.00005);//delta 1/p + Tracklet aveTrack(aveSegML1,momentum,matrix,0); + UniqueTracks.push_back(aveTrack); + } + else if(allSameSign) { + aveP = aveP/nAmbigP; + float pT = aveP*sin(tracks.at(tk1).getML1seg().alpha()); + float pz = aveP*cos(tracks.at(tk1).getML1seg().alpha()); + Amg::Vector3D momentum(pT*cos(tracks.at(tk1).globalPosition().phi()),pT*sin(tracks.at(tk1).globalPosition().phi()),pz); + Tracklet MyTrack = tracks.at(tk1); + MyTrack.momentum(momentum); + MyTrack.charge(tracks.at(tk1).charge()); + UniqueTracks.push_back(MyTrack); + } + else { + aveX = aveX/(float)AmbigTracks.size(); + aveY = aveY/(float)AmbigTracks.size(); + aveZ = aveZ/(float)AmbigTracks.size(); + Amg::Vector3D gpos(aveX,aveY,aveZ); + aveAlpha = aveAlpha/(float)AmbigTracks.size(); + float alphaErr = tracks.at(tk1).getML1seg().alphaError(); + float rErr = tracks.at(tk1).getML1seg().rError(); + float zErr = tracks.at(tk1).getML1seg().zError(); + int Chamber = tracks.at(tk1).mdtChamber(); + int ChEta = tracks.at(tk1).mdtChEta(); + int ChPhi = tracks.at(tk1).mdtChPhi(); + TrackletSegment aveSegML1(Chamber,ChEta,ChPhi,tracks.at(tk1).getML1seg().getChMidPoint(),aveAlpha,alphaErr,gpos,rErr,zErr,tracks.at(tk1).getML1seg().mdtHitsOnTrack(),0); + float pT = 10000.0*sin(aveSegML1.alpha()); + float pz = 10000.0*cos(aveSegML1.alpha()); + Amg::Vector3D momentum(pT*cos(aveSegML1.globalPosition().phi()),pT*sin(aveSegML1.globalPosition().phi()),pz); + AmgSymMatrix(5) matrix; + matrix.setIdentity(); + matrix(0,0) = sq(tracks.at(tk1).getML1seg().rError());//delta R + matrix(1,1) = sq(tracks.at(tk1).getML1seg().zError());//delta z + matrix(2,2) = sq(0.0000001);//delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber) + matrix(3,3) = sq(tracks.at(tk1).getML1seg().alphaError());//delta theta + matrix(4,4) = sq(0.00005);//delta 1/p + Tracklet aveTrack(aveSegML1,momentum,matrix,0); + UniqueTracks.push_back(aveTrack); + } + }//end barrel Tracks + + //Endcap tracks + else if( (tracks.at(tk1).mdtChamber() > 11 && tracks.at(tk1).mdtChamber() <= 21) || tracks.at(tk1).mdtChamber() == 49 ) { + std::vector<Muon::MdtPrepData*> AllMdts; + for(unsigned int i=0; i<AmbigTracks.size(); ++i) { + std::vector<Muon::MdtPrepData*> mdts = AmbigTracks.at(i).mdtHitsOnTrack(); + std::vector<Muon::MdtPrepData*> tmpAllMdt = AllMdts; + for(unsigned int m1=0; m1<mdts.size(); ++m1) { + bool isNewHit = true; + for(unsigned int m2=0; m2<tmpAllMdt.size(); ++m2) { + if(mdts.at(m1)->identify() == tmpAllMdt.at(m2)->identify()) { + isNewHit = false; + break; + } + } + if(isNewHit) AllMdts.push_back(mdts.at(m1)); + }//end loop on mdts + }//end loop on ambiguous tracks + + std::vector<TrackletSegment> MyECsegs = TrackletSegmentFitter(AllMdts); + if(MyECsegs.size() > 0) { + TrackletSegment ECseg = MyECsegs.at(0); + ECseg.clearMdt(); + float pT = 10000.0*sin(ECseg.alpha()); + float pz = 10000.0*cos(ECseg.alpha()); + Amg::Vector3D momentum(pT*cos(ECseg.globalPosition().phi()),pT*sin(ECseg.globalPosition().phi()),pz); + AmgSymMatrix(5) matrix; + matrix.setIdentity(); + matrix(0,0) = sq(ECseg.rError());//delta R + matrix(1,1) = sq(ECseg.zError());//delta z + matrix(2,2) = sq(0.0000001);//delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber) + matrix(3,3) = sq(ECseg.alphaError());//delta theta + matrix(4,4) = sq(0.00005);//delta 1/p (endcap tracks are straight lines with no momentum that we can measure ...) + Tracklet MyCombTrack(MyECsegs.at(0),ECseg,momentum,matrix,0); + UniqueTracks.push_back(MyCombTrack); + } + else UniqueTracks.push_back(tracks.at(tk1)); + }//end endcap tracks + + }//end loop on tracks -- tk1 + + return UniqueTracks; + } + +} + diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.h b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.h new file mode 100755 index 0000000000000..b072ce1e4de07 --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/MSVertexTrackletTool.h @@ -0,0 +1,88 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef MSVERTEXTRACKLETTOOL_H +#define MSVERTEXTRACKLETTOOL_H + +#include "AthenaBaseComps/AthAlgTool.h" +#include "GaudiKernel/ToolHandle.h" +#include "MSVertexToolInterfaces/IMSVertexTrackletTool.h" +#include "MuonIdHelpers/MuonIdHelperTool.h" +#include "GeoPrimitives/GeoPrimitives.h" +#include "GeoAdaptors/GeoMuonHits.h" +#include "Identifier/Identifier.h" +#include "MSVertexUtils/TrackletSegment.h" +#include "MSVertexUtils/Tracklet.h" +#include <utility> +#include <vector> + + +namespace Muon { + + class MSVertexTrackletTool : virtual public IMSVertexTrackletTool, public AthAlgTool + { + + public : + /** default constructor */ + MSVertexTrackletTool (const std::string& type, const std::string& name, const IInterface* parent); + /** destructor */ + virtual ~MSVertexTrackletTool(); + + static const InterfaceID& interfaceID(); + + virtual StatusCode initialize(void); + virtual StatusCode finalize(void); + + private: + //tool handles & private data members + + const MdtIdHelper* m_mdtIdHelper; + static const MdtIdHelper* mdtCompareIdHelper; + + std::string m_TPContainer; + float m_SeedResidual; + float m_minSegFinderChi2; + float m_BarrelDeltaAlphaCut; + float m_maxDeltabCut; + float m_EndcapDeltaAlphaCut; + float m_DeltaAlphaCut; + float m_DeltabCut; + float m_TrackPhiAngle; + + bool m_tightTrackletRequirement; + + int nMDT; + float PI; + float k_BIL; + float k_BML; + float k_BMS; + float k_BOL; + + + public: + StatusCode findTracklets(std::vector<Tracklet>& traklets); + + private: + //private functions + int SortMDThits(std::vector<std::vector<Muon::MdtPrepData*> >& SortedMdt); + bool SortMDT(Identifier& i1, Identifier& i2); + std::vector<TrackletSegment> TrackletSegmentFitter(std::vector<Muon::MdtPrepData*>& mdts); + std::vector<TrackletSegment> TrackletSegmentFitterCore(std::vector<Muon::MdtPrepData*>& mdts,std::vector<std::pair<float,float> >& SeedParams); + std::vector<std::pair<float,float> > SegSeeds(std::vector<Muon::MdtPrepData*>& mdts); + float SeedResiduals(std::vector<Muon::MdtPrepData*>& mdts, float slope, float inter); + std::vector<TrackletSegment> CleanSegments(std::vector<TrackletSegment>& segs); + bool DeltabCalc(TrackletSegment& ML1seg, TrackletSegment& ML2seg); + float TrackMomentum(int chamber,float deltaAlpha); + float TrackMomentumError(TrackletSegment& ml1, TrackletSegment& ml2); + float TrackMomentumError(TrackletSegment& ml1); + std::vector<Tracklet> ResolveAmbiguousTracklets(std::vector<Tracklet>& tracks); + void convertToTrackParticles(std::vector<Tracklet>& tracklets); + float sq(float x) { return (x)*(x); } + static bool mdtComp(const Muon::MdtPrepData* mprd1, const Muon::MdtPrepData* mprd2); + + }; + + +} +#endif diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_entries.cxx b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_entries.cxx new file mode 100644 index 0000000000000..534bae88dc449 --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_entries.cxx @@ -0,0 +1,19 @@ + +#include "GaudiKernel/DeclareFactoryEntries.h" +#include "../MSVertexTrackletTool.h" +#include "../MSVertexRecoTool.h" + +using namespace Muon; + +DECLARE_TOOL_FACTORY( MSVertexTrackletTool ) +DECLARE_TOOL_FACTORY( MSVertexRecoTool ) + +DECLARE_FACTORY_ENTRIES( MSVertexTools ) +{ + DECLARE_TOOL( MSVertexTrackletTool ) + DECLARE_TOOL( MSVertexRecoTool ) +} + + + + diff --git a/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_load.cxx b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_load.cxx new file mode 100644 index 0000000000000..2d776463a2c5d --- /dev/null +++ b/MuonSpectrometer/MSVertexReconstruction/MSVertexTools/src/components/MSVertexTools_load.cxx @@ -0,0 +1,5 @@ + +#include "GaudiKernel/LoadFactoryEntries.h" + +LOAD_FACTORY_ENTRIES( MSVertexTools ) + -- GitLab