// $Id: PndCAGBTracker.cxx,v 1.12 2010/09/01 10:38:27 ikulakov Exp $ // ************************************************************************** // This file is property of and copyright by the ALICE HLT Project * // ALICE Experiment at CERN, All rights reserved. * // * // Primary Authors: Sergey Gorbunov * // Ivan Kisel * // for The ALICE HLT Project. * // * // Developed by: Igor Kulakov * // Maksym Zyzak * // * // Permission to use, copy, modify and distribute this software and its * // documentation strictly for non-commercial purposes is hereby granted * // without fee, provided that the above copyright notice appears in all * // copies and that both the copyright notice and this permission notice * // appear in the supporting documentation. The authors make no claims * // about the suitability of this software for any purpose. It is * // provided "as is" without express or implied warranty. * // * //*************************************************************************** #include "PndCAStationSTT.h" #include "PndCAGBTracker.h" #include "PndCAGBHit.h" #include "PndCAGBTrack.h" #include "PndCATrackParam.h" #include "PndCAMath.h" #include "PndCATrackLinearisationVector.h" #include "PndCAPerformance.h" #include "TStopwatch.h" #include "PndCATarget.h" #include "PndCAHits.h" #include "PndCAHitsV.h" #include "PndCATracks.h" #include #include #include #include using namespace std; //TODO DELL ME!!! #include "PndCAPerformance.h" #include "PndCAMCPoint.h" #include "PndCAPerformanceBase.h" #ifdef CATRACKER_DISPLAY #include "PndCADisplay.h" #endif struct TrackHitRecord{ int fPrevHit; // index of previous TrackHitRecord of same track in an array of records unsigned short fIHit; char fStation; }; vector gTrackHitRecords; const int StartStationShift = 1;//3; PndCANPlets::PndCANPlets( const PndCANPletsV& p ):PndCAStationArray( p.NStations(), p.OnStation(0).HitsRef() ){ for( int iS = 0; iS < NStations(); ++iS ) { PndCAElementsOnStation& tOnSta = OnStation( iS ); const PndCAElementsOnStation& ts = p.OnStation(iS); tOnSta.clear(); tOnSta.reserve(ts.size()*float_v::Size); for( unsigned int iT = 0; iT < ts.size(); iT++ ) { const PndCANPletV& t = ts[iT]; foreach_bit( unsigned int iV, t.IsValid() ) { tOnSta.push_back( PndCANPlet( PndCATrackParam( t.Param(), iV ) ) ); PndCANPlet &nPlet = tOnSta.back(); int irec=t.fLastHit[iV]; if( irec<0 ) continue; int nHits = t.N(); nPlet.fIHit.clear(); nPlet.fIHit.resize(nHits); for( int i=nHits-1; i>=0; i--){ if( irec<0 ){ cout<<"CA tracker: something wrong with hit links!!!"<> fParameters; // fSlices[iSlice].Initialize( param ); } void PndCAGBTracker::WriteEvent( FILE *file ) const { // write event to the file const int nHits = NHits(); int written = std::fwrite( &nHits, sizeof( int ), 1, file ); assert( written == 1 ); written = std::fwrite( &fHits[0], sizeof( PndCAGBHit ), nHits, file ); assert( written == nHits ); std::fflush( file ); UNUSED_PARAM1(written); } bool PndCAGBTracker::SaveTracksInFile(string prefix) const { ofstream out((prefix+"tracks.data").data()); if ( !out.is_open() ) return 0; // ostream& out = cout; out << fNTracks << std::endl; for ( int itr = 0; itr < fNTracks; itr++ ) { PndCAGBTrack &t = fTracks[itr]; for ( int ih = t.FirstHitRef(), i = 0; i < t.NHits(); ih++, i++ ) { out << fTrackHits[ih] << " "; } out << endl; } return 1; } // void PndCAGBTracker::ReadTracks( std::istream &in ) // { // //* Read tracks from file // // in >> fTime; // fSliceTrackerTime = fTime; // fStatTime[0] += fTime; // fStatNEvents++; // delete[] fTrackHits; // fTrackHits = 0; // int nTrackHits = 0; // in >> nTrackHits; // fTrackHits = new int [nTrackHits]; // for ( int ih = 0; ih < nTrackHits; ih++ ) { // in >> TrackHits()[ih]; // } // delete[] fTracks; // fTracks = 0; // in >> fNTracks; // fTracks = new PndCAGBTrack[fNTracks]; // for ( int itr = 0; itr < fNTracks; itr++ ) { // PndCAGBTrack &t = Tracks()[itr]; // in >> t; // } // } void PndCAGBTracker::SetHits( std::vector &hits) { const int NHits2 = hits.size(); fNHits = 0; fHits.resize(0); fHits.reserve(NHits2); //cout<<"Z min, max: "<10 ){ //cout<<"read angle "<= PndCAParameters::MaxNStations ){ cout<<"CA tracker: wrong hit station number: "<<(int) l.IRow()<<" out of "<> Size; fHits.clear(); fHits.reserve(Size); fNHits = 0; for (int i = 0; i < Size; i++){ PndCAGBHit l; ifile >> l; if( fabs(l.Angle())>10 ){ //cout<<"read angle "<= PndCAParameters::MaxNStations ){ cout<<"CA tracker: wrong hit station number: "<<(int) l.IRow()<<" out of "< &stHits = hits.OnStation( ist ); //cout<<"station "<=PndCAParameters::NMVDStations ){ //cout<< hit.fAngle - (p12+p6*hit.fISec)<.001 ) exit(0); } if( hit.fISec<0 || hit.fISec>=6 ){ cout<<"CA tracker: Wrong hit sector "< &stHits = hits.OnStationConst( ista ); for( unsigned int ih=0; ih lastSec ){ sta.fSectors[iSec].fFirstHit = ih; sta.fSectors[iSec].fNHits = 0; lastSec = iSec; } sta.fSectors[iSec].fNHits++; } } // estimate PV float xT = 0, yT = 0, zT = 0; #ifdef USE_DBG_TIMERS timer.Stop(); fGTi["init "] = timer; timer.Start(1); TStopwatch itimer; #endif /// Find Tracks // set parameters; TODO depend on iteration const float kCorr = 1;//.2; // correction on pulls width const float kCorr2 = kCorr*kCorr; fPick_m = 3.*kCorr; fPick_r = 5.*kCorr; fPickNeighbour = 3.*kCorr; TRACK_PROB_CUT = 0.01; TRACK_CHI2_CUT = 10.*kCorr2; TRIPLET_CHI2_CUT = 15.*kCorr2; // TMath::Prob(20,-3+1+6) = TMath::Prob(15,2) = 5e-04 // Set correction in order to take into account overlaping // The reason is that low momentum tracks are too curved and goes not from target direction. That's why hit sort is not work idealy fMaxDX0 = 0; fMaxInvMom = 2; fTarget = PndCATarget( xT, yT, zT, 1, 1, fMaxInvMom/3.f, GetParameters().VtxFieldValue(), 3 ); // 3 so triplets can have NDF=1 fMaxDX0 = 0; #ifdef USE_DBG_TIMERS itimer.Stop(); fTi[0]["init "] = itimer; itimer.Start(1); TStopwatch ptimer; ptimer.Start(1); #endif //cout<<"MaxCellLength = "< pick ) return false; // neighbours must have same qp chi2 *= chi2; return true; } // find and store neighbours triplets // to make recursive search faster saves only neighbours with level = level - 1 void PndCAGBTracker::FindNeighbours( PndCANPlets& triplets ) { for( int iS = triplets.NStations() - 1; iS >= 0; --iS ) { // CHECKME PndCAElementsOnStation& ts1 = triplets.OnStation( iS ); for( unsigned int iT1 = 0; iT1 < ts1.size(); ++iT1 ) { PndCANPlet& t1 = ts1[iT1]; int neighIStation = t1.ISta(0) + StartStationShift; if ( neighIStation >= triplets.NStations() ) continue; // triplets can't start from this station PndCAElementsOnStation& ts2 = triplets.OnStation( neighIStation ); char maxLevel = -1; // maxLevel of neighbour triplets vector< pair > neighCands; // save neighbour candidates for( unsigned int iT2 = 0; iT2 < ts2.size(); ++iT2 ) { const PndCANPlet& t2 = ts2[iT2]; float chi2; if( !IsRightNeighbour( t1, t2, fPick, chi2 ) ) continue; if ( maxLevel < t2.Level() ) maxLevel = t2.Level(); if ( maxLevel == t2.Level() ) neighCands.push_back(pair(chi2 + t2.Chi2Level(),iT2)); } t1.Level() = maxLevel + 1; // save for( unsigned int iN = 0; iN < neighCands.size(); ++iN ) { const PndCANPlet& t2 = ts2[ neighCands[iN].second ]; if ( maxLevel == t2.Level() ) { t1.Neighbours().push_back( neighCands[iN] ); } } sort( t1.Neighbours().begin(), t1.Neighbours().end() ); if ( t1.NNeighbours() ) { t1.Chi2Level() = t1.Chi2Neighbours(0); const pair tmp = t1.Neighbours()[0]; // leave only one. CHECKME for 1000tracks events t1.Neighbours().clear(); t1.Neighbours().push_back( tmp ); } } // iTrip1 //sort( ts1.begin(), ts1.end(), PndCANPlet::compare ); } // iStation } void PndCAGBTracker::CreateTracks( const PndCANPlets& triplets, PndCATracks& tracks ) { const int Nlast = PndCAParameters::LastCellLength; // N hits in the rightmost triplet int min_level = 1; // min level to start triplet. So min track length = min_level+3. // collect consequtive: the longest tracks, shorter, more shorter and so on for (int ilev = NStations()-Nlast; ilev >= min_level; ilev--){ // choose length PndCATracks vtrackcandidate( tracks.HitsRef() ); // how many levels to check const unsigned char min_best_l = (ilev > min_level) ? ilev-1 : min_level; // lose maximum one hit and find all hits for min_level // const unsigned char min_best_l = ilev + 3; // find all hits (slower) // find candidates for( int istaF = 0; istaF <= NStations()-Nlast-ilev; istaF++ ){ const PndCAElementsOnStation& tsF = triplets.OnStation( istaF ); for( unsigned int iTrip=0; iTrip < tsF.size(); iTrip++ ){ const PndCANPlet* tripF = &(tsF[iTrip]); if (0) { // ghost supression !!! if ( tripF->Level() == 0 ) continue; // ghost suppression // find track with 3 hits only if it was created from a chain of triplets, but not from only one triplet if ( tripF->Level() < ilev ) continue; // try only triplets, which can start track with ilev+3 length. w\o it have more ghosts, but efficiency either if ( (ilev == 0) && (tripF->ISta(0) != 0) ) continue; // ghost supression // collect only MAPS tracks-triplets } else if ( tripF->Level() < min_best_l ) continue; PndCATrack curr_tr; bool isUsed = false; for( int i = 0; i < tripF->N(); i++ ) { if( triplets.OnStation( tripF->ISta(0) ).GetHit( i, iTrip ).IsUsed() ) { isUsed = true; break; } curr_tr.AddHit( tripF->IHit(i) ); } if (isUsed) continue; curr_tr.Level()++; curr_tr.Chi2() = tripF->Param().Chi2(); PndCATrack best_tr = curr_tr; unsigned int nCalls = 0; FindBestCandidate(istaF, best_tr, iTrip, curr_tr, min_best_l, triplets, nCalls ); if ( best_tr.Level() < min_best_l ) continue; if ( best_tr.NHits() < PndCAParameters::MinimumHitsForRecoTrack ) continue; // if ( best_tr.Fit( *tracks.HitsRef(), fTarget, GetParameters(), tripF->QMomentum() ).QPt() > 0.5*fMaxInvMom ) continue; // fit to determine Chi2 and NDF vtrackcandidate.push_back(best_tr); // best_tr.SetHitsAsUsed(*tracks.HitsRef()); TODO // tracks.push_back(best_tr); } } // istaF // select and save best candidates vtrackcandidate.SelectAndSaveTracks( tracks ); } // ilev } // --------- Merger --------- void PndCAGBTracker::InvertCholetsky(float a[15]) const { float d[5], uud, u[5][5]; for(int i=0; i<5; i++) { d[i]=0.f; for(int j=0; j<5; j++) u[i][j]=0.; } for(int i=0; i<5; i++) { uud=0.; for(int j=0; j > > InNeighbour(NTracksS); vector< vector< pair > > OutNeighbour(NTracksS); vector fittedTracks; fittedTracks.reserve(NTracksS); for ( int iT1 = 0; iT1 < NTracksS; iT1++ ) { PndCATrack& t1 = tracks[iT1]; // CHECKME why fit backward doesn't help. CHECKME why fit without target doesn't work PndCATrackParam p1 = t1.Fit( *tracks.HitsRef(), fTarget, GetParameters() ); fittedTracks.push_back(p1); } for ( int iT1 = 0; iT1 < NTracksS; iT1++ ) { PndCATrack& t1 = tracks[iT1]; if ( t1.NHits() <= 0 ) continue; const PndCATrackParam &p1 = fittedTracks[iT1]; for ( int iT2 = 0; iT2 < NTracksS; iT2++ ) { PndCATrack& t2 = tracks[iT2]; if ( t2.NHits() <= 0 ) continue; const int nStaDiff = t2.IHits().front().s - t1.IHits().back().s; if ( nStaDiff <= 0 ) continue; PndCATrackParam p2 = fittedTracks[iT2]; // if ( t2.NHits() >= t1.NHits() ) // CHECK me: why it is better to always use p2 p2.Transport( (*tracks.HitsRef())[t1.IHits().back()], GetParameters().cBz() ); // else // p1.Transport( (*tracks.HitsRef())[t2.IHits().back()], GetParameters().cBz() ); float C[15], r[5], chi2(0); FilterTracks( p1.Par(), p1.Cov(), p2.Par(), p2.Cov(), r, C, chi2); if ( chi2 > 20 ) continue; // TMath::Prob(20,5) = 1.25e-03 chi2 += 10*nStaDiff; // nStaDiff is more important than chi2 - this is an empiric choice OutNeighbour[iT1].push_back( pair( chi2, iT2 ) ); InNeighbour[iT2].push_back( pair( chi2, iT1 ) ); } // iT2 } // iT1 // sort by chi2 for ( int iT1 = 0; iT1 < NTracksS; iT1++ ) { sort( InNeighbour[iT1].begin(), InNeighbour[iT1].end() ); sort( OutNeighbour[iT1].begin(), OutNeighbour[iT1].end() ); } // combine best neighbours. TODO: speed up by sorting all pairs together by chi2 bool allPairsFound = false; while( !allPairsFound ) { allPairsFound = true; // { // dbg // cout << " new " << endl; // for ( int iT1 = 0; iT1 < NTracksS; iT1++ ) { // for( unsigned int iN = 0; iN < InNeighbour[iT1].size(); iN++ ) { // cout << iT1 << " " << iN << " I " << InNeighbour[iT1][iN].first << " " << InNeighbour[iT1][iN].second << endl; // } // for( unsigned int iN = 0; iN < OutNeighbour[iT1].size(); iN++ ) { // cout << iT1 << " " << iN << " O " << OutNeighbour[iT1][iN].first << " " << OutNeighbour[iT1][iN].second << endl; // } // } // } for ( int iT1 = 0; iT1 < NTracksS; iT1++ ) { PndCATrack& t1 = tracks[iT1]; if ( t1.NHits() <= 0 ) continue; // find best unused outer track int iT2 = -1; for( unsigned int iN = 0; iN < OutNeighbour[iT1].size(); iN++ ) { iT2 = OutNeighbour[iT1][iN].second; if( iT2 >= 0 ) { break; } } if (iT2 < 0) continue; // find best unused inner track for outer track int iT21 = -1; for( unsigned int iN = 0; iN < InNeighbour[iT2].size(); iN++ ) { iT21 = InNeighbour[iT2][iN].second; if( iT21 >= 0 ) { break; } } assert(iT21 >= 0); // at least iT1 should be found if ( iT1 != iT21 ) { allPairsFound = false; continue; // find them at the next iteration } PndCATrack& t2 = tracks[iT2]; // atach track for ( int ih = 0; ih < t2.NHits(); ih++ ) { t1.AddHit( t2.IHits()[ih] ); } t2.IHits().clear(); // clean connections for( unsigned int iN = 0; iN < OutNeighbour[iT1].size(); iN++ ) { const int iT22 = OutNeighbour[iT1][iN].second; if ( iT22 < 0 ) continue; for( unsigned int iN2 = 0; iN2 < InNeighbour[iT22].size(); iN2++ ) { if( InNeighbour[iT22][iN2].second == iT1 ) { InNeighbour[iT22][iN2].second = -1; break; } } } for( unsigned int iN = 0; iN < InNeighbour[iT2].size(); iN++ ) { const int iT22 = InNeighbour[iT2][iN].second; if ( iT22 < 0 ) continue; for( unsigned int iN2 = 0; iN2 < OutNeighbour[iT22].size(); iN2++ ) { if( OutNeighbour[iT22][iN2].second == iT2 ) { OutNeighbour[iT22][iN2].second = -1; break; } } } for( unsigned int iN = 0; iN < OutNeighbour[iT2].size(); iN++ ) { const int iT22 = OutNeighbour[iT2][iN].second; if ( iT22 < 0 ) continue; for( unsigned int iN2 = 0; iN2 < InNeighbour[iT22].size(); iN2++ ) { if( InNeighbour[iT22][iN2].second == iT2 ) { InNeighbour[iT22][iN2].second = iT1; break; } } } OutNeighbour[iT1] = OutNeighbour[iT2]; InNeighbour[iT2].clear(); OutNeighbour[iT2].clear(); // check the newly created track iT1--; } } // allPairsFound // remove tracks, which were atached to other tracks PndCATracks tracks_saved = tracks; tracks.clear(); for ( int iT1 = 0; iT1 < NTracksS; iT1++ ) { PndCATrack& t1 = tracks_saved[iT1]; if (t1.NHits() != 0) tracks.push_back(t1); } // currently is not needed } void PndCAGBTracker::FindBestCandidate(int ista, PndCATrack &bT, // best track int currITrip, // index of current triplet PndCATrack &cT, // current track unsigned char min_best_l, const PndCANPlets& triplets, unsigned int& nCalls) { // if (nCalls > 100) return; // avoid long processing in confusing cases nCalls++; const PndCAElementsOnStation& trs = triplets.OnStation( ista ); const PndCANPlet* curr_trip = &(trs[currITrip]); if (curr_trip->Level() == 0){ // the end of the track -> check and store // -- finish with current track // if( cT.Level() < min_best_l - 1 ) return; // suppose that only one hit can be added by extender // cT.Fit( *trs.HitsRef(), fTarget, GetParameters() ); // fit to determine Chi2 and NDF // takes 20 times more time then the rest // -- select the best if ( (cT.NDF() > bT.NDF()) || ( (cT.NDF() == bT.NDF()) && (cT.Chi2() < bT.Chi2()) ) ) bT = cT; } else { // level != 0 // try to extend. try all possible triplets int NNeighs = curr_trip->NNeighbours(); for (int in = 0; in < NNeighs; in++) { int newITrip = curr_trip->INeighbours(in); const PndCANPlet* new_trip = &(triplets.OnStation( curr_trip->ISta(0) + StartStationShift )[newITrip]); // check new triplet bool isUsed = false; const int newTripLength = new_trip->N(); ASSERT( newTripLength - curr_trip->N() >= -1, newTripLength << " - " << curr_trip->N() ); for( int i = curr_trip->N() - 1; i < newTripLength; i++ ) { if( triplets.OnStation( new_trip->ISta(0) ).GetHit( i, newITrip ).IsUsed() ) { isUsed = true; break; } } if (isUsed) { // cT.Fit( *trs.HitsRef(), fTarget, GetParameters() ); // fit to determine Chi2 and NDF // no used hits allowed -> compare and store track if ( (cT.NDF() > bT.NDF()) || ( (cT.NDF() == bT.NDF()) && (cT.Chi2() < bT.Chi2()) ) ) bT = cT; } else{ // add new triplet to the current track // restore current track // save current tracklet PndCATrack nT = cT; // new track // add new triplet nT.Level()++; int start = curr_trip->N() - StartStationShift; if( start<0 ) start = 0; for( int i = start; i < newTripLength; i++ ) { nT.AddHit( new_trip->IHit(i) ); } const float qp1 = curr_trip->QMomentum(); const float qp2 = new_trip->QMomentum(); float dqp = fabs(qp1 - qp2); float Cqp = curr_trip->QMomentumErr(); Cqp += new_trip->QMomentumErr(); dqp = dqp/Cqp; nT.Chi2() += dqp*dqp; FindBestCandidate(new_trip->ISta(0), bT, newITrip, nT, min_best_l, triplets, nCalls); } // add triplet to track } // for neighbours } // level = 0 } void PndCAGBTracker::Create1Plets( const PndCATarget& target, const PndCAHits& hits, PndCAElementsOnStation &r, int iS ) { //const PndCAStation &station = GetParameters().Station( iS ); const PndCAElementsOnStation& hs = hits.OnStation(iS); r.clear(); r.reserve(hs.size()/float_v::Size + 1); TrackHitRecord hitRecord; hitRecord.fPrevHit = -1; hitRecord.fStation = iS; for( unsigned int iH = 0; iH < hs.size(); iH += float_v::Size ) { float_m valid = static_cast(uint_v::IndexesFromZero() < uint_v(hs.size() - iH) ); PndCAHitV hit( &(hs[iH]), valid ); PndCATrackParamVector param; float_m active = hit.IsValid(); if(1){ // start from target param.InitByTarget(target); param.InitDirection( hit.X0() - target.X0(), hit.X1() - target.X1(), hit.X2() - target.X2() ); param.SetAngle( hit.Angle() ); param.SetISec( hit.ISec() ); active &= param.Transport0ToX( hit.X0(), GetParameters().cBz(), float_m(true) ); active &= param.Filter( hit, GetParameters(), active ); } else { // start from hit -> does not work for some reason /* param.InitByHit( hit, GetParameters(), target.DQMom() ); param.InitDirection( hit.X0() - target.X0(), hit.X1() - target.X1(), hit.X2() - target.X2() ); // works only of t.X() = Y() = 0 param.SetAngle( hit.Angle() ); param.SetISec( hit.ISec() ); active &= param.AddTarget( target, active ); */ } if( active.isEmpty() ) continue; uint_v iHit = uint_v::IndexesFromZero() + iH; r.resize(r.size()+1); PndCANPletV &nPlet = r.back(); nPlet.fNHits=1; nPlet.fParam=param; nPlet.fIsValid = active; nPlet.fLastHit = -1; foreach_bit( unsigned int iBit, active ) { hitRecord.fIHit = iHit[iBit]; nPlet.fLastHit[iBit] = gTrackHitRecords.size(); gTrackHitRecords.push_back(hitRecord); } } } void PndCAGBTracker::CreateNPlets( const PndCATarget& target, const PndCAHits& hits, PndCAElementsOnStation &nPlets, int iS, int cellLength ) { //if( iS tmp0; PndCAElementsOnStation tmp1; tmp0.fHitsRef = &hits; tmp0.fISta = iS; tmp1.fHitsRef = &hits; tmp1.fISta = iS; PndCAElementsOnStation *rCurr = &tmp0; PndCAElementsOnStation *rNext = &tmp1; Create1Plets( target, hits, *rCurr, iS ); for( int iLen=1; iLen<=cellLength; iLen++){ if ( rCurr->size() <= 0 ) break; if ( (*rCurr)[0].N() >= GetParameters().Station( iS ).CellLength ) break; rNext->clear(); PickUpHits( *rCurr, *rNext, iS+iLen ); PndCAElementsOnStation *rr = rCurr; rCurr=rNext; rNext = rr; //cout<<" station "<size()<<" plets of length "< store(3000); void PndCAGBTracker::PickUpHits( PndCAElementsOnStation& a, PndCAElementsOnStation& r, int iS ) { //new(&r) PndCAElementsOnStation( a.HitsRef() ); r.SetStation( a.IStation() ); r.clear(); if ( a.size() <= 0 ) return; r.reserve(5*a.size()); const float_v Pick2 = float_v(3.5*3.5);//fPick*fPick; //int iS = a.IStation()+ N-1; const PndCAHits* allHits = a.HitsRef(); const PndCAElementsOnStation &hits = allHits->OnStationConst( iS ); const PndCAStation &station = GetParameters().Station( iS ); PndCAStationSTT &stationMy = fStations[iS]; const float_v p6 = float_v(TMath::TwoPi()/6.); const float_v p12 = float_v(TMath::TwoPi()/12.); if ( station.NDF == 1 ) { // STT tubes const float_v sb = stationMy.fSin; const float_v cb = stationMy.fCos; store.resize(1); unsigned int vN = 0, vM=0; float_v err2R = stationMy.fResolution; for( unsigned int iD1 = 0; iD1 < a.size(); ++iD1 ) { PndCANPletV& D1 = a[iD1]; float_m valid1G = D1.IsValid(); if( valid1G.isEmpty() ) continue; PndCATrackParamVector ¶m = D1.ParamRef(); if( iS==PndCAParameters::NMVDStations ){ float_v secAngle = p12+p6*param.ISec(); valid1G &= param.Rotate( -param.Angle() + secAngle, .999f, valid1G ); } valid1G &= param.Transport0( int_v(iS), GetParameters(), valid1G ); if( valid1G.isEmpty() ) continue; D1.SetValid( valid1G ); const float_v& c00 = param.fC[0]; const float_v& c10 = param.fC[1]; const float_v& c11 = param.fC[2]; const float_v& c20 = param.fC[3]; const float_v& c21 = param.fC[4]; // F = CH' const float_v F0 = cb*c00 + sb*c10; const float_v F1 = cb*c10 + sb*c11; const float_v F2 = cb*c20 + sb*c21; const float_v HCH = ( F0*cb + F1*sb ); const float_v err2U = ( HCH + err2R); const float_v pickUp2 = Pick2*err2U; float_v trSinPhiU = cb*param.SinPhi() + sb*param.DzDs(); float_v trU = cb*param.Y() + sb*param.Z(); float_v trUCorr = -rsqrt(float_v(1.f) - trSinPhiU*trSinPhiU ); int secMin = param.fISec.min(valid1G); int secMax = param.fISec.max(valid1G); for( int iSec=secMin; iSec<=secMax; iSec++){ int_m sectorOK = valid1G & ( int_v(iSec) == param.ISec() ); if( sectorOK.isEmpty() ) continue; PndCAStationSTTSector §or = stationMy.fSectors[iSec]; if( sector.fNHits>30 ) continue; //SG!!! for( int jh=0; jh