diff --git a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h index 37c97a92a3eaa..68fb60361d40d 100644 --- a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h +++ b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h @@ -2,6 +2,7 @@ #define Geometry_TrackerGeometryBuilder_phase1PixelTopology_h #include +#include namespace phase1PixelTopology { @@ -29,6 +30,66 @@ namespace phase1PixelTopology { }; + template + constexpr auto map_to_array_helper(Function f, std::index_sequence) + -> std::array::type, sizeof...(Indices)> + { + return {{ f(Indices)... }}; + } + + template + constexpr auto map_to_array(Function f) + -> std::array::type, N> + { + return map_to_array_helper(f, std::make_index_sequence{}); + } + + + constexpr uint32_t findMaxModuleStride() { + bool go = true; + int n=2; + while (go) { + for (uint8_t i=1; i<11; ++i) { + if (layerStart[i]%n !=0) {go=false; break;} + } + if(!go) break; + n*=2; + } + return n/2; + } + + constexpr uint32_t maxModuleStride = findMaxModuleStride(); + + + constexpr uint8_t findLayer(uint32_t detId) { + for (uint8_t i=0; i<11; ++i) if (detId layer = map_to_array(findLayerFromCompact); + + constexpr bool validateLayerIndex() { + bool res=true; + for (auto i=0U; i=layerStart[layer[j]]); + res &=(i(ori)==bp); } + using namespace phase1PixelTopology; + for (auto i=0U; i=layerStart[layer[i]]); + assert(i +#include + +class AtomicPairCounter { +public: + + using c_type = unsigned long long int; + + AtomicPairCounter(){} + AtomicPairCounter(c_type i) { counter.ac=i;} + + __device__ __host__ + AtomicPairCounter & operator=(c_type i) { counter.ac=i; return *this;} + + struct Counters { + uint32_t n; // in a "One to Many" association is the number of "One" + uint32_t m; // in a "One to Many" association is the total number of associations + }; + + union Atomic2 { + Counters counters; + c_type ac; + }; + +#ifdef __CUDACC__ + + static constexpr c_type incr = 1UL<<32; + + __device__ __host__ + Counters get() const { return counter.counters;} + + // increment n by 1 and m by i. return previous value + __device__ + Counters add(uint32_t i) { + c_type c = i; + c+=incr; + Atomic2 ret; + ret.ac = atomicAdd(&counter.ac,c); + return ret.counters; + } + +#endif + +private: + + Atomic2 counter; + +}; + + +#endif diff --git a/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h b/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h index 8652aa97a709f..66525d13db8dc 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h +++ b/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h @@ -80,7 +80,8 @@ template struct SimpleVector { } #endif // __CUDACC__ - + inline constexpr bool empty() const { return m_size==0;} + inline constexpr bool full() const { return m_size==m_capacity;} inline constexpr T& operator[](int i) { return m_data[i]; } inline constexpr const T& operator[](int i) const { return m_data[i]; } inline constexpr void reset() { m_size = 0; } diff --git a/HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h b/HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h index 8dcefdce65ab4..a060fe6799b6c 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h +++ b/HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h @@ -80,6 +80,10 @@ template struct VecArray { return T(); } + inline constexpr T const * begin() const { return m_data;} + inline constexpr T const * end() const { return m_data+m_size;} + inline constexpr T * begin() { return m_data;} + inline constexpr T * end() { return m_data+m_size;} inline constexpr int size() const { return m_size; } inline constexpr T& operator[](int i) { return m_data[i]; } inline constexpr const T& operator[](int i) const { return m_data[i]; } diff --git a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h index 3a6545bed17df..61ccc7484d68b 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -19,6 +19,8 @@ #ifdef __CUDACC__ #include "HeterogeneousCore/CUDAUtilities/interface/prefixScan.h" #endif +#include "HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h" + #ifdef __CUDACC__ namespace cudautils { @@ -38,8 +40,7 @@ namespace cudautils { template __global__ - void fillFromVector(Histo * __restrict__ h, uint32_t nh, T const * __restrict__ v, uint32_t const * __restrict__ offsets, - uint32_t * __restrict__ ws ) { + void fillFromVector(Histo * __restrict__ h, uint32_t nh, T const * __restrict__ v, uint32_t const * __restrict__ offsets) { auto i = blockIdx.x * blockDim.x + threadIdx.x; if(i >= offsets[nh]) return; auto off = cuda_std::upper_bound(offsets, offsets + nh + 1, i); @@ -47,27 +48,43 @@ namespace cudautils { int32_t ih = off - offsets - 1; assert(ih >= 0); assert(ih < nh); - (*h).fill(v[i], i, ws, ih); + (*h).fill(v[i], i, ih); + } + + template + void launchZero(Histo * __restrict__ h, cudaStream_t stream) { + uint32_t * off = (uint32_t *)( (char*)(h) +offsetof(Histo,off)); + cudaMemsetAsync(off,0, 4*Histo::totbins(),stream); + } + + template + void launchFinalize(Histo * __restrict__ h, uint8_t * __restrict__ ws, cudaStream_t stream) { + uint32_t * off = (uint32_t *)( (char*)(h) +offsetof(Histo,off)); + size_t wss = Histo::wsSize(); + CubDebugExit(cub::DeviceScan::InclusiveSum(ws, wss, off, off, Histo::totbins(), stream)); } template - void fillManyFromVector(Histo * __restrict__ h, typename Histo::Counter * __restrict__ ws, + void fillManyFromVector(Histo * __restrict__ h, uint8_t * __restrict__ ws, uint32_t nh, T const * __restrict__ v, uint32_t const * __restrict__ offsets, uint32_t totSize, int nthreads, cudaStream_t stream) { - uint32_t * off = (uint32_t *)( (char*)(h) +offsetof(Histo,off)); - cudaMemsetAsync(off,0, 4*Histo::totbins(),stream); + launchZero(h,stream); auto nblocks = (totSize + nthreads - 1) / nthreads; countFromVector<<>>(h, nh, v, offsets); cudaCheck(cudaGetLastError()); - size_t wss = Histo::totbins(); - CubDebugExit(cub::DeviceScan::InclusiveSum(ws, wss, off, off, Histo::totbins(), stream)); - cudaMemsetAsync(ws,0, 4*Histo::totbins(),stream); - fillFromVector<<>>(h, nh, v, offsets,ws); + launchFinalize(h,ws,stream); + fillFromVector<<>>(h, nh, v, offsets); cudaCheck(cudaGetLastError()); } + template + __global__ + void finalizeBulk(AtomicPairCounter const * apc, Assoc * __restrict__ assoc) { + assoc->bulkFinalizeFill(*apc); + } + } // namespace cudautils #endif @@ -149,8 +166,8 @@ class HistoContainer { uint32_t * v =nullptr; void * d_temp_storage = nullptr; size_t temp_storage_bytes = 0; - cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, v, v, totbins()-1); - return std::max(temp_storage_bytes,size_t(totbins())); + cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, v, v, totbins()); + return temp_storage_bytes; } #endif @@ -176,22 +193,79 @@ class HistoContainer { #endif } + static __host__ __device__ + __forceinline__ + uint32_t atomicDecrement(Counter & x) { + #ifdef __CUDA_ARCH__ + return atomicSub(&x, 1); + #else + return x--; + #endif + } + + __host__ __device__ + __forceinline__ + void countDirect(T b) { + assert(b0); + bins[w-1] = j; + } + + +#ifdef __CUDACC__ + __device__ + __forceinline__ + uint32_t bulkFill(AtomicPairCounter & apc, index_type const * v, uint32_t n) { + auto c = apc.add(n); + off[c.m] = c.n; + for(int j=0; j=totbins()) return; + off[i]=n; + } + + +#endif + + __host__ __device__ __forceinline__ void count(T t) { uint32_t b = bin(t); assert(b0); + bins[w-1] = j; } @@ -202,31 +276,35 @@ class HistoContainer { assert(b0); + bins[w-1] = j; } #ifdef __CUDACC__ __device__ __forceinline__ void finalize(Counter * ws) { - blockPrefixScan(off+1,totbins()-1,ws); + assert(off[totbins()-1]==0); + blockPrefixScan(off,totbins(),ws); + assert(off[totbins()-1]==off[totbins()-2]); } __host__ #endif void finalize() { - for(uint32_t i=2; i +using OneToManyAssoc = HistoContainer; + #endif // HeterogeneousCore_CUDAUtilities_HistoContainer_h diff --git a/HeterogeneousCore/CUDAUtilities/test/AtomicPairCounter_t.cu b/HeterogeneousCore/CUDAUtilities/test/AtomicPairCounter_t.cu new file mode 100644 index 0000000000000..c52988b2dd0d9 --- /dev/null +++ b/HeterogeneousCore/CUDAUtilities/test/AtomicPairCounter_t.cu @@ -0,0 +1,66 @@ +#include "HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h" + +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +__global__ +void update(AtomicPairCounter * dc, uint32_t * ind, uint32_t * cont, uint32_t n) { + auto i = blockIdx.x*blockDim.x + threadIdx.x; + if (i>=n) return; + + auto m = i%11; + m = m%6 +1; // max 6, no 0 + auto c = dc->add(m); + assert(c.mget().m==n); + ind[n]= dc->get().n; +} + +__global__ +void verify(AtomicPairCounter const * dc, uint32_t const * ind, uint32_t const * cont, uint32_t n) { + auto i = blockIdx.x*blockDim.x + threadIdx.x; + if (i>=n) return; + assert(0==ind[0]); + assert(dc->get().m==n); + assert(ind[n] == dc->get().n); + auto ib = ind[i]; + auto ie = ind[i+1]; + auto k = cont[ib++]; + assert(k +int main() { + + AtomicPairCounter * dc_d; + cudaMalloc(&dc_d, sizeof(AtomicPairCounter)); + cudaMemset(dc_d, 0, sizeof(AtomicPairCounter)); + + std::cout << "size " << sizeof(AtomicPairCounter) << std::endl; + + constexpr uint32_t N=20000; + constexpr uint32_t M=N*6; + uint32_t *n_d, *m_d; + cudaMalloc(&n_d, N*sizeof(int)); + // cudaMemset(n_d, 0, N*sizeof(int)); + cudaMalloc(&m_d, M*sizeof(int)); + + + update<<<2000, 512 >>>(dc_d,n_d,m_d,10000); + finalize<<<1,1 >>>(dc_d,n_d,m_d,10000); + verify<<<2000, 512 >>>(dc_d,n_d,m_d,10000); + + AtomicPairCounter dc; + cudaMemcpy(&dc, dc_d, sizeof(AtomicPairCounter), cudaMemcpyDeviceToHost); + + std::cout << dc.get().n << ' ' << dc.get().m << std::endl; + + return 0; +} diff --git a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml index 1049aed41fd5f..57df6834bba83 100644 --- a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml +++ b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml @@ -1,21 +1,35 @@ - + + + + + + + + + + + + + + + - + @@ -25,21 +39,34 @@ + - + + + + + + + + + + + + + diff --git a/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cpp b/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cpp index ca8167ab10894..c61a5004f8bd9 100644 --- a/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cpp +++ b/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cpp @@ -35,22 +35,20 @@ void go() { Hist h; Hist4 h4; - typename Hist::Counter ws[Hist::totbins()]; - typename Hist4::Counter ws4[Hist4::totbins()]; for (int it=0; it<5; ++it) { for (long long j = 0; j < N; j++) v[j]=rgen(eng); if (it==2) for (long long j = N/2; j < N/2+N/4; j++) v[j]=4; h.zero();h4.zero(); assert(h.size()==0);assert(h4.size()==0); - for (auto & i: ws) i=0; - for (auto & i: ws4) i=0; for (long long j = 0; j < N; j++) { h.count(v[j]); if(j<2000) h4.count(v[j],2); else h4.count(v[j],j%4); } + assert(h.size()==0); + assert(h4.size()==0); h.finalize(); h4.finalize(); - assert(h.off[0]==0); assert(h.size()==N); - assert(h4.off[0]==0); assert(h4.size()==N); - for (long long j = 0; j < N; j++) { h.fill(v[j],j,ws); if(j<2000) h4.fill(v[j],j,ws4,2); else h4.fill(v[j],j,ws4,j%4); } + for (long long j = 0; j < N; j++) { h.fill(v[j],j); if(j<2000) h4.fill(v[j],2); else h4.fill(v[j],j,j%4); } + assert(h.off[0]==0); + assert(h4.off[0]==0); assert(h.size()==N); assert(h4.size()==N); diff --git a/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cu b/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cu index 8d640b728e25c..67cbd30b7e786 100644 --- a/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cu +++ b/HeterogeneousCore/CUDAUtilities/test/HistoContainer_t.cu @@ -42,7 +42,7 @@ void go() { Hist h; auto h_d = cuda::memory::device::make_unique(current_device, 1); - auto ws_d = cuda::memory::device::make_unique(current_device, Hist::totbins()); + auto ws_d = cuda::memory::device::make_unique(current_device, Hist::wsSize()); auto off_d = cuda::memory::device::make_unique(current_device, nParts+1); diff --git a/HeterogeneousCore/CUDAUtilities/test/OneHistoContainer_t.cu b/HeterogeneousCore/CUDAUtilities/test/OneHistoContainer_t.cu index c3682578163c8..0d529fd7e7b03 100644 --- a/HeterogeneousCore/CUDAUtilities/test/OneHistoContainer_t.cu +++ b/HeterogeneousCore/CUDAUtilities/test/OneHistoContainer_t.cu @@ -18,27 +18,23 @@ void mykernel(T const * __restrict__ v, uint32_t N) { if (threadIdx.x==0) printf("start kernel for %d data\n",N); using Hist = HistoContainer; - constexpr auto wss = Hist::totbins(); - - if (threadIdx.x==0) printf("ws size %d\n",wss); __shared__ Hist hist; - __shared__ typename Hist::Counter ws[wss]; + __shared__ typename Hist::Counter ws[32]; - for (auto j=threadIdx.x; j +#include +#include +#include +#include +#include + +#include + + +constexpr uint32_t MaxElem=64000; +constexpr uint32_t MaxTk=8000; +constexpr uint32_t MaxAssocs = 4*MaxTk; +using Assoc = OneToManyAssoc; + +using TK = std::array; + +__global__ +void count(TK const * __restrict__ tk, Assoc * __restrict__ assoc, uint32_t n) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + auto k = i/4; + auto j = i - 4*k; + assert(j<4); + if (k>=n) return; + if (tk[k][j]countDirect(tk[k][j]); + +} + +__global__ +void fill(TK const * __restrict__ tk, Assoc * __restrict__ assoc, uint32_t n) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + auto k = i/4; + auto j = i - 4*k; + assert(j<4); + if (k>=n) return; + if (tk[k][j]fillDirect(tk[k][j],k); + +} + +__global__ +void verify(Assoc * __restrict__ assoc) { + assert(assoc->size()=n) return; + auto m = tk[k][3]bulkFill(*apc,&tk[k][0],m); +} + + + + +int main() { + + + std::cout << "OneToManyAssoc " << Assoc::nbins() << ' ' << Assoc::capacity() << ' '<< Assoc::wsSize() << std::endl; + + + if (cuda::device::count() == 0) { + std::cerr << "No CUDA devices on this system" << "\n"; + exit(EXIT_FAILURE); + } + + auto current_device = cuda::device::current::get(); + + + std::mt19937 eng; + + std::geometric_distribution rdm(0.8); + + constexpr uint32_t N = 4000; + + std::vector> tr(N); + + // fill with "index" to element + long long ave=0; + int imax=0; + auto n=0U; + auto z=0U; + auto nz=0U; + for (auto i=0U; i<4U; ++i) { + auto j=0U; + while(j[]>(current_device, N); + assert(v_d.get()); + auto a_d = cuda::memory::device::make_unique(current_device,1); + auto ws_d = cuda::memory::device::make_unique(current_device, Assoc::wsSize()); + + cuda::memory::copy(v_d.get(), tr.data(), N*sizeof(std::array)); + + cudautils::launchZero(a_d.get(),0); + + auto nThreads = 256; + auto nBlocks = (4*N + nThreads - 1) / nThreads; + + count<<>>(v_d.get(),a_d.get(),N); + + cudautils::launchFinalize(a_d.get(),ws_d.get(),0); + verify<<<1,1>>>(a_d.get()); + fill<<>>(v_d.get(),a_d.get(),N); + + Assoc la; + cuda::memory::copy(&la,a_d.get(),sizeof(Assoc)); + std::cout << la.size() << std::endl; + imax = 0; + ave=0; + z=0; + for (auto i=0U; i>>(dc_d,v_d.get(),a_d.get(),N); + cudautils::finalizeBulk<<>>(dc_d,a_d.get()); + + AtomicPairCounter dc; + cudaMemcpy(&dc, dc_d, sizeof(AtomicPairCounter), cudaMemcpyDeviceToHost); + + std::cout << "final counter value " << dc.get().n << ' ' << dc.get().m << std::endl; + + + cuda::memory::copy(&la,a_d.get(),sizeof(Assoc)); + std::cout << la.size() << std::endl; + imax = 0; + ave=0; + for (auto i=0U; i>>(c); + cudaDeviceSynchronize(); + return c==1; + +} diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h index 26bb4f9244c6a..27e7495ad055b 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h @@ -81,10 +81,9 @@ namespace gpuClustering { constexpr uint32_t maxPixInModule = 4000; constexpr auto nbins = phase1PixelTopology::numColsInModule + 2; //2+2; using Hist = HistoContainer; - constexpr auto wss = Hist::totbins(); __shared__ Hist hist; - __shared__ typename Hist::Counter ws[wss]; - for (auto j=threadIdx.x; j(gpu_.owner_16bit_, gpu_.owner_16bit_pitch_, 4); cudaCheck(cudaMalloc((void **) & gpu_.hist_d, sizeof(HitsOnGPU::Hist))); - cudaCheck(cudaMalloc((void **) & gpu_.hws_d, 4*HitsOnGPU::Hist::totbins())); + cudaCheck(cudaMalloc((void **) & gpu_.hws_d, HitsOnGPU::Hist::wsSize())); cudaCheck(cudaMalloc((void **) & gpu_d, sizeof(HitsOnGPU))); - gpu_.me_d = gpu_d; - cudaCheck(cudaMemcpyAsync(gpu_d, &gpu_, sizeof(HitsOnGPU), cudaMemcpyDefault, cudaStream.id())); // Feels a bit dumb but constexpr arrays are not supported for device code // TODO: should be moved to EventSetup (or better ideas?) // Would it be better to use "constant memory"? cudaCheck(cudaMalloc((void **) & d_phase1TopologyLayerStart_, 11 * sizeof(uint32_t))); cudaCheck(cudaMemcpyAsync(d_phase1TopologyLayerStart_, phase1PixelTopology::layerStart, 11 * sizeof(uint32_t), cudaMemcpyDefault, cudaStream.id())); + cudaCheck(cudaMalloc((void **) & d_phase1TopologyLayer_, phase1PixelTopology::layer.size() * sizeof(uint8_t))); + cudaCheck(cudaMemcpyAsync(d_phase1TopologyLayer_, phase1PixelTopology::layer.data(), phase1PixelTopology::layer.size() * sizeof(uint8_t), cudaMemcpyDefault, cudaStream.id())); + + gpu_.phase1TopologyLayerStart_d = d_phase1TopologyLayerStart_; + gpu_.phase1TopologyLayer_d = d_phase1TopologyLayer_; + + gpu_.me_d = gpu_d; + cudaCheck(cudaMemcpyAsync(gpu_d, &gpu_, sizeof(HitsOnGPU), cudaMemcpyDefault, cudaStream.id())); cudaCheck(cudaMallocHost(&h_hitsModuleStart_, (gpuClustering::MaxNumModules+1) * sizeof(uint32_t))); @@ -113,6 +119,7 @@ namespace pixelgpudetails { cudaCheck(cudaFree(gpu_.hws_d)); cudaCheck(cudaFree(gpu_d)); cudaCheck(cudaFree(d_phase1TopologyLayerStart_)); + cudaCheck(cudaFree(d_phase1TopologyLayer_)); cudaCheck(cudaFreeHost(h_hitsModuleStart_)); cudaCheck(cudaFreeHost(h_owner_32bit_)); diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.h b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.h index aff529791aeca..dcc80308c4463 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.h +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.h @@ -50,6 +50,7 @@ namespace pixelgpudetails { HitsOnGPU gpu_; uint32_t nhits_ = 0; uint32_t *d_phase1TopologyLayerStart_ = nullptr; + uint8_t *d_phase1TopologyLayer_ = nullptr; uint32_t *h_hitsModuleStart_ = nullptr; uint16_t *h_detInd_ = nullptr; int32_t *h_charge_ = nullptr; diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitHeterogeneous.cc b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitHeterogeneous.cc index 00cfbe0baa5c0..68f53a47157d4 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitHeterogeneous.cc +++ b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitHeterogeneous.cc @@ -277,7 +277,7 @@ void SiPixelRecHitHeterogeneous::run(const edm::HandlecommonParams(), cpeParams->detParams(me), clusParams, ic); - pixelCPEforGPU::error(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); + pixelCPEforGPU::errorFromDB(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); chargeh[h] = clusParams.charge[ic]; @@ -135,8 +135,8 @@ namespace gpuPixelRecHits { xl[h]= clusParams.xpos[ic]; yl[h]= clusParams.ypos[ic]; - xe[h]= clusParams.xerr[ic]; - ye[h]= clusParams.yerr[ic]; + xe[h]= clusParams.xerr[ic]*clusParams.xerr[ic]; + ye[h]= clusParams.yerr[ic]*clusParams.yerr[ic]; mr[h]= clusParams.minRow[ic]; mc[h]= clusParams.minCol[ic]; diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h b/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h index 9d0fe7a279799..ea6eaf8458dde 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h +++ b/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h @@ -37,7 +37,7 @@ namespace siPixelRecHitsHeterogeneousProduct { using Hist = HistoContainer; Hist * hist_d; - typename Hist::Counter * hws_d; + uint8_t * hws_d; HitsOnGPU const * me_d = nullptr; @@ -46,6 +46,11 @@ namespace siPixelRecHitsHeterogeneousProduct { size_t owner_32bit_pitch_; void *owner_16bit_; size_t owner_16bit_pitch_; + + // forwarded from PixelRecHit (FIXME) + uint32_t const * phase1TopologyLayerStart_d; + uint8_t const * phase1TopologyLayer_d; + }; struct HitsOnCPU { diff --git a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc index af7dd7337084e..eb51dd5a2eaeb 100644 --- a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc +++ b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc @@ -125,7 +125,84 @@ void PixelCPEFast::fillParamsForGpu() { auto vv = p.theDet->surface().position(); auto rr = pixelCPEforGPU::Rotation(p.theDet->surface().rotation()); g.frame = pixelCPEforGPU::Frame(vv.x(),vv.y(),vv.z(),rr); - } + + + // errors ..... + ClusterParamGeneric cp; + auto gvx = p.theOrigin.x() + 40.f*m_commonParamsGPU.thePitchX; + auto gvy = p.theOrigin.y(); + auto gvz = 1.f/p.theOrigin.z(); + //--- Note that the normalization is not required as only the ratio used + + // calculate angles + cp.cotalpha = gvx*gvz; + cp.cotbeta = gvy*gvz; + + cp.with_track_angle = false; + + auto lape = p.theDet->localAlignmentError(); + if ( lape.invalid() ) lape = LocalError(); // zero.... + +#ifdef DUMP_ERRORS + auto m=10000.f; + for (float qclus = 15000; qclus<35000; qclus+=15000){ + errorFromTemplates(p,cp,qclus); + + std::cout << i << ' ' << qclus << ' ' << cp.pixmx + << ' ' << m*cp.sigmax << ' ' << m*cp.sx1 << ' ' << m*cp.sx2 + << ' ' << m*cp.sigmay << ' ' << m*cp.sy1 << ' ' << m*cp.sy2 + << std::endl; + } + std::cout << i << ' ' << m*std::sqrt(lape.xx()) <<' '<< m*std::sqrt(lape.yy()) << std::endl; +#endif + + + errorFromTemplates(p,cp,20000.f); + g.sx[0] = cp.sigmax; + g.sx[1] = cp.sx1; + g.sx[2] = cp.sx2; + + g.sy[0] = cp.sigmay; + g.sy[1] = cp.sy1; + g.sy[2] = cp.sy2; + + + /* + // from run1?? + if (i<96) { + g.sx[0] = 0.00120; + g.sx[1] = 0.00115; + g.sx[2] = 0.0050; + + g.sy[0] = 0.00210; + g.sy[1] = 0.00375; + g.sy[2] = 0.0085; + } else if (g.isBarrel) { + g.sx[0] = 0.00120; + g.sx[1] = 0.00115; + g.sx[2] = 0.0050; + + g.sy[0] = 0.00210; + g.sy[1] = 0.00375; + g.sy[2] = 0.0085; + } else { + g.sx[0] = 0.0020; + g.sx[1] = 0.0020; + g.sx[2] = 0.0050; + + g.sy[0] = 0.0021; + g.sy[1] = 0.0021; + g.sy[2] = 0.0085; + } + */ + + + for (int i=0; i<3; ++i) { + g.sx[i] = std::sqrt(g.sx[i]*g.sx[i]+lape.xx()); + g.sy[i] = std::sqrt(g.sy[i]*g.sy[i]+lape.yy()); + } + + } } PixelCPEFast::~PixelCPEFast() {} @@ -143,25 +220,15 @@ PixelCPEBase::ClusterParam* PixelCPEFast::createClusterParam(const SiPixelCluste return new ClusterParamGeneric(cl); } -//----------------------------------------------------------------------------- -//! Hit position in the local frame (in cm). Unlike other CPE's, this -//! one converts everything from the measurement frame (in channel numbers) -//! into the local frame (in centimeters). -//----------------------------------------------------------------------------- -LocalPoint -PixelCPEFast::localPosition(DetParam const & theDetParam, ClusterParam & theClusterParamBase) const -{ - ClusterParamGeneric & theClusterParam = static_cast(theClusterParamBase); - assert(!theClusterParam.with_track_angle); - - if ( UseErrorsFromTemplates_ ) { - - float qclus = theClusterParam.theCluster->charge(); + +void +PixelCPEFast::errorFromTemplates(DetParam const & theDetParam, ClusterParamGeneric & theClusterParam, float qclus) const +{ float locBz = theDetParam.bz; float locBx = theDetParam.bx; //cout << "PixelCPEFast::localPosition(...) : locBz = " << locBz << endl; - + theClusterParam.pixmx = std::numeric_limits::max(); // max pixel charge for truncation of 2-D cluster theClusterParam.sigmay = -999.9; // CPE Generic y-error for multi-pixel cluster @@ -170,28 +237,43 @@ PixelCPEFast::localPosition(DetParam const & theDetParam, ClusterParam & theClus theClusterParam.sy2 = -999.9; // CPE Generic y-error for single double-pixel cluster theClusterParam.sx1 = -999.9; // CPE Generic x-error for single single-pixel cluster theClusterParam.sx2 = -999.9; // CPE Generic x-error for single double-pixel cluster - + float dummy; - + SiPixelGenError gtempl(thePixelGenError_); int gtemplID_ = theDetParam.detTemplateId; - - theClusterParam.qBin_ = gtempl.qbin( gtemplID_, theClusterParam.cotalpha, theClusterParam.cotbeta, locBz, locBx, qclus, + + theClusterParam.qBin_ = gtempl.qbin( gtemplID_, theClusterParam.cotalpha, theClusterParam.cotbeta, locBz, locBx, qclus, false, theClusterParam.pixmx, theClusterParam.sigmay, dummy, theClusterParam.sigmax, dummy, theClusterParam.sy1, dummy, theClusterParam.sy2, dummy, theClusterParam.sx1, dummy, theClusterParam.sx2, dummy ); - + theClusterParam.sigmax = theClusterParam.sigmax * micronsToCm; theClusterParam.sx1 = theClusterParam.sx1 * micronsToCm; theClusterParam.sx2 = theClusterParam.sx2 * micronsToCm; - + theClusterParam.sigmay = theClusterParam.sigmay * micronsToCm; theClusterParam.sy1 = theClusterParam.sy1 * micronsToCm; theClusterParam.sy2 = theClusterParam.sy2 * micronsToCm; - - } // if ( UseErrorsFromTemplates_ ) +} + +//----------------------------------------------------------------------------- +//! Hit position in the local frame (in cm). Unlike other CPE's, this +//! one converts everything from the measurement frame (in channel numbers) +//! into the local frame (in centimeters). +//----------------------------------------------------------------------------- +LocalPoint +PixelCPEFast::localPosition(DetParam const & theDetParam, ClusterParam & theClusterParamBase) const +{ + ClusterParamGeneric & theClusterParam = static_cast(theClusterParamBase); + + assert(!theClusterParam.with_track_angle); + + if ( UseErrorsFromTemplates_ ) { + errorFromTemplates(theDetParam, theClusterParam, theClusterParam.theCluster->charge()); + } else { theClusterParam.qBin_ = 0; } diff --git a/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py b/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py index 34ee6fadb04de..e784b53b7ce1f 100644 --- a/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py +++ b/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py @@ -4,7 +4,7 @@ # # for STARTUP ONLY use try and use Offline 3D PV from pixelTracks, with adaptive vertex # -#from RecoPixelVertexing.PixelVertexFinding.PixelVertexes_cff import * -from RecoVertex.PrimaryVertexProducer.OfflinePixel3DPrimaryVertices_cfi import * +from RecoPixelVertexing.PixelVertexFinding.PixelVertexes_cff import * +#from RecoVertex.PrimaryVertexProducer.OfflinePixel3DPrimaryVertices_cfi import * recopixelvertexingTask = cms.Task(pixelTracksTask,pixelVertices) recopixelvertexing = cms.Sequence(recopixelvertexingTask) diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py index 99a3a9321062b..15224adb78cc3 100644 --- a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py +++ b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py @@ -6,6 +6,7 @@ def customizePixelTracksForProfiling(process): process.out = cms.OutputModule("AsciiOutputModule", outputCommands = cms.untracked.vstring( "keep *_pixelTracks_*_*", + "keep *_pixelVertices_*_*", ), verbosity = cms.untracked.uint32(0), ) @@ -19,17 +20,12 @@ def customizePixelTracksForProfiling(process): def customizePixelTracksForProfilingDisableConversion(process): process = customizePixelTracksForProfiling(process) - # Turn off cluster shape filter so that CA doesn't depend on clusters - process.pixelTracksHitQuadruplets.SeedComparitorPSet = cms.PSet(ComponentName = cms.string("none")) - - # Replace pixel track producer with a dummy one for now - from RecoPixelVertexing.PixelTrackFitting.pixelTrackProducerFromCUDA_cfi import pixelTrackProducerFromCUDA as _pixelTrackProducerFromCUDA - process.pixelTracks = _pixelTrackProducerFromCUDA.clone() - # Disable conversions to legacy process.siPixelClustersPreSplitting.gpuEnableConversion = False process.siPixelRecHitsPreSplitting.gpuEnableConversion = False process.pixelTracksHitQuadruplets.gpuEnableConversion = False + process.pixelTracks.gpuEnableConversion = False + process.pixelVertices.gpuEnableConversion = False return process @@ -40,5 +36,6 @@ def customizePixelTracksForProfilingDisableTransfer(process): process.siPixelClustersPreSplitting.gpuEnableTransfer = False process.siPixelRecHitsPreSplitting.gpuEnableTransfer = False process.pixelTracksHitQuadruplets.gpuEnableTransfer = False + process.pixelVertices.gpuEnableTransfer = False return process diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h new file mode 100644 index 0000000000000..73fd9210bc378 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h @@ -0,0 +1,94 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_FitResult_h +#define RecoPixelVertexing_PixelTrackFitting_interface_FitResult_h + +#include + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +namespace Rfit +{ + +constexpr double d = 1.e-4; //!< used in numerical derivative (J2 in Circle_fit()) +constexpr unsigned int max_nop = 4; //!< In order to avoid use of dynamic memory + +constexpr auto Dynamic = Eigen::Dynamic; +using VectorXd = Eigen::VectorXd; +using MatrixXd = Eigen::MatrixXd; +using MatrixNd = Eigen::Matrix; +using ArrayNd = Eigen::Array; +using Matrix2Nd = Eigen::Matrix; +using Matrix3Nd = Eigen::Matrix; +using Matrix2xNd = Eigen::Matrix; +using Array2xNd = Eigen::Array; +using Matrix3xNd = Eigen::Matrix; +using MatrixNx3d = Eigen::Matrix; +using MatrixNx5d = Eigen::Matrix; +using VectorNd = Eigen::Matrix; +using Vector2Nd = Eigen::Matrix; +using Vector3Nd = Eigen::Matrix; +using RowVectorNd = Eigen::Matrix; +using RowVector2Nd = Eigen::Matrix; + + +using Vector2d = Eigen::Vector2d; +using Vector3d = Eigen::Vector3d; +using Vector4d = Eigen::Vector4d; +using Matrix2d = Eigen::Matrix2d; +using Matrix3d = Eigen::Matrix3d; +using Matrix4d = Eigen::Matrix4d; +using Matrix5d = Eigen::Matrix; +using Matrix6d = Eigen::Matrix; +using Vector5d = Eigen::Matrix; + +using Matrix3f = Eigen::Matrix3f; + +using u_int = unsigned int; + + +struct circle_fit +{ + Vector3d par; //!< parameter: (X0,Y0,R) + Matrix3d cov; + /*!< covariance matrix: \n + |cov(X0,X0)|cov(Y0,X0)|cov( R,X0)| \n + |cov(X0,Y0)|cov(Y0,Y0)|cov( R,Y0)| \n + |cov(X0, R)|cov(Y0, R)|cov( R, R)| + */ + int64_t q; //!< particle charge + double chi2 = 0.0; +}; + +struct line_fit +{ + Vector2d par; //!<(cotan(theta),Zip) + Matrix2d cov; + /*!< + |cov(c_t,c_t)|cov(Zip,c_t)| \n + |cov(c_t,Zip)|cov(Zip,Zip)| + */ + double chi2 = 0.0; +}; + +struct helix_fit +{ + Vector5d par; //!<(phi,Tip,pt,cotan(theta)),Zip) + Matrix5d cov; + /*!< ()->cov() \n + |(phi,phi)|(Tip,phi)|(p_t,phi)|(c_t,phi)|(Zip,phi)| \n + |(phi,Tip)|(Tip,Tip)|(p_t,Tip)|(c_t,Tip)|(Zip,Tip)| \n + |(phi,p_t)|(Tip,p_t)|(p_t,p_t)|(c_t,p_t)|(Zip,p_t)| \n + |(phi,c_t)|(Tip,c_t)|(p_t,c_t)|(c_t,c_t)|(Zip,c_t)| \n + |(phi,Zip)|(Tip,Zip)|(p_t,Zip)|(c_t,Zip)|(Zip,Zip)| + */ + float chi2_circle; + float chi2_line; +// Vector4d fast_fit; + int32_t q; //!< particle charge +} __attribute__((aligned(16))); + +} // namespace RFit +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index 6de1c77bbac12..c7d475c4d48b9 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -1,90 +1,16 @@ #ifndef RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h #define RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h -#include +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" -#include -#include -#include - -#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" - -#ifndef RFIT_DEBUG -#define RFIT_DEBUG 0 -#endif // RFIT_DEBUG namespace Rfit { -using namespace Eigen; - -constexpr double d = 1.e-4; //!< used in numerical derivative (J2 in Circle_fit()) -constexpr unsigned int max_nop = 4; //!< In order to avoid use of dynamic memory - -using MatrixNd = Eigen::Matrix; -using ArrayNd = Eigen::Array; -using Matrix2Nd = Eigen::Matrix; -using Matrix3Nd = Eigen::Matrix; -using Matrix2xNd = Eigen::Matrix; -using Array2xNd = Eigen::Array; -using Matrix3xNd = Eigen::Matrix; -using MatrixNx3d = Eigen::Matrix; -using MatrixNx5d = Eigen::Matrix; -using VectorNd = Eigen::Matrix; -using Vector2Nd = Eigen::Matrix; -using Vector3Nd = Eigen::Matrix; -using RowVectorNd = Eigen::Matrix; -using RowVector2Nd = Eigen::Matrix; -using Matrix5d = Eigen::Matrix; -using Matrix6d = Eigen::Matrix; -using Vector5d = Eigen::Matrix; -using u_int = unsigned int; - -struct circle_fit -{ - Vector3d par; //!< parameter: (X0,Y0,R) - Matrix3d cov; - /*!< covariance matrix: \n - |cov(X0,X0)|cov(Y0,X0)|cov( R,X0)| \n - |cov(X0,Y0)|cov(Y0,Y0)|cov( R,Y0)| \n - |cov(X0, R)|cov(Y0, R)|cov( R, R)| - */ - int64_t q; //!< particle charge - double chi2 = 0.0; -}; - -struct line_fit -{ - Vector2d par; //!<(cotan(theta),Zip) - Matrix2d cov; - /*!< - |cov(c_t,c_t)|cov(Zip,c_t)| \n - |cov(c_t,Zip)|cov(Zip,Zip)| - */ - double chi2 = 0.0; -}; - -struct helix_fit -{ - Vector5d par; //!<(phi,Tip,pt,cotan(theta)),Zip) - Matrix5d cov; - /*!< ()->cov() \n - |(phi,phi)|(Tip,phi)|(p_t,phi)|(c_t,phi)|(Zip,phi)| \n - |(phi,Tip)|(Tip,Tip)|(p_t,Tip)|(c_t,Tip)|(Zip,Tip)| \n - |(phi,p_t)|(Tip,p_t)|(p_t,p_t)|(c_t,p_t)|(Zip,p_t)| \n - |(phi,c_t)|(Tip,c_t)|(p_t,c_t)|(c_t,c_t)|(Zip,c_t)| \n - |(phi,Zip)|(Tip,Zip)|(p_t,Zip)|(c_t,Zip)|(Zip,Zip)| - */ - double chi2_circle = 0.0; - double chi2_line = 0.0; - Vector4d fast_fit; - int64_t q; //!< particle charge - // VectorXd time; // TO FIX just for profiling -} __attribute__((aligned(16))); template __host__ __device__ void printIt(C* m, const char* prefix = "") { -#if RFIT_DEBUG +#ifdef RFIT_DEBUG for (u_int r = 0; r < m->rows(); ++r) { for (u_int c = 0; c < m->cols(); ++c) @@ -107,10 +33,8 @@ __host__ __device__ inline T sqr(const T a) /*! \brief Compute cross product of two 2D vector (assuming z component 0), returning z component of the result. - \param a first 2D vector in the product. \param b second 2D vector in the product. - \return z component of the cross product. */ @@ -119,92 +43,92 @@ __host__ __device__ inline double cross2D(const Vector2d& a, const Vector2d& b) return a.x() * b.y() - a.y() * b.x(); } +/*! Compute the Radiation length in the uniform hypothesis + * + * The Pixel detector, barrel and forward, is considered as an omogeneous + * cilinder of material, whose radiation lengths has been derived from the TDR + * plot that shows that 16cm correspond to 0.06 radiation lengths. Therefore + * one radiation length corresponds to 16cm/0.06 =~ 267 cm. All radiation + * lengths are computed using this unique number, in both regions, barrel and + * endcap. + * + * NB: no angle corrections nor projections are computed inside this routine. + * It is therefore the responsibility of the caller to supply the proper + * lengths in input. These lenghts are the path travelled by the particle along + * its trajectory, namely the so called S of the helix in 3D space. + * + * \param length_values vector of incremental distances that will be translated + * into radiation length equivalent. Each radiation length i is computed + * incrementally with respect to the previous length i-1. The first lenght has + * no reference point (i.e. it has the dca). + * + * \return incremental radiation lengths that correspond to each segment. + */ -__host__ __device__ inline void computeRadLenEff(const Vector4d& fast_fit, - const double B, - double & radlen_eff, - double & theta, - bool & in_forward) { - double X_barrel = 0.015; - double X_forward = 0.05; - theta = atan(fast_fit(3)); - // atan returns values in [-pi/2, pi/2], we need [0, pi] - theta = theta < 0. ? theta + M_PI : theta; - radlen_eff = X_barrel / std::abs(sin(theta)); - in_forward = (theta <= 0.398 or theta >= 2.743); - if (in_forward) - radlen_eff = X_forward / std::abs(cos(theta)); - assert(radlen_eff > 0.); - double p_t = fast_fit(2) * B; - // We have also to correct the radiation lenght in the x-y plane. Since we - // do not know the angle of incidence of the track at this point, we - // arbitrarily set the correction proportional to the inverse of the - // transerse momentum. The cut-off is at 1 Gev, set using a single Muon Pt - // gun and verifying that, at that momentum, not additional correction is, - // in fact, needed. This is an approximation. - if (std::abs(p_t/1.) < 1.) - radlen_eff /= std::abs(p_t/1.); +__host__ __device__ inline +void computeRadLenUniformMaterial(const VectorNd &length_values, + VectorNd & rad_lengths) { + // Radiation length of the pixel detector in the uniform assumption, with + // 0.06 rad_len at 16 cm + constexpr double XX_0_inv = 0.06/16.; +// const double XX_0 = 1000.*16.f/(0.06); + u_int n = length_values.rows(); + rad_lengths(0) = length_values(0)*XX_0_inv; + for (u_int j = 1; j < n; ++j) { + rad_lengths(j) = std::abs(length_values(j)-length_values(j-1))*XX_0_inv; + } } /*! \brief Compute the covariance matrix along cartesian S-Z of points due to multiple Coulomb scattering to be used in the line_fit, for the barrel and forward cases. - + The input covariance matrix is in the variables s-z, original and + unrotated. + The multiple scattering component is computed in the usual linear + approximation, using the 3D path which is computed as the squared root of + the squared sum of the s and z components passed in. + Internally a rotation by theta is performed and the covariance matrix + returned is the one in the direction orthogonal to the rotated S3D axis, + i.e. along the rotated Z axis. + The choice of the rotation is not arbitrary, but derived from the fact that + putting the horizontal axis along the S3D direction allows the usage of the + ordinary least squared fitting techiques with the trivial parametrization y + = mx + q, avoiding the patological case with m = +/- inf, that would + correspond to the case at eta = 0. */ + __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, const Vector4d& fast_fit, VectorNd const& s_arcs, VectorNd const& z_values, + const double theta, const double B) { #if RFIT_DEBUG Rfit::printIt(&s_arcs, "Scatter_cov_line - s_arcs: "); #endif u_int n = s_arcs.rows(); - double p_t = fast_fit(2) * B; + double p_t = std::min(20.,fast_fit(2) * B); // limit pt to avoid too small error!!! double p_2 = p_t * p_t * (1. + 1. / (fast_fit(3) * fast_fit(3))); - double radlen_eff = 0.; - double theta = 0.; - bool in_forward = false; - computeRadLenEff(fast_fit, B, radlen_eff, theta, in_forward); - - const double sig2 = .000225 / p_2 * sqr(1 + 0.038 * log(radlen_eff)) * radlen_eff; - for (u_int k = 0; k < n; ++k) - { - for (u_int l = k; l < n; ++l) - { - for (u_int i = 0; i < std::min(k, l); ++i) - { -#if RFIT_DEBUG - printf("Scatter_cov_line - B: %f\n", B); - printf("Scatter_cov_line - radlen_eff: %f, p_t: %f, p2: %f\n", radlen_eff, p_t, p_2); - printf("Scatter_cov_line - sig2:%f, theta: %f\n", sig2, theta); - printf("Scatter_cov_line - Adding to element %d, %d value %f\n", n + k, n + l, (s_arcs(k) - s_arcs(i)) * (s_arcs(l) - s_arcs(i)) * sig2 / sqr(sqr(sin(theta)))); -#endif - if (in_forward) { - cov_sz(k, l) += (z_values(k) - z_values(i)) * (z_values(l) - z_values(i)) * sig2 / sqr(sqr(cos(theta))); - cov_sz(l, k) = cov_sz(k, l); - } else { - cov_sz(n + k, n + l) += (s_arcs(k) - s_arcs(i)) * (s_arcs(l) - s_arcs(i)) * sig2 / sqr(sqr(sin(theta))); - cov_sz(n + l, n + k) = cov_sz(n + k, n + l); - } - } - } - } + VectorNd rad_lengths_S(n); + // See documentation at http://eigen.tuxfamily.org/dox/group__TutorialArrayClass.html + // Basically, to perform cwise operations on Matrices and Vectors, you need + // to transform them into Array-like objects. + VectorNd S_values = s_arcs.array() * s_arcs.array() + z_values.array() * z_values.array(); + S_values = S_values.array().sqrt(); + computeRadLenUniformMaterial(S_values, rad_lengths_S); + VectorNd sig2_S(n); + sig2_S = .000225 / p_2 * (1. + 0.038 * rad_lengths_S.array().log()).abs2() * rad_lengths_S.array(); #if RFIT_DEBUG Rfit::printIt(&cov_sz, "Scatter_cov_line - cov_sz: "); #endif Matrix2Nd rot = MatrixXd::Zero(2 * n, 2 * n); for (u_int i = 0; i < n; ++i) { - rot(i, i) = cos(theta); - rot(n + i, n + i) = cos(theta); - u_int j = (i + n); - // Signs seem to be wrong for the off-diagonal element, but we are - // inverting x-y in the input vector, since theta is the angle between - // the z axis and the line, and we are putting the s values, which are Y, - // in the first position. A simple sign flip will take care of it. - rot(i, j) = i < j ? sin(theta) : -sin(theta); + rot(i, i) = sin(theta); + rot(n + i, n + i) = sin(theta); + rot(i, n+i) = cos(theta); + rot(n + i, i) = -cos(theta); } #if RFIT_DEBUG @@ -212,62 +136,75 @@ __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, #endif Matrix2Nd tmp = rot*cov_sz*rot.transpose(); - // We are interested only in the errors in the rotated s -axis which, in - // our formalism, are in the upper square matrix. + for (u_int k = 0; k < n; ++k) + { + for (u_int l = k; l < n; ++l) + { + for (u_int i = 0; i < std::min(k, l); ++i) + { + tmp(k + n, l + n) += std::abs(S_values(k) - S_values(i)) * std::abs(S_values(l) - S_values(i)) * sig2_S(i); + } + tmp(l + n, k + n) = tmp(k + n, l + n); + } + } + // We are interested only in the errors orthogonal to the rotated s-axis + // which, in our formalism, are in the lower square matrix. #if RFIT_DEBUG Rfit::printIt(&tmp, "Scatter_cov_line - tmp: "); #endif - return tmp.block(0, 0, n, n); + return tmp.block(n, n, n, n); } /*! \brief Compute the covariance matrix (in radial coordinates) of points in the transverse plane due to multiple Coulomb scattering. - \param p2D 2D points in the transverse plane. \param fast_fit fast_fit Vector4d result of the previous pre-fit structured in this form:(X0, Y0, R, Tan(Theta))). \param B magnetic field use to compute p - \return scatter_cov_rad errors due to multiple scattering. - \warning input points must be ordered radially from the detector center (from inner layer to outer ones; points on the same layer must ordered too). - \bug currently works only for points in the barrel. - \details Only the tangential component is computed (the radial one is negligible). - */ -// X in input TO FIX __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, const Vector4d& fast_fit, VectorNd const& rad, double B) { u_int n = p2D.cols(); - double p_t = fast_fit(2) * B; + double p_t = std::min(20.,fast_fit(2) * B); // limit pt to avoid too small error!!! double p_2 = p_t * p_t * (1. + 1. / (fast_fit(3) * fast_fit(3))); - double radlen_eff = 0.; - double theta = 0.; - bool in_forward = false; - computeRadLenEff(fast_fit, B, radlen_eff, theta, in_forward); + double theta = atan(fast_fit(3)); + theta = theta < 0. ? theta + M_PI : theta; + VectorNd s_values(n); + VectorNd rad_lengths(n); + const Vector2d o(fast_fit(0), fast_fit(1)); + // associated Jacobian, used in weights and errors computation + for (u_int i = 0; i < n; ++i) + { // x + Vector2d p = p2D.block(0, i, 2, 1) - o; + const double cross = cross2D(-o, p); + const double dot = (-o).dot(p); + const double atan2_ = atan2(cross, dot); + s_values(i) = std::abs(atan2_ * fast_fit(2)); + } + computeRadLenUniformMaterial(s_values*sqrt(1. + 1./(fast_fit(3)*fast_fit(3))), rad_lengths); MatrixNd scatter_cov_rad = MatrixXd::Zero(n, n); - const double sig2 = .000225 / p_2 * sqr(1 + 0.038 * log(radlen_eff)) * radlen_eff; + VectorNd sig2(n); + sig2 = (1. + 0.038 * rad_lengths.array().log()).abs2() * rad_lengths.array(); + sig2 *= 0.000225 / ( p_2 * sqr(sin(theta)) ); for (u_int k = 0; k < n; ++k) { for (u_int l = k; l < n; ++l) { for (u_int i = 0; i < std::min(k, l); ++i) { - if (in_forward) { - scatter_cov_rad(k, l) += (rad(k) - rad(i)) * (rad(l) - rad(i)) * sig2 / sqr(cos(theta)); - } else { - scatter_cov_rad(k, l) += (rad(k) - rad(i)) * (rad(l) - rad(i)) * sig2 / sqr(sin(theta)); - } - scatter_cov_rad(l, k) = scatter_cov_rad(k, l); + scatter_cov_rad(k, l) += (rad(k) - rad(i)) * (rad(l) - rad(i)) * sig2(i); } + scatter_cov_rad(l, k) = scatter_cov_rad(k, l); } } #if RFIT_DEBUG @@ -279,10 +216,8 @@ __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, /*! \brief Transform covariance matrix from radial (only tangential component) to Cartesian coordinates (only transverse plane component). - \param p2D 2D points in the transverse plane. \param cov_rad covariance matrix in radial coordinate. - \return cov_cart covariance matrix in Cartesian coordinates. */ @@ -321,12 +256,9 @@ __host__ __device__ inline Matrix2Nd cov_radtocart(const Matrix2xNd& p2D, transverse plane component) to radial coordinates (both radial and tangential component but only diagonal terms, correlation between different point are not managed). - \param p2D 2D points in transverse plane. \param cov_cart covariance matrix in Cartesian coordinates. - \return cov_rad covariance matrix in raidal coordinate. - \warning correlation between different point are not computed. */ __host__ __device__ inline MatrixNd cov_carttorad(const Matrix2xNd& p2D, @@ -354,15 +286,12 @@ __host__ __device__ inline MatrixNd cov_carttorad(const Matrix2xNd& p2D, transverse plane component) to coordinates system orthogonal to the pre-fitted circle in each point. Further information in attached documentation. - \param p2D 2D points in transverse plane. \param cov_cart covariance matrix in Cartesian coordinates. \param fast_fit fast_fit Vector4d result of the previous pre-fit structured in this form:(X0, Y0, R, tan(theta))). - \return cov_rad covariance matrix in the pre-fitted circle's orthogonal system. - */ __host__ __device__ inline MatrixNd cov_carttorad_prefit(const Matrix2xNd& p2D, const Matrix2Nd& cov_cart, @@ -394,12 +323,9 @@ __host__ __device__ inline MatrixNd cov_carttorad_prefit(const Matrix2xNd& p2D, \brief Compute the points' weights' vector for the circle fit when multiple scattering is managed. Further information in attached documentation. - \param cov_rad_inv covariance matrix inverse in radial coordinated (or, beter, pre-fitted circle's orthogonal system). - \return weight VectorNd points' weights' vector. - \bug I'm not sure this is the right way to compute the weights for non diagonal cov matrix. Further investigation needed. */ @@ -409,31 +335,12 @@ __host__ __device__ inline VectorNd Weight_circle(const MatrixNd& cov_rad_inv) return cov_rad_inv.colwise().sum().transpose(); } -/*! - \brief Compute the points' weights' vector for the line fit (ODR). - Results from a pre-fit is needed in order to take the orthogonal (to the - line) component of the errors. - - \param x_err2 squared errors in the x axis. - \param y_err2 squared errors in the y axis. - \param tan_theta tangent of theta (angle between y axis and line). - - \return weight points' weights' vector for the line fit (ODR). -*/ - -__host__ __device__ inline VectorNd Weight_line(const ArrayNd& x_err2, const ArrayNd& y_err2, const double& tan_theta) -{ - return (1. + sqr(tan_theta)) * 1. / (x_err2 + y_err2 * sqr(tan_theta)); -} - /*! \brief Find particle q considering the sign of cross product between particles velocity (estimated by the first 2 hits) and the vector radius between the first hit and the center of the fitted circle. - \param p2D 2D points in transverse plane. \param par_uvr result of the circle fit in this form: (X0,Y0,R). - \return q int 1 or -1. */ @@ -445,7 +352,6 @@ __host__ __device__ inline int64_t Charge(const Matrix2xNd& p2D, const Vector3d& /*! \brief Transform circle parameter from (X0,Y0,R) to (phi,Tip,p_t) and consequently covariance matrix. - \param circle_uvr parameter (X0,Y0,R), covariance matrix to be transformed and particle charge. \param B magnetic field in Gev/cm/c unit. @@ -464,56 +370,20 @@ __host__ __device__ inline void par_uvrtopak(circle_fit& circle, const double B, const double temp2 = sqr(circle.par(0)) * 1. / temp0; const double temp3 = 1. / temp1 * circle.q; Matrix3d J4; - J4 << -circle.par(1) * temp2 * 1. / sqr(circle.par(0)), temp2 * 1. / circle.par(0), 0., circle.par(0) * temp3, circle.par(1) * temp3, -circle.q, 0., 0., B; + J4 << -circle.par(1) * temp2 * 1. / sqr(circle.par(0)), temp2 * 1. / circle.par(0), 0., + circle.par(0) * temp3, circle.par(1) * temp3, -circle.q, + 0., 0., B; circle.cov = J4 * circle.cov * J4.transpose(); } circle.par = par_pak; } -/*! - \brief Compute the error propagation to obtain the square errors in the - x axis for the line fit. If errors have not been computed in the circle fit - than an'approximation is made. - Further information in attached documentation. - - \param V hits' covariance matrix. - \param circle result of the previous circle fit (only the covariance matrix - is needed) TO FIX - \param J Jacobian of the transformation producing x values. - \param error flag for error computation. - - \return x_err2 squared errors in the x axis. -*/ - -__host__ __device__ inline VectorNd X_err2(const Matrix3Nd& V, const circle_fit& circle, const MatrixNx5d& J, - const bool error, u_int n) -{ - VectorNd x_err2(n); - for (u_int i = 0; i < n; ++i) - { - Matrix5d Cov = MatrixXd::Zero(5, 5); - if (error) - Cov.block(0, 0, 3, 3) = circle.cov; - Cov(3, 3) = V(i, i); - Cov(4, 4) = V(i + n, i + n); - Cov(3, 4) = Cov(4, 3) = V(i, i + n); - Eigen::Matrix tmp; - tmp = J.row(i) * Cov * J.row(i).transpose().eval(); - x_err2(i) = tmp(0, 0); - } - return x_err2; -} - /*! \brief Compute the eigenvector associated to the minimum eigenvalue. - \param A the Matrix you want to know eigenvector and eigenvalue. \param chi2 the double were the chi2-related quantity will be stored. - \return the eigenvector associated to the minimum eigenvalue. - \warning double precision is needed for a correct assessment of chi2. - \details The minimus eigenvalue is related to chi2. We exploit the fact that the matrix is symmetrical and small (2x2 for line fit and 3x3 for circle fit), so the SelfAdjointEigenSolver from Eigen @@ -521,7 +391,6 @@ __host__ __device__ inline VectorNd X_err2(const Matrix3Nd& V, const circle_fit& and 3x3 Matrix) wich computes eigendecomposition of given matrix using a fast closed-form algorithm. For this optimization the matrix type must be known at compiling time. - */ __host__ __device__ inline Vector3d min_eigen3D(const Matrix3d& A, double& chi2) @@ -529,7 +398,7 @@ __host__ __device__ inline Vector3d min_eigen3D(const Matrix3d& A, double& chi2) #if RFIT_DEBUG printf("min_eigen3D - enter\n"); #endif - SelfAdjointEigenSolver solver(3); + Eigen::SelfAdjointEigenSolver solver(3); solver.computeDirect(A); int min_index; chi2 = solver.eigenvalues().minCoeff(&min_index); @@ -542,12 +411,9 @@ __host__ __device__ inline Vector3d min_eigen3D(const Matrix3d& A, double& chi2) /*! \brief A faster version of min_eigen3D() where double precision is not needed. - \param A the Matrix you want to know eigenvector and eigenvalue. \param chi2 the double were the chi2-related quantity will be stored - \return the eigenvector associated to the minimum eigenvalue. - \detail The computedDirect() method of SelfAdjointEigenSolver for 3x3 Matrix indeed, use trigonometry function (it solves a third degree equation) which speed up in single precision. @@ -555,7 +421,7 @@ __host__ __device__ inline Vector3d min_eigen3D(const Matrix3d& A, double& chi2) __host__ __device__ inline Vector3d min_eigen3D_fast(const Matrix3d& A) { - SelfAdjointEigenSolver solver(3); + Eigen::SelfAdjointEigenSolver solver(3); solver.computeDirect(A.cast()); int min_index; solver.eigenvalues().minCoeff(&min_index); @@ -564,12 +430,9 @@ __host__ __device__ inline Vector3d min_eigen3D_fast(const Matrix3d& A) /*! \brief 2D version of min_eigen3D(). - \param A the Matrix you want to know eigenvector and eigenvalue. \param chi2 the double were the chi2-related quantity will be stored - \return the eigenvector associated to the minimum eigenvalue. - \detail The computedDirect() method of SelfAdjointEigenSolver for 2x2 Matrix do not use special math function (just sqrt) therefore it doesn't speed up significantly in single precision. @@ -577,7 +440,7 @@ __host__ __device__ inline Vector3d min_eigen3D_fast(const Matrix3d& A) __host__ __device__ inline Vector2d min_eigen2D(const Matrix2d& A, double& chi2) { - SelfAdjointEigenSolver solver(2); + Eigen::SelfAdjointEigenSolver solver(2); solver.computeDirect(A); int min_index; chi2 = solver.eigenvalues().minCoeff(&min_index); @@ -587,14 +450,10 @@ __host__ __device__ inline Vector2d min_eigen2D(const Matrix2d& A, double& chi2) /*! \brief A very fast helix fit: it fits a circle by three points (first, middle and last point) and a line by two points (first and last). - \param hits points to be fitted - \return result in this form: (X0,Y0,R,tan(theta)). - \warning points must be passed ordered (from internal layer to external) in order to maximize accuracy and do not mistake tan(theta) sign. - \details This fast fit is used as pre-fit which is needed for: - weights estimation and chi2 computation in line fit (fundamental); - weights estimation and chi2 computation in circle fit (useful); @@ -668,7 +527,6 @@ __host__ __device__ inline Vector4d Fast_fit(const Matrix3xNd& hits) \brief Fit a generic number of 2D points with a circle using Riemann-Chernov algorithm. Covariance matrix of fitted parameter is optionally computed. Multiple scattering (currently only in barrel layer) is optionally handled. - \param hits2D 2D points to be fitted. \param hits_cov2D covariance matrix of 2D points. \param fast_fit pre-fit result in this form: (X0,Y0,R,tan(theta)). @@ -676,21 +534,18 @@ __host__ __device__ inline Vector4d Fast_fit(const Matrix3xNd& hits) \param B magnetic field \param error flag for error computation. \param scattering flag for multiple scattering - \return circle circle_fit: -par parameter of the fitted circle in this form (X0,Y0,R); \n -cov covariance matrix of the fitted parameter (not initialized if error = false); \n -q charge of the particle; \n -chi2. - \warning hits must be passed ordered from inner to outer layer (double hits on the same layer must be ordered too) so that multiple scattering is treated properly. \warning Multiple scattering for barrel is still not tested. \warning Multiple scattering for endcap hits is not handled (yet). Do not fit endcap hits with scattering = true ! - \bug for small pt (<0.3 Gev/c) chi2 could be slightly underestimated. \bug further investigation needed for error propagation with multiple scattering. @@ -780,10 +635,10 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, // COST FUNCTION // compute - Matrix3d A = Matrix3d::Zero(); - const Vector3d r0 = p3D * weight; // center of gravity + Matrix3d A; // = Matrix3d::Zero(); + Vector3d r0; r0.noalias() = p3D * weight; // center of gravity const Matrix3xNd X = p3D.colwise() - r0; - A = X * G * X.transpose(); + A.noalias() = X * G * X.transpose(); printIt(&A, "circle_fit - A:"); #if RFIT_DEBUG @@ -803,7 +658,7 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, #if RFIT_DEBUG printf("circle_fit - AFTER MIN_EIGEN 1\n"); #endif - Matrix cm; + Eigen::Matrix cm; #if RFIT_DEBUG printf("circle_fit - AFTER MIN_EIGEN 2\n"); #endif @@ -850,8 +705,8 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, printf("circle_fit - ERROR PRPAGATION ACTIVATED 2\n"); #endif { - Matrix cm; - Matrix cm2; + Eigen::Matrix cm; + Eigen::Matrix cm2; cm = mc.transpose() * V * mc; // cm2 = mc * mc.transpose(); const double c = cm(0, 0); @@ -891,7 +746,7 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, { for (u_int j = i; j < 3; ++j) { - Matrix tmp; + Eigen::Matrix tmp; tmp = weight.transpose() * C[i][j] * weight; const double c = tmp(0, 0); C0(i, j) = c; //weight.transpose() * C[i][j] * weight; @@ -924,9 +779,11 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, constexpr u_int nu[6][2] = {{0, 0}, {0, 1}, {0, 2}, {1, 1}, {1, 2}, {2, 2}}; Matrix6d E; // cov matrix of the 6 independent elements of A + #pragma unroll for (u_int a = 0; a < 6; ++a) { const u_int i = nu[a][0], j = nu[a][1]; + #pragma unroll for (u_int b = a; b < 6; ++b) { const u_int k = nu[b][0], l = nu[b][1]; @@ -951,14 +808,14 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, if (i == j) { - Matrix cm; + Eigen::Matrix cm; cm = s_v.col(i).transpose() * (t0 + t1); const double c = cm(0, 0); E(a, b) = 0. + c; } else { - Matrix cm; + Eigen::Matrix cm; cm = (s_v.col(i).transpose() * t0) + (s_v.col(j).transpose() * t1); const double c = cm(0, 0); E(a, b) = 0. + c; //(s_v.col(i).transpose() * t0) + (s_v.col(j).transpose() * t1); @@ -969,7 +826,8 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, } printIt(&E, "circle_fit - E:"); - Matrix J2; // Jacobian of min_eigen() (numerically computed) + Eigen::Matrix J2; // Jacobian of min_eigen() (numerically computed) + #pragma unroll for (u_int a = 0; a < 6; ++a) { const u_int i = nu[a][0], j = nu[a][1]; @@ -988,9 +846,8 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, Cvc.block(0, 0, 3, 3) = t0; Cvc.block(0, 3, 3, 1) = t1; Cvc.block(3, 0, 1, 3) = t1.transpose(); - Matrix cm1; - // Matrix cm2; - Matrix cm3; + Eigen::Matrix cm1; + Eigen::Matrix cm3; cm1 = (v.transpose() * C0 * v); // cm2 = (C0.cwiseProduct(t0)).sum(); cm3 = (r0.transpose() * t0 * r0); @@ -1000,11 +857,11 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, } printIt(&Cvc, "circle_fit - Cvc:"); - Matrix J3; // Jacobian (v0,v1,v2,c)->(X0,Y0,R) + Eigen::Matrix J3; // Jacobian (v0,v1,v2,c)->(X0,Y0,R) { const double t = 1. / h; J3 << -v2x2_inv, 0, v(0) * sqr(v2x2_inv) * 2., 0, 0, -v2x2_inv, v(1) * sqr(v2x2_inv) * 2., 0, - 0, 0, -h * sqr(v2x2_inv) * 2. - (2. * c + v(2)) * v2x2_inv * t, -t; + v(0)*v2x2_inv*t, v(1)*v2x2_inv*t, -h * sqr(v2x2_inv) * 2. - (2. * c + v(2)) * v2x2_inv * t, -t; } printIt(&J3, "circle_fit - J3:"); @@ -1027,7 +884,6 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, /*! \brief Fit of helix parameter cotan(theta)) and Zip by projection on the pre-fitted cylinder and line fit on its surface. - \param hits hits coordinates. \param hits_cov covariance matrix of the hits. \param circle cylinder parameter, their covariance (if computed, otherwise @@ -1035,20 +891,15 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, \param fast_fit result of the previous fast fit in this form: (X0,Y0,R,cotan(theta))). \param error flag for error computation. - \return line line_fit: -par parameter of the line in this form: (cotan(theta)), Zip); \n -cov covariance matrix of the fitted parameter; \n -chi2. - \warning correlation between R and z are neglected, this could be relevant if geometry detector provides sloped modules in the R/z plane. - \bug chi2 and errors could be slightly underestimated for small eta (<0.2) when pt is small (<0.3 Gev/c). - \todo multiple scattering treatment. - \details Line fit is made by orthogonal distance regression where correlation between coordinates in the transverse plane (x,y) and z are neglected (for a barrel + endcap geometry this is a very good @@ -1059,21 +910,22 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, errors. */ -__host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, - const Matrix3Nd& hits_cov, - const circle_fit& circle, - const Vector4d& fast_fit, - const double B, - const bool error = true) +__host__ __device__ inline line_fit Line_fit_odr(const Matrix3xNd& hits, + const Matrix3Nd& hits_cov, + const circle_fit& circle, + const Vector4d& fast_fit, + const double B, + const bool error = true) { u_int n = hits.cols(); + double theta = -circle.q*atan(fast_fit(3)); + theta = theta < 0. ? theta + M_PI : theta; // PROJECTION ON THE CILINDER - Matrix2xNd p2D(2, n); - MatrixNx5d Jx(n, 5); + Matrix2xNd p2D = MatrixXd::Zero(2, n); + Eigen::Matrix Jx; #if RFIT_DEBUG printf("Line_fit - B: %g\n", B); - printIt(&hits, "Line_fit points: "); printIt(&hits_cov, "Line_fit covs: "); #endif @@ -1085,8 +937,11 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, const Vector2d o(circle.par(0), circle.par(1)); // associated Jacobian, used in weights and errors computation + Matrix2Nd cov_sz = MatrixXd::Zero(2 * n, 2 * n); for (u_int i = 0; i < n; ++i) { // x + Matrix6d Cov = MatrixXd::Zero(6, 6); + Matrix2d Cov_sz_single = MatrixXd::Zero(2, 2); Vector2d p = hits.block(0, i, 2, 1) - o; const double cross = cross2D(-o, p); const double dot = (-o).dot(p); @@ -1095,9 +950,9 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, const double atan2_ = -circle.q * atan2(cross, dot); p2D(0, i) = atan2_ * circle.par(2); - // associated Jacobian, used in weights and errors computation + // associated Jacobian, used in weights and errors- computation const double temp0 = -circle.q * circle.par(2) * 1. / (sqr(dot) + sqr(cross)); - double d_X0 = 0, d_Y0 = 0, d_R = 0.; // good approximation for big pt and eta + double d_X0 = 0., d_Y0 = 0., d_R = 0.; // good approximation for big pt and eta if (error) { d_X0 = -temp0 * ((p(1) + o(1)) * dot - (p(0) - o(0)) * cross); @@ -1106,7 +961,19 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, } const double d_x = temp0 * (o(1) * dot + o(0) * cross); const double d_y = temp0 * (-o(0) * dot + o(1) * cross); - Jx.row(i) << d_X0, d_Y0, d_R, d_x, d_y; + Jx << d_X0, d_Y0, d_R, d_x, d_y, 0., 0., 0., 0., 0., 0., 1.; +// Jx << d_X0, d_Y0, d_R, p(1)/p.norm(), -p(0)/p.norm(), 0, 0, 0, 0, 0, 0, 1.; + Cov.block(0, 0, 3, 3) = circle.cov; + Cov(3, 3) = hits_cov(i, i); + Cov(4, 4) = hits_cov(i + n, i + n); + Cov(5, 5) = hits_cov(i + 2*n, i + 2*n); + Cov(3, 4) = Cov(4, 3) = hits_cov(i, i + n); + Cov(3, 5) = Cov(5, 3) = hits_cov(i, i + 2*n); + Cov(4, 5) = Cov(5, 4) = hits_cov(i + n, i + 2*n); + Cov_sz_single = Jx * Cov * Jx.transpose(); + cov_sz(i, i) = Cov_sz_single(0, 0); + cov_sz(i + n, i + n) = Cov_sz_single(1, 1); + cov_sz(i, i + n) = cov_sz(i + n, i) = Cov_sz_single(0, 1); } // Math of d_{X0,Y0,R,x,y} all verified by hand @@ -1114,43 +981,25 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, p2D.row(1) = hits.row(2); // WEIGHT COMPUTATION - Matrix2Nd cov_sz = MatrixXd::Zero(2 * n, 2 * n); - VectorNd x_err2 = X_err2(hits_cov, circle, Jx, error, n); - VectorNd y_err2 = hits_cov.block(2 * n, 2 * n, n, n).diagonal(); - cov_sz.block(0, 0, n, n) = x_err2.asDiagonal(); - cov_sz.block(n, n, n, n) = y_err2.asDiagonal(); #if RFIT_DEBUG printIt(&cov_sz, "line_fit - cov_sz:"); #endif - MatrixNd cov_with_ms = Scatter_cov_line(cov_sz, fast_fit, p2D.row(0), p2D.row(1), B); + MatrixNd cov_with_ms = Scatter_cov_line(cov_sz, fast_fit, p2D.row(0), p2D.row(1), theta, B); #if RFIT_DEBUG printIt(&cov_with_ms, "line_fit - cov_with_ms: "); #endif - Matrix4d G, G4; - G4 = cov_with_ms.inverse(); + Matrix4d G; + G = cov_with_ms.inverse(); #if RFIT_DEBUG - printIt(&G4, "line_fit - cov_with_ms.inverse():"); + printIt(&G, "line_fit - cov_with_ms.inverse():"); #endif - double renorm = G4.sum(); - G4 *= 1. / renorm; + double renorm = G.sum(); + G *= 1. / renorm; #if RFIT_DEBUG - printIt(&G4, "line_fit - G4:"); + printIt(&G, "line_fit - G4:"); #endif - G = G4; - const VectorNd weight = Weight_circle(G); - - - VectorNd err2_inv = cov_with_ms.diagonal(); - err2_inv = err2_inv.cwiseInverse(); -// const VectorNd err2_inv = Weight_line(x_err2, y_err2, fast_fit(3)); -// const VectorNd weight = err2_inv * 1. / err2_inv.sum(); -#if RFIT_DEBUG - printIt(&x_err2, "Line_fit - x_err2: "); - printIt(&y_err2, "Line_fit - y_err2: "); - printIt(&err2_inv, "Line_fit - err2_inv: "); - printIt(&weight, "Line_fit - weight: "); -#endif + const VectorNd weight = Weight_circle(G); // COST FUNCTION @@ -1162,16 +1011,12 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, const Matrix2xNd X = p2D.colwise() - r0; Matrix2d A = Matrix2d::Zero(); A = X * G * X.transpose(); -// for (u_int i = 0; i < n; ++i) -// { -// A += err2_inv(i) * (X.col(i) * X.col(i).transpose()); -// } #if RFIT_DEBUG printIt(&A, "Line_fit - A: "); #endif - // minimize + // minimize. v is normalized!! double chi2; Vector2d v = min_eigen2D(A, chi2); #if RFIT_DEBUG @@ -1179,18 +1024,17 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, printf("Line_fit chi2: %e\n", chi2); #endif - // n *= (chi2>0) ? 1 : -1; //TO FIX // This hack to be able to run on GPU where the automatic assignment to a // double from the vector multiplication is not working. - Matrix cm; + Eigen::Matrix cm; cm = -v.transpose() * r0; const double c = cm(0, 0); // COMPUTE LINE PARAMETER line_fit line; line.par << -v(0) / v(1), // cotan(theta)) - -c * sqrt(sqr(v(0)) + sqr(v(1))) * 1. / v(1); // Zip - line.chi2 = abs(chi2); + -c / v(1); // Zip + line.chi2 = abs(chi2*renorm); #if RFIT_DEBUG printIt(&(line.par), "Line_fit - line.par: "); printf("Line_fit - v norm: %e\n", sqrt(v(0)*v(0) + v(1)*v(1))); @@ -1206,33 +1050,33 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, { // The norm is taken from Chernov, properly adapted to the weights case. double norm = v.transpose() * A * v; - norm /= weight.sum(); +// double norm_empirical = cov_with_ms.diagonal().mean(); #if RFIT_DEBUG + printf("Chi_2: %g\n", chi2); + printf("Norm: %g\n", norm); + printf("weight.sum(): %g\n", weight.sum()); printf("Line_fit - norm: %e\n", norm); #endif - const double sig2 = 1. / (A(0, 0) + A(1, 1)) * norm; + + const double sig2 = norm/(A(0,0) + A(1,1)); C(0, 0) = sig2 * v1_2; C(1, 1) = sig2 * v0_2; - C(0, 1) = C(1, 0) = -sig2 * v(0) * v(1); - const VectorNd weight_2 = (weight).array().square(); - const Vector2d C0(weight_2.dot(x_err2), weight_2.dot(y_err2)); - C.block(0, 2, 2, 1) = C.block(2, 0, 1, 2).transpose() = -C.block(0, 0, 2, 2) * r0; - Matrix tmp = (r0.transpose() * C.block(0, 0, 2, 2) * r0); - C(2, 2) = v0_2 * C0(0) + v1_2 * C0(1) + C0(0) * C(0, 0) + C0(1) * C(1, 1) + tmp(0, 0); + C(1, 0) = C(0, 1) = -sig2 * v(0) * v(1); + C(2, 2) = sig2 * (v(0)*r0(1)-v(1)*r0(0))*(v(0)*r0(1)-v(1)*r0(0)) + (sig2/n)*(A(0,0)+A(1,1)); + C(0, 2) = C(2, 0) = sig2*(v(0)*r0(1)-v(1)*r0(0))*v(1); + C(1, 2) = C(2, 1) = - sig2*(v(0)*r0(1)-v(1)*r0(0))*v(0); } #if RFIT_DEBUG printIt(&C, "line_fit - C:"); #endif - Matrix J; // Jacobian of (v,c) -> (cotan(theta)),Zip) + Eigen::Matrix J; // Jacobian of (v,c) -> (cotan(theta)),Zip) { const double t0 = 1. / v(1); const double t1 = sqr(t0); - const double sqrt_ = sqrt(v1_2 + v0_2); - const double t2 = 1. / sqrt_; - J << -t0, v(0) * t1, 0, -c * v(0) * t0 * t2, v0_2 * c * t1 * t2, -sqrt_ * t0; + J << -t0, v(0) * t1, 0., 0., c * t1, -t0; } - Matrix JT = J.transpose().eval(); + Eigen::Matrix JT = J.transpose().eval(); #if RFIT_DEBUG printIt(&J, "line_fit - J:"); #endif @@ -1245,6 +1089,184 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, return line; } +/*! \brief Perform an ordinary least square fit in the s-z plane to compute + * the parameters cotTheta and Zip. + * + * The fit is performed in the rotated S3D-Z' plane, following the formalism of + * Frodesen, Chapter 10, p. 259. + * + * The system has been rotated to both try to use the combined errors in s-z + * along Z', as errors in the Y direction and to avoid the patological case of + * degenerate lines with angular coefficient m = +/- inf. + * + * The rotation is using the information on the theta angle computed in the + * fast fit. The rotation is such that the S3D axis will be the X-direction, + * while the rotated Z-axis will be the Y-direction. This pretty much follows + * what is done in the same fit in the Broken Line approach. + */ + +__host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, + const Matrix3Nd& hits_cov, + const circle_fit& circle, + const Vector4d& fast_fit, + const double B, + const bool error = true) { + auto n = hits.cols(); + double theta = -circle.q*atan(fast_fit(3)); + theta = theta < 0. ? theta + M_PI : theta; + + // PROJECTION ON THE CILINDER + // + // p2D will be: + // [s1, s2, s3, ..., sn] + // [z1, z2, z3, ..., zn] + // s values will be ordinary x-values + // z values will be ordinary y-values + + Matrix2xNd p2D(2, n); + Eigen::Matrix Jx; + + p2D << MatrixXd::Zero(2, n); + Jx << MatrixXd::Zero(2, 6); + +#if RFIT_DEBUG + printf("Line_fit - B: %g\n", B); + printIt(&hits, "Line_fit points: "); + printIt(&hits_cov, "Line_fit covs: "); +#endif + // x & associated Jacobian + // cfr https://indico.cern.ch/event/663159/contributions/2707659/attachments/1517175/2368189/Riemann_fit.pdf + // Slide 11 + // a ==> -o i.e. the origin of the circle in XY plane, negative + // b ==> p i.e. distances of the points wrt the origin of the circle. + const Vector2d o(circle.par(0), circle.par(1)); + + // associated Jacobian, used in weights and errors computation + Matrix2Nd cov_sz = MatrixXd::Zero(2 * n, 2 * n); + Matrix6d Cov(6,6); + Matrix2d Cov_sz_single(2, 2); + for (u_int i = 0; i < n; ++i) + { + Vector2d p = hits.block(0, i, 2, 1) - o; + const double cross = cross2D(-o, p); + const double dot = (-o).dot(p); + // atan2(cross, dot) give back the angle in the transverse plane so tha the + // final equation reads: x_i = -q*R*theta (theta = angle returned by atan2) + const double atan2_ = -circle.q * atan2(cross, dot); +// p2D.coeffRef(1, i) = atan2_ * circle.par(2); + p2D(0, i) = atan2_ * circle.par(2); + + // associated Jacobian, used in weights and errors- computation + const double temp0 = -circle.q * circle.par(2) * 1. / (sqr(dot) + sqr(cross)); + double d_X0 = 0., d_Y0 = 0., d_R = 0.; // good approximation for big pt and eta + if (error) + { + d_X0 = -temp0 * ((p(1) + o(1)) * dot - (p(0) - o(0)) * cross); + d_Y0 = temp0 * ((p(0) + o(0)) * dot - (o(1) - p(1)) * cross); + d_R = atan2_; + } + const double d_x = temp0 * (o(1) * dot + o(0) * cross); + const double d_y = temp0 * (-o(0) * dot + o(1) * cross); + Jx << d_X0, d_Y0, d_R, d_x, d_y, 0., 0., 0., 0., 0., 0., 1.; + + Cov << MatrixXd::Zero(6, 6); + Cov_sz_single << MatrixXd::Zero(2, 2); + Cov.block(0, 0, 3, 3) = circle.cov; + Cov(3, 3) = hits_cov(i, i); // x errors + Cov(4, 4) = hits_cov(i + n, i + n); // y errors + Cov(5, 5) = hits_cov(i + 2*n, i + 2*n); // z errors + Cov(3, 4) = Cov(4, 3) = hits_cov(i, i + n); // cov_xy + Cov(3, 5) = Cov(5, 3) = hits_cov(i, i + 2*n); // cov_xz + Cov(4, 5) = Cov(5, 4) = hits_cov(i + n, i + 2*n); // cov_yz + Cov_sz_single = Jx * Cov * Jx.transpose(); + cov_sz(i, i) = Cov_sz_single(0, 0); + cov_sz(i + n, i + n) = Cov_sz_single(1, 1); + cov_sz(i, i + n) = cov_sz(i + n, i) = Cov_sz_single(0, 1); + } + // Math of d_{X0,Y0,R,x,y} all verified by hand + p2D.row(1) = hits.row(2); + + // The following matrix will contain errors orthogonal to the rotated S + // component only, with the Multiple Scattering properly treated!! + MatrixNd cov_with_ms = Scatter_cov_line(cov_sz, fast_fit, p2D.row(0), p2D.row(1), theta, B); +#if RFIT_DEBUG + printIt(&cov_sz, "line_fit - cov_sz:"); + printIt(&cov_with_ms, "line_fit - cov_with_ms: "); +#endif + + // Prepare the Rotation Matrix to rotate the points + Eigen::Matrix rot = Eigen::Matrix::Zero(); + rot << sin(theta), cos(theta), -cos(theta), sin(theta); + + // Rotate Points with the shape [2, n] + Matrix2xNd p2D_rot = rot*p2D; + +#if RFIT_DEBUG + printf("Fast fit Tan(theta): %g\n", fast_fit(3)); + printf("Rotation angle: %g\n", theta); + printIt(&rot, "Rotation Matrix:"); + printIt(&p2D, "Original Hits(s,z):"); + printIt(&p2D_rot, "Rotated hits(S3D, Z'):"); + printIt(&rot, "Rotation Matrix:"); +#endif + + // Build the A Matrix + Matrix2xNd A(2,n); + A << MatrixXd::Ones(1, n), p2D_rot.row(0); // rotated s values + +#if RFIT_DEBUG + printIt(&A, "A Matrix:"); +#endif + + // Build A^T V-1 A, where V-1 is the covariance of only the Y components. + MatrixNd Vy_inv = cov_with_ms.inverse(); + Eigen::Matrix Inv_Cov = A*Vy_inv*A.transpose(); + + // Compute the Covariance Matrix of the fit parameters + Eigen::Matrix Cov_params = Inv_Cov.inverse(); + + // Now Compute the Parameters in the form [2,1] + // The first component is q. + // The second component is m. + Eigen::Matrix sol = Cov_params*A*Vy_inv*p2D_rot.row(1).transpose(); + + +#if RFIT_DEBUG + printIt(&sol, "Rotated solutions:"); +#endif + + // We need now to transfer back the results in the original s-z plane + auto common_factor = 1./(sin(theta)-sol(1,0)*cos(theta)); + Eigen::Matrix J = Eigen::Matrix::Zero(); + J << 0., common_factor*common_factor, common_factor, sol(0,0)*cos(theta)*common_factor*common_factor; + + double m = common_factor*(sol(1,0)*sin(theta)+cos(theta)); + double q = common_factor*sol(0,0); + auto cov_mq = J * Cov_params * J.transpose(); + + VectorNd res = p2D_rot.row(1).transpose() - A.transpose() * sol; + double chi2 = res.transpose()*Vy_inv*res; + chi2 = chi2 / float(n); + + line_fit line; + line.par << m, q; + line.cov << cov_mq; + line.chi2 = chi2; + +#if RFIT_DEBUG + printf("Common_factor: %g\n", common_factor); + printIt(&J, "Jacobian:"); + printIt(&sol, "Rotated solutions:"); + printIt(&Cov_params, "Cov_params:"); + printIt(&cov_mq, "Rotated Covariance Matrix:"); + printIt(&(line.par), "Real Parameters:"); + printIt(&(line.cov), "Real Covariance Matrix:"); + printf("Chi2: %g\n", chi2); +#endif + + return line; +} + /*! \brief Helix fit by three step: -fast pre-fit (see Fast_fit() for further info); \n @@ -1253,14 +1275,11 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, -line fit of hits projected on cylinder surface by orthogonal distance regression (see Line_fit for further info). \n Points must be passed ordered (from inner to outer layer). - \param hits Matrix3xNd hits coordinates in this form: \n |x0|x1|x2|...|xn| \n |y0|y1|y2|...|yn| \n |z0|z1|z2|...|zn| - \param hits_cov Matrix3Nd covariance matrix in this form (()->cov()): \n - |(x0,x0)|(x1,x0)|(x2,x0)|.|(y0,x0)|(y1,x0)|(y2,x0)|.|(z0,x0)|(z1,x0)|(z2,x0)| \n |(x0,x1)|(x1,x1)|(x2,x1)|.|(y0,x1)|(y1,x1)|(y2,x1)|.|(z0,x1)|(z1,x1)|(z2,x1)| \n |(x0,x2)|(x1,x2)|(x2,x2)|.|(y0,x2)|(y1,x2)|(y2,x2)|.|(z0,x2)|(z1,x2)|(z2,x2)| \n @@ -1272,15 +1291,12 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, |(x0,z0)|(x1,z0)|(x2,z0)|.|(y0,z0)|(y1,z0)|(y2,z0)|.|(z0,z0)|(z1,z0)|(z2,z0)| \n |(x0,z1)|(x1,z1)|(x2,z1)|.|(y0,z1)|(y1,z1)|(y2,z1)|.|(z0,z1)|(z1,z1)|(z2,z1)| \n |(x0,z2)|(x1,z2)|(x2,z2)|.|(y0,z2)|(y1,z2)|(y2,z2)|.|(z0,z2)|(z1,z2)|(z2,z2)| - \param B magnetic field in the center of the detector in Gev/cm/c unit, in order to perform pt calculation. \param error flag for error computation. \param scattering flag for multiple scattering treatment. (see Circle_fit() documentation for further info). - \warning see Circle_fit(), Line_fit() and Fast_fit() warnings. - \bug see Circle_fit(), Line_fit() and Fast_fit() bugs. */ @@ -1317,4 +1333,4 @@ inline helix_fit Helix_fit(const Matrix3xNd& hits, const Matrix3Nd& hits_cov, co } // namespace Rfit -#endif // RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h b/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h new file mode 100644 index 0000000000000..4a1763f312ac4 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h @@ -0,0 +1,35 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_pixelTrackHeterogeneousProduct_h +#define RecoPixelVertexing_PixelTrackFitting_interface_pixelTrackHeterogeneousProduct_h + +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" + +namespace pixelTrackHeterogeneousProduct { + + static constexpr uint32_t maxTracks() { return 10000;} + + using CPUProduct = int; // dummy + + struct TracksOnGPU { + + Rfit::helix_fit * helix_fit_results_d; + + TracksOnGPU const * me_d = nullptr; + + }; + + struct TracksOnCPU { + + Rfit::helix_fit * helix_fit_results; + TracksOnGPU const * gpu_d = nullptr; + uint32_t nTracks; + }; + + using GPUProduct = TracksOnCPU; // FIXME fill cpu vectors on demand + + using HeterogeneousPixelTuples = HeterogeneousProductImpl, + heterogeneous::GPUCudaProduct >; +} + +} + +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml index c549e05d69f55..d8177a0e9447c 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml @@ -1,4 +1,7 @@ + + + diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc index eadfda8cb6a26..7f13c7218eafa 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc @@ -1,3 +1,4 @@ +#include "storeTracks.h" #include "PixelTrackProducer.h" #include "FWCore/Framework/interface/Event.h" @@ -59,62 +60,6 @@ void PixelTrackProducer::produce(edm::Event& ev, const edm::EventSetup& es) es.get().get(httopo); // store tracks - store(ev, tracks, *httopo); + storeTracks(ev, tracks, *httopo); } -void PixelTrackProducer::store(edm::Event& ev, const TracksWithTTRHs& tracksWithHits, const TrackerTopology& ttopo) -{ - auto tracks = std::make_unique(); - auto recHits = std::make_unique(); - auto trackExtras = std::make_unique(); - - int cc = 0, nTracks = tracksWithHits.size(); - - for (int i = 0; i < nTracks; i++) - { - reco::Track* track = tracksWithHits.at(i).first; - const SeedingHitSet& hits = tracksWithHits.at(i).second; - - for (unsigned int k = 0; k < hits.size(); k++) - { - TrackingRecHit *hit = hits[k]->hit()->clone(); - - track->appendHitPattern(*hit, ttopo); - recHits->push_back(hit); - } - tracks->push_back(*track); - delete track; - - } - - LogDebug("TrackProducer") << "put the collection of TrackingRecHit in the event" << "\n"; - edm::OrphanHandle ohRH = ev.put(std::move(recHits)); - - edm::RefProd hitCollProd(ohRH); - for (int k = 0; k < nTracks; k++) - { - reco::TrackExtra theTrackExtra{}; - - //fill the TrackExtra with TrackingRecHitRef - unsigned int nHits = tracks->at(k).numberOfValidHits(); - theTrackExtra.setHits(hitCollProd, cc, nHits); - cc +=nHits; - AlgebraicVector5 v = AlgebraicVector5(0,0,0,0,0); - reco::TrackExtra::TrajParams trajParams(nHits,LocalTrajectoryParameters(v,1.)); - reco::TrackExtra::Chi2sFive chi2s(nHits,0); - theTrackExtra.setTrajParams(std::move(trajParams),std::move(chi2s)); - trackExtras->push_back(theTrackExtra); - } - - LogDebug("TrackProducer") << "put the collection of TrackExtra in the event" << "\n"; - edm::OrphanHandle ohTE = ev.put(std::move(trackExtras)); - - for (int k = 0; k < nTracks; k++) - { - const reco::TrackExtraRef theTrackExtraRef(ohTE,k); - (tracks->at(k)).setExtra(theTrackExtraRef); - } - - ev.put(std::move(tracks)); - -} diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h index 6bc6d2815c8e7..7e0d5d73b03fc 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h @@ -2,7 +2,6 @@ #define PixelTrackProducer_h #include "FWCore/Framework/interface/stream/EDProducer.h" -#include "RecoPixelVertexing/PixelTrackFitting/interface/TracksWithHits.h" #include "RecoPixelVertexing/PixelTrackFitting/interface/PixelTrackReconstruction.h" #include "PixelTrackReconstructionGPU.h" @@ -22,7 +21,6 @@ class PixelTrackProducer : public edm::stream::EDProducer<> { void produce(edm::Event& ev, const edm::EventSetup& es) override; private: - void store(edm::Event& ev, const pixeltrackfitting::TracksWithTTRHs& selectedTracks, const TrackerTopology& ttopo); bool runOnGPU_; PixelTrackReconstruction theReconstruction; PixelTrackReconstructionGPU theGPUReconstruction; diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index 6a3bf229cc67f..c05fa9172ffe3 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -1,4 +1,8 @@ -#include "FWCore/Framework/interface/global/EDProducer.h" +#include "HeterogeneousCore/CUDACore/interface/GPUCuda.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "HeterogeneousCore/Producer/interface/HeterogeneousEDProducer.h" +#include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" + #include "FWCore/Framework/interface/Event.h" #include "FWCore/Framework/interface/Frameworkfwd.h" #include "FWCore/Framework/interface/MakerMacros.h" @@ -6,39 +10,206 @@ #include "FWCore/ParameterSet/interface/ParameterSetDescription.h" #include "RecoTracker/TkHitPairs/interface/RegionsSeedingHitSets.h" +#include "DataFormats/BeamSpot/interface/BeamSpot.h" + +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelTrackBuilder.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelTrackCleaner.h" + +// track stuff +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "DataFormats/TrackReco/interface/TrackExtra.h" +#include "DataFormats/Common/interface/OrphanHandle.h" + +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" + +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" +#include "storeTracks.h" + + /** * This class will eventually be the one creating the reco::Track * objects from the output of GPU CA. Now it is just to produce * something persistable. */ -class PixelTrackProducerFromCUDA: public edm::global::EDProducer<> { +class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer> { public: + + using Input = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; + using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + + + using Output = HeterogeneousProductImpl, + heterogeneous::GPUCudaProduct >; + explicit PixelTrackProducerFromCUDA(const edm::ParameterSet& iConfig); ~PixelTrackProducerFromCUDA() override = default; static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); - void produce(edm::StreamID id, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + void beginStreamGPUCuda(edm::StreamID streamId, + cuda::stream_t<> &cudaStream) override { + } + void acquireGPUCuda(const edm::HeterogeneousEvent &iEvent, + const edm::EventSetup &iSetup, + cuda::stream_t<> &cudaStream) override; + void produceGPUCuda(edm::HeterogeneousEvent &iEvent, + const edm::EventSetup &iSetup, + cuda::stream_t<> &cudaStream) override; + void produceCPU(edm::HeterogeneousEvent &iEvent, + const edm::EventSetup &iSetup) override; + private: + + TuplesOnCPU const * tuples_=nullptr; + + edm::EDGetTokenT tBeamSpot_; + edm::EDGetTokenT gpuToken_; edm::EDGetTokenT srcToken_; - bool enabled_; + bool enableConversion_; }; PixelTrackProducerFromCUDA::PixelTrackProducerFromCUDA(const edm::ParameterSet& iConfig): - srcToken_(consumes(iConfig.getParameter("src"))) + HeterogeneousEDProducer(iConfig), + tBeamSpot_(consumes(iConfig.getParameter("beamSpot"))), + gpuToken_(consumes(iConfig.getParameter("src"))), + enableConversion_ (iConfig.getParameter("gpuEnableConversion")) { - produces(); + if (enableConversion_) { + srcToken_ = consumes(iConfig.getParameter("src")); + produces(); + produces(); + produces(); + } + else { + produces(); // dummy + } +// produces(); } void PixelTrackProducerFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { edm::ParameterSetDescription desc; + desc.add("beamSpot", edm::InputTag("offlineBeamSpot")); desc.add("src", edm::InputTag("pixelTracksHitQuadruplets")); + desc.add("gpuEnableConversion", true); + + + HeterogeneousEDProducer::fillPSetDescription(desc); descriptions.addWithDefaultLabel(desc); } -void PixelTrackProducerFromCUDA::produce(edm::StreamID id, edm::Event& iEvent, const edm::EventSetup& iSetup) const { - iEvent.put(std::make_unique(0)); +void PixelTrackProducerFromCUDA::acquireGPUCuda(const edm::HeterogeneousEvent &iEvent, + const edm::EventSetup &iSetup, + cuda::stream_t<> &cudaStream) { + + edm::Handle gh; + iEvent.getByToken(gpuToken_, gh); + //auto const & gTuples = *gh; + // std::cout << "tuples from gpu " << gTuples.nTuples << std::endl; + + tuples_ = gh.product(); + +} + + +void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, + const edm::EventSetup &iSetup, + cuda::stream_t<> &cudaStream) { + // iEvent.put(std::make_unique(0)); + if (!enableConversion_) return; + + // std::cout << "Converting gpu helix in reco tracks" << std::endl; + + edm::ESHandle fieldESH; + iSetup.get().get(fieldESH); + + + PixelTrackBuilder builder; + + pixeltrackfitting::TracksWithTTRHs tracks; + edm::ESHandle httopo; + iSetup.get().get(httopo); + + + edm::Handle hitSets; + iEvent.getByToken(srcToken_, hitSets); + const auto & hitSet = *hitSets->begin(); + auto b = hitSet.begin(); auto e = hitSet.end(); + // std::cout << "reading hitset " << e-b << std::endl; + + // const auto & region = hitSet.region(); + // std::cout << "origin " << region.origin() << std::endl; + + edm::Handle bsHandle; + iEvent.getByToken( tBeamSpot_, bsHandle); + const auto & bsh = *bsHandle; + // std::cout << "beamspot " << bsh.x0() << ' ' << bsh.y0() << ' ' << bsh.z0() << std::endl; + GlobalPoint bs(bsh.x0(),bsh.y0(),bsh.z0()); + + std::vector hits; + hits.reserve(4); + + uint32_t nh=0; // current hitset + assert(tuples_->indToEdm.size()==tuples_->nTuples); + for (uint32_t it=0; itnTuples; ++it) { + auto q = tuples_->quality[it]; + if (q != pixelTuplesHeterogeneousProduct::loose ) continue; // FIXME + assert(tuples_->indToEdm[it]==nh); // filled on CPU! + auto const & shits = *(b+nh); + auto nHits = shits.size(); hits.resize(nHits); + for (unsigned int iHit = 0; iHit < nHits; ++iHit) hits[iHit] = shits[iHit]; + + // mind: this values are respect the beamspot! + auto const &fittedTrack = tuples_->helix_fit_results[it]; + + // std::cout << "tk " << it << ": " << fittedTrack.q << ' ' << fittedTrack.par[2] << ' ' << std::sqrt(fittedTrack.cov(2, 2)) << std::endl; + + int iCharge = fittedTrack.q; + float valPhi = fittedTrack.par(0); + float valTip = fittedTrack.par(1); + float valPt = fittedTrack.par(2); + float valCotTheta = fittedTrack.par(3); + float valZip = fittedTrack.par(4); + + float errValPhi = std::sqrt(fittedTrack.cov(0, 0)); + float errValTip = std::sqrt(fittedTrack.cov(1, 1)); + float errValPt = std::sqrt(fittedTrack.cov(2, 2)); + float errValCotTheta = std::sqrt(fittedTrack.cov(3, 3)); + float errValZip = std::sqrt(fittedTrack.cov(4, 4)); + + float chi2 = fittedTrack.chi2_line + fittedTrack.chi2_circle; + + Measurement1D phi(valPhi, errValPhi); + Measurement1D tip(valTip, errValTip); + + Measurement1D pt(valPt, errValPt); + Measurement1D cotTheta(valCotTheta, errValCotTheta); + Measurement1D zip(valZip, errValZip); + + std::unique_ptr track( + builder.build(pt, phi, cotTheta, tip, zip, chi2, iCharge, hits, + fieldESH.product(), bs)); + if (!track) continue; + // filter??? + tracks.emplace_back(track.release(), shits); + ++nh; + } + assert(nh==e-b); + // std::cout << "processed " << nh << " good tuples " << tracks.size() << std::endl; + + // store tracks + storeTracks(iEvent, tracks, *httopo); +} + + +void PixelTrackProducerFromCUDA::produceCPU( + edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup) +{ + throw cms::Exception("NotImplemented") << "CPU version is no longer implemented"; } DEFINE_FWK_MODULE(PixelTrackProducerFromCUDA); diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h new file mode 100644 index 0000000000000..48abab5237587 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h @@ -0,0 +1,77 @@ +#ifndef RecoPixelVertexingPixelTrackFittingStoreTracks_H +#define RecoPixelVertexingPixelTrackFittingStoreTracks_H + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "DataFormats/TrackReco/interface/TrackExtra.h" +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/TracksWithHits.h" + +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" + +template +void storeTracks(Ev & ev, const pixeltrackfitting::TracksWithTTRHs& tracksWithHits, const TrackerTopology& ttopo) +{ + auto tracks = std::make_unique(); + auto recHits = std::make_unique(); + auto trackExtras = std::make_unique(); + + int cc = 0, nTracks = tracksWithHits.size(); + + for (int i = 0; i < nTracks; i++) + { + reco::Track* track = tracksWithHits.at(i).first; + const SeedingHitSet& hits = tracksWithHits.at(i).second; + + for (unsigned int k = 0; k < hits.size(); k++) + { + TrackingRecHit *hit = hits[k]->hit()->clone(); + + track->appendHitPattern(*hit, ttopo); + recHits->push_back(hit); + } + tracks->push_back(*track); + delete track; + + } + + LogDebug("TrackProducer") << "put the collection of TrackingRecHit in the event" << "\n"; + edm::OrphanHandle ohRH = ev.put(std::move(recHits)); + + edm::RefProd hitCollProd(ohRH); + for (int k = 0; k < nTracks; k++) + { + reco::TrackExtra theTrackExtra{}; + + //fill the TrackExtra with TrackingRecHitRef + unsigned int nHits = tracks->at(k).numberOfValidHits(); + theTrackExtra.setHits(hitCollProd, cc, nHits); + cc +=nHits; + AlgebraicVector5 v = AlgebraicVector5(0,0,0,0,0); + reco::TrackExtra::TrajParams trajParams(nHits,LocalTrajectoryParameters(v,1.)); + reco::TrackExtra::Chi2sFive chi2s(nHits,0); + theTrackExtra.setTrajParams(std::move(trajParams),std::move(chi2s)); + trackExtras->push_back(theTrackExtra); + } + + LogDebug("TrackProducer") << "put the collection of TrackExtra in the event" << "\n"; + edm::OrphanHandle ohTE = ev.put(std::move(trackExtras)); + + for (int k = 0; k < nTracks; k++) + { + const reco::TrackExtraRef theTrackExtraRef(ohTE,k); + (tracks->at(k)).setExtra(theTrackExtraRef); + } + + ev.put(std::move(tracks)); + +} + +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py index 728b3fec47f39..e868ff1921965 100644 --- a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py +++ b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py @@ -50,7 +50,10 @@ doublets = "pixelTracksHitDoublets", SeedComparitorPSet = dict(clusterShapeCacheSrc = 'siPixelClusterShapeCachePreSplitting') ) + from Configuration.ProcessModifiers.gpu_cff import gpu +from RecoPixelVertexing.PixelTriplets.caHitQuadrupletHeterogeneousEDProducer_cfi import caHitQuadrupletHeterogeneousEDProducer as _caHitQuadrupletHeterogeneousEDProducer +gpu.toReplaceWith(pixelTracksHitQuadruplets, _caHitQuadrupletHeterogeneousEDProducer) gpu.toModify(pixelTracksHitQuadruplets, trackingRegions = "pixelTracksTrackingRegions") # for trackingLowPU @@ -67,6 +70,10 @@ ) trackingLowPU.toModify(pixelTracks, SeedingHitSets = "pixelTracksHitTriplets") +from Configuration.ProcessModifiers.gpu_cff import gpu +from RecoPixelVertexing.PixelTrackFitting.pixelTrackProducerFromCUDA_cfi import pixelTrackProducerFromCUDA as _pixelTrackProducerFromCUDA +gpu.toReplaceWith(pixelTracks, _pixelTrackProducerFromCUDA) + pixelTracksTask = cms.Task( pixelTracksTrackingRegions, pixelFitterByHelixProjections, diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc index dd1460adaf05a..41778e0571438 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc @@ -59,9 +59,9 @@ std::unique_ptr PixelFitterByRiemannParaboloid::run( isBarrel[i] = recHit->detUnit()->type().isBarrel(); } - Matrix riemannHits(3, nhits); + Eigen::Matrix riemannHits(3, nhits); - Matrix riemannHits_cov = + Eigen::Matrix riemannHits_cov = MatrixXd::Zero(3 * nhits, 3 * nhits); for (unsigned int i = 0; i < nhits; ++i) { diff --git a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml index d6beb57b862b8..b4b5e3a335bcb 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml @@ -1,3 +1,4 @@ + @@ -11,19 +12,48 @@ + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + diff --git a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc index 77b5d1bebe6b6..b630d0ee4ae27 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc +++ b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc @@ -5,8 +5,13 @@ #include #include #include // unique_ptr +#include + +#include +#include #include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +//#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" using namespace std; using namespace Eigen; @@ -164,161 +169,266 @@ Matrix New_par(const Matrix& gen_par, const int& cha return new_par; } -void test_helix_fit() { +template +void computePull(std::array & fit, const char * label, + int n_, int iteration, const Vector5d & true_par) { + Eigen::Matrix score(41, iteration); + + std::string histo_name("Phi Pull"); + histo_name += label; + TH1F phi_pull(histo_name.data(), histo_name.data(), 100, -10., 10.); + histo_name = "dxy Pull "; + histo_name += label; + TH1F dxy_pull(histo_name.data(), histo_name.data(), 100, -10., 10.); + histo_name = "dz Pull "; + histo_name += label; + TH1F dz_pull(histo_name.data(), histo_name.data(), 100, -10., 10.); + histo_name = "Theta Pull "; + histo_name += label; + TH1F theta_pull(histo_name.data(), histo_name.data(), 100, -10., 10.); + histo_name = "Pt Pull "; + histo_name += label; + TH1F pt_pull(histo_name.data(), histo_name.data(), 100, -10., 10.); + histo_name = "Phi Error "; + histo_name += label; + TH1F phi_error(histo_name.data(), histo_name.data(), 100, 0., 0.1); + histo_name = "dxy error "; + histo_name += label; + TH1F dxy_error(histo_name.data(), histo_name.data(), 100, 0., 0.1); + histo_name = "dz error "; + histo_name += label; + TH1F dz_error(histo_name.data(), histo_name.data(), 100, 0., 0.1); + histo_name = "Theta error "; + histo_name += label; + TH1F theta_error(histo_name.data(), histo_name.data(), 100, 0., 0.1); + histo_name = "Pt error "; + histo_name += label; + TH1F pt_error(histo_name.data(), histo_name.data(), 100, 0., 0.1); + for (int x = 0; x < iteration; x++) { + // Compute PULLS information + score(0, x) = (fit[x].par(0) - true_par(0)) / sqrt(fit[x].cov(0, 0)); + score(1, x) = (fit[x].par(1) - true_par(1)) / sqrt(fit[x].cov(1, 1)); + score(2, x) = (fit[x].par(2) - true_par(2)) / sqrt(fit[x].cov(2, 2)); + score(3, x) = (fit[x].par(3) - true_par(3)) / sqrt(fit[x].cov(3, 3)); + score(4, x) = (fit[x].par(4) - true_par(4)) / sqrt(fit[x].cov(4, 4)); + phi_pull.Fill(score(0, x)); + dxy_pull.Fill(score(1, x)); + pt_pull.Fill(score(2, x)); + theta_pull.Fill(score(3, x)); + dz_pull.Fill(score(4, x)); + phi_error.Fill(sqrt(fit[x].cov(0, 0))); + dxy_error.Fill(sqrt(fit[x].cov(1, 1))); + pt_error.Fill(sqrt(fit[x].cov(2, 2))); + theta_error.Fill(sqrt(fit[x].cov(3, 3))); + dz_error.Fill(sqrt(fit[x].cov(4, 4))); + score(5, x) = + (fit[x].par(0) - true_par(0)) * (fit[x].par(1) - true_par(1)) / (fit[x].cov(0, 1)); + score(6, x) = + (fit[x].par(0) - true_par(0)) * (fit[x].par(2) - true_par(2)) / (fit[x].cov(0, 2)); + score(7, x) = + (fit[x].par(1) - true_par(1)) * (fit[x].par(2) - true_par(2)) / (fit[x].cov(1, 2)); + score(8, x) = + (fit[x].par(3) - true_par(3)) * (fit[x].par(4) - true_par(4)) / (fit[x].cov(3, 4)); + score(9, x) = fit[x].chi2_circle; + score(25, x) = fit[x].chi2_line; + score(10, x) = sqrt(fit[x].cov(0, 0)) / fit[x].par(0) * 100; + score(13, x) = sqrt(fit[x].cov(3, 3)) / fit[x].par(3) * 100; + score(14, x) = sqrt(fit[x].cov(4, 4)) / fit[x].par(4) * 100; + score(15, x) = (fit[x].par(0) - true_par(0)) * (fit[x].par(3) - true_par(3)) / + sqrt(fit[x].cov(0, 0)) / sqrt(fit[x].cov(3, 3)); + score(16, x) = (fit[x].par(1) - true_par(1)) * (fit[x].par(3) - true_par(3)) / + sqrt(fit[x].cov(1, 1)) / sqrt(fit[x].cov(3, 3)); + score(17, x) = (fit[x].par(2) - true_par(2)) * (fit[x].par(3) - true_par(3)) / + sqrt(fit[x].cov(2, 2)) / sqrt(fit[x].cov(3, 3)); + score(18, x) = (fit[x].par(0) - true_par(0)) * (fit[x].par(4) - true_par(4)) / + sqrt(fit[x].cov(0, 0)) / sqrt(fit[x].cov(4, 4)); + score(19, x) = (fit[x].par(1) - true_par(1)) * (fit[x].par(4) - true_par(4)) / + sqrt(fit[x].cov(1, 1)) / sqrt(fit[x].cov(4, 4)); + score(20, x) = (fit[x].par(2) - true_par(2)) * (fit[x].par(4) - true_par(4)) / + sqrt(fit[x].cov(2, 2)) / sqrt(fit[x].cov(4, 4)); + score(21, x) = (fit[x].par(0) - true_par(0)) * (fit[x].par(1) - true_par(1)) / + sqrt(fit[x].cov(0, 0)) / sqrt(fit[x].cov(1, 1)); + score(22, x) = (fit[x].par(0) - true_par(0)) * (fit[x].par(2) - true_par(2)) / + sqrt(fit[x].cov(0, 0)) / sqrt(fit[x].cov(2, 2)); + score(23, x) = (fit[x].par(1) - true_par(1)) * (fit[x].par(2) - true_par(2)) / + sqrt(fit[x].cov(1, 1)) / sqrt(fit[x].cov(2, 2)); + score(24, x) = (fit[x].par(3) - true_par(3)) * (fit[x].par(4) - true_par(4)) / + sqrt(fit[x].cov(3, 3)) / sqrt(fit[x].cov(4, 4)); + score(30, x) = fit[x].par(0); + score(31, x) = fit[x].par(1); + score(32, x) = fit[x].par(2); + score(33, x) = fit[x].par(3); + score(34, x) = fit[x].par(4); + score(35, x) = sqrt(fit[x].cov(0,0)); + score(36, x) = sqrt(fit[x].cov(1,1)); + score(37, x) = sqrt(fit[x].cov(2,2)); + score(38, x) = sqrt(fit[x].cov(3,3)); + score(39, x) = sqrt(fit[x].cov(4,4)); + + } + + double phi_ = score.row(0).mean(); + double a_ = score.row(1).mean(); + double pt_ = score.row(2).mean(); + double coT_ = score.row(3).mean(); + double Zip_ = score.row(4).mean(); + std::cout << std::setprecision(5) << std::scientific << label << " AVERAGE FITTED VALUES: \n" + << "phi: " << score.row(30).mean() << " +/- " << score.row(35).mean() << " [+/-] " << sqrt(score.row(35).array().abs2().mean() - score.row(35).mean()*score.row(35).mean()) << std::endl + << "d0: " << score.row(31).mean() << " +/- " << score.row(36).mean() << " [+/-] " << sqrt(score.row(36).array().abs2().mean() - score.row(36).mean()*score.row(36).mean()) << std::endl + << "pt: " << score.row(32).mean() << " +/- " << score.row(37).mean() << " [+/-] " << sqrt(score.row(37).array().abs2().mean() - score.row(37).mean()*score.row(37).mean()) << std::endl + << "coT: " << score.row(33).mean() << " +/- " << score.row(38).mean() << " [+/-] " << sqrt(score.row(38).array().abs2().mean() - score.row(38).mean()*score.row(38).mean()) << std::endl + << "Zip: " << score.row(34).mean() << " +/- " << score.row(39).mean() << " [+/-] " << sqrt(score.row(39).array().abs2().mean() - score.row(39).mean()*score.row(39).mean()) << std::endl; + + Matrix5d correlation; + correlation << 1., score.row(21).mean(), score.row(22).mean(), score.row(15).mean(), + score.row(20).mean(), score.row(21).mean(), 1., score.row(23).mean(), score.row(16).mean(), + score.row(19).mean(), score.row(22).mean(), score.row(23).mean(), 1., score.row(17).mean(), + score.row(20).mean(), score.row(15).mean(), score.row(16).mean(), score.row(17).mean(), 1., + score.row(24).mean(), score.row(18).mean(), score.row(19).mean(), score.row(20).mean(), + score.row(24).mean(), 1.; + + cout << "\n" << label << " PULLS (mean, sigma, relative_error):\n" + << "phi: " << phi_ << " " + << sqrt((score.row(0).array() - phi_).square().sum() / (iteration - 1)) << " " + << abs(score.row(10).mean()) << "%\n" + << "a0 : " << a_ << " " + << sqrt((score.row(1).array() - a_).square().sum() / (iteration - 1)) << " " + << abs(score.row(11).mean()) << "%\n" + << "pt : " << pt_ << " " + << sqrt((score.row(2).array() - pt_).square().sum() / (iteration - 1)) << " " + << abs(score.row(12).mean()) << "%\n" + << "coT: " << coT_ << " " + << sqrt((score.row(3).array() - coT_).square().sum() / (iteration - 1)) << " " + << abs(score.row(13).mean()) << "%\n" + << "Zip: " << Zip_ << " " + << sqrt((score.row(4).array() - Zip_).square().sum() / (iteration - 1)) << " " + << abs(score.row(14).mean()) << "%\n\n" + << "cov(phi,a0)_: " << score.row(5).mean() << "\n" + << "cov(phi,pt)_: " << score.row(6).mean() << "\n" + << "cov(a0,pt)_: " << score.row(7).mean() << "\n" + << "cov(coT,Zip)_: " << score.row(8).mean() << "\n\n" + << "chi2_circle: " << score.row(9).mean() << " vs " << n_ - 3 << "\n" + << "chi2_line: " << score.row(25).mean() << " vs " << n_ - 2 << "\n\n" + << "correlation matrix:\n" + << correlation << "\n\n" + << endl; + + phi_pull.Fit("gaus", "Q"); + dxy_pull.Fit("gaus", "Q"); + dz_pull.Fit("gaus", "Q"); + theta_pull.Fit("gaus", "Q"); + pt_pull.Fit("gaus", "Q"); + phi_pull.Write(); + dxy_pull.Write(); + dz_pull.Write(); + theta_pull.Write(); + pt_pull.Write(); + phi_error.Write(); + dxy_error.Write(); + dz_error.Write(); + theta_error.Write(); + pt_error.Write(); +} + + +void test_helix_fit(bool getcin) { int n_; - int iteration; - int debug2 = 0; bool return_err; const double B_field = 3.8 * c_speed / pow(10, 9) / 100; Matrix gen_par; Vector5d true_par; Vector5d err; -// while (1) { - generator.seed(1); - int debug = 0; - debug2 = 0; - std::cout << std::setprecision(6); - cout << "_________________________________________________________________________\n"; - cout << "n x(cm) y(cm) z(cm) phi(grad) R(Gev/c) eta iteration return_err debug" << endl; -// cin >> n_ >> gen_par(0) >> gen_par(1) >> gen_par(2) >> gen_par(3) >> gen_par(4) >> gen_par(5) >> -// iteration >> return_err >> debug2; - n_ = 4; - gen_par(0) = -0.1; // x - gen_par(1) = 0.1; // y - gen_par(2) = -1.; // z - gen_par(3) = 45.; // phi - gen_par(4) = 10.; // R (p_t) - gen_par(5) = 1.; // eta - iteration = 1; - return_err = true; - debug2 = 1; - - iteration *= 10; - gen_par = New_par(gen_par, 1, B_field); - true_par = True_par(gen_par, 1, B_field); - Matrix3xNd hits; - Matrix3Nd hits_cov; - unique_ptr helix(new helix_fit[iteration]); -// helix_fit* helix = new helix_fit[iteration]; - Matrix score(41, iteration); - - for (int i = 0; i < iteration; i++) { - if (debug2 == 1 && i == (iteration - 1)) { - debug = 1; - } - hits_gen gen; - gen = Hits_gen(n_, gen_par); -// gen.hits = MatrixXd::Zero(3, 4); -// gen.hits_cov = MatrixXd::Zero(3 * 4, 3 * 4); -// gen.hits.col(0) << 1.82917642593, 2.0411875248, 7.18495464325; -// gen.hits.col(1) << 4.47041416168, 4.82704305649, 18.6394691467; -// gen.hits.col(2) << 7.25991010666, 7.74653434753, 30.6931324005; -// gen.hits.col(3) << 8.99161434174, 9.54262828827, 38.1338043213; - helix[i] = Rfit::Helix_fit(gen.hits, gen.hits_cov, B_field, return_err); + generator.seed(1); + std::cout << std::setprecision(6); + cout << "_________________________________________________________________________\n"; + cout << "n x(cm) y(cm) z(cm) phi(grad) R(Gev/c) eta iteration return_err debug" << endl; + if (getcin) { + cout << "hits: "; + cin >> n_; + cout << "x: "; + cin >> gen_par(0); + cout << "y: "; + cin >> gen_par(1); + cout << "z: "; + cin >> gen_par(2); + cout << "phi: "; + cin >> gen_par(3); + cout << "p_t: "; + cin >> gen_par(4); + cout << "eta: "; + cin >> gen_par(5); + } else { + n_ = 4; + gen_par(0) = -0.1; // x + gen_par(1) = 0.1; // y + gen_par(2) = -1.; // z + gen_par(3) = 45.; // phi + gen_par(4) = 10.; // R (p_t) + gen_par(5) = 1.; // eta + } + return_err = true; - if (debug) - cout << std::setprecision(10) - << "phi: " << helix[i].par(0) << " +/- " << sqrt(helix[i].cov(0, 0)) << " vs " - << true_par(0) << endl - << "Tip: " << helix[i].par(1) << " +/- " << sqrt(helix[i].cov(1, 1)) << " vs " - << true_par(1) << endl - << "p_t: " << helix[i].par(2) << " +/- " << sqrt(helix[i].cov(2, 2)) << " vs " - << true_par(2) << endl - << "theta:" << helix[i].par(3) << " +/- " << sqrt(helix[i].cov(3, 3)) << " vs " - << true_par(3) << endl - << "Zip: " << helix[i].par(4) << " +/- " << sqrt(helix[i].cov(4, 4)) << " vs " - << true_par(4) << endl - << "charge:" << helix[i].q << " vs 1" << endl - << "covariance matrix:" << endl - << helix[i].cov << endl - << "Initial hits:\n" << gen.hits << endl - << "Initial Covariance:\n" << gen.hits_cov << endl; - } + const int iteration = 5000; + gen_par = New_par(gen_par, 1, B_field); + true_par = True_par(gen_par, 1, B_field); + Matrix3xNd hits; + Matrix3Nd hits_cov; + std::array helixRiemann_fit; +// std::array helixBrokenLine_fit; - for (int x = 0; x < iteration; x++) { - // Compute PULLS information - score(0, x) = (helix[x].par(0) - true_par(0)) / sqrt(helix[x].cov(0, 0)); - score(1, x) = (helix[x].par(1) - true_par(1)) / sqrt(helix[x].cov(1, 1)); - score(2, x) = (helix[x].par(2) - true_par(2)) / sqrt(helix[x].cov(2, 2)); - score(3, x) = (helix[x].par(3) - true_par(3)) / sqrt(helix[x].cov(3, 3)); - score(4, x) = (helix[x].par(4) - true_par(4)) / sqrt(helix[x].cov(4, 4)); - score(5, x) = - (helix[x].par(0) - true_par(0)) * (helix[x].par(1) - true_par(1)) / (helix[x].cov(0, 1)); - score(6, x) = - (helix[x].par(0) - true_par(0)) * (helix[x].par(2) - true_par(2)) / (helix[x].cov(0, 2)); - score(7, x) = - (helix[x].par(1) - true_par(1)) * (helix[x].par(2) - true_par(2)) / (helix[x].cov(1, 2)); - score(8, x) = - (helix[x].par(3) - true_par(3)) * (helix[x].par(4) - true_par(4)) / (helix[x].cov(3, 4)); - score(9, x) = helix[x].chi2_circle; - score(25, x) = helix[x].chi2_line; - score(10, x) = sqrt(helix[x].cov(0, 0)) / helix[x].par(0) * 100; - score(13, x) = sqrt(helix[x].cov(3, 3)) / helix[x].par(3) * 100; - score(14, x) = sqrt(helix[x].cov(4, 4)) / helix[x].par(4) * 100; - score(15, x) = (helix[x].par(0) - true_par(0)) * (helix[x].par(3) - true_par(3)) / - sqrt(helix[x].cov(0, 0)) / sqrt(helix[x].cov(3, 3)); - score(16, x) = (helix[x].par(1) - true_par(1)) * (helix[x].par(3) - true_par(3)) / - sqrt(helix[x].cov(1, 1)) / sqrt(helix[x].cov(3, 3)); - score(17, x) = (helix[x].par(2) - true_par(2)) * (helix[x].par(3) - true_par(3)) / - sqrt(helix[x].cov(2, 2)) / sqrt(helix[x].cov(3, 3)); - score(18, x) = (helix[x].par(0) - true_par(0)) * (helix[x].par(4) - true_par(4)) / - sqrt(helix[x].cov(0, 0)) / sqrt(helix[x].cov(4, 4)); - score(19, x) = (helix[x].par(1) - true_par(1)) * (helix[x].par(4) - true_par(4)) / - sqrt(helix[x].cov(1, 1)) / sqrt(helix[x].cov(4, 4)); - score(20, x) = (helix[x].par(2) - true_par(2)) * (helix[x].par(4) - true_par(4)) / - sqrt(helix[x].cov(2, 2)) / sqrt(helix[x].cov(4, 4)); - score(21, x) = (helix[x].par(0) - true_par(0)) * (helix[x].par(1) - true_par(1)) / - sqrt(helix[x].cov(0, 0)) / sqrt(helix[x].cov(1, 1)); - score(22, x) = (helix[x].par(0) - true_par(0)) * (helix[x].par(2) - true_par(2)) / - sqrt(helix[x].cov(0, 0)) / sqrt(helix[x].cov(2, 2)); - score(23, x) = (helix[x].par(1) - true_par(1)) * (helix[x].par(2) - true_par(2)) / - sqrt(helix[x].cov(1, 1)) / sqrt(helix[x].cov(2, 2)); - score(24, x) = (helix[x].par(3) - true_par(3)) * (helix[x].par(4) - true_par(4)) / - sqrt(helix[x].cov(3, 3)) / sqrt(helix[x].cov(4, 4)); - } + std::cout << "\nTrue parameters: " + << "phi: " << true_par(0) << " " + << "dxy: " << true_par(1) << " " + << "pt: " << true_par(2) << " " + << "CotT: " << true_par(3) << " " + << "Zip: " << true_par(4) << " " + << std::endl; + auto start = std::chrono::high_resolution_clock::now(); + auto delta = start-start; + for (int i = 0; i < 100*iteration; i++) { + hits_gen gen; + gen = Hits_gen(n_, gen_par); + // gen.hits = MatrixXd::Zero(3, 4); + // gen.hits_cov = MatrixXd::Zero(3 * 4, 3 * 4); + // gen.hits.col(0) << 1.82917642593, 2.0411875248, 7.18495464325; + // gen.hits.col(1) << 4.47041416168, 4.82704305649, 18.6394691467; + // gen.hits.col(2) << 7.25991010666, 7.74653434753, 30.6931324005; + // gen.hits.col(3) << 8.99161434174, 9.54262828827, 38.1338043213; + delta -= std::chrono::high_resolution_clock::now()-start; + helixRiemann_fit[i%iteration] = Rfit::Helix_fit(gen.hits, gen.hits_cov, B_field, return_err); + delta += std::chrono::high_resolution_clock::now()-start; - double phi_ = score.row(0).mean(); - double a_ = score.row(1).mean(); - double pt_ = score.row(2).mean(); - double coT_ = score.row(3).mean(); - double Zip_ = score.row(4).mean(); - Matrix5d correlation; - correlation << 1., score.row(21).mean(), score.row(22).mean(), score.row(15).mean(), - score.row(20).mean(), score.row(21).mean(), 1., score.row(23).mean(), score.row(16).mean(), - score.row(19).mean(), score.row(22).mean(), score.row(23).mean(), 1., score.row(17).mean(), - score.row(20).mean(), score.row(15).mean(), score.row(16).mean(), score.row(17).mean(), 1., - score.row(24).mean(), score.row(18).mean(), score.row(19).mean(), score.row(20).mean(), - score.row(24).mean(), 1.; +// helixBrokenLine_fit[i] = BrokenLine::Helix_fit(gen.hits, gen.hits_cov, B_field); - cout << "\nPULLS:\n" - << "phi: " << phi_ << " " - << sqrt((score.row(0).array() - phi_).square().sum() / (iteration - 1)) << " " - << abs(score.row(10).mean()) << "%\n" - << "a0 : " << a_ << " " - << sqrt((score.row(1).array() - a_).square().sum() / (iteration - 1)) << " " - << abs(score.row(11).mean()) << "%\n" - << "pt : " << pt_ << " " - << sqrt((score.row(2).array() - pt_).square().sum() / (iteration - 1)) << " " - << abs(score.row(12).mean()) << "%\n" - << "coT: " << coT_ << " " - << sqrt((score.row(3).array() - coT_).square().sum() / (iteration - 1)) << " " - << abs(score.row(13).mean()) << "%\n" - << "Zip: " << Zip_ << " " - << sqrt((score.row(4).array() - Zip_).square().sum() / (iteration - 1)) << " " - << abs(score.row(14).mean()) << "%\n\n" - << "cov(phi,a0)_: " << score.row(5).mean() << "\n" - << "cov(phi,pt)_: " << score.row(6).mean() << "\n" - << "cov(a0,pt)_: " << score.row(7).mean() << "\n" - << "cov(coT,Zip)_: " << score.row(8).mean() << "\n\n" - << "chi2_circle: " << score.row(9).mean() << " vs " << n_ - 3 << "\n" - << "chi2_line: " << score.row(25).mean() << " vs " << n_ - 2 << "\n\n" - << "correlation matrix:\n" - << correlation << "\n\n" - << endl; -// } + if (helixRiemann_fit[i%iteration].par(0)>10.) std::cout << "error" << std::endl; + if (0==i) + cout << std::setprecision(6) + << "phi: " << helixRiemann_fit[i].par(0) << " +/- " << sqrt(helixRiemann_fit[i].cov(0, 0)) << " vs " + << true_par(0) << endl + << "Tip: " << helixRiemann_fit[i].par(1) << " +/- " << sqrt(helixRiemann_fit[i].cov(1, 1)) << " vs " + << true_par(1) << endl + << "p_t: " << helixRiemann_fit[i].par(2) << " +/- " << sqrt(helixRiemann_fit[i].cov(2, 2)) << " vs " + << true_par(2) << endl + << "theta:" << helixRiemann_fit[i].par(3) << " +/- " << sqrt(helixRiemann_fit[i].cov(3, 3)) << " vs " + << true_par(3) << endl + << "Zip: " << helixRiemann_fit[i].par(4) << " +/- " << sqrt(helixRiemann_fit[i].cov(4, 4)) << " vs " + << true_par(4) << endl + << "charge:" << helixRiemann_fit[i].q << " vs 1" << endl + << "covariance matrix:" << endl + << helixRiemann_fit[i].cov << endl + << "Initial hits:\n" << gen.hits << endl + << "Initial Covariance:\n" << gen.hits_cov << endl; + + } + std::cout << "elapsted time " << double(std::chrono::duration_cast(delta).count())/1.e6 << std::endl; + computePull(helixRiemann_fit, "Riemann", n_, iteration, true_par); +// computePull(helixBrokenLine_fit, "BrokenLine", n_, iteration, true_par); } -int main() { - test_helix_fit(); +int main(int nargs, char**) { + TFile f("TestFitResults.root", "RECREATE"); + test_helix_fit(nargs>1); + f.Close(); return 0; } + diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu index 7b1125eebc312..3338b0bce57b1 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu @@ -10,6 +10,7 @@ using namespace Eigen; +/* __global__ void kernelFullFit(Rfit::Matrix3xNd * hits, Rfit::Matrix3Nd * hits_cov, @@ -23,50 +24,55 @@ void kernelFullFit(Rfit::Matrix3xNd * hits, Vector4d fast_fit = Rfit::Fast_fit(*hits); u_int n = hits->cols(); - Rfit::VectorNd rad = (hits->block(0, 0, 2, n).colwise().norm()); + Rfit::VectorNd rad = (hits->block(0, 0, 2, n).colwise().norm()).eval(); Rfit::Matrix2xNd hits2D_local = (hits->block(0,0,2,n)).eval(); Rfit::Matrix2Nd hits_cov2D_local = (hits_cov->block(0, 0, 2 * n, 2 * n)).eval(); Rfit::printIt(&hits2D_local, "kernelFullFit - hits2D_local: "); Rfit::printIt(&hits_cov2D_local, "kernelFullFit - hits_cov2D_local: "); - /* - printf("kernelFullFit - hits address: %p\n", hits); - printf("kernelFullFit - hits_cov address: %p\n", hits_cov); - printf("kernelFullFit - hits_cov2D address: %p\n", &hits2D_local); - printf("kernelFullFit - hits_cov2D_local address: %p\n", &hits_cov2D_local); - */ - /* At some point I gave up and locally construct block on the stack, so that - the next invocation to Rfit::Circle_fit works properly. Failing to do so - implied basically an empty collection of hits and covariances. That could - have been partially fixed if values of the passed in matrices would have - been printed on screen since that, maybe, triggered internally the real - creations of the blocks. To be understood and compared against the myriad - of compilation warnings we have. - */ - (*circle_fit_resultsGPU) = - Rfit::Circle_fit(hits->block(0,0,2,n), hits_cov->block(0, 0, 2 * n, 2 * n), - fast_fit, rad, B, errors); - /* + + //printf("kernelFullFit - hits address: %p\n", hits); + // printf("kernelFullFit - hits_cov address: %p\n", hits_cov); + //printf("kernelFullFit - hits_cov2D address: %p\n", &hits2D_local); + //printf("kernelFullFit - hits_cov2D_local address: %p\n", &hits_cov2D_local); + + // At some point I gave up and locally construct block on the stack, so that + // the next invocation to Rfit::Circle_fit works properly. Failing to do so + // implied basically an empty collection of hits and covariances. That could + // have been partially fixed if values of the passed in matrices would have + // been printed on screen since that, maybe, triggered internally the real + // creations of the blocks. To be understood and compared against the myriad + // of compilation warnings we have. + // + + + //(*circle_fit_resultsGPU) = + // Rfit::Circle_fit(hits->block(0,0,2,n), hits_cov->block(0, 0, 2 * n, 2 * n), + // fast_fit, rad, B, errors); + (*circle_fit_resultsGPU) = Rfit::Circle_fit(hits2D_local, hits_cov2D_local, - fast_fit, rad, B, errors, scattering); - */ + fast_fit, rad, B, errors); + (*line_fit_resultsGPU) = Rfit::Line_fit(*hits, *hits_cov, *circle_fit_resultsGPU, fast_fit, errors); return; } +*/ __global__ void kernelFastFit(Rfit::Matrix3xNd * hits, Vector4d * results) { - (*results) = Rfit::Fast_fit(*hits); + auto i = blockIdx.x*blockDim.x + threadIdx.x; + results[i] = Rfit::Fast_fit(hits[i]); } __global__ void kernelCircleFit(Rfit::Matrix3xNd * hits, Rfit::Matrix3Nd * hits_cov, Vector4d * fast_fit_input, double B, Rfit::circle_fit * circle_fit_resultsGPU) { - u_int n = hits->cols(); - Rfit::VectorNd rad = (hits->block(0, 0, 2, n).colwise().norm()); + auto i = blockIdx.x*blockDim.x + threadIdx.x; + u_int n = hits[i].cols(); + Rfit::VectorNd rad = (hits[i].block(0, 0, 2, n).colwise().norm()); #if TEST_DEBUG printf("fast_fit_input(0): %f\n", (*fast_fit_input)(0)); @@ -82,9 +88,9 @@ void kernelCircleFit(Rfit::Matrix3xNd * hits, printf("hits_cov(11,11): %f\n", (*hits_cov)(11,11)); printf("B: %f\n", B); #endif - (*circle_fit_resultsGPU) = - Rfit::Circle_fit(hits->block(0,0,2,n), hits_cov->block(0, 0, 2 * n, 2 * n), - *fast_fit_input, rad, B, false); + circle_fit_resultsGPU[i] = + Rfit::Circle_fit(hits[i].block(0,0,2,n), hits_cov[i].block(0, 0, 2 * n, 2 * n), + fast_fit_input[i], rad, B, true); } __global__ @@ -94,7 +100,8 @@ void kernelLineFit(Rfit::Matrix3xNd * hits, Vector4d * fast_fit, Rfit::line_fit * line_fit) { - (*line_fit) = Rfit::Line_fit(*hits, *hits_cov, *circle_fit, *fast_fit, true); + auto i = blockIdx.x*blockDim.x + threadIdx.x; + line_fit[i] = Rfit::Line_fit(hits[i], hits_cov[i], circle_fit[i], fast_fit[i], true); } void fillHitsAndHitsCov(Rfit::Matrix3xNd & hits, Rfit::Matrix3Nd & hits_cov) { @@ -123,14 +130,16 @@ void testFit() { constexpr double B = 0.0113921; Rfit::Matrix3xNd hits(3,4); Rfit::Matrix3Nd hits_cov = MatrixXd::Zero(12,12); - Rfit::Matrix3xNd * hitsGPU = new Rfit::Matrix3xNd(3,4); + Rfit::Matrix3xNd * hitsGPU = nullptr;; Rfit::Matrix3Nd * hits_covGPU = nullptr; - Vector4d * fast_fit_resultsGPU = new Vector4d(); + Vector4d * fast_fit_resultsGPU = nullptr; Vector4d * fast_fit_resultsGPUret = new Vector4d(); - Rfit::circle_fit * circle_fit_resultsGPU = new Rfit::circle_fit(); + Rfit::circle_fit * circle_fit_resultsGPU = nullptr; Rfit::circle_fit * circle_fit_resultsGPUret = new Rfit::circle_fit(); + Rfit::line_fit * line_fit_resultsGPU = nullptr; fillHitsAndHitsCov(hits, hits_cov); + // FAST_FIT_CPU Vector4d fast_fit_results = Rfit::Fast_fit(hits); @@ -139,12 +148,20 @@ void testFit() { #endif std::cout << "Fitted values (FastFit, [X0, Y0, R, tan(theta)]):\n" << fast_fit_results << std::endl; - // FAST_FIT GPU - cudaMalloc((void**)&hitsGPU, sizeof(Rfit::Matrix3xNd(3,4))); - cudaMalloc((void**)&fast_fit_resultsGPU, sizeof(Vector4d)); - cudaMemcpy(hitsGPU, &hits, sizeof(Rfit::Matrix3xNd(3,4)), cudaMemcpyHostToDevice); + // for timing purposes we fit 4096 tracks + constexpr uint32_t Ntracks = 4096; + cudaCheck(cudaMalloc((void **)&hitsGPU, Ntracks*sizeof(Rfit::Matrix3xNd(3,4)))); + cudaCheck(cudaMalloc((void **)&hits_covGPU, Ntracks*sizeof(Rfit::Matrix3Nd(12,12)))); + cudaMalloc((void**)&fast_fit_resultsGPU, Ntracks*sizeof(Vector4d)); + cudaCheck(cudaMalloc((void **)&line_fit_resultsGPU, Ntracks*sizeof(Rfit::line_fit))); + cudaCheck(cudaMalloc((void **)&circle_fit_resultsGPU, Ntracks*sizeof(Rfit::circle_fit))); + for (auto i=0U; i>>(hitsGPU, fast_fit_resultsGPU); + // FAST_FIT GPU + kernelFastFit<<>>(hitsGPU, fast_fit_resultsGPU); cudaDeviceSynchronize(); cudaMemcpy(fast_fit_resultsGPUret, fast_fit_resultsGPU, sizeof(Vector4d), cudaMemcpyDeviceToHost); @@ -161,11 +178,8 @@ void testFit() { std::cout << "Fitted values (CircleFit):\n" << circle_fit_results.par << std::endl; // CIRCLE_FIT GPU - cudaMalloc((void **)&hits_covGPU, sizeof(Rfit::Matrix3Nd(12,12))); - cudaMalloc((void **)&circle_fit_resultsGPU, sizeof(Rfit::circle_fit)); - cudaMemcpy(hits_covGPU, &hits_cov, sizeof(Rfit::Matrix3Nd(12,12)), cudaMemcpyHostToDevice); - kernelCircleFit<<<1,1>>>(hitsGPU, hits_covGPU, + kernelCircleFit<<>>(hitsGPU, hits_covGPU, fast_fit_resultsGPU, B, circle_fit_resultsGPU); cudaDeviceSynchronize(); @@ -179,12 +193,9 @@ void testFit() { std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; // LINE_FIT GPU - Rfit::line_fit * line_fit_resultsGPU = nullptr; Rfit::line_fit * line_fit_resultsGPUret = new Rfit::line_fit(); - cudaMalloc((void **)&line_fit_resultsGPU, sizeof(Rfit::line_fit)); - - kernelLineFit<<<1,1>>>(hitsGPU, hits_covGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); + kernelLineFit<<>>(hitsGPU, hits_covGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); cudaDeviceSynchronize(); cudaMemcpy(line_fit_resultsGPUret, line_fit_resultsGPU, sizeof(Rfit::line_fit), cudaMemcpyDeviceToHost); @@ -192,6 +203,7 @@ void testFit() { assert(isEqualFuzzy(line_fit_results.par, line_fit_resultsGPUret->par)); } +/* void testFitOneGo(bool errors, double epsilon=1e-6) { constexpr double B = 0.0113921; Rfit::Matrix3xNd hits(3,4); @@ -221,13 +233,17 @@ void testFitOneGo(bool errors, double epsilon=1e-6) { Rfit::circle_fit * circle_fit_resultsGPU = nullptr; // new Rfit::circle_fit(); Rfit::circle_fit * circle_fit_resultsGPUret = new Rfit::circle_fit(); - cudaCheck(cudaMalloc((void **)&hitsGPU, sizeof(Rfit::Matrix3xNd(3,4)))); - cudaCheck(cudaMalloc((void **)&hits_covGPU, sizeof(Rfit::Matrix3Nd(12,12)))); - cudaCheck(cudaMalloc((void **)&line_fit_resultsGPU, sizeof(Rfit::line_fit))); - cudaCheck(cudaMalloc((void **)&circle_fit_resultsGPU, sizeof(Rfit::circle_fit))); - cudaCheck(cudaMemcpy(hitsGPU, &hits, sizeof(Rfit::Matrix3xNd(3,4)), cudaMemcpyHostToDevice)); - cudaCheck(cudaMemcpy(hits_covGPU, &hits_cov, sizeof(Rfit::Matrix3Nd(12,12)), cudaMemcpyHostToDevice)); - + // for timing purposes we fit 4096 tracks + constexpr uint32_t Ntracks = 4096; + cudaCheck(cudaMalloc((void **)&hitsGPU, Ntracks*sizeof(Rfit::Matrix3xNd(3,4)))); + cudaCheck(cudaMalloc((void **)&hits_covGPU, Ntracks*sizeof(Rfit::Matrix3Nd(12,12)))); + cudaCheck(cudaMalloc((void **)&line_fit_resultsGPU, Ntracks*sizeof(Rfit::line_fit))); + cudaCheck(cudaMalloc((void **)&circle_fit_resultsGPU, Ntracks*sizeof(Rfit::circle_fit))); + for (auto i=0U; i>>(hitsGPU, hits_covGPU, B, errors, circle_fit_resultsGPU, line_fit_resultsGPU); cudaCheck(cudaDeviceSynchronize()); @@ -251,15 +267,18 @@ void testFitOneGo(bool errors, double epsilon=1e-6) { cudaDeviceReset(); } +*/ int main (int argc, char * argv[]) { -// testFit(); + testFit(); std::cout << "TEST FIT, NO ERRORS" << std::endl; + +/* testFitOneGo(false); std::cout << "TEST FIT, ERRORS AND SCATTER" << std::endl; testFitOneGo(true, 1e-5); - +*/ return 0; } diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp new file mode 100644 index 0000000000000..5467cc27a6e2c --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp @@ -0,0 +1,69 @@ +#include + +#include +#include + +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "test_common.h" + +using namespace Eigen; + +void fillHitsAndHitsCov(Rfit::Matrix3xNd & hits, Rfit::Matrix3Nd & hits_cov) { + hits << 1.98645, 4.72598, 7.65632, 11.3151, + 2.18002, 4.88864, 7.75845, 11.3134, + 2.46338, 6.99838, 11.808, 17.793; + hits_cov(0,0) = 7.14652e-06; + hits_cov(1,1) = 2.15789e-06; + hits_cov(2,2) = 1.63328e-06; + hits_cov(3,3) = 6.27919e-06; + hits_cov(4,4) = 6.10348e-06; + hits_cov(5,5) = 2.08211e-06; + hits_cov(6,6) = 1.61672e-06; + hits_cov(7,7) = 6.28081e-06; + hits_cov(8,8) = 5.184e-05; + hits_cov(9,9) = 1.444e-05; + hits_cov(10,10) = 6.25e-06; + hits_cov(11,11) = 3.136e-05; + hits_cov(0,4) = hits_cov(4,0) = -5.60077e-06; + hits_cov(1,5) = hits_cov(5,1) = -1.11936e-06; + hits_cov(2,6) = hits_cov(6,2) = -6.24945e-07; + hits_cov(3,7) = hits_cov(7,3) = -5.28e-06; +} + +void testFit() { + constexpr double B = 0.0113921; + Rfit::Matrix3xNd hits(3,4); + Rfit::Matrix3Nd hits_cov = MatrixXd::Zero(12,12); + + fillHitsAndHitsCov(hits, hits_cov); + std::cout << "Generated hits:\n" << hits << std::endl; + + + // FAST_FIT_CPU + Vector4d fast_fit_results = Rfit::Fast_fit(hits); + + std::cout << "Fitted values (FastFit, [X0, Y0, R, tan(theta)]):\n" << fast_fit_results << std::endl; + + + // CIRCLE_FIT CPU + u_int n = hits.cols(); + Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + + Rfit::circle_fit circle_fit_results = Rfit::Circle_fit(hits.block(0, 0, 2, n), + hits_cov.block(0, 0, 2 * n, 2 * n), + fast_fit_results, rad, B, false); + std::cout << "Fitted values (CircleFit):\n" << circle_fit_results.par << std::endl; + + // LINE_FIT CPU + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_cov, circle_fit_results, fast_fit_results, true); + std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; + +} + +int main (int argc, char * argv[]) { + testFit(); + return 0; +} + diff --git a/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h b/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h new file mode 100644 index 0000000000000..fa538256ed010 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h @@ -0,0 +1,128 @@ +#ifndef RecoPixelVertexingPixelTripletsCircleEq_H +#define RecoPixelVertexingPixelTripletsCircleEq_H +/** +| 1) circle is parameterized as: | +| C*[(X-Xp)**2+(Y-Yp)**2] - 2*alpha*(X-Xp) - 2*beta*(Y-Yp) = 0 | +| Xp,Yp is a point on the track; | +| C = 1/r0 is the curvature ( sign of C is charge of particle ); | +| alpha & beta are the direction cosines of the radial vector at Xp,Yp | +| i.e. alpha = C*(X0-Xp), | +| beta = C*(Y0-Yp), | +| where center of circle is at X0,Y0. | +| | +| Slope dy/dx of tangent at Xp,Yp is -alpha/beta. | +| 2) the z dimension of the helix is parameterized by gamma = dZ/dSperp | +| this is also the tangent of the pitch angle of the helix. | +| with this parameterization, (alpha,beta,gamma) rotate like a vector. | +| 3) For tracks going inward at (Xp,Yp), C, alpha, beta, and gamma change sign| +| +*/ + +#include + +template +class CircleEq { + +public: + + CircleEq(){} + + constexpr CircleEq(T x1, T y1, + T x2, T y2, + T x3, T y3) { + compute(x1,y1,x2,y2,x3,y3); + } + + constexpr void compute(T x1, T y1, + T x2, T y2, + T x3, T y3); + + // dca to origin divided by curvature + constexpr T dca0() const { + auto x = m_c*m_xp + m_alpha; + auto y = m_c*m_yp + m_beta; + return std::sqrt(x*x+y*y) - T(1); + } + + // dca to given point (divided by curvature) + constexpr T dca(T x, T y) const { + x = m_c*(m_xp-x) + m_alpha; + y = m_c*(m_yp-y) + m_beta; + return std::sqrt(x*x+y*y) - T(1); + + } + + // curvature + constexpr auto curvature() const { return m_c;} + + + // alpha and beta + constexpr std::pair cosdir() const { + return std::make_pair(m_alpha, m_beta); + } + + + // alpha and beta af given point + constexpr std::pair cosdir(T x, T y) const { + return std::make_pair(m_alpha - m_c*(x-m_xp), m_beta - m_c*(y-m_yp)); + } + + // center + constexpr std::pair center() const { + return std::make_pair(m_xp + m_alpha/m_c, m_yp + m_beta/m_c); + } + + constexpr auto radius() const { return T(1)/m_c;} + + T m_xp=0; + T m_yp=0; + T m_c=0; + T m_alpha=0; + T m_beta=0; + +}; + + +template +constexpr void CircleEq::compute(T x1, T y1, + T x2, T y2, + T x3, T y3) { + bool noflip = std::abs(x3-x1) < std::abs(y3-y1); + + auto x1p = noflip ? x1-x2 : y1-y2; + auto y1p = noflip ? y1-y2 : x1-x2; + auto d12 = x1p*x1p + y1p*y1p; + auto x3p = noflip ? x3-x2 : y3-y2; + auto y3p = noflip ? y3-y2 : x3-x2; + auto d32 = x3p*x3p + y3p*y3p; + + auto num = x1p*y3p-y1p*x3p; // num also gives correct sign for CT + auto det = d12*y3p-d32*y1p; + + /* + auto ct = num/det; + auto sn = det>0 ? T(1.) : T(-1.); + auto st2 = (d12*x3p-d32*x1p)/det; + auto seq = T(1.) +st2*st2; + auto al2 = sn/std::sqrt(seq); + auto be2 = -st2*al2; + ct *= T(2.)*al2; + */ + + auto st2 = (d12*x3p-d32*x1p); + auto seq = det*det +st2*st2; + auto al2 = T(1.)/std::sqrt(seq); + auto be2 = -st2*al2; + auto ct = T(2.)*num*al2; + al2 *=det; + + m_xp = x2; + m_yp = y2; + m_c = noflip ? ct : -ct; + m_alpha = noflip ? al2 : -be2; + m_beta = noflip ? be2 : -al2; + +} + +#endif + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h b/RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h new file mode 100644 index 0000000000000..942404a9313e3 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h @@ -0,0 +1,34 @@ +#ifndef RecoPixelVertexing_PixelTriplets_plugins_CAConstants_h +#define RecoPixelVertexing_PixelTriplets_plugins_CAConstants_h + +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h" +#include "RecoLocalTracker/SiPixelClusterizer/interface/PixelTrackingGPUConstants.h" + + +namespace CAConstants { + + // constants + constexpr uint32_t maxNumberOfQuadruplets() { return 10000; } + constexpr uint32_t maxCellsPerHit() { return 128; } + constexpr uint32_t maxNumberOfLayerPairs() { return 13; } + constexpr uint32_t maxNumberOfLayers() { return 10; } + constexpr uint32_t maxNumberOfDoublets() { return 262144; } + constexpr uint32_t maxTuples() { return 10000;} + + // types + using hindex_type = uint16_t; // FIXME from siPixelRecHitsHeterogeneousProduct + using tindex_type = uint16_t; // for tuples + using OuterHitOfCell = GPU::VecArray< uint32_t, maxCellsPerHit()>; + using TuplesContainer = OneToManyAssoc; + using HitToTuple = OneToManyAssoc; // 3.5 should be enough + +} + + + +#endif + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc index fa86cf42688bc..141b960b993f0 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc @@ -33,7 +33,10 @@ class CAHitNtupletHeterogeneousEDProducer heterogeneous::GPUCuda, heterogeneous::CPU>> { public: - using PixelRecHitsH = siPixelRecHitsHeterogeneousProduct::HeterogeneousPixelRecHit; + using PixelRecHitsH = siPixelRecHitsHeterogeneousProduct::HeterogeneousPixelRecHit; + using GPUProduct = pixelTuplesHeterogeneousProduct::GPUProduct; + using CPUProduct = pixelTuplesHeterogeneousProduct::CPUProduct; + using Output = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; CAHitNtupletHeterogeneousEDProducer(const edm::ParameterSet &iConfig); @@ -71,8 +74,6 @@ class CAHitNtupletHeterogeneousEDProducer CAHitNtupletHeterogeneousEDProducer::CAHitNtupletHeterogeneousEDProducer( const edm::ParameterSet &iConfig) : HeterogeneousEDProducer(iConfig), - regionToken_(consumes>( - iConfig.getParameter("trackingRegions"))), gpuHits_(consumesHeterogeneous(iConfig.getParameter("heterogeneousPixelRecHitSrc"))), cpuHits_(consumes(iConfig.getParameter("heterogeneousPixelRecHitSrc"))), GPUGenerator_(iConfig, consumesCollector()), @@ -80,17 +81,20 @@ CAHitNtupletHeterogeneousEDProducer::CAHitNtupletHeterogeneousEDProducer( enableConversion_(iConfig.getParameter("gpuEnableConversion")), enableTransfer_(enableConversion_ || iConfig.getParameter("gpuEnableTransfer")) { - produces(); + produces(); + if(enableConversion_) { + regionToken_ = consumes>(iConfig.getParameter("trackingRegions")); + produces(); + } } void CAHitNtupletHeterogeneousEDProducer::fillDescriptions( edm::ConfigurationDescriptions &descriptions) { edm::ParameterSetDescription desc; - desc.add("doublets", edm::InputTag(""))->setComment("Not really used, kept to keep the python parameters"); desc.add("trackingRegions", edm::InputTag("globalTrackingRegionFromBeamSpot")); desc.add("heterogeneousPixelRecHitSrc", edm::InputTag("siPixelRecHitsPreSplitting")); - desc.add("doRiemannFit", true); + desc.add("doRiemannFit", true); // mandatory! desc.add("gpuEnableTransfer", true); desc.add("gpuEnableConversion", true); @@ -109,47 +113,42 @@ void CAHitNtupletHeterogeneousEDProducer::acquireGPUCuda( const edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) { - seedingHitSets_ = std::make_unique(); - - edm::Handle> hregions; - iEvent.getByToken(regionToken_, hregions); - const auto ®ions = *hregions; - assert(regions.size()<=1); - - if (regions.empty()) { - emptyRegions = true; - return; - } - - const TrackingRegion ®ion = regions[0]; - edm::Handle gh; iEvent.getByToken(gpuHits_, gh); auto const & gHits = *gh; GPUGenerator_.buildDoublets(gHits,cudaStream.id()); - seedingHitSets_->reserve(regions.size(), localRA_.upper()); GPUGenerator_.initEvent(iEvent.event(), iSetup); LogDebug("CAHitNtupletHeterogeneousEDProducer") - << "Creating ntuplets for " << regions.size() - << " regions"; + << "Creating ntuplets on GPU"; + + GPUGenerator_.hitNtuplets(gHits, iSetup, doRiemannFit_, enableTransfer_, cudaStream.id()); + + - GPUGenerator_.hitNtuplets(region, gHits, iSetup, doRiemannFit_, enableTransfer_, cudaStream.id()); } void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) { - GPUGenerator_.cleanup(cudaStream.id()); - - if (not emptyRegions and enableConversion_) { + if (enableConversion_) { edm::Handle> hregions; iEvent.getByToken(regionToken_, hregions); const auto ®ions = *hregions; + assert(regions.size()<=1); + + if (regions.empty()) { + emptyRegions = true; + return; + } + + seedingHitSets_ = std::make_unique(); + seedingHitSets_->reserve(regions.size(), localRA_.upper()); + edm::Handle gh; iEvent.getByToken(cpuHits_, gh); auto const & rechits = *gh; @@ -165,8 +164,13 @@ void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( index++; } localRA_.update(seedingHitSets_->size()); + iEvent.put(std::move(seedingHitSets_)); } - iEvent.put(std::move(seedingHitSets_)); + + auto output = std::make_unique(GPUGenerator_.getOutput()); + iEvent.put(std::move(output), heterogeneous::DisableTransfer{}); + GPUGenerator_.cleanup(cudaStream.id()); + } void CAHitNtupletHeterogeneousEDProducer::produceCPU( diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index ea905e013d335..75e3913855013 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -15,7 +15,6 @@ #include "TrackingTools/DetLayers/interface/BarrelDetLayer.h" #include "CAHitQuadrupletGeneratorGPU.h" -#include "CAHitQuadrupletGeneratorKernels.h" namespace { @@ -29,52 +28,24 @@ constexpr unsigned int CAHitQuadrupletGeneratorGPU::minLayers; CAHitQuadrupletGeneratorGPU::CAHitQuadrupletGeneratorGPU( const edm::ParameterSet &cfg, - edm::ConsumesCollector &iC) - : extraHitRPhitolerance(cfg.getParameter("extraHitRPhitolerance")), // extra window in ThirdHitPredictionFromCircle range (divide by R to get phi) - maxChi2(cfg.getParameter("maxChi2")), - fitFastCircle(cfg.getParameter("fitFastCircle")), - fitFastCircleChi2Cut(cfg.getParameter("fitFastCircleChi2Cut")), - useBendingCorrection(cfg.getParameter("useBendingCorrection")), + edm::ConsumesCollector &iC) : + kernels(cfg.getParameter("earlyFishbone"),cfg.getParameter("lateFishbone")), caThetaCut(cfg.getParameter("CAThetaCut")), caPhiCut(cfg.getParameter("CAPhiCut")), caHardPtCut(cfg.getParameter("CAHardPtCut")) { - edm::ParameterSet comparitorPSet = cfg.getParameter("SeedComparitorPSet"); - std::string comparitorName = comparitorPSet.getParameter("ComponentName"); - if (comparitorName != "none") { - theComparitor.reset(SeedComparitorFactory::get()->create(comparitorName, comparitorPSet, iC)); - } } void CAHitQuadrupletGeneratorGPU::fillDescriptions(edm::ParameterSetDescription &desc) { - desc.add("extraHitRPhitolerance", 0.1); - desc.add("fitFastCircle", false); - desc.add("fitFastCircleChi2Cut", false); - desc.add("useBendingCorrection", false); desc.add("CAThetaCut", 0.00125); desc.add("CAPhiCut", 10); desc.add("CAHardPtCut", 0); - desc.addOptional("CAOnlyOneLastHitPerLayerFilter")->setComment( - "Deprecated and has no effect. To be fully removed later when the " - "parameter is no longer used in HLT configurations."); - edm::ParameterSetDescription descMaxChi2; - descMaxChi2.add("pt1", 0.2); - descMaxChi2.add("pt2", 1.5); - descMaxChi2.add("value1", 500); - descMaxChi2.add("value2", 50); - descMaxChi2.add("enabled", true); - desc.add("maxChi2", descMaxChi2); - - edm::ParameterSetDescription descComparitor; - descComparitor.add("ComponentName", "none"); - descComparitor.setAllowAnything(); // until we have moved SeedComparitor to EDProducers too - desc.add("SeedComparitorPSet", descComparitor); + desc.add("earlyFishbone",false); + desc.add("lateFishbone",true); } void CAHitQuadrupletGeneratorGPU::initEvent(edm::Event const& ev, edm::EventSetup const& es) { - if (theComparitor) - theComparitor->init(ev, es); - bField_ = 1 / PixelRecoUtilities::fieldInInvGev(es); + fitter.setBField(1 / PixelRecoUtilities::fieldInInvGev(es)); } @@ -83,7 +54,6 @@ CAHitQuadrupletGeneratorGPU::~CAHitQuadrupletGeneratorGPU() { } void CAHitQuadrupletGeneratorGPU::hitNtuplets( - TrackingRegion const& region, HitsOnCPU const& hh, edm::EventSetup const& es, bool doRiemannFit, @@ -91,8 +61,7 @@ void CAHitQuadrupletGeneratorGPU::hitNtuplets( cudaStream_t cudaStream) { hitsOnCPU = &hh; - int index = 0; - launchKernels(region, index, hh, doRiemannFit, transferToCPU, cudaStream); + launchKernels(hh, doRiemannFit, transferToCPU, cudaStream); } void CAHitQuadrupletGeneratorGPU::fillResults( @@ -109,23 +78,22 @@ void CAHitQuadrupletGeneratorGPU::fillResults( auto const & foundQuads = fetchKernelResult(index); unsigned int numberOfFoundQuadruplets = foundQuads.size(); - const QuantityDependsPtEval maxChi2Eval = maxChi2.evaluator(es); - // re-used throughout - std::array bc_r; - std::array bc_z; - std::array bc_errZ2; std::array gps; std::array ges; std::array barrels; std::array phits; + indToEdm.clear(); + indToEdm.resize(numberOfFoundQuadruplets,64000); + + int nbad=0; // loop over quadruplets for (unsigned int quadId = 0; quadId < numberOfFoundQuadruplets; ++quadId) { auto isBarrel = [](const unsigned id) -> bool { return id == PixelSubdetector::PixelBarrel; }; - bool bad = false; + bool bad = pixelTuplesHeterogeneousProduct::bad == quality_[quadId]; for (unsigned int i = 0; i < 4; ++i) { auto k = foundQuads[quadId][i]; assert(kcompatible(tmpTriplet)) { - continue; - } - } - - float chi2 = std::numeric_limits::quiet_NaN(); - // TODO: Do we have any use case to not use bending correction? - if (useBendingCorrection) { - // Following PixelFitterByConformalMappingAndLine - const float simpleCot = (gps.back().z() - gps.front().z()) / - (gps.back().perp() - gps.front().perp()); - const float pt = 1.f / PixelRecoUtilities::inversePt(abscurv, es); - for (int i = 0; i < 4; ++i) { - const GlobalPoint &point = gps[i]; - const GlobalError &error = ges[i]; - bc_r[i] = sqrt(sqr(point.x() - region.origin().x()) + - sqr(point.y() - region.origin().y())); - bc_r[i] += pixelrecoutilities::LongitudinalBendingCorrection(pt, es)( - bc_r[i]); - bc_z[i] = point.z() - region.origin().z(); - bc_errZ2[i] = - (barrels[i]) ? error.czz() : error.rerr(point) * sqr(simpleCot); - } - RZLine rzLine(bc_r, bc_z, bc_errZ2, RZLine::ErrZ2_tag()); - chi2 = rzLine.chi2(); - } else { - RZLine rzLine(gps, ges, barrels); - chi2 = rzLine.chi2(); - } - if (edm::isNotFinite(chi2) || chi2 > thisMaxChi2) { - continue; - } - // TODO: Do we have any use case to not use circle fit? Maybe - // HLT where low-pT inefficiency is not a problem? - if (fitFastCircle) { - FastCircleFit c(gps, ges); - chi2 += c.chi2(); - if (edm::isNotFinite(chi2)) - continue; - if (fitFastCircleChi2Cut && chi2 > thisMaxChi2) - continue; - } + if (bad) { nbad++; quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; continue;} + if (quality_[quadId] != pixelTuplesHeterogeneousProduct::loose) continue; // FIXME remove dup + result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); - + indToEdm[quadId] = result[index].size()-1; } // end loop over quads -} -void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, - int regionIndex, HitsOnCPU const & hh, - bool doRiemannFit, - bool transferToCPU, - cudaStream_t cudaStream) -{ - assert(regionIndex < maxNumberOfRegions_); - assert(0==regionIndex); - - h_foundNtupletsVec_[regionIndex]->reset(); - - auto nhits = hh.nHits; - assert(nhits <= PixelGPUConstants::maxNumberOfHits); - auto blockSize = 64; - auto numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; - //kernel_connect<<>>( - wrapperConnect(numberOfBlocks, blockSize, cudaStream, - d_foundNtupletsVec_[regionIndex], // needed only to be reset, ready for next kernel - hh.gpu_d, - device_theCells_, device_nCells_, - device_isOuterHitOfCell_, - region.ptMin(), - region.originRBound(), caThetaCut, caPhiCut, caHardPtCut, - maxNumberOfDoublets_, PixelGPUConstants::maxNumberOfHits); - - //kernel_find_ntuplets<<>>( - wrapperFindNtuplets(numberOfBlocks, blockSize, cudaStream, - device_theCells_, device_nCells_, - d_foundNtupletsVec_[regionIndex], - 4, maxNumberOfDoublets_); - - numberOfBlocks = (std::max(int(nhits), maxNumberOfDoublets_) + blockSize - 1)/blockSize; - //kernel_checkOverflows<<>>( - wrapperCheckOverflows(numberOfBlocks, blockSize, cudaStream, - d_foundNtupletsVec_[regionIndex], - device_theCells_, device_nCells_, - device_isOuterHitOfCell_, nhits, - maxNumberOfDoublets_); - - // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(d_foundNtupletsVec_[regionIndex], 10); - // wrapperPrintFoundNtuplets(cudaStream, d_foundNtupletsVec_[regionIndex], 10); +#ifdef GPU_DEBUG + std::cout << "Q Final quads " << result[index].size() << ' ' << nbad << std::endl; +#endif - if (doRiemannFit) { - //kernelFastFitAllHits<<>>( - wrapperFastFitAllHits(numberOfBlocks, 512, cudaStream, - d_foundNtupletsVec_[regionIndex], hh.gpu_d, 4, bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); - - blockSize = 256; - numberOfBlocks = (maxNumberOfQuadruplets_ + blockSize - 1) / blockSize; - - //kernelCircleFitAllHits<<>>( - wrapperCircleFitAllHits(numberOfBlocks, blockSize, cudaStream, - d_foundNtupletsVec_[regionIndex], 4, bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); - - //kernelLineFitAllHits<<>>( - wrapperLineFitAllHits(numberOfBlocks, blockSize, cudaStream, - d_foundNtupletsVec_[regionIndex], bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); - } +} - if (transferToCPU) { - cudaCheck(cudaMemcpyAsync(h_foundNtupletsVec_[regionIndex], d_foundNtupletsVec_[regionIndex], - sizeof(GPU::SimpleVector), - cudaMemcpyDeviceToHost, cudaStream)); - cudaCheck(cudaMemcpyAsync(h_foundNtupletsData_[regionIndex], d_foundNtupletsData_[regionIndex], - maxNumberOfQuadruplets_*sizeof(Quadruplet), - cudaMemcpyDeviceToHost, cudaStream)); - } +void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() +{ + //product + cudaFree(gpu_.tuples_d); + cudaFree(gpu_.helix_fit_results_d); + cudaFree(gpu_.quality_d); + cudaFree(gpu_.apc_d); + cudaFree(gpu_d); + cudaFreeHost(tuples_); + cudaFreeHost(helix_fit_results_); + cudaFreeHost(quality_); } void CAHitQuadrupletGeneratorGPU::allocateOnGPU() { - ////////////////////////////////////////////////////////// - // ALLOCATIONS FOR THE INTERMEDIATE RESULTS (STAYS ON WORKER) - ////////////////////////////////////////////////////////// - - cudaCheck(cudaMalloc(&device_theCells_, - maxNumberOfLayerPairs_ * maxNumberOfDoublets_ * sizeof(GPUCACell))); - cudaCheck(cudaMalloc(&device_nCells_, sizeof(uint32_t))); - cudaCheck(cudaMemset(device_nCells_, 0, sizeof(uint32_t))); - - cudaCheck(cudaMalloc(&device_isOuterHitOfCell_, - PixelGPUConstants::maxNumberOfHits * sizeof(GPU::VecArray))); - cudaCheck(cudaMemset(device_isOuterHitOfCell_, 0, - PixelGPUConstants::maxNumberOfHits * sizeof(GPU::VecArray))); - - h_foundNtupletsVec_.resize(maxNumberOfRegions_); - h_foundNtupletsData_.resize(maxNumberOfRegions_); - d_foundNtupletsVec_.resize(maxNumberOfRegions_); - d_foundNtupletsData_.resize(maxNumberOfRegions_); - - // FIXME this could be rewritten with a single pair of cudaMallocHost / cudaMalloc - for (int i = 0; i < maxNumberOfRegions_; ++i) { - cudaCheck(cudaMallocHost(&h_foundNtupletsData_[i], sizeof(Quadruplet) * maxNumberOfQuadruplets_)); - cudaCheck(cudaMallocHost(&h_foundNtupletsVec_[i], sizeof(GPU::SimpleVector))); - GPU::make_SimpleVector(h_foundNtupletsVec_[i], maxNumberOfQuadruplets_, h_foundNtupletsData_[i]); - cudaCheck(cudaMalloc(&d_foundNtupletsData_[i], sizeof(Quadruplet) * maxNumberOfQuadruplets_)); - cudaCheck(cudaMemset(d_foundNtupletsData_[i], 0x00, sizeof(Quadruplet) * maxNumberOfQuadruplets_)); - cudaCheck(cudaMalloc(&d_foundNtupletsVec_[i], sizeof(GPU::SimpleVector))); - auto tmp_foundNtuplets = GPU::make_SimpleVector(maxNumberOfQuadruplets_, d_foundNtupletsData_[i]); - cudaCheck(cudaMemcpy(d_foundNtupletsVec_[i], & tmp_foundNtuplets, sizeof(GPU::SimpleVector), cudaMemcpyDefault)); - } + constexpr auto maxNumberOfQuadruplets_ = CAConstants::maxNumberOfQuadruplets(); - // Riemann-Fit related allocations - cudaCheck(cudaMalloc(&hitsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::Matrix3xNd(3, 4)))); - cudaCheck(cudaMemset(hitsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::Matrix3xNd(3, 4)))); + //product + cudaCheck(cudaMalloc(&gpu_.tuples_d, sizeof(TuplesOnGPU::Container))); + cudaCheck(cudaMalloc(&gpu_.apc_d, sizeof(AtomicPairCounter))); + cudaCheck(cudaMalloc(&gpu_.helix_fit_results_d, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); + cudaCheck(cudaMalloc(&gpu_.quality_d, sizeof(Quality)*maxNumberOfQuadruplets_)); - cudaCheck(cudaMalloc(&hits_covGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::Matrix3Nd(12, 12)))); - cudaCheck(cudaMemset(hits_covGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::Matrix3Nd(12, 12)))); + cudaCheck(cudaMalloc(&gpu_d, sizeof(TuplesOnGPU))); + gpu_.me_d = gpu_d; + cudaCheck(cudaMemcpy(gpu_d, &gpu_, sizeof(TuplesOnGPU), cudaMemcpyDefault)); - cudaCheck(cudaMalloc(&fast_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Eigen::Vector4d))); - cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Eigen::Vector4d))); + cudaCheck(cudaMallocHost(&tuples_, sizeof(TuplesOnGPU::Container))); + cudaCheck(cudaMallocHost(&helix_fit_results_, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); + cudaCheck(cudaMallocHost(&quality_, sizeof(Quality)*maxNumberOfQuadruplets_)); - cudaCheck(cudaMalloc(&circle_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::circle_fit))); - cudaCheck(cudaMemset(circle_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::circle_fit))); + kernels.allocateOnGPU(); + fitter.allocateOnGPU(gpu_.tuples_d, gpu_.helix_fit_results_d); - cudaCheck(cudaMalloc(&line_fit_resultsGPU_, maxNumberOfQuadruplets_ * sizeof(Rfit::line_fit))); - cudaCheck(cudaMemset(line_fit_resultsGPU_, 0x00, maxNumberOfQuadruplets_ * sizeof(Rfit::line_fit))); - cudaCheck(cudaMalloc(&helix_fit_resultsGPU_, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); - cudaCheck(cudaMemset(helix_fit_resultsGPU_, 0x00, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); } -void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() +void CAHitQuadrupletGeneratorGPU::launchKernels(HitsOnCPU const & hh, + bool doRiemannFit, + bool transferToCPU, + cudaStream_t cudaStream) { - for (size_t i = 0; i < h_foundNtupletsVec_.size(); ++i) - { - cudaFreeHost(h_foundNtupletsVec_[i]); - cudaFreeHost(h_foundNtupletsData_[i]); - cudaFree(d_foundNtupletsVec_[i]); - cudaFree(d_foundNtupletsData_[i]); + + kernels.launchKernels(hh, gpu_, cudaStream); + if (doRiemannFit) { + fitter.launchKernels(hh, hh.nHits, CAConstants::maxNumberOfQuadruplets(), cudaStream); + kernels.classifyTuples(hh, gpu_, cudaStream); + } + if (transferToCPU) { + cudaCheck(cudaMemcpyAsync(tuples_,gpu_.tuples_d, + sizeof(TuplesOnGPU::Container), + cudaMemcpyDeviceToHost, cudaStream)); + + cudaCheck(cudaMemcpyAsync(helix_fit_results_,gpu_.helix_fit_results_d, + sizeof(Rfit::helix_fit)*CAConstants::maxNumberOfQuadruplets(), + cudaMemcpyDeviceToHost, cudaStream)); + + cudaCheck(cudaMemcpyAsync(quality_,gpu_.quality_d, + sizeof(Quality)*CAConstants::maxNumberOfQuadruplets(), + cudaMemcpyDeviceToHost, cudaStream)); + } - cudaFree(device_theCells_); - cudaFree(device_isOuterHitOfCell_); - cudaFree(device_nCells_); - - // Free Riemann Fit stuff - cudaFree(hitsGPU_); - cudaFree(hits_covGPU_); - cudaFree(fast_fit_resultsGPU_); - cudaFree(circle_fit_resultsGPU_); - cudaFree(line_fit_resultsGPU_); - cudaFree(helix_fit_resultsGPU_); } void CAHitQuadrupletGeneratorGPU::cleanup(cudaStream_t cudaStream) { - // this lazily resets temporary memory for the next event, and is not needed for reading the output - cudaCheck(cudaMemsetAsync(device_isOuterHitOfCell_, 0, - PixelGPUConstants::maxNumberOfHits * sizeof(GPU::VecArray), - cudaStream)); - cudaCheck(cudaMemsetAsync(device_nCells_, 0, sizeof(uint32_t), cudaStream)); + kernels.cleanup(cudaStream); } + + std::vector> -CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) +CAHitQuadrupletGeneratorGPU::fetchKernelResult(int) { - assert(0==regionIndex); - h_foundNtupletsVec_[regionIndex]->set_data(h_foundNtupletsData_[regionIndex]); - - std::vector> quadsInterface(h_foundNtupletsVec_[regionIndex]->size()); - for (int i = 0; i < h_foundNtupletsVec_[regionIndex]->size(); ++i) { - for (int j = 0; j<4; ++j) quadsInterface[i][j] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[j]; + assert(tuples_); + auto const & tuples = *tuples_; + + uint32_t sizes[7]={0}; + std::vector ntk(10000); + auto add = [&](uint32_t hi) { if (hi>=ntk.size()) ntk.resize(hi+1); ++ntk[hi];}; + + std::vector> quadsInterface; quadsInterface.reserve(10000); + + nTuples_=0; + for (auto i = 0U; i < tuples.nbins(); ++i) { + auto sz = tuples.size(i); + if (sz==0) break; // we know cannot be less then 3 + ++nTuples_; + ++sizes[sz]; + for (auto j=tuples.begin(i); j!=tuples.end(i); ++j) add(*j); + if (sz<4) continue; + quadsInterface.emplace_back(std::array()); + quadsInterface.back()[0] = tuples.begin(i)[0]; + quadsInterface.back()[1] = tuples.begin(i)[1]; + quadsInterface.back()[2] = tuples.begin(i)[2]; // [sz-2]; + quadsInterface.back()[3] = tuples.begin(i)[3]; // [sz-1]; } + +#ifdef GPU_DEBUG + long long ave =0; int nn=0; for (auto k : ntk) if(k>0){ave+=k; ++nn;} + std::cout << "Q Produced " << quadsInterface.size() << " quadruplets: "; + for (auto i=3; i<7; ++i) std::cout << sizes[i] << ' '; + std::cout << "max/ave " << *std::max_element(ntk.begin(),ntk.end())<<'/'<gpu_d, tuples_, helix_fit_results_, quality_, gpu_d, nTuples_}; + } + void cleanup(cudaStream_t stream); void fillResults(const TrackingRegion ®ion, SiPixelRecHitCollectionNew const & rechits, std::vector& result, @@ -73,116 +87,35 @@ class CAHitQuadrupletGeneratorGPU { private: - std::unique_ptr theComparitor; - - class QuantityDependsPtEval { - public: - - QuantityDependsPtEval(float v1, float v2, float c1, float c2) : - value1_(v1), value2_(v2), curvature1_(c1), curvature2_(c2) { - } - - float value(float curvature) const { - if (value1_ == value2_) // not enabled - return value1_; - - if (curvature1_ < curvature) - return value1_; - if (curvature2_ < curvature && curvature <= curvature1_) - return value2_ + (curvature - curvature2_) / (curvature1_ - curvature2_) * (value1_ - value2_); - return value2_; - } - - private: - const float value1_; - const float value2_; - const float curvature1_; - const float curvature2_; - }; - - // Linear interpolation (in curvature) between value1 at pt1 and - // value2 at pt2. If disabled, value2 is given (the point is to - // allow larger/smaller values of the quantity at low pt, so it - // makes more sense to have the high-pt value as the default). - - class QuantityDependsPt { - public: - - explicit QuantityDependsPt(const edm::ParameterSet& pset) : - value1_(pset.getParameter("value1")), - value2_(pset.getParameter("value2")), - pt1_(pset.getParameter("pt1")), - pt2_(pset.getParameter("pt2")), - enabled_(pset.getParameter("enabled")) { - if (enabled_ && pt1_ >= pt2_) - throw cms::Exception("Configuration") << "PixelQuadrupletGenerator::QuantityDependsPt: pt1 (" << pt1_ << ") needs to be smaller than pt2 (" << pt2_ << ")"; - if (pt1_ <= 0) - throw cms::Exception("Configuration") << "PixelQuadrupletGenerator::QuantityDependsPt: pt1 needs to be > 0; is " << pt1_; - if (pt2_ <= 0) - throw cms::Exception("Configuration") << "PixelQuadrupletGenerator::QuantityDependsPt: pt2 needs to be > 0; is " << pt2_; - } - - QuantityDependsPtEval evaluator(const edm::EventSetup& es) const { - if (enabled_) { - return QuantityDependsPtEval(value1_, value2_, - PixelRecoUtilities::curvature(1.f / pt1_, es), - PixelRecoUtilities::curvature(1.f / pt2_, es)); - } - return QuantityDependsPtEval(value2_, value2_, 0.f, 0.f); - } - - private: - const float value1_; - const float value2_; - const float pt1_; - const float pt2_; - const bool enabled_; - }; - - void launchKernels(const TrackingRegion &, int, HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t); - std::vector> fetchKernelResult(int); + void launchKernels(HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t); + - float bField_; + std::vector> fetchKernelResult(int); - const float extraHitRPhitolerance; - const QuantityDependsPt maxChi2; - const bool fitFastCircle; - const bool fitFastCircleChi2Cut; - const bool useBendingCorrection; + CAHitQuadrupletGeneratorKernels kernels; + RiemannFitOnGPU fitter; + // not really used at the moment const float caThetaCut = 0.00125f; const float caPhiCut = 0.1f; const float caHardPtCut = 0.f; - static constexpr int maxNumberOfQuadruplets_ = 10000; - static constexpr int maxCellsPerHit_ = 256; - static constexpr int maxNumberOfLayerPairs_ = 13; - static constexpr int maxNumberOfLayers_ = 10; - static constexpr int maxNumberOfDoublets_ = 262144; - static constexpr int maxNumberOfRegions_ = 2; - - std::vector*> h_foundNtupletsVec_; - std::vector h_foundNtupletsData_; - - std::vector*> d_foundNtupletsVec_; - std::vector d_foundNtupletsData_; - GPUCACell* device_theCells_ = nullptr; - GPU::VecArray< unsigned int, maxCellsPerHit_>* device_isOuterHitOfCell_ = nullptr; - uint32_t* device_nCells_ = nullptr; + // products + std::vector indToEdm; // index of tuple in reco tracks.... + TuplesOnGPU * gpu_d = nullptr; // copy of the structure on the gpu itself: this is the "Product" + TuplesOnGPU::Container * tuples_ = nullptr; + Rfit::helix_fit * helix_fit_results_ = nullptr; + Quality * quality_ = nullptr; + uint32_t nTuples_ = 0; + TuplesOnGPU gpu_; + // input HitsOnCPU const * hitsOnCPU=nullptr; RecHitsMap hitmap_ = RecHitsMap(nullptr); - // Riemann Fit stuff - Rfit::Matrix3xNd *hitsGPU_ = nullptr; - Rfit::Matrix3Nd *hits_covGPU_ = nullptr; - Eigen::Vector4d *fast_fit_resultsGPU_ = nullptr; - Rfit::circle_fit *circle_fit_resultsGPU_ = nullptr; - Rfit::line_fit *line_fit_resultsGPU_ = nullptr; - Rfit::helix_fit * helix_fit_resultsGPU_ = nullptr; }; #endif // RecoPixelVertexing_PixelTriplets_plugins_CAHitQuadrupletGeneratorGPU_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cc new file mode 100644 index 0000000000000..d00a5db18610c --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cc @@ -0,0 +1,44 @@ +#include "CAHitQuadrupletGeneratorKernels.h" + + +void +CAHitQuadrupletGeneratorKernels::deallocateOnGPU() +{ + + cudaFree(device_theCells_); + cudaFree(device_isOuterHitOfCell_); + cudaFree(device_nCells_); +// cudaFree(device_hitToTuple_); + cudaFree(device_hitToTuple_apc_); + +} + +void CAHitQuadrupletGeneratorKernels::allocateOnGPU() +{ + ////////////////////////////////////////////////////////// + // ALLOCATIONS FOR THE INTERMEDIATE RESULTS (STAYS ON WORKER) + ////////////////////////////////////////////////////////// + + cudaCheck(cudaMalloc(&device_theCells_, + CAConstants::maxNumberOfLayerPairs() * CAConstants::maxNumberOfDoublets() * sizeof(GPUCACell))); + cudaCheck(cudaMalloc(&device_nCells_, sizeof(uint32_t))); + cudaCheck(cudaMemset(device_nCells_, 0, sizeof(uint32_t))); + + cudaCheck(cudaMalloc(&device_isOuterHitOfCell_, + PixelGPUConstants::maxNumberOfHits * sizeof(CAConstants::OuterHitOfCell))); + cudaCheck(cudaMemset(device_isOuterHitOfCell_, 0, + PixelGPUConstants::maxNumberOfHits * sizeof(CAConstants::OuterHitOfCell))); + +// cudaCheck(cudaMalloc(&device_hitToTuple_, sizeof(HitToTuple))); + cudaCheck(cudaMalloc(&device_hitToTuple_apc_, sizeof(AtomicPairCounter))); + +} + +void CAHitQuadrupletGeneratorKernels::cleanup(cudaStream_t cudaStream) { + // this lazily resets temporary memory for the next event, and is not needed for reading the output + cudaCheck(cudaMemsetAsync(device_isOuterHitOfCell_, 0, + PixelGPUConstants::maxNumberOfHits * sizeof(CAConstants::OuterHitOfCell), + cudaStream)); + cudaCheck(cudaMemsetAsync(device_nCells_, 0, sizeof(uint32_t), cudaStream)); +} + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index 61c3ff0799637..92fff26e7de29 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -1,416 +1,326 @@ +// +// Author: Felice Pantaleo, CERN +// + +#include "CAHitQuadrupletGeneratorKernels.h" #include -#include #include -#include - -#include "HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h" -#include "HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h" #include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" #include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" -#include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" -#include "CAHitQuadrupletGeneratorKernels.h" #include "GPUCACell.h" #include "gpuPixelDoublets.h" +#include "gpuFishbone.h" +#include "CAConstants.h" -__global__ -void kernelFastFitAllHits( - GPU::SimpleVector * foundNtuplets, - siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hhp, - int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit) -{ - int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); - if (helix_start >= foundNtuplets->size()) { - return; - } +using namespace gpuPixelDoublets; + +using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; +using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; +using Quality = pixelTuplesHeterogeneousProduct::Quality; -#ifdef GPU_DEBUG - printf("BlockDim.x: %d, BlockIdx.x: %d, threadIdx.x: %d, helix_start: %d, cumulative_size: %d\n", - blockDim.x, blockIdx.x, threadIdx.x, helix_start, foundNtuplets->size()); -#endif - - hits[helix_start].resize(3, hits_in_fit); - hits_cov[helix_start].resize(3 * hits_in_fit, 3 * hits_in_fit); - - // Prepare data structure - for (unsigned int i = 0; i < hits_in_fit; ++i) { - auto hit = (*foundNtuplets)[helix_start].hitId[i]; - // printf("Hit global_x: %f\n", hhp->xg_d[hit]); - float ge[6]; - hhp->cpeParams->detParams(hhp->detInd_d[hit]).frame.toGlobal(hhp->xerr_d[hit], 0, hhp->yerr_d[hit], ge); - // printf("Error: %d: %f,%f,%f,%f,%f,%f\n",hhp->detInd_d[hit],ge[0],ge[1],ge[2],ge[3],ge[4],ge[5]); - - hits[helix_start].col(i) << hhp->xg_d[hit], hhp->yg_d[hit], hhp->zg_d[hit]; - - for (auto j = 0; j < 3; ++j) { - for (auto l = 0; l < 3; ++l) { - // Index numerology: - // i: index of the hits/point (0,..,3) - // j: index of space component (x,y,z) - // l: index of space components (x,y,z) - // ge is always in sync with the index i and is formatted as: - // ge[] ==> [xx, xy, xz, yy, yz, zz] - // in (j,l) notation, we have: - // ge[] ==> [(0,0), (0,1), (0,2), (1,1), (1,2), (2,2)] - // so the index ge_idx corresponds to the matrix elements: - // | 0 1 2 | - // | 1 3 4 | - // | 2 4 5 | - auto ge_idx = j + l + (j > 0 and l > 0); - hits_cov[helix_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; - } - } - } - fast_fit[helix_start] = Rfit::Fast_fit(hits[helix_start]); -} -void wrapperFastFitAllHits( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hhp, - int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit) -{ - kernelFastFitAllHits<<>>( - foundNtuplets, - hhp, - hits_in_fit, - B, - results, - hits, - hits_cov, - circle_fit, - fast_fit, - line_fit); - cudaCheck(cudaGetLastError()); -} __global__ -void kernelCircleFitAllHits( - GPU::SimpleVector * foundNtuplets, - int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit) -{ - int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); - if (helix_start >= foundNtuplets->size()) { - return; - } +void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, AtomicPairCounter * apc, + GPUCACell const * __restrict__ cells, uint32_t const * __restrict__ nCells, + GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, + uint32_t nHits) { + + __shared__ uint32_t killedCell; + killedCell=0; + __syncthreads(); + + auto idx = threadIdx.x + blockIdx.x * blockDim.x; + #ifdef GPU_DEBUG + if (0==idx) { + printf("number of found cells %d, found tuples %d with total hits %d,%d\n",*nCells, apc->get().m, foundNtuplets->size(), apc->get().n); + assert(foundNtuplets->size(apc->get().m)==0); + assert(foundNtuplets->size()==apc->get().n); + } -#ifdef GPU_DEBUG - printf("blockDim.x: %d, blockIdx.x: %d, threadIdx.x: %d, helix_start: %d, cumulative_size: %d\n", - blockDim.x, blockIdx.x, threadIdx.x, helix_start, foundNtuplets->size()); -#endif - auto n = hits[helix_start].cols(); + if(idxnbins()) { + if (foundNtuplets->size(idx)>5) printf("ERROR %d, %d\n", idx, foundNtuplets->size(idx)); + assert(foundNtuplets->size(idx)<6); + for (auto ih = foundNtuplets->begin(idx); ih!=foundNtuplets->end(idx); ++ih) assert(*ih=CAConstants::maxNumberOfDoublets()) printf("Cells overflow\n"); + } - circle_fit[helix_start] = - Rfit::Circle_fit(hits[helix_start].block(0, 0, 2, n), - hits_cov[helix_start].block(0, 0, 2 * n, 2 * n), - fast_fit[helix_start], rad, B, true); + if (idx < (*nCells) ) { + auto &thisCell = cells[idx]; + if (thisCell.theOuterNeighbors.full()) //++tooManyNeighbors[thisCell.theLayerPairId]; + printf("OuterNeighbors overflow %d in %d\n", idx, thisCell.theLayerPairId); + if (thisCell.theTracks.full()) //++tooManyTracks[thisCell.theLayerPairId]; + printf("Tracks overflow %d in %d\n", idx, thisCell.theLayerPairId); + if (thisCell.theDoubletId<0) atomicAdd(&killedCell,1); + } + if (idx < nHits) { + if (isOuterHitOfCell[idx].full()) // ++tooManyOuterHitOfCell; + printf("OuterHitOfCell overflow %d\n", idx); + } -#ifdef GPU_DEBUG - printf("kernelCircleFitAllHits circle.par(0): %d %f\n", helix_start, circle_fit[helix_start].par(0)); - printf("kernelCircleFitAllHits circle.par(1): %d %f\n", helix_start, circle_fit[helix_start].par(1)); - printf("kernelCircleFitAllHits circle.par(2): %d %f\n", helix_start, circle_fit[helix_start].par(2)); -#endif + __syncthreads(); +// if (threadIdx.x==0) printf("number of killed cells %d\n",killedCell); } -void wrapperCircleFitAllHits( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit) -{ - kernelCircleFitAllHits<<>>( - foundNtuplets, - hits_in_fit, - B, - results, - hits, - hits_cov, - circle_fit, - fast_fit, - line_fit); - cudaCheck(cudaGetLastError()); -} __global__ -void kernelLineFitAllHits( - GPU::SimpleVector * foundNtuplets, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit) -{ - int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); - if (helix_start >= foundNtuplets->size()) { - return; - } - -#ifdef GPU_DEBUG - printf("blockDim.x: %d, blockIdx.x: %d, threadIdx.x: %d, helix_start: %d, cumulative_size: %d\n", - blockDim.x, blockIdx.x, threadIdx.x, helix_start, foundNtuplets->size()); -#endif - - line_fit[helix_start] = Rfit::Line_fit(hits[helix_start], hits_cov[helix_start], circle_fit[helix_start], fast_fit[helix_start], B, true); - - par_uvrtopak(circle_fit[helix_start], B, true); +void +kernel_fishboneCleaner(GPUCACell const * cells, uint32_t const * __restrict__ nCells, + pixelTuplesHeterogeneousProduct::Quality * quality + ) { - // Grab helix_fit from the proper location in the output vector - auto & helix = results[helix_start]; - helix.par << circle_fit[helix_start].par, line_fit[helix_start].par; + constexpr auto bad = pixelTuplesHeterogeneousProduct::bad; - // TODO: pass properly error booleans - - helix.cov = Eigen::MatrixXd::Zero(5, 5); - helix.cov.block(0, 0, 3, 3) = circle_fit[helix_start].cov; - helix.cov.block(3, 3, 2, 2) = line_fit[helix_start].cov; + auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; - helix.q = circle_fit[helix_start].q; - helix.chi2_circle = circle_fit[helix_start].chi2; - helix.chi2_line = line_fit[helix_start].chi2; + if (cellIndex >= (*nCells) ) return; + auto const & thisCell = cells[cellIndex]; + if (thisCell.theDoubletId>=0) return; -#ifdef GPU_DEBUG - printf("kernelLineFitAllHits line.par(0): %d %f\n", helix_start, circle_fit[helix_start].par(0)); - printf("kernelLineFitAllHits line.par(1): %d %f\n", helix_start, line_fit[helix_start].par(1)); -#endif -} + for (auto it : thisCell.theTracks) quality[it] = bad; -void wrapperLineFitAllHits( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit) -{ - kernelLineFitAllHits<<>>( - foundNtuplets, - B, - results, - hits, - hits_cov, - circle_fit, - fast_fit, - line_fit); - cudaCheck(cudaGetLastError()); } __global__ -void kernelCheckOverflows( - GPU::SimpleVector *foundNtuplets, - GPUCACell const * __restrict__ cells, - uint32_t const * __restrict__ nCells, - GPU::VecArray const * __restrict__ isOuterHitOfCell, - uint32_t nHits, - uint32_t maxNumberOfDoublets) -{ - auto idx = threadIdx.x + blockIdx.x * blockDim.x; - #ifdef GPU_DEBUG - if (0==idx) - printf("number of found cells %d\n",*nCells); - #endif - if (idx < (*nCells) ) { - auto &thisCell = cells[idx]; - if (thisCell.theOuterNeighbors.full()) //++tooManyNeighbors[thisCell.theLayerPairId]; - printf("OuterNeighbors overflow %d in %d\n", idx, thisCell.theLayerPairId); - } - if (idx < nHits) { - if (isOuterHitOfCell[idx].full()) // ++tooManyOuterHitOfCell; - printf("OuterHitOfCell overflow %d\n", idx); - } -} +void +kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict__ nCells, + TuplesOnGPU::Container * foundNtuplets, + Rfit::helix_fit const * __restrict__ hfit, + pixelTuplesHeterogeneousProduct::Quality * quality + ) { -void wrapperCheckOverflows( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector *foundNtuplets, - GPUCACell const * cells, - uint32_t const * nCells, - GPU::VecArray const * isOuterHitOfCell, - uint32_t nHits, - uint32_t maxNumberOfDoublets) -{ - kernelCheckOverflows<<>>( - foundNtuplets, - cells, - nCells, - isOuterHitOfCell, - nHits, - maxNumberOfDoublets); - cudaCheck(cudaGetLastError()); + constexpr auto bad = pixelTuplesHeterogeneousProduct::bad; + constexpr auto dup = pixelTuplesHeterogeneousProduct::dup; + // constexpr auto loose = pixelTuplesHeterogeneousProduct::loose; + + auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; + + if (cellIndex >= (*nCells) ) return; + auto const & thisCell = cells[cellIndex]; + if (thisCell.theDoubletId<0) return; + + float mc=1000.f; uint16_t im=60000; uint32_t maxNh=0; + + // find maxNh + for (auto it : thisCell.theTracks) { + if (quality[it] == bad) continue; + auto nh = foundNtuplets->size(it); + maxNh = std::max(nh,maxNh); + } + // find min chi2 + for (auto it : thisCell.theTracks) { + auto nh = foundNtuplets->size(it); + if (nh!=maxNh) continue; + if (quality[it]!= bad && + hfit[it].chi2_line+hfit[it].chi2_circle < mc) { + mc=hfit[it].chi2_line+hfit[it].chi2_circle; + im=it; + } + } + // mark duplicates + for (auto it : thisCell.theTracks) { + if (quality[it]!= bad && it!=im) quality[it] = dup; //no race: simple assignment of the same constant + } } __global__ -void kernelConnect( - GPU::SimpleVector * foundNtuplets, - GPUCACell::Hits const * __restrict__ hhp, - GPUCACell * cells, uint32_t const * __restrict__ nCells, - GPU::VecArray const * __restrict__ isOuterHitOfCell, - float ptmin, - float region_origin_radius, const float thetaCut, - const float phiCut, const float hardPtCut, - unsigned int maxNumberOfDoublets_, unsigned int maxNumberOfHits_) -{ +void +kernel_connect(AtomicPairCounter * apc1, AtomicPairCounter * apc2, // just to zero them, + GPUCACell::Hits const * __restrict__ hhp, + GPUCACell * cells, uint32_t const * __restrict__ nCells, + GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell) { + auto const & hh = *hhp; - constexpr float region_origin_x = 0.; - constexpr float region_origin_y = 0.; + // 87 cm/GeV = 1/(3.8T * 0.3) + // take less than radius given by the hardPtCut and reject everything below + // auto hardCurvCut = 1.f/(hardPtCut * 87.f); + constexpr auto hardCurvCut = 1.f/(0.35f * 87.f); // FIXME VI tune + constexpr auto ptmin = 0.9f; // FIXME original "tune" auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; - if (0==cellIndex) foundNtuplets->reset(); // ready for next kernel + if (0==cellIndex) { (*apc1)=0; (*apc2)=0; }// ready for next kernel if (cellIndex >= (*nCells) ) return; auto const & thisCell = cells[cellIndex]; + if (thisCell.theDoubletId<0) return; auto innerHitId = thisCell.get_inner_hit_id(); auto numberOfPossibleNeighbors = isOuterHitOfCell[innerHitId].size(); auto vi = isOuterHitOfCell[innerHitId].data(); for (auto j = 0; j < numberOfPossibleNeighbors; ++j) { auto otherCell = __ldg(vi+j); - + if (cells[otherCell].theDoubletId<0) continue; if (thisCell.check_alignment(hh, - cells[otherCell], ptmin, region_origin_x, region_origin_y, - region_origin_radius, thetaCut, phiCut, hardPtCut) + cells[otherCell], ptmin, hardCurvCut) ) { cells[otherCell].theOuterNeighbors.push_back(cellIndex); } } } -void wrapperConnect( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - GPUCACell::Hits const * hhp, - GPUCACell * cells, - uint32_t const * nCells, - GPU::VecArray const * isOuterHitOfCell, - float ptmin, - float region_origin_radius, - const float thetaCut, - const float phiCut, - const float hardPtCut, - unsigned int maxNumberOfDoublets_, - unsigned int maxNumberOfHits_) +__global__ +void kernel_find_ntuplets( + GPUCACell * __restrict__ cells, uint32_t const * nCells, + TuplesOnGPU::Container * foundNtuplets, AtomicPairCounter * apc, + unsigned int minHitsPerNtuplet) { - kernelConnect<<>>( - foundNtuplets, - hhp, - cells, - nCells, - isOuterHitOfCell, - ptmin, - region_origin_radius, - thetaCut, - phiCut, - hardPtCut, - maxNumberOfDoublets_, - maxNumberOfHits_); - cudaCheck(cudaGetLastError()); -} -__global__ -void kernelFindNtuplets( - GPUCACell * const __restrict__ cells, - uint32_t const * nCells, - GPU::SimpleVector *foundNtuplets, - unsigned int minHitsPerNtuplet, - unsigned int maxNumberOfDoublets_) -{ auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; - if (cellIndex >= (*nCells)) - return; - auto & thisCell = cells[cellIndex]; - if (thisCell.theLayerPairId != 0 and thisCell.theLayerPairId != 3 and thisCell.theLayerPairId != 8) - return; // inner layer is 0 FIXME - GPU::VecArray stack; + if (cellIndex >= (*nCells) ) return; + auto &thisCell = cells[cellIndex]; + if (thisCell.theLayerPairId!=0 && thisCell.theLayerPairId!=3 && thisCell.theLayerPairId!=8) return; // inner layer is 0 FIXME + GPUCACell::TmpTuple stack; stack.reset(); - thisCell.find_ntuplets(cells, foundNtuplets, stack, minHitsPerNtuplet); + thisCell.find_ntuplets(cells, *foundNtuplets, *apc, stack, minHitsPerNtuplet); assert(stack.size()==0); - // printf("in %d found quadruplets: %d\n", cellIndex, foundNtuplets->size()); + // printf("in %d found quadruplets: %d\n", cellIndex, apc->get()); } -void wrapperFindNtuplets( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPUCACell * const cells, - uint32_t const * nCells, - GPU::SimpleVector *foundNtuplets, - unsigned int minHitsPerNtuplet, - unsigned int maxNumberOfDoublets) -{ - kernelFindNtuplets<<>>( - cells, - nCells, - foundNtuplets, - minHitsPerNtuplet, - maxNumberOfDoublets); - cudaCheck(cudaGetLastError()); + +__global__ +void kernel_VerifyFit(TuplesOnGPU::Container const * __restrict__ tuples, + Rfit::helix_fit const * __restrict__ fit_results, + Quality * __restrict__ quality) { + + auto idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx>= tuples->nbins()) return; + if (tuples->size(idx)==0) { + return; + } + + quality[idx] = pixelTuplesHeterogeneousProduct::bad; + + // only quadruplets + if (tuples->size(idx)<4) { + return; + } + + bool isNaN = false; + for (int i=0; i<5; ++i) { + isNaN |= fit_results[idx].par(i)!=fit_results[idx].par(i); + } + isNaN |= !(fit_results[idx].chi2_line+fit_results[idx].chi2_circle < 100.f); // catch NaN as well + + // impose "region cuts" (NaN safe) + // phi,Tip,pt,cotan(theta)),Zip + bool ok = std::abs(fit_results[idx].par(1)) < 0.1f + && fit_results[idx].par(2) > 0.3f + && std::abs(fit_results[idx].par(4)) < 12.f; + ok &= (!isNaN); + quality[idx] = ok ? pixelTuplesHeterogeneousProduct::loose : pixelTuplesHeterogeneousProduct::bad; } __global__ -void kernelPrintFoundNtuplets(GPU::SimpleVector *foundNtuplets, int maxPrint) -{ +void kernel_print_found_ntuplets(TuplesOnGPU::Container * foundNtuplets, uint32_t maxPrint) { for (int i = 0; i < std::min(maxPrint, foundNtuplets->size()); ++i) { printf("\nquadruplet %d: %d %d %d %d\n", i, - (*foundNtuplets)[i].hitId[0], - (*foundNtuplets)[i].hitId[1], - (*foundNtuplets)[i].hitId[2], - (*foundNtuplets)[i].hitId[3] + (*(*foundNtuplets).begin(i)), + (*(*foundNtuplets).begin(i)+1), + (*(*foundNtuplets).begin(i)+2), + (*(*foundNtuplets).begin(i)+3) ); - } } -void wrapperPrintFoundNtuplets( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector *foundNtuplets, - int maxPrint) +void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... + HitsOnCPU const & hh, + TuplesOnGPU & tuples_d, + cudaStream_t cudaStream) { - kernelPrintFoundNtuplets<<>>( - foundNtuplets, - maxPrint); + auto & gpu_ = tuples_d; + auto maxNumberOfDoublets_ = CAConstants::maxNumberOfDoublets(); + + + auto nhits = hh.nHits; + assert(nhits <= PixelGPUConstants::maxNumberOfHits); + + if (earlyFishbone_) { + auto blockSize = 128; + auto stride = 4; + auto numberOfBlocks = (nhits + blockSize - 1)/blockSize; + numberOfBlocks *=stride; + + fishbone<<>>( + hh.gpu_d, + device_theCells_, device_nCells_, + device_isOuterHitOfCell_, + nhits, stride, false + ); + cudaCheck(cudaGetLastError()); + } + + auto blockSize = 64; + auto numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; + kernel_connect<<>>( + gpu_.apc_d, device_hitToTuple_apc_, // needed only to be reset, ready for next kernel + hh.gpu_d, + device_theCells_, device_nCells_, + device_isOuterHitOfCell_ + ); cudaCheck(cudaGetLastError()); -} -void wrapperDoubletsFromHisto( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPUCACell * cells, - uint32_t * nCells, - siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hits, - GPU::VecArray * isOuterHitOfCell) -{ - gpuPixelDoublets::getDoubletsFromHisto<<>>(cells, nCells, hits, isOuterHitOfCell); + kernel_find_ntuplets<<>>( + device_theCells_, device_nCells_, + gpu_.tuples_d, + gpu_.apc_d, + 4 + ); + cudaCheck(cudaGetLastError()); + + numberOfBlocks = (TuplesOnGPU::Container::totbins() + blockSize - 1)/blockSize; + cudautils::finalizeBulk<<>>(gpu_.apc_d,gpu_.tuples_d); + + numberOfBlocks = (std::max(nhits, maxNumberOfDoublets_) + blockSize - 1)/blockSize; + kernel_checkOverflows<<>>( + gpu_.tuples_d, gpu_.apc_d, + device_theCells_, device_nCells_, + device_isOuterHitOfCell_, nhits + ); + cudaCheck(cudaGetLastError()); + + if (lateFishbone_) { + auto stride=4; + numberOfBlocks = (nhits + blockSize - 1)/blockSize; + numberOfBlocks *=stride; + fishbone<<>>( + hh.gpu_d, + device_theCells_, device_nCells_, + device_isOuterHitOfCell_, + nhits, stride, true + ); + cudaCheck(cudaGetLastError()); + } + + // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(gpu_.tuples_d, 10); + } + + +void CAHitQuadrupletGeneratorKernels::buildDoublets(HitsOnCPU const & hh, cudaStream_t stream) { + auto nhits = hh.nHits; + + int threadsPerBlock = gpuPixelDoublets::getDoubletsFromHistoMaxBlockSize; + int blocks = (3 * nhits + threadsPerBlock - 1) / threadsPerBlock; + gpuPixelDoublets::getDoubletsFromHisto<<>>(device_theCells_, device_nCells_, hh.gpu_d, device_isOuterHitOfCell_); cudaCheck(cudaGetLastError()); } + +void CAHitQuadrupletGeneratorKernels::classifyTuples(HitsOnCPU const & hh, TuplesOnGPU & tuples, cudaStream_t cudaStream) { + auto blockSize = 64; + auto numberOfBlocks = (CAConstants::maxNumberOfQuadruplets() + blockSize - 1)/blockSize; + kernel_VerifyFit<<>>(tuples.tuples_d, tuples.helix_fit_results_d, tuples.quality_d); + + numberOfBlocks = (CAConstants::maxNumberOfDoublets() + blockSize - 1)/blockSize; + kernel_fishboneCleaner<<>>(device_theCells_, device_nCells_,tuples.quality_d); + + numberOfBlocks = (CAConstants::maxNumberOfDoublets() + blockSize - 1)/blockSize; + kernel_fastDuplicateRemover<<>>(device_theCells_, device_nCells_,tuples.tuples_d,tuples.helix_fit_results_d, tuples.quality_d); + +} + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h index e1775bd1e445b..97b5617a54f89 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h @@ -1,93 +1,51 @@ #ifndef RecoPixelVertexing_PixelTriplets_plugins_CAHitQuadrupletGeneratorKernels_h #define RecoPixelVertexing_PixelTriplets_plugins_CAHitQuadrupletGeneratorKernels_h -#include -#include "HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h" -#include "HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h" #include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" -#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" + +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" #include "GPUCACell.h" -void wrapperFastFitAllHits( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hhp, - int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit); - -void wrapperCircleFitAllHits( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit); - -void wrapperLineFitAllHits( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Eigen::Vector4d *fast_fit, - Rfit::line_fit *line_fit); - -void wrapperCheckOverflows( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector *foundNtuplets, - GPUCACell const * cells, - uint32_t const * nCells, - GPU::VecArray const * isOuterHitOfCell, - uint32_t nHits, - uint32_t maxNumberOfDoublets); - -void wrapperConnect( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector * foundNtuplets, - GPUCACell::Hits const * hhp, - GPUCACell * cells, - uint32_t const * nCells, - GPU::VecArray const * isOuterHitOfCell, - float ptmin, - float region_origin_radius, - const float thetaCut, - const float phiCut, - const float hardPtCut, - unsigned int maxNumberOfDoublets_, - unsigned int maxNumberOfHits); - -void wrapperFindNtuplets( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPUCACell * const cells, - uint32_t const * nCells, - GPU::SimpleVector *foundNtuplets, - unsigned int minHitsPerNtuplet, - unsigned int maxNumberOfDoublets); - -void wrapperPrintFoundNtuplets( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPU::SimpleVector *foundNtuplets, - int maxPrint); - -void wrapperDoubletsFromHisto( - int gridSize, int blockSize, cudaStream_t cudaStream, - GPUCACell * cells, - uint32_t * nCells, - siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hits, - GPU::VecArray * isOuterHitOfCell); +class CAHitQuadrupletGeneratorKernels { +public: + + using HitsOnGPU = siPixelRecHitsHeterogeneousProduct::HitsOnGPU; + using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; + + using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; + + using HitToTuple = CAConstants::HitToTuple; + + CAHitQuadrupletGeneratorKernels(bool earlyFishbone, bool lateFishbone) : + earlyFishbone_(earlyFishbone), + lateFishbone_(lateFishbone){} + ~CAHitQuadrupletGeneratorKernels() { deallocateOnGPU();} + + void launchKernels(HitsOnCPU const & hh, TuplesOnGPU & tuples_d, cudaStream_t cudaStream); + + void classifyTuples(HitsOnCPU const & hh, TuplesOnGPU & tuples_d, cudaStream_t cudaStream); + + void buildDoublets(HitsOnCPU const & hh, cudaStream_t stream); + void allocateOnGPU(); + void deallocateOnGPU(); + void cleanup(cudaStream_t cudaStream); + +private: + + // workspace + GPUCACell* device_theCells_ = nullptr; + GPUCACell::OuterHitOfCell* device_isOuterHitOfCell_ = nullptr; + uint32_t* device_nCells_ = nullptr; + + HitToTuple * device_hitToTuple_ = nullptr; + AtomicPairCounter * device_hitToTuple_apc_ = nullptr; + + + const bool earlyFishbone_; + const bool lateFishbone_; + +}; #endif // RecoPixelVertexing_PixelTriplets_plugins_CAHitQuadrupletGeneratorKernels_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 43fcd88fa30de..dbd4eecbaab3c 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -10,19 +10,25 @@ #include "HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h" #include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" +#include "RecoPixelVertexing/PixelTriplets/interface/CircleEq.h" -struct Quadruplet { - using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; - hindex_type hitId[4]; -}; +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" class GPUCACell { public: + static constexpr int maxCellsPerHit = 128; // was 256 + using OuterHitOfCell = GPU::VecArray< unsigned int, maxCellsPerHit>; + + using Hits = siPixelRecHitsHeterogeneousProduct::HitsOnGPU; using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; + using TmpTuple = GPU::VecArray; + + using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; + GPUCACell() = default; #ifdef __CUDACC__ @@ -39,6 +45,7 @@ class GPUCACell { theInnerZ = __ldg(hh.zg_d+innerHitId); theInnerR = __ldg(hh.rg_d+innerHitId); theOuterNeighbors.reset(); + theTracks.reset(); } __device__ __forceinline__ float get_inner_x(Hits const & hh) const { return __ldg(hh.xg_d+theInnerHitId); } @@ -50,6 +57,9 @@ class GPUCACell { __device__ __forceinline__ float get_inner_r(Hits const & hh) const { return theInnerR; } // { return __ldg(hh.rg_d+theInnerHitId); } // { return theInnerR; } __device__ __forceinline__ float get_outer_r(Hits const & hh) const { return __ldg(hh.rg_d+theOuterHitId); } + __device__ __forceinline__ float get_inner_detId(Hits const & hh) const { return __ldg(hh.detInd_d+theInnerHitId); } + __device__ __forceinline__ float get_outer_detId(Hits const & hh) const { return __ldg(hh.detInd_d+theOuterHitId); } + constexpr unsigned int get_inner_hit_id() const { return theInnerHitId; } @@ -69,10 +79,9 @@ class GPUCACell { __device__ bool check_alignment(Hits const & hh, - GPUCACell const & otherCell, const float ptmin, - const float region_origin_x, const float region_origin_y, - const float region_origin_radius, const float thetaCut, - const float phiCut, const float hardPtCut) const + GPUCACell const & otherCell, + const float ptmin, + const float hardCurvCut) const { auto ri = get_inner_r(hh); auto zi = get_inner_z(hh); @@ -82,11 +91,9 @@ class GPUCACell { auto r1 = otherCell.get_inner_r(hh); auto z1 = otherCell.get_inner_z(hh); - bool aligned = areAlignedRZ(r1, z1, ri, zi, ro, zo, ptmin, thetaCut); - return (aligned && - haveSimilarCurvature(hh, otherCell, ptmin, region_origin_x, - region_origin_y, region_origin_radius, phiCut, - hardPtCut)); + bool aligned = areAlignedRZ(r1, z1, ri, zi, ro, zo, ptmin, 0.003f); // 2.f*thetaCut); // FIXME tune cuts + return (aligned && dcaCut(hh, otherCell, otherCell.get_inner_detId(hh)<96 ? 0.15f : 0.25f, hardCurvCut)); // FIXME tune cuts + // region_origin_radius_plus_tolerance, hardCurvCut)); } __device__ __forceinline__ @@ -106,13 +113,12 @@ class GPUCACell { return tan_12_13_half_mul_distance_13_squared * pMin <= thetaCut * distance_13_squared * radius_diff; } - __device__ + + __device__ bool - haveSimilarCurvature(Hits const & hh, GPUCACell const & otherCell, - const float ptmin, const float region_origin_x, - const float region_origin_y, - const float region_origin_radius, const float phiCut, - const float hardPtCut) const { + dcaCut(Hits const & hh, GPUCACell const & otherCell, + const float region_origin_radius_plus_tolerance, + const float maxCurv) const { auto x1 = otherCell.get_inner_x(hh); auto y1 = otherCell.get_inner_y(hh); @@ -123,69 +129,12 @@ class GPUCACell { auto x3 = get_outer_x(hh); auto y3 = get_outer_y(hh); - float distance_13_squared = (x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3); - float tan_12_13_half_mul_distance_13_squared = - fabs(y1 * (x2 - x3) + y2 * (x3 - x1) + y3 * (x1 - x2)); - // high pt : just straight - if (tan_12_13_half_mul_distance_13_squared * ptmin <= - 1.0e-4f * distance_13_squared) { - - float distance_3_beamspot_squared = - (x3 - region_origin_x) * (x3 - region_origin_x) + - (y3 - region_origin_y) * (y3 - region_origin_y); - - float dot_bs3_13 = ((x1 - x3) * (region_origin_x - x3) + - (y1 - y3) * (region_origin_y - y3)); - float proj_bs3_on_13_squared = - dot_bs3_13 * dot_bs3_13 / distance_13_squared; - - float distance_13_beamspot_squared = - distance_3_beamspot_squared - proj_bs3_on_13_squared; - - return distance_13_beamspot_squared < - (region_origin_radius + phiCut) * (region_origin_radius + phiCut); - } - - // 87 cm/GeV = 1/(3.8T * 0.3) + CircleEq eq(x1,y1,x2,y2,x3,y3); - // take less than radius given by the hardPtCut and reject everything below - float minRadius = hardPtCut * 87.f; // FIXME move out and use real MagField + if (eq.curvature() > maxCurv) return false; - auto det = (x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2); + return std::abs(eq.dca0()) < region_origin_radius_plus_tolerance*std::abs(eq.curvature()); - auto offset = x2 * x2 + y2 * y2; - - auto bc = (x1 * x1 + y1 * y1 - offset) * 0.5f; - - auto cd = (offset - x3 * x3 - y3 * y3) * 0.5f; - - auto idet = 1.f / det; - - auto x_center = (bc * (y2 - y3) - cd * (y1 - y2)) * idet; - auto y_center = (cd * (x1 - x2) - bc * (x2 - x3)) * idet; - - auto radius = std::sqrt((x2 - x_center) * (x2 - x_center) + - (y2 - y_center) * (y2 - y_center)); - - if (radius < minRadius) - return false; // hard cut on pt - - auto centers_distance_squared = - (x_center - region_origin_x) * (x_center - region_origin_x) + - (y_center - region_origin_y) * (y_center - region_origin_y); - auto region_origin_radius_plus_tolerance = region_origin_radius + phiCut; - auto minimumOfIntersectionRange = - (radius - region_origin_radius_plus_tolerance) * - (radius - region_origin_radius_plus_tolerance); - - if (centers_distance_squared >= minimumOfIntersectionRange) { - auto maximumOfIntersectionRange = - (radius + region_origin_radius_plus_tolerance) * - (radius + region_origin_radius_plus_tolerance); - return centers_distance_squared <= maximumOfIntersectionRange; - } - - return false; } // trying to free the track building process from hardcoded layers, leaving @@ -195,9 +144,10 @@ class GPUCACell { __device__ inline void find_ntuplets( - GPUCACell const * __restrict__ cells, - GPU::SimpleVector *foundNtuplets, - GPU::VecArray &tmpNtuplet, + GPUCACell * __restrict__ cells, + TuplesOnGPU::Container & foundNtuplets, + AtomicPairCounter & apc, + TmpTuple & tmpNtuplet, const unsigned int minHitsPerNtuplet) const { // the building process for a track ends if: @@ -206,34 +156,35 @@ class GPUCACell { // the ntuplets is then saved if the number of hits it contains is greater // than a threshold - tmpNtuplet.push_back_unsafe(theInnerHitId); - assert(tmpNtuplet.size()<=3); + tmpNtuplet.push_back_unsafe(theDoubletId); + assert(tmpNtuplet.size()<=4); - if ((unsigned int)(tmpNtuplet.size()) >= minHitsPerNtuplet-1) { - Quadruplet tmpQuadruplet; - for (unsigned int i = 0; i < minHitsPerNtuplet-1; ++i) { - tmpQuadruplet.hitId[i] = tmpNtuplet[i]; - } - tmpQuadruplet.hitId[minHitsPerNtuplet-1] = theOuterHitId; - foundNtuplets->push_back(tmpQuadruplet); - } - else { + if(theOuterNeighbors.size()>0) { // continue for (int j = 0; j < theOuterNeighbors.size(); ++j) { auto otherCell = theOuterNeighbors[j]; - cells[otherCell].find_ntuplets(cells, foundNtuplets, tmpNtuplet, + cells[otherCell].find_ntuplets(cells, foundNtuplets, apc, tmpNtuplet, minHitsPerNtuplet); } + } else { // if long enough save... + if ((unsigned int)(tmpNtuplet.size()) >= minHitsPerNtuplet-1) { + hindex_type hits[6]; auto nh=0U; + for (auto c : tmpNtuplet) hits[nh++] = cells[c].theInnerHitId; + hits[nh] = theOuterHitId; + uint16_t it = foundNtuplets.bulkFill(apc,hits,tmpNtuplet.size()+1); + for (auto c : tmpNtuplet) cells[c].theTracks.push_back(it); + } } tmpNtuplet.pop_back(); - assert(tmpNtuplet.size() < 3); + assert(tmpNtuplet.size() < 4); } #endif // __CUDACC__ - GPU::VecArray< unsigned int, 40> theOuterNeighbors; + GPU::VecArray< uint32_t, 36> theOuterNeighbors; + GPU::VecArray< uint16_t, 42> theTracks; - int theDoubletId; - int theLayerPairId; + int32_t theDoubletId; + int32_t theLayerPairId; private: float theInnerZ; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUHitsAndDoublets.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUHitsAndDoublets.h deleted file mode 100644 index 4f24c3331f460..0000000000000 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUHitsAndDoublets.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// Author: Felice Pantaleo, CERN -// - -#ifndef RecoPixelVertexing_PixelTriplets_GPUHitsAndDoublets_h -#define RecoPixelVertexing_PixelTriplets_GPUHitsAndDoublets_h - -#include - -struct GPULayerHits -{ - unsigned int layerId; - size_t size; - float * x; - float * y; - float * z; -}; - -struct HostLayerHits -{ - unsigned int layerId; - size_t size; - std::vector x; - std::vector y; - std::vector z; -}; - -struct GPULayerDoublets -{ - size_t size; - unsigned int innerLayerId; - unsigned int outerLayerId; - unsigned int * indices; -}; - -struct HostLayerDoublets -{ - size_t size; - unsigned int innerLayerId; - unsigned int outerLayerId; - std::vector indices; -}; - -#endif diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc new file mode 100644 index 0000000000000..0fdca092e5e6d --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc @@ -0,0 +1,38 @@ +#include "RiemannFitOnGPU.h" + +void RiemannFitOnGPU::allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit::helix_fit * helix_fit_results) { + + tuples_d = tuples; + helix_fit_results_d = helix_fit_results; + + assert(tuples_d); assert(helix_fit_results_d); + + cudaCheck(cudaMalloc(&hitsGPU_, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd(3, 4)))); + cudaCheck(cudaMemset(hitsGPU_, 0x00, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd(3, 4)))); + + cudaCheck(cudaMalloc(&hits_covGPU_, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3Nd(12, 12)))); + cudaCheck(cudaMemset(hits_covGPU_, 0x00, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3Nd(12, 12)))); + + cudaCheck(cudaMalloc(&fast_fit_resultsGPU_, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d))); + cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d))); + + cudaCheck(cudaMalloc(&circle_fit_resultsGPU_, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::circle_fit))); + cudaCheck(cudaMemset(circle_fit_resultsGPU_, 0x00, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::circle_fit))); + + cudaCheck(cudaMalloc(&line_fit_resultsGPU_, maxNumberOfConcurrentFits_ * sizeof(Rfit::line_fit))); + cudaCheck(cudaMemset(line_fit_resultsGPU_, 0x00, maxNumberOfConcurrentFits_ * sizeof(Rfit::line_fit))); + +} + +void RiemannFitOnGPU::deallocateOnGPU() { + + cudaFree(hitsGPU_); + cudaFree(hits_covGPU_); + cudaFree(fast_fit_resultsGPU_); + cudaFree(circle_fit_resultsGPU_); + cudaFree(line_fit_resultsGPU_); + +} + + + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu new file mode 100644 index 0000000000000..d13c428179c94 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -0,0 +1,207 @@ +// +// Author: Felice Pantaleo, CERN +// + +#include "RiemannFitOnGPU.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" + +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" +#include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" + + +using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; + +using HitsOnGPU = siPixelRecHitsHeterogeneousProduct::HitsOnGPU; +using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; + +using namespace Eigen; + +__global__ +void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtuplets, + HitsOnGPU const * __restrict__ hhp, + int hits_in_fit, + Rfit::Matrix3xNd *hits, + Rfit::Matrix3Nd *hits_cov, + Rfit::Vector4d *fast_fit, + uint32_t offset) +{ + + assert(fast_fit); assert(foundNtuplets); + + auto local_start = (blockIdx.x * blockDim.x + threadIdx.x); + auto helix_start = local_start + offset; + + if (helix_start>=foundNtuplets->nbins()) return; + if (foundNtuplets->size(helix_start)begin(helix_start); + for (unsigned int i = 0; i < hits_in_fit; ++i) { + auto hit = hitId[i]; + // printf("Hit global: %f,%f,%f\n", hhp->xg_d[hit],hhp->yg_d[hit],hhp->zg_d[hit]); + float ge[6]; + hhp->cpeParams->detParams(hhp->detInd_d[hit]).frame.toGlobal(hhp->xerr_d[hit], 0, hhp->yerr_d[hit], ge); + // printf("Error: %d: %f,%f,%f,%f,%f,%f\n",hhp->detInd_d[hit],ge[0],ge[1],ge[2],ge[3],ge[4],ge[5]); + + hits[local_start].col(i) << hhp->xg_d[hit], hhp->yg_d[hit], hhp->zg_d[hit]; + + // Index numerology: + // i: index of the hits/point (0,..,3) + // j: index of space component (x,y,z) + // l: index of space components (x,y,z) + // ge is always in sync with the index i and is formatted as: + // ge[] ==> [xx, xy, yy, xz, yz, zz] + // in (j,l) notation, we have: + // ge[] ==> [(0,0), (0,1), (1,1), (0,2), (1,2), (2,2)] + // so the index ge_idx corresponds to the matrix elements: + // | 0 1 3 | + // | 1 2 4 | + // | 3 4 5 | + auto ge_idx = 0; auto j=0; auto l=0; + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; + ge_idx = 2; j=1; l=1; + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; + ge_idx = 5; j=2; l=2; + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; + ge_idx = 1; j=1; l=0; + hits_cov[local_start](i + l * hits_in_fit, i + j * hits_in_fit) = + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; + ge_idx = 3; j=2; l=0; + hits_cov[local_start](i + l * hits_in_fit, i + j * hits_in_fit) = + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; + ge_idx = 4; j=2; l=1; + hits_cov[local_start](i + l * hits_in_fit, i + j * hits_in_fit) = + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; + + } + fast_fit[local_start] = Rfit::Fast_fit(hits[local_start]); + + // no NaN here.... + assert(fast_fit[local_start](0)==fast_fit[local_start](0)); + assert(fast_fit[local_start](1)==fast_fit[local_start](1)); + assert(fast_fit[local_start](2)==fast_fit[local_start](2)); + assert(fast_fit[local_start](3)==fast_fit[local_start](3)); + +} + +__global__ +void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtuplets, + int hits_in_fit, + double B, + Rfit::Matrix3xNd const * __restrict__ hits, + Rfit::Matrix3Nd const * __restrict__ hits_cov, + Rfit::circle_fit *circle_fit, + Rfit::Vector4d const * __restrict__ fast_fit, + uint32_t offset) +{ + assert(circle_fit); + + auto local_start = (blockIdx.x * blockDim.x + threadIdx.x); + auto helix_start = local_start + offset; + + if (helix_start>=foundNtuplets->nbins()) return; + if (foundNtuplets->size(helix_start)=foundNtuplets->nbins()) return; + if (foundNtuplets->size(helix_start)>>( + tuples_d, hh.gpu_d, 4, + hitsGPU_, hits_covGPU_, fast_fit_resultsGPU_,offset); + cudaCheck(cudaGetLastError()); + + kernelCircleFitAllHits<<>>( + tuples_d, 4, bField_, + hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, offset); + cudaCheck(cudaGetLastError()); + + + kernelLineFitAllHits<<>>( + tuples_d, 4, bField_, helix_fit_results_d, + hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, + line_fit_resultsGPU_, offset); + cudaCheck(cudaGetLastError()); + } +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h new file mode 100644 index 0000000000000..9352a8127b19b --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h @@ -0,0 +1,49 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_plugins_RiemannFitOnGPU_h +#define RecoPixelVertexing_PixelTrackFitting_plugins_RiemannFitOnGPU_h + +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" + +namespace siPixelRecHitsHeterogeneousProduct { + struct HitsOnCPU; +} + +class RiemannFitOnGPU { +public: + + using HitsOnGPU = siPixelRecHitsHeterogeneousProduct::HitsOnGPU; + using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; + + using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; + + RiemannFitOnGPU() = default; + ~RiemannFitOnGPU() { deallocateOnGPU();} + + void setBField(double bField) { bField_ = bField;} + void launchKernels(HitsOnCPU const & hh, uint32_t nhits, uint32_t maxNumberOfTuples, cudaStream_t cudaStream); + + void allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit::helix_fit * helix_fit_results); + void deallocateOnGPU(); + + +private: + + static constexpr uint32_t maxNumberOfConcurrentFits_ = 2000; + + // fowarded + TuplesOnGPU::Container const * tuples_d = nullptr; + double bField_; + Rfit::helix_fit * helix_fit_results_d = nullptr; + + + + // Riemann Fit internals + Rfit::Matrix3xNd *hitsGPU_ = nullptr; + Rfit::Matrix3Nd *hits_covGPU_ = nullptr; + Eigen::Vector4d *fast_fit_resultsGPU_ = nullptr; + Rfit::circle_fit *circle_fit_resultsGPU_ = nullptr; + Rfit::line_fit *line_fit_resultsGPU_ = nullptr; + +}; + +#endif diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h new file mode 100644 index 0000000000000..41e99fa3754fd --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -0,0 +1,92 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_plugins_gpuFishbone_h +#define RecoLocalTracker_SiPixelRecHits_plugins_gpuFishbone_h + +#include +#include +#include +#include +#include +#include + +#include "DataFormats/Math/interface/approx_atan2.h" +#include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" +#include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" + +#include "GPUCACell.h" +#include "HeterogeneousCore/CUDAUtilities/interface/GPUVecArray.h" + +namespace gpuPixelDoublets { + +// __device__ +// __forceinline__ + __global__ + void fishbone( + GPUCACell::Hits const * __restrict__ hhp, + GPUCACell * cells, uint32_t const * __restrict__ nCells, + GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, + uint32_t nHits, + uint32_t stride, bool checkTrack) { + + constexpr auto maxCellsPerHit = GPUCACell::maxCellsPerHit; + + + auto const & hh = *hhp; + uint8_t const * __restrict__ layerp = hh.phase1TopologyLayer_d; + auto layer = [&](uint16_t id) { return __ldg(layerp+id/phase1PixelTopology::maxModuleStride);}; + + auto ldx = threadIdx.x + blockIdx.x * blockDim.x; + auto idx = ldx/stride; + auto first = ldx - idx*stride; + assert(first=nHits) return; + auto const & vc = isOuterHitOfCell[idx]; + auto s = vc.size(); + if (s<2) return; + // if alligned kill one of the two. + auto const & c0 = cells[vc[0]]; + auto xo = c0.get_outer_x(hh); + auto yo = c0.get_outer_y(hh); + auto zo = c0.get_outer_z(hh); + float x[maxCellsPerHit], y[maxCellsPerHit],z[maxCellsPerHit], n[maxCellsPerHit]; + uint16_t d[maxCellsPerHit]; // uint8_t l[maxCellsPerHit]; + uint32_t cc[maxCellsPerHit]; + auto sg=0; + for (uint32_t ic=0; ic= 0.99999f*n[ic]*n[jc]) { + // alligned: kill farthest (prefer consecutive layers) + if (n[ic]>n[jc]) { + ci.theDoubletId=-1; + break; + } else { + cj.theDoubletId=-1; + } + } + } //cj + } // ci + } + +} + +#endif diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h index d4b44f64573c6..02a175fcc2903 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h @@ -13,10 +13,11 @@ #include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" #include "GPUCACell.h" +#include "CAConstants.h" namespace gpuPixelDoublets { - constexpr uint32_t MaxNumOfDoublets = 1024*1024*256; + constexpr uint32_t MaxNumOfDoublets = CAConstants::maxNumberOfDoublets(); // not really relevant template __device__ @@ -29,7 +30,7 @@ namespace gpuPixelDoublets { Hist const & __restrict__ hist, uint32_t const * __restrict__ offsets, siPixelRecHitsHeterogeneousProduct::HitsOnGPU const & __restrict__ hh, - GPU::VecArray< unsigned int, 256> * isOuterHitOfCell, + GPUCACell::OuterHitOfCell * isOuterHitOfCell, int16_t const * __restrict__ phicuts, float const * __restrict__ minz, float const * __restrict__ maxz, @@ -122,7 +123,8 @@ namespace gpuPixelDoublets { if (std::min(std::abs(int16_t(iphi[oi]-mep)), std::abs(int16_t(mep-iphi[oi]))) > iphicut) continue; if (z0cutoff(oi) || ptcut(oi)) continue; - auto ind = atomicInc(nCells, MaxNumOfDoublets); + auto ind = atomicAdd(nCells, 1); + if (ind>=MaxNumOfDoublets) {atomicSub(nCells, 1); break; } // move to SimpleVector?? // int layerPairId, int doubletId, int innerHitId, int outerHitId) cells[ind].init(hh, pairLayerId, ind, i, oi); isOuterHitOfCell[oi].push_back(ind); @@ -130,9 +132,10 @@ namespace gpuPixelDoublets { ++tot; } } +#ifdef GPU_DEBUG if (tooMany > 0) - printf("OuterHitOfCell full for %d in layer %d/%d, %d:%d %d,%d\n", i, inner, outer, kl, kh, nmin, tot); - + printf("OuterHitOfCell full for %d in layer %d/%d, %d,%d %d\n", i, inner, outer, nmin, tot, tooMany); +#endif } // loop in block... } @@ -144,7 +147,7 @@ namespace gpuPixelDoublets { void getDoubletsFromHisto(GPUCACell * cells, uint32_t * nCells, siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * __restrict__ hhp, - GPU::VecArray * isOuterHitOfCell) + GPUCACell::OuterHitOfCell * isOuterHitOfCell) { constexpr int nPairs = 13; constexpr const uint8_t layerPairs[2*nPairs] = { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h b/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h new file mode 100644 index 0000000000000..a1c4a26f2fff6 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h @@ -0,0 +1,65 @@ +#ifndef RecoPixelVertexingPixelTripletsPixelTuplesHeterogeneousProduct_H +#define RecoPixelVertexingPixelTripletsPixelTuplesHeterogeneousProduct_H + + +#include +#include + +#include "HeterogeneousCore/Product/interface/HeterogeneousProduct.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" + +#include "HeterogeneousCore/CUDAUtilities/interface/CUDAHostAllocator.h" + +#include "RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h" + +namespace siPixelRecHitsHeterogeneousProduct { + struct HitsOnGPU; +} + +namespace pixelTuplesHeterogeneousProduct { + + enum Quality: uint8_t { bad, dup, loose, strict, tight, highPurity }; + + using CPUProduct = int; // dummy + + struct TuplesOnGPU { + using Container = CAConstants::TuplesContainer; + + Container * tuples_d; + AtomicPairCounter * apc_d; + + Rfit::helix_fit * helix_fit_results_d = nullptr; + Quality * quality_d = nullptr; + + TuplesOnGPU const * me_d = nullptr; + + }; + + struct TuplesOnCPU { + + std::vector indToEdm; // index of tuple in reco tracks.... + + + using Container = TuplesOnGPU::Container; + + siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hitsOnGPU_d = nullptr; // forwarding + + Container const * tuples = nullptr; + + Rfit::helix_fit const * helix_fit_results = nullptr; + Quality * quality = nullptr; + + TuplesOnGPU const * gpu_d = nullptr; + uint32_t nTuples; + }; + + using GPUProduct = TuplesOnCPU; // FIXME fill cpu vectors on demand + + using HeterogeneousPixelTuples = HeterogeneousProductImpl, + heterogeneous::GPUCudaProduct >; +} + + + +#endif + diff --git a/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py index 8497eba9f759f..c72c07ae5a721 100644 --- a/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py +++ b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py @@ -2,7 +2,3 @@ from RecoPixelVertexing.PixelTriplets.caHitQuadrupletDefaultEDProducer_cfi import caHitQuadrupletDefaultEDProducer as _caHitQuadrupletDefaultEDProducer caHitQuadrupletEDProducer = _caHitQuadrupletDefaultEDProducer.clone() - -from Configuration.ProcessModifiers.gpu_cff import gpu -from RecoPixelVertexing.PixelTriplets.caHitQuadrupletHeterogeneousEDProducer_cfi import caHitQuadrupletHeterogeneousEDProducer as _caHitQuadrupletHeterogeneousEDProducer -gpu.toReplaceWith(caHitQuadrupletEDProducer, _caHitQuadrupletHeterogeneousEDProducer) diff --git a/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml b/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml index 1de3629887ec9..9f5d10ad020e9 100644 --- a/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml @@ -21,3 +21,4 @@ + diff --git a/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp new file mode 100644 index 0000000000000..cbbcea96d1ee8 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp @@ -0,0 +1,99 @@ +#include "RecoPixelVertexing/PixelTriplets/interface/CircleEq.h" +#include + + +struct OriCircle { + + using T = float; + + float radius=0; + float x_center=0; + float y_center=0; + + + constexpr OriCircle(T x1, T y1, + T x2, T y2, + T x3, T y3) { + compute(x1,y1,x2,y2,x3,y3); + } + + // dca to origin + constexpr T dca0() const { + return std::sqrt(x_center*x_center + y_center*y_center) - radius; + } + + // dca to given point + constexpr T dca(T x, T y) const { + x-=x_center; + y-=y_center; + return std::sqrt(x*x+y*y)-radius; + } + + + constexpr void compute(T x1, T y1, + T x2, T y2, + T x3, T y3) { + + auto det = (x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2); + + auto offset = x2 * x2 + y2 * y2; + + auto bc = (x1 * x1 + y1 * y1 - offset) * 0.5f; + + auto cd = (offset - x3 * x3 - y3 * y3) * 0.5f; + + auto idet = 1.f / det; + + x_center = (bc * (y2 - y3) - cd * (y1 - y2)) * idet; + y_center = (cd * (x1 - x2) - bc * (x2 - x3)) * idet; + + radius = std::sqrt((x2 - x_center) * (x2 - x_center) + + (y2 - y_center) * (y2 - y_center)); + + } +}; + + +#include + +template +bool equal(T a, T b) { + // return float(a-b)==0; + return std::abs(float(a-b)) < std::abs(0.01f*a); +} + + + +int main() { + + float r1=4, r2=8, r3=15; + for(float phi=-3; phi<3.1; phi+=0.5) { + float x1=r1*cos(phi); + float x2=r2*cos(phi); + float y1=r1*sin(phi); + float y2=r2*sin(phi); + for(float phi3=phi-0.31; phi3 eq(x1,y1,x2,y2,x3,y3); + // std::cout << "r " << ori.radius <<' '<< eq.radius() << std::endl; + assert( equal(ori.radius, std::abs(eq.radius())) ); + auto c = eq.center(); + auto dir = eq.cosdir(); + assert (equal(1.f,dir.first*dir.first+dir.second*dir.second)); + assert( equal(ori.x_center,c.first) ); + assert( equal(ori.y_center,c.second) ); + // std::cout << "dca " << ori.dca0() <<' '<< eq.radius()*eq.dca0() << std::endl; + assert( equal( std::abs(ori.dca0()), std::abs(eq.radius()*eq.dca0())) ); + // std::cout << "dca " << ori.dca(1.,1.) <<' '<< eq.radius()*eq.dca(1.,1.) << std::endl; + assert( equal( std::abs(ori.dca(1.,1.)), std::abs(eq.radius()*eq.dca(1.,1.))) ); + + } + } + + + + return 0; +} diff --git a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb index ee8295f85a00a..12027fe01cdd8 100644 --- a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb +++ b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb @@ -133,7 +133,53 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "def dca(h, first, curv=False):\n", + " \n", + " x1 = h['r1']*np.cos(h['phi1']) if first else h['r2']*np.cos(h['phi2'])\n", + " y1 = h['r1']*np.sin(h['phi1']) if first else h['r2']*np.sin(h['phi2'])\n", + " x2 = h['r2']*np.cos(h['phi2']) if first else h['r3']*np.cos(h['phi3'])\n", + " y2 = h['r2']*np.sin(h['phi2']) if first else h['r3']*np.sin(h['phi3'])\n", + " x3 = h['r3']*np.cos(h['phi3']) if first else h['r4']*np.cos(h['phi4'])\n", + " y3 = h['r3']*np.sin(h['phi3']) if first else h['r4']*np.sin(h['phi4'])\n", + " \n", + " \n", + " noflip = abs(x3-x1) < abs(y3-y1)\n", + " x1p = np.where(noflip, x1-x2, y1-y2)\n", + " y1p = np.where(noflip, y1-y2, x1-x2)\n", + " d12 = x1p*x1p + y1p*y1p\n", + " x3p = np.where(noflip, x3-x2, y3-y2)\n", + " y3p = np.where(noflip, y3-y2, x3-x2)\n", + " d32 = x3p*x3p + y3p*y3p\n", + " num = x1p*y3p-y1p*x3p # num also gives correct sign for CT\n", + " det = d12*y3p-d32*y1p\n", + "\n", + " st2 = d12*x3p-d32*x1p\n", + " seq = det*det +st2*st2\n", + " al2 = 1./np.sqrt(seq)\n", + " be2 = -st2*al2\n", + " ct = 2.*num*al2\n", + " al2 *=det\n", + " m_xp = x2\n", + " m_yp = y2\n", + " m_c = np.where(noflip, ct, -ct)\n", + " m_alpha = np.where(noflip, al2, -be2)\n", + " m_beta = np.where(noflip, be2, -al2)\n", + "\n", + " if curv : return m_c\n", + " \n", + " x = m_c*m_xp + m_alpha\n", + " y = m_c*m_yp + m_beta\n", + " return (np.sqrt(x*x+y*y) - 1.)/m_c\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -170,12 +216,19 @@ " ro = np.maximum(a,b)\n", " ri = np.minimum(a,b)\n", " dr = ro-ri\n", - " return dr/np.sqrt(4./(c*c) -ri*ro);\n" + " return dr/np.sqrt(4./(c*c) -ri*ro);\n", + "\n", + "def zAtR(h,r) :\n", + " zi = h['z1']\n", + " zo = h['z3']\n", + " ri = h['r1']\n", + " ro = h['r3']\n", + " return zi + (r-ri)*(zo-zi)/(ro-ri)\n" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -207,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -224,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -241,7 +294,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -258,7 +311,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -278,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -336,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -345,7 +398,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -613,7 +666,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -714,7 +767,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ @@ -723,27 +776,268 @@ " 'r'+n : hh['rg'],\n", " 'phi'+n : hh['phi'],\n", " 'pt'+n : hh['pt'],\n", + " 'det'+n : hh['det'],\n", + " 'trackID' : hh['trackID']\n", + " })\n", + "\n", + "def buildXYZ(hh,n) :\n", + " return pd.DataFrame({ 'z'+n : hh['zg'],\n", + " 'x'+n : hh['xg'],\n", + " 'y'+n : hh['yg'],\n", + " 'pt'+n : hh['pt'],\n", + " 'det'+n : hh['det'],\n", + " 'phi'+n : hh['phi'],\n", " 'trackID' : hh['trackID']\n", " })\n" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ - "def fishBone(q,t,p,c) :\n", - " return pd.DataFrame({ 'th' : t,\n", - " 'pz' : p,\n", - " 'curv' : c,\n", - " 'trackID' : q['trackID']\n", - " })\n" + "def fishBone(hi,hj) :\n", + " mpt=600\n", + "# maxc = 1000./(mpt*87.)\n", + " fb = pd.merge(pd.merge(buildXYZ(hi,'0'),buildXYZ(hj,'1'),on='trackID'),buildXYZ(hj,'2'),on='trackID')\n", + "# pc = phicut(quadc['r1'],quadc['r2'],maxc)\n", + "# d1 = (quadc['phi2']-quadc['phi1'])/pc\n", + " cut = np.logical_and(abs(fb['phi0']-fb['phi1'])<0.05,abs(fb['phi0']-fb['phi2'])<0.05)\n", + " cut = np.logical_and(cut,fb['pt0']>mpt)\n", + " return fb[np.logical_and(cut,fb['det1'].995], bins=100,log=True)\n", + " plt.show()\n", + " plt.hist(d[d>.9999], bins=100,log=True)\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADmhJREFUeJzt3X+sZOVdx/H3t2yK8Qe3wG6ThoUuuIhdTUvrdWs02ooaL+CWFomwNUratZutQaOJiTT1LxLiNiZWiSRmbXFLTUBstemGJZRQyWqEwlIFFjfQZduGhSa7iF6NiSL16x9zkOF272V+nTkz3/t+JZOdeebM3Oc7c/dzn3nOc85EZiJJqusNXXdAktQug16SijPoJak4g16SijPoJak4g16SijPoJak4g16SijPoJam4DV13AGDjxo25ZcuWrrshSXPl0UcffSEzN73edjMR9Fu2bOHw4cNdd0OS5kpEfHOQ7TqduomIHRGxb3l5uctuSFJpnQZ9Zh7IzN0LCwtddkOSSnNnrCQVZ9BLUnEGvSQV585YSSrOnbGSVJxTN5JU3EwcMCVJ68WWG+9+ze1v7L2y9Z/piF6SijPoJak4V91IUnGuupGk4py6kaTiDHpJKs6gl6TiDHpJKs6gl6TiDHpJKs6gl6TiPGBKkorzgClJKs6pG0kqzqCXpOIMekkqzqCXpOIMekkqzqCXpOIMekkqzqCXpOIMekkqzlMgSFJxngJBkopz6kaSijPoJak4g16SijPoJak4g16SijPoJak4g16SijPoJak4g16SijPoJak4g16SijPoJak4g16SijPoJam4iQd9RLwtIv40Ij4XER+d9PNLkoYzUNBHxG0RcTIijqxoX4qIpyLiWETcCJCZRzNzD/BLwE9MvsuSpGEMOqLfDyz1N0TEGcCtwOXANmBnRGxr7nsfcDdwcGI9lSSNZKCgz8xDwIsrmrcDxzLzeGa+BNwJXNVs/8XMvBz45dWeMyJ2R8ThiDh86tSp0XovSXpdG8Z47HnAs323TwDvjoj3AlcDZ7LGiD4z9wH7ABYXF3OMfkiS1jBO0J9WZj4APDDp55UkjWacVTfPAef33d7ctA0sInZExL7l5eUxuiFJWss4Qf8IcHFEXBgRbwSuA744zBNk5oHM3L2wsDBGNyRJaxl0eeUdwIPAJRFxIiJ2ZebLwA3AvcBR4K7MfLK9rkqSRjHQHH1m7lyl/SAuoZSkmdbpKRCco5ek9nUa9M7RS1L7PKmZJBXn1I0kFefUjSQV59SNJBVn0EtScQa9JBXnzlhJKs6dsZJUnFM3klScQS9JxRn0klScO2MlqTh3xkpScU7dSFJxBr0kFWfQS1JxBr0kFeeqG0kqzlU3klScUzeSVJxBL0nFGfSSVJxBL0nFGfSSVJxBL0nFuY5ekopzHb0kFefUjSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnGe60aSivNcN5JUnFM3klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxW2Y9BNGxPuBK4GzgE9n5pcm/TMkSYMbaEQfEbdFxMmIOLKifSkinoqIYxFxI0BmfiEzPwLsAa6dfJclScMYdOpmP7DU3xARZwC3ApcD24CdEbGtb5Pfa+6XJHVooKDPzEPAiyuatwPHMvN4Zr4E3AlcFT2fAO7JzK+u9pwRsTsiDkfE4VOnTo3af0nS6xhnZ+x5wLN9t080bb8B/CxwTUTsWe3BmbkvMxczc3HTpk1jdEOStJaJ74zNzFuAWyb9vJKk0Ywzon8OOL/v9uambWARsSMi9i0vL4/RDUnSWsYZ0T8CXBwRF9IL+OuADw7zBJl5ADiwuLj4kTH6IUkzbcuNd3f68wddXnkH8CBwSUSciIhdmfkycANwL3AUuCszn2yvq5KkUQw0os/Mnau0HwQOTrRHklRA16P4fp2eAsE5eklqX6dBn5kHMnP3wsJCl92QpNI8qZkkFefUjSQV59SNJBXn1I0kFWfQS1JxBr0kFefOWEkqbuJnrxyG57qRVMksHQ3br9Ogl6R51B/o39h7ZYc9GYxz9JJUnEEvScW5M1aSivPIWEkqzqkbSSrOVTeSNIZZXVLZzxG9JBXniF7SurTaWvh5WyM/CFfdSFJxrrqRpOKco5ek4gx6SSrOoJek4lx1I0kDmIf18qtxRC9JxRn0klRcp1M3EbED2LF169YuuyGpQ20coFTxoKdx+FWCksYy7VA1xIfn1I0kFeeqG0mtWLlKxdF3dwx6Saua5DSJUy7dMegllTbIH5jV1sjP89r5fga9pJk3SBD7KWF17oyVpOIc0Usqoco0SxsMeklTZyhPl0EvaWIM8NnkHL0kFee5bqTi1tOXYOv0PNeNNENmIXy7nH5pu/71OrXk1I0kFefOWM2dWRj1zqv1OqJd7xzRS1JxBr0kFefUjbSGUaaJZmFqqe0pmmlMATnNNDmO6CWpOEf0jVkYham2YX/H/J3UpDiil6TiHNEPyVHW/FltrtejRF/lfHhtjuglqThH9HNomoeJT2N0O87Pm2Zfx12BM8420jgc0UtScY7o1QlHsZNXYe282jHxEX1EXBQRn46Iz036uSVJwxtoRB8RtwG/AJzMzB/ua18C/hg4A/hUZu7NzOPALoN+cPMyRz3r5um1cHSsaRp0RL8fWOpviIgzgFuBy4FtwM6I2DbR3kmSxjZQ0GfmIeDFFc3bgWOZeTwzXwLuBK6acP8kSWMaZ2fsecCzfbdPAO+OiHOBm4F3RsTHMvP3T/fgiNgN7Aa44IILxujG+jCpj/oehj+fBvk6QGk1E191k5n/AuwZYLt9wD6AxcXFnHQ/JEk946y6eQ44v+/25qZNkjRDxhnRPwJcHBEX0gv464APDvMEEbED2LF169YxujF5Kz8OrzZlsd6mQdaaJqj0Gg17NOs8vpdaXwYa0UfEHcCDwCURcSIidmXmy8ANwL3AUeCuzHxymB+emQcyc/fCwsKw/ZYkDWigEX1m7lyl/SBwcKI9kiRNVKenQOhi6maUVQrDPmaQj/VtnOxqVlZgtNGPWX4tpv26z8r7rPnR6UnNnLqRpPZ59kpJKs6gl6Ti5n6OfpCviVP7/IINaXY5Ry9JxTl1I0nFGfSSVNzcz9GvpssvuJ6F55mXn7uWWeyTNI+co5ek4py6kaTiDHpJKs6gl6TiDHpJKq7sqhu9Ple1SOuDq24kqTinbiSpOINekooz6CWpOINekopbF6tuulxd4sqW2eV7o/XCVTeSVJxTN5JUnEEvScUZ9JJUnEEvScUZ9JJUnEEvScV1GvQRsSMi9i0vL3fZDUkqzXX0klRcZGbXfSAiTgHfHPHhG4EXJtideWDN64M1rw/j1PzWzNz0ehvNRNCPIyIOZ+Zi1/2YJmteH6x5fZhGze6MlaTiDHpJKq5C0O/rugMdsOb1wZrXh9Zrnvs5eknS2iqM6CVJa5ipoI+IpYh4KiKORcSNp7n/rRFxf0Q8HhEPRMTmvvs+ERFHmsu1fe37I+LrEfFPzeXSadUziJZqjoi4OSKejoijEfGb06pnEC3V/Hd97/HzEfGFadUziJZq/pmI+GpT899HRLvf4DOklmq+rKn5SER8JiI6/fKkfhFxW0ScjIgjq9wfEXFL83o8HhHv6rvv+oj4WnO5vq/9RyLiieYxt0REjNS5zJyJC3AG8AxwEfBG4DFg24pt/gq4vrl+GfDZ5vqVwH30vjHre4BHgLOa+/YD13Rd35Rr/hBwO/CG5vabu6617ZpXPP7zwK92XesU3uengbc1138d2N91rW3WTG9g+izwA812NwG7uq61r56fAt4FHFnl/iuAe4AAfgz4StN+DnC8+ffs5vrZzX0PN9tG89jLR+nbLI3otwPHMvN4Zr4E3AlctWKbbcCXm+t/23f/NuBQZr6cmf8JPA4sTaHP42qr5o8CN2Xm/wJk5skWaxhWq+9zRJxFLzRmaUTfVs1JLwABFoDnW+r/KNqo+Vzgpcx8utnuPuAXW6xhKJl5CHhxjU2uAm7PnoeAN0XEW4CfB+7LzBcz81/p1bXU3HdWZj6UvdS/HXj/KH2bpaA/j95f61ecaNr6PQZc3Vz/APB9EXFu074UEd8dERuBnwbO73vczc1HpU9GxJntdH8kbdX8/cC1EXE4Iu6JiItbq2B4bb7P0PuPcH9m/vvEez66tmr+NeBgRJwAfgXY21L/R9FGzS8AGyLilYOLruE73/9Zttprslb7idO0D22Wgn4QvwO8JyL+EXgP8Bzw7cz8EnAQ+AfgDuBB4NvNYz4G/CDwo/Q+Gv3utDs9plFqPhP4r+wdbfdnwG1T7/V4Rqn5FTub++bNKDX/NnBFZm4G/hz4w6n3ejxD1dyMaq8DPhkRDwP/wXe+/zqNWQr653jtX+fNTdv/y8znM/PqzHwn8PGm7d+af2/OzEsz8+fozWc93bR/q/mo9N/0/jNsb7+UgbVSM72//H/dXP8b4O3tlTC0tmqmGf1tB+5ut4ShTbzmiNgEvCMzv9I8xV8CP95yHcNo6//zg5n5k5m5HThE3/s/B1Z7TdZq33ya9uFNcmfEOBd6O16OAxfy6s6bH1qxzUZe3cF4M715aOjt+Dm3uf524Aiwobn9lubfAP4I2Nt1rVOoeS/w4eb6e4FHuq617Zqbtj3AZ7qucRo1N5cXeHXH5C7g813XOoXf7Tc3/54J3A9c1nWtK2rawuo7Y6/ktTtjH27azwG+Tm9H7NnN9XOa+1bujL1ipH51/cKseCGuoPcX+hng403bTcD7muvXAF9rtvkUcGbT/l3APzeXh4BL+57zy8ATzS/LXwDf23WdU6j5TfRGtU/Q+9j7jq7rbLvm5v4HgKWu65vi+/yB5j1+rKn9oq7rnELNfwAcBZ4CfqvrGlfUewfwLeB/6H2q3kVv8LGnuT+AW5vX4wlgse+xHwaONZcP9bUvNtn1DPAnNAe5DnvxyFhJKm6W5uglSS0w6CWpOINekooz6CWpOINekooz6CWpOINekooz6CWpuP8DYjvjibMm+e8AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAEJCAYAAACXCJy4AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEEZJREFUeJzt3X+spFV9x/H3x90uVg0IQpUC28UstW78w9Zb0DZW0ogu6kprbMvWRmhNN2rofyauoUmbJk2obU00kOBGCbFJQWqsXcuaVVsNtkFlaf0BruhKrCyhBbSuMWlqCd/+MQ86XO7c+9w7M3fmznm/kg0zZ545zzns7uee/T5nnklVIUlafE+b9QAkSZvDwJekRhj4ktQIA1+SGmHgS1IjDHxJaoSBL0mNMPAlqRETD/wklyb5XJIbk1w66f4lSRuzvc9BSW4CXgc8XFUvGmrfC7wX2AZ8oKquAwr4IfB04GSf/s8+++zatWvX+kYuSY27++67H62qc/oenz63VkjyawxC/ENPBH6SbcA3gMsYBPtdwH7g61X1eJLnAu+pqjet1f/S0lIdO3as75glSUCSu6tqqe/xvUo6VXUH8L1lzRcDJ6rq/qr6EXArcEVVPd69/t/AaX0HIkmarl4lnRHOAx4Yen4SuCTJG4BXA88Grh/15iQHgAMAO3fuHGMYkqQ+xgn8FVXVR4GP9jjuEHAIBiWdSY9DkvRk4+zSeRC4YOj5+V1bb0n2JTl06tSpMYYhSepjnMC/C7goyYVJdgBXAofX00FVfbyqDpxxxhljDEOS1EevwE9yC3An8IIkJ5O8paoeA64BjgLHgduq6t7pDVWSNI5eNfyq2j+i/QhwZKMnT7IP2Ld79+6NdiFJ6mmmt1awpCNJm2fiu3QkSavbdfD2Hz/+9nWv3bTzznSF7y4dSdo8lnQkqRHeHlmSGmHgS1IjrOFLUiOs4UtSIyzpSFIjDHxJaoQ1fElqhDV8SWqEJR1JaoSBL0mNMPAlqREGviQ1wl06ktQId+lIUiMs6UhSIwx8SWqEgS9JjTDwJakR7tKRpEa4S0eSGmFJR5IaYeBLUiMMfElqhIEvSY0w8CWpEQa+JDXCwJekRhj4ktQIP2krSY3wk7aS1AhLOpLUCANfkhph4EtSIwx8SWqEgS9JjTDwJakRBr4kNcLAl6RGGPiS1AgDX5IaMZXAT/LMJMeSvG4a/UuS1q9X4Ce5KcnDSe5Z1r43yX1JTiQ5OPTSO4HbJjlQSdJ4+q7wbwb2Djck2QbcAFwO7AH2J9mT5DLga8DDExynJGlM2/scVFV3JNm1rPli4ERV3Q+Q5FbgCuBZwDMZ/BD4nyRHqurxiY1YkrQhvQJ/hPOAB4aenwQuqaprAJJcDTw6KuyTHAAOAOzcuXOMYUiS+pjaLp2qurmq/nGV1w9V1VJVLZ1zzjnTGoYkqTNO4D8IXDD0/PyurTe/8UqSNs84gX8XcFGSC5PsAK4EDq+nA7/xSpI2T99tmbcAdwIvSHIyyVuq6jHgGuAocBy4rarund5QJUnj6LtLZ/+I9iPAkY2ePMk+YN/u3bs32oUkqSe/xFySGuG9dCSpETMNfHfpSNLmsaQjSY2wpCNJjTDwJakR1vAlqRHW8CWpEZZ0JKkRBr4kNcIaviQ1whq+JDXCko4kNcLAl6RGGPiS1Agv2kpSI7xoK0mNsKQjSY0w8CWpEQa+JDXCwJekRmyf9QAkqQW7Dt4+6yG4LVOSWuG2TElqhDV8SWqEgS9JjTDwJakRBr4kNcLAl6RGGPiS1AgDX5IaYeBLUiP8pK0kNcJP2kpSIyzpSFIjDHxJaoSBL0mNMPAlqREGviQ1wsCXpEYY+JLUCANfkhph4EtSIwx8SWrExAM/yQuT3JjkI0neNun+JUkb0yvwk9yU5OEk9yxr35vkviQnkhwEqKrjVfVW4LeBX538kCVJG9F3hX8zsHe4Ick24AbgcmAPsD/Jnu611wO3A0cmNlJJ0lh6BX5V3QF8b1nzxcCJqrq/qn4E3Apc0R1/uKouB940ycFKkjZu+xjvPQ94YOj5SeCSJJcCbwBOY5UVfpIDwAGAnTt3jjEMSVIf4wT+iqrqs8Bnexx3CDgEsLS0VJMehyTpycYJ/AeBC4aen9+19ZZkH7Bv9+7dYwxDkubProO3z3oITzHOtsy7gIuSXJhkB3AlcHg9HfiNV5K0efpuy7wFuBN4QZKTSd5SVY8B1wBHgePAbVV17/SGKkkaR6+STlXtH9F+hDG2XlrSkaTN45eYS1IjvJeOJDVipoGfZF+SQ6dOnZrlMCSpCZZ0JKkRlnQkqREGviQ1whq+JDXCGr4kNcKSjiQ1wsCXpEZM/PbI6+GtFSQtknm8Q+Ywa/iS1AhLOpLUCANfkhph4EtSI/zglSQ1wou2ktQISzqS1IiZ7sOXpK1u3vfeD3OFL0mNMPAlqRGWdCRpnbZSGWeY2zIlqRFuy5SkRljDl6RGWMOXpB62at1+mCt8SWqEgS9JjTDwJakRBr4kNcKLtpI0wiJcqB3mCl+SGuEnbSWpETMt6VTVx4GPLy0t/eEsxyGpPaPKNd++7rWbPJLNY0lHkhrhRVtJC2F4xT7OKn3RLtQOM/AlLbRFDvD1sqQjSY1whS9p4biqX5krfElqhIEvSY2wpCNpy7J0sz4GvqQtxZDfOANf0twz5CdjKoGf5DeA1wKnAx+sqk9O4zySFpchP3m9Az/JTcDrgIer6kVD7XuB9wLbgA9U1XVV9THgY0nOBP4KMPAlrcmQn6717NK5Gdg73JBkG3ADcDmwB9ifZM/QIX/cvS5JmrHeK/yquiPJrmXNFwMnqup+gCS3AlckOQ5cB3yiqv5tQmOVtIW5ep+9cffhnwc8MPT8ZNf2R8ArgTcmeetKb0xyIMmxJMceeeSRMYchSVrLVC7aVtX7gPetccwh4BDA0tJSTWMckqSfGDfwHwQuGHp+ftfWS5J9wL7du3ePOQxJs7K8VDN8a2LLOPNl3MC/C7goyYUMgv5K4Hf7vtlvvJK2ptWC3JCfX+vZlnkLcClwdpKTwJ9U1QeTXAMcZbAt86aquncqI5U0UZP6whBtHevZpbN/RPsR4MhGTm5JR5pv/lBYLH6JubQFrPcLt6fxBd2WarY+76UjLQgDWWuZaeBb0pFGM8A1aZZ0pDkyDyE/D2PQdFjSkbawSYWzId8Gv+JQkhphDV9qiCv5tlnDl2bA/e2aBWv40jKbHcauurVZrOFLUiOs4UubxJW8Zs0avjRFhrzmiTV8qafV7vu+2nHSvDDwNXPrvUg6qYuqfYLZ8NYiMfA1EW4zlOafF22lDXL1r63Gi7baNPPwrwBDWi2zpKO5NavavrSoDPzGbNVQdGUujc/AX1CzDPat+kNFWnQG/pyZxneRTtJWCXP/RSA9VROBPy8hNc44DDBJ43Jb5giTCuc+n8ach5Wyd4iUFp/bMvUUfW8hsJG+Jn28pP6aKOlM0rytzMfRN1wNYWkxbPnAdzfK2qYV2P4gkLaWLR/4m2GcYPMGXZLmhd94JUmNcIU/xJW2pEW2sIG/2eHtDwtJ886SjiQ1YqFW+K6yJWm0ma7wk+xLcujUqVOzHIYkNcFP2mpL8191Un/W8CWpEQtVw18vV4eSWuIKX5Ia0dwK31W9pFa5wpekRhj4ktQIA1+SGmHgS1IjDHxJaoSBL0mNMPAlqREGviQ1wsCXpEakqmY9BpI8AvzHBt9+NvDoBIezFTjnNjjnNowz55+rqnP6HjwXgT+OJMeqamnW49hMzrkNzrkNmzlnSzqS1AgDX5IasQiBf2jWA5gB59wG59yGTZvzlq/hS5L6WYQVviSph5kHfpKzknwqyTe7/5454rirumO+meSqofaXJPlqkhNJ3pckq/Wb5BeS3Jnkf5O8Y9k59ia5r+vr4ALNOd1xJ5J8JckvDfX17iT3Jjk+3NcCz3dnkk928/1akl2Tnu+8zbl7/fQkJ5NcP435ztOck7w4g7/j93btvzOFua6aFUlOS/Lh7vUvDP85S/Kurv2+JK9eq88kF3Z9nOj63LHWOUaqqpn+At4NHOweHwT+YoVjzgLu7/57Zvf4zO61LwIvBQJ8Arh8tX6BnwF+Gfhz4B1D59gGfAt4PrAD+DKwZ0Hm/JruuHTv+0LX/ivAv3Zz3wbcCVy6qPPtXvsscFn3+FnAMxb593joXO8F/ha4fhrznac5Az8PXNQ9/lngIeDZE5znmlkBvB24sXt8JfDh7vGe7vjTgAu7frat1idwG3Bl9/hG4G2rnWPVsU/rN38d//PuA87tHp8L3LfCMfuB9w89f3/Xdi7w9ZWOW6tf4E95cuC/DDg69PxdwLsWYc5PvHf5+bs53w38NPAM4BjwwgWe7x7gXxbxz/WoOXePXwLcClzNdAN/bua87JxfpvsBMKF5rpkVwFHgZd3j7Qw+WJXlxz5x3Kg+u/c8Cmxffu5R51ht7DMv6QDPraqHusf/CTx3hWPOAx4Yen6yazuve7y8vW+/fc4xDZs95xX7qqo7gc8wWAE9xOAP0vENzWh1czFfBiu/7yf5aJJ/T/KXSbZtcE5rmYs5J3ka8NfAk8qXUzIXcx4+WZKLGayYv7WumayuT1b8+Jiqegw4BTxnlfeOan8O8P2uj+XnGnWOkTblS8yTfBp43govXTv8pKoqycS3DU2r39VshTkn2Q28EDi/a/pUkpdX1efWe76tMF8Gf95fDvwi8B3gwwxWvR/cyDm3yJzfDhypqpOZwOWZLTJnAJKcC/wNcFVVPT7psWxFmxL4VfXKUa8l+a8k51bVQ91v0MMrHPYgcOnQ8/MZ1GIf5Cdh9UT7g93jPv0uP8cFI/patzmb86i5/R7w+ar6YTeuTzD4J+O6A3+LzHc78KWqur8b18cY1H43FPhbZM4vA16e5O0MrlnsSPLDqtrQpoQtMmeSnA7cDlxbVZ/vOb2++mTFE8ecTLIdOAP47hrvXan9u8Czk2zvVvHDx486x0jzUNI5DDxxpf4q4B9WOOYo8KokZ3ZX6F/FoPzwEPCDJC/trui/eej9ffoddhdwUXdFfAeDiyCHNzqpNWz2nA8Db+52NbwUONX18x3gFUm2J/kp4BXANEo68zLfuxj85XniZlO/DnxtYrN8srmYc1W9qap2VtUuBmWdD2007HuYizl3f3//nsFcPzLhOUK/rBge8xuBf65Bsf0wcGW3w+ZC4CIGF6tX7LN7z2e6PuCp81/pHKNN6kLGRn8xqDn9E/BN4NPAWV37EvCBoeP+ADjR/fr9ofYl4B4GNbrr+cmHyUb1+zwGdbAfAN/vHp/evfYa4BtdX9cu0JwD3NAd/1VgqWvfxuDC13EGwfeeRZ5v99plwFe69puBHYs+56E+r2a6F23nYs4M/uX6f8CXhn69eMJzfUpWAH8GvL57/HTg77o5fhF4/tB7r+3edx/dTqRRfXbtz+/6ONH1edpa5xj1y0/aSlIj5qGkI0naBAa+JDXCwJekRhj4ktQIA1+S5liS38rgRnCPJxnrqxANfEmaE0kuTXLzsuZ7gDcAd4zb/6Z80laStDHV3d9qErfGcIUvSY1whS9JM5bkCwzukf8s4KwkX+peemdVHZ3UeQx8SZqxqroEBjV84Oqqunoa57GkI0mNMPAlaY4l+c0kJxnc6vr2JBsu8XjzNElqhCt8SWqEgS9JjTDwJakRBr4kNcLAl6RGGPiS1AgDX5IaYeBLUiP+HymiLHR/z7O/AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plotFB(hb1,hb2)\n", + "plotFB(hb2,hb3)\n", + "plotFB(hb3,hb4)\n", + "plotFB(hb1,hf1)\n", + "plotFB(hf1,hf2)\n", + "plotFB(hf2,hf3)" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -758,87 +1052,137 @@ "540108\n", "483260\n", "931967\n", + "pentuplets\n", + "612902\n", + "triplets\n", + "111601\n", + "47552\n", "4230185\n", - " phi1 pt1 r1 trackID z1 phi2 pt2 r2 \\\n", - "0 -0.534839 1698 3.209876 10000005 -2.03853 -0.522814 1698 6.636066 \n", - "1 -0.534839 1698 3.209876 10000005 -2.03853 -0.522814 1698 6.636066 \n", - "2 -0.534839 1698 3.209876 10000005 -2.03853 -0.522814 1698 6.636066 \n", - "3 -0.534839 1698 3.209876 10000005 -2.03853 -0.522814 1698 6.636066 \n", - "4 -0.534839 1698 3.209876 10000005 -2.03853 -0.508068 1698 6.638935 \n", + " det1 phi1 pt1 r1 trackID z1 det2 phi2 pt2 \\\n", + "0 83 -0.534839 1698 3.209876 10000005 -2.03853 299 -0.522814 1698 \n", + "1 83 -0.534839 1698 3.209876 10000005 -2.03853 299 -0.522814 1698 \n", + "2 83 -0.534839 1698 3.209876 10000005 -2.03853 299 -0.522814 1698 \n", + "3 83 -0.534839 1698 3.209876 10000005 -2.03853 299 -0.522814 1698 \n", + "4 83 -0.534839 1698 3.209876 10000005 -2.03853 299 -0.508068 1698 \n", + "\n", + " r2 ... det3 phi3 pt3 r3 z3 det4 \\\n", + "0 6.636066 ... 642 -0.506715 1698 11.091832 -8.901971 1146 \n", + "1 6.636066 ... 642 -0.506715 1698 11.091832 -8.901971 1146 \n", + "2 6.636066 ... 642 -0.506715 1698 11.091832 -8.901971 1146 \n", + "3 6.636066 ... 642 -0.506715 1698 11.091832 -8.901971 1146 \n", + "4 6.638935 ... 642 -0.506715 1698 11.091832 -8.901971 1146 \n", + "\n", + " phi4 pt4 r4 z4 \n", + "0 -0.469588 1698 16.203526 -13.062537 \n", + "1 -0.464408 1698 16.201468 -13.123804 \n", + "2 -0.488653 1698 16.214857 -13.366333 \n", + "3 -0.471780 1698 16.204531 -13.442224 \n", + "4 -0.469588 1698 16.203526 -13.062537 \n", + "\n", + "[5 rows x 21 columns]\n", + " det1 phi1 pt1 r1 trackID z1 det2 phi2 pt2 \\\n", + "0 19 1.342908 791 3.215434 10000094 -6.674059 137 1.316544 791 \n", + "1 3 0.042201 662 3.147929 10000100 -6.337495 97 0.071146 662 \n", + "2 3 0.038396 662 3.150830 10000100 -6.388875 97 0.071146 662 \n", + "3 92 -0.362996 487 2.798809 10000109 0.902371 308 -0.413166 487 \n", + "4 92 -0.362996 487 2.798809 10000109 0.902371 308 -0.413166 487 \n", "\n", - " z2 phi3 pt3 r3 z3 phi4 pt4 r4 \\\n", - "0 -5.024552 -0.506715 1698 11.091832 -8.901971 -0.469588 1698 16.203526 \n", - "1 -5.024552 -0.506715 1698 11.091832 -8.901971 -0.464408 1698 16.201468 \n", - "2 -5.024552 -0.506715 1698 11.091832 -8.901971 -0.488653 1698 16.214857 \n", - "3 -5.024552 -0.506715 1698 11.091832 -8.901971 -0.471780 1698 16.204531 \n", - "4 -5.171747 -0.506715 1698 11.091832 -8.901971 -0.469588 1698 16.203526 \n", + " r2 ... det3 phi3 pt3 r3 z3 det4 \\\n", + "0 6.533934 ... 392 1.287120 791 10.669562 -23.916361 1605 \n", + "1 6.962423 ... 320 0.107237 662 11.064787 -24.246792 1599 \n", + "2 6.962423 ... 320 0.107237 662 11.064787 -24.246792 1599 \n", + "3 7.010263 ... 644 -0.463537 487 11.100529 1.352261 1195 \n", + "4 7.010263 ... 644 -0.463537 487 11.100529 1.352261 1196 \n", "\n", - " z4 \n", - "0 -13.062537 \n", - "1 -13.123804 \n", - "2 -13.366333 \n", - "3 -13.442224 \n", - "4 -13.062537 \n", - " phi1 pt1 r1 trackID z1 phi2 pt2 r2 \\\n", - "0 1.342908 791 3.215434 10000094 -6.674059 1.316544 791 6.533934 \n", - "1 0.042201 662 3.147929 10000100 -6.337495 0.071146 662 6.962423 \n", - "2 0.038396 662 3.150830 10000100 -6.388875 0.071146 662 6.962423 \n", - "3 -0.362996 487 2.798809 10000109 0.902371 -0.413166 487 7.010263 \n", - "4 -0.362996 487 2.798809 10000109 0.902371 -0.413166 487 7.010263 \n", + " phi4 pt4 r4 z4 \n", + "0 1.260540 791 14.376982 -32.514194 \n", + "1 0.133849 662 14.090476 -31.091267 \n", + "2 0.133849 662 14.090476 -31.091267 \n", + "3 -2.994291 487 8.460827 30.964153 \n", + "4 -2.864020 487 5.959154 31.328598 \n", "\n", - " z2 phi3 pt3 r3 z3 phi4 pt4 r4 \\\n", - "0 -14.353554 1.287120 791 10.669562 -23.916361 1.260540 791 14.376982 \n", - "1 -14.991426 0.107237 662 11.064787 -24.246792 0.133849 662 14.090476 \n", - "2 -14.991426 0.107237 662 11.064787 -24.246792 0.133849 662 14.090476 \n", - "3 1.127291 -0.463537 487 11.100529 1.352261 -2.994291 487 8.460827 \n", - "4 1.127291 -0.463537 487 11.100529 1.352261 -2.864020 487 5.959154 \n", + "[5 rows x 21 columns]\n", + " det1 phi1 pt1 r1 trackID z1 det2 phi2 pt2 \\\n", + "0 66 -1.615744 740 3.405559 10000002 -9.927705 256 -1.644181 740 \n", + "1 66 -1.615744 740 3.405559 10000002 -9.927705 256 -1.644181 740 \n", + "2 26 1.914170 2464 2.671153 10000010 -9.608046 160 1.903031 2464 \n", + "3 26 1.910948 2464 2.670602 10000010 -9.678793 160 1.903031 2464 \n", + "4 26 1.922151 2464 2.672636 10000010 -9.662353 160 1.903031 2464 \n", "\n", - " z4 \n", - "0 -32.514194 \n", - "1 -31.091267 \n", - "2 -31.091267 \n", - "3 30.964153 \n", - "4 31.328598 \n", - " phi1 pt1 r1 trackID z1 phi2 pt2 r2 \\\n", - "0 -1.615744 740 3.405559 10000002 -9.927705 -1.644181 740 7.273387 \n", - "1 -1.615744 740 3.405559 10000002 -9.927705 -1.644181 740 7.273387 \n", - "2 1.914170 2464 2.671153 10000010 -9.608046 1.903031 2464 6.863056 \n", - "3 1.910948 2464 2.670602 10000010 -9.678793 1.903031 2464 6.863056 \n", - "4 1.922151 2464 2.672636 10000010 -9.662353 1.903031 2464 6.863056 \n", + " r2 ... det3 phi3 pt3 r3 z3 det4 \\\n", + "0 7.273387 ... 1567 -1.673348 740 10.764234 -33.038609 1735 \n", + "1 7.273387 ... 1623 -1.672782 740 10.705758 -32.855053 1735 \n", + "2 6.863056 ... 1583 1.898713 2464 8.804473 -33.619747 1664 \n", + "3 6.863056 ... 1583 1.898713 2464 8.804473 -33.619747 1664 \n", + "4 6.863056 ... 1583 1.898713 2464 8.804473 -33.619747 1664 \n", "\n", - " z2 phi3 pt3 r3 z3 phi4 pt4 r4 \\\n", - "0 -22.09573 -1.673348 740 10.764234 -33.038609 -1.692311 740 13.137235 \n", - "1 -22.09573 -1.672782 740 10.705758 -32.855053 -1.692311 740 13.137235 \n", - "2 -26.02704 1.898713 2464 8.804473 -33.619747 1.895945 2464 10.021165 \n", - "3 -26.02704 1.898713 2464 8.804473 -33.619747 1.895945 2464 10.021165 \n", - "4 -26.02704 1.898713 2464 8.804473 -33.619747 1.895945 2464 10.021165 \n", + " phi4 pt4 r4 z4 \n", + "0 -1.692311 740 13.137235 -40.457005 \n", + "1 -1.692311 740 13.137235 -40.457005 \n", + "2 1.895945 2464 10.021165 -38.362865 \n", + "3 1.895945 2464 10.021165 -38.362865 \n", + "4 1.895945 2464 10.021165 -38.362865 \n", "\n", - " z4 \n", - "0 -40.457005 \n", - "1 -40.457005 \n", - "2 -38.362865 \n", - "3 -38.362865 \n", - "4 -38.362865 \n", - " phi1 pt1 r1 trackID z1 phi2 pt2 r2 \\\n", - "0 1.914170 2464 2.671153 10000010 -9.608046 1.898713 2464 8.804473 \n", - "1 1.910948 2464 2.670602 10000010 -9.678793 1.898713 2464 8.804473 \n", - "2 1.922151 2464 2.672636 10000010 -9.662353 1.898713 2464 8.804473 \n", - "3 2.955944 1148 2.702642 10000032 -10.496306 2.928084 1148 8.442572 \n", - "4 2.955944 1148 2.702642 10000032 -10.496306 2.928084 1148 8.442572 \n", + "[5 rows x 21 columns]\n", + " det1 phi1 pt1 r1 trackID z1 det2 phi2 pt2 \\\n", + "0 26 1.914170 2464 2.671153 10000010 -9.608046 1583 1.898713 2464 \n", + "1 26 1.910948 2464 2.670602 10000010 -9.678793 1583 1.898713 2464 \n", + "2 26 1.922151 2464 2.672636 10000010 -9.662353 1583 1.898713 2464 \n", + "3 42 2.955944 1148 2.702642 10000032 -10.496306 1530 2.928084 1148 \n", + "4 42 2.955944 1148 2.702642 10000032 -10.496306 1530 2.928084 1148 \n", "\n", - " z2 phi3 pt3 r3 z3 phi4 pt4 r4 \\\n", - "0 -33.619747 1.895945 2464 10.021165 -38.362865 1.890452 2464 12.449927 \n", - "1 -33.619747 1.895945 2464 10.021165 -38.362865 1.890452 2464 12.449927 \n", - "2 -33.619747 1.895945 2464 10.021165 -38.362865 1.890452 2464 12.449927 \n", - "3 -34.392139 2.919404 1148 10.328422 -42.279213 2.910286 1148 12.075643 \n", - "4 -34.392139 2.921753 1148 9.830442 -40.189011 2.910286 1148 12.075643 \n", + " r2 ... det3 phi3 pt3 r3 z3 det4 \\\n", + "0 8.804473 ... 1664 1.895945 2464 10.021165 -38.362865 1776 \n", + "1 8.804473 ... 1664 1.895945 2464 10.021165 -38.362865 1776 \n", + "2 8.804473 ... 1664 1.895945 2464 10.021165 -38.362865 1776 \n", + "3 8.442572 ... 1642 2.919404 1148 10.328422 -42.279213 1838 \n", + "4 8.442572 ... 1670 2.921753 1148 9.830442 -40.189011 1838 \n", "\n", - " z4 \n", - "0 -47.847328 \n", - "1 -47.847328 \n", - "2 -47.847328 \n", - "3 -49.587833 \n", - "4 -49.587833 \n" + " phi4 pt4 r4 z4 \n", + "0 1.890452 2464 12.449927 -47.847328 \n", + "1 1.890452 2464 12.449927 -47.847328 \n", + "2 1.890452 2464 12.449927 -47.847328 \n", + "3 2.910286 1148 12.075643 -49.587833 \n", + "4 2.910286 1148 12.075643 -49.587833 \n", + "\n", + "[5 rows x 21 columns]\n", + " det1 phi1 pt1 r1 trackID z1 det2 phi2 pt2 \\\n", + "0 35 2.637022 2080 3.098070 10000034 -4.770354 186 2.646844 2080 \n", + "1 43 2.640189 2080 2.788378 10000034 -4.232756 186 2.646844 2080 \n", + "2 27 1.656182 419 2.715456 10000037 -2.543939 155 1.604027 419 \n", + "3 20 1.074587 775 3.318337 10000053 4.562479 133 1.103368 775 \n", + "4 19 1.363694 1260 3.217331 10000077 -5.626595 146 1.380944 1260 \n", + "\n", + " r2 ... det4 phi4 pt4 r4 z4 det5 phi5 pt5 r5 z5 \n", + "0 6.528466 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "1 6.528466 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "2 6.535251 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "3 6.909348 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "4 7.170426 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "\n", + "[5 rows x 26 columns]\n", + " det1 phi1 pt1 r1 trackID z1 det2 phi2 pt2 \\\n", + "0 26 1.914170 2464 2.671153 10000010 -9.608046 160 1.903031 2464 \n", + "1 26 1.910948 2464 2.670602 10000010 -9.678793 160 1.903031 2464 \n", + "2 26 1.922151 2464 2.672636 10000010 -9.662353 160 1.903031 2464 \n", + "3 42 2.955944 1148 2.702642 10000032 -10.496306 200 2.936726 1148 \n", + "4 42 2.955944 1148 2.702642 10000032 -10.496306 200 2.936726 1148 \n", + "\n", + " r2 ... det4 phi4 pt4 r4 z4 det5 \\\n", + "0 6.863056 ... 1664 1.895945 2464 10.021165 -38.362865 1776 \n", + "1 6.863056 ... 1664 1.895945 2464 10.021165 -38.362865 1776 \n", + "2 6.863056 ... 1664 1.895945 2464 10.021165 -38.362865 1776 \n", + "3 6.584924 ... 1642 2.919404 1148 10.328422 -42.279213 1838 \n", + "4 6.584924 ... 1670 2.921753 1148 9.830442 -40.189011 1838 \n", + "\n", + " phi5 pt5 r5 z5 \n", + "0 1.890452 2464 12.449927 -47.847328 \n", + "1 1.890452 2464 12.449927 -47.847328 \n", + "2 1.890452 2464 12.449927 -47.847328 \n", + "3 2.910286 1148 12.075643 -49.587833 \n", + "4 2.910286 1148 12.075643 -49.587833 \n", + "\n", + "[5 rows x 26 columns]\n" ] } ], @@ -862,18 +1206,79 @@ "t112 = pd.merge(t11,build(hf2,'3'),on='trackID')\n", "t1123 = pd.merge(t112,build(hf3,'4'),on='trackID')\n", "print len(t1123)\n", + "print 'pentuplets'\n", + "t12123 = pd.merge(t1212,build(hf3,'5'),on='trackID')\n", + "print len(t12123)\n", + "\n", + "# try to get triplets in gap\n", + "print 'triplets'\n", + "t1230 = pd.merge(t123,build(hb4,'4'),on='trackID',how='left')\n", + "t1230 = t1230[t1230.isnull()['z4']]\n", + "print len(t1230)\n", + "t1230 = pd.merge(t1230,build(hf1,'5'),on='trackID',how='left')\n", + "t1230 = t1230[t1230.isnull()['z5']]\n", + "print len(t1230)\n", "\n", "qall = pd.concat([t1234,t1231,t1212,t1123])\n", "print len(qall)\n", "print t1234.head()\n", "print t1231.head()\n", "print t1212.head()\n", - "print t1123.head()" + "print t1123.head()\n", + "print t1230.head()\n", + "print t12123.head()" ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "hole = zAtR(t1230[t1230['pt3']>600],16)\n", + "plt.hist(hole[abs(hole)<40],log=True, bins=100)\n", + "plt.show()\n", + "plt.hist(abs(hole[abs(hole)<40]),log=True, bins=100)\n", + "plt.show()\n", + "penta = zAtR(t12123[t12123['pt3']>600],6.5)\n", + "plt.hist(penta[abs(penta)<50],log=True, bins=100)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -910,18 +1315,18 @@ " thcut2 = alignRPZ(quadc,'r',True)\n", " pzcut2 = alignRPZ(quadc,'phi',True)\n", "\n", - " curv = curvature(quadc,0.6,0.02,0.2,True)\n", - " rad = curvature(quadc,0.6,0.02,0.2,True,True)\n", - " field = rad/quadc['pt1']\n", + " dc = dca(quadc,True)\n", + " curv1 = dca(quadc,True,True)\n", + " field = curv1-1/(0.087*quadc['pt1'])\n", " print 'field'\n", - " plt.hist(field[abs(field)<500],log=True, bins=100)\n", + " plt.hist(field[abs(field)<5],log=True, bins=100)\n", " plt.show()\n", - " print 'thcut,pzcut,curvcut',len(thcut)\n", + " print 'thcut,pzcut,dcacut',len(thcut)\n", " plt.hist(thcut[thcut<0.004],log=True, bins=100)\n", " plt.show()\n", " plt.hist(pzcut[pzcut<0.4],log=True, bins=100)\n", " plt.show()\n", - " plt.hist(curv[abs(curv)<0.4],log=True, bins=100)\n", + " plt.hist(dc[abs(dc)<0.3],log=True, bins=100)\n", " plt.show()\n", "\n", " print 'thcut2,pzcut2',len(thcut2)\n", @@ -936,18 +1341,22 @@ " pzcut = alignRZ(quadc,'phi',1.0,False)\n", " thcut2 = alignRPZ(quadc,'r',False)\n", " pzcut2 = alignRPZ(quadc,'phi',False)\n", - " curv = curvature(quadc,0.6,0.02,0.2,False)\n", - " rad = curvature(quadc,0.6,0.02,0.2,False,True)\n", - " field = rad/quadc['pt1']\n", + " dc = dca(quadc,False)\n", + " curv2 = dca(quadc,False,True)\n", + " field =curv2 -1/(0.087*quadc['pt1'])\n", " print 'field'\n", - " plt.hist(field[abs(field)<500],log=True, bins=100)\n", + " plt.hist(field[abs(field)<5],log=True, bins=100)\n", " plt.show()\n", - " print 'thcut,pzcut,curvcut',len(thcut)\n", + " print 'delta curv'\n", + " dcu = curv2-curv1\n", + " plt.hist(dcu[abs(dcu)<0.15],log=True, bins=100)\n", + " plt.show() \n", + " print 'thcut,pzcut,dcacut',len(thcut)\n", " plt.hist(thcut[thcut<0.004],log=True, bins=100)\n", " plt.show()\n", " plt.hist(pzcut[pzcut<0.4],log=True, bins=100)\n", " plt.show()\n", - " plt.hist(curv[abs(curv)<0.4],log=True, bins=100)\n", + " plt.hist(dc[abs(dc)<0.3],log=True, bins=100)\n", " plt.show()\n", "\n", " \n", @@ -960,7 +1369,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1054,7 +1463,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 58, "metadata": {}, "outputs": [ { @@ -1067,7 +1476,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1079,7 +1488,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 227975\n" + "thcut,pzcut,dcacut 227975\n" ] }, { @@ -1104,7 +1513,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1149,7 +1558,24 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta curv\n" + ] + }, + { + "data": { + "image/png": "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\n", "text/plain": [ "
" ] @@ -1161,7 +1587,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 227975\n" + "thcut,pzcut,dcacut 227975\n" ] }, { @@ -1186,7 +1612,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1231,7 +1657,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1243,7 +1669,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 40709\n" + "thcut,pzcut,dcacut 40709\n" ] }, { @@ -1268,7 +1694,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1313,7 +1739,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1325,7 +1751,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 40709\n" + "delta curv\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 40709\n" ] }, { @@ -1350,7 +1793,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1395,7 +1838,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1407,7 +1850,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 152561\n" + "thcut,pzcut,dcacut 152561\n" ] }, { @@ -1432,7 +1875,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1477,7 +1920,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEIhJREFUeJzt3X2sZHddx/H3xy2LkYfysCtg22VLthA2mIBOimgIVWmytWyLSLAbScA03QCp/xgTazAh0X8KKglNa2BDm4KRltpo3bVLyoM0NaTFXQSRtgGWFejWyhaQm+BTQb7+MbN0ut1777l3Hs7c332/kk1nzpx75nOnc7/3d7/nN7+TqkKS1K6f6DuAJGm2LPSS1DgLvSQ1zkIvSY2z0EtS4yz0ktQ4C70kNc5CL0mNs9BLUuPO6jsAwLZt22rnzp19x5CkDeVzn/vct6tq+2r79Vrok+wF9u7atYujR4/2GUWSNpwk3+iyX6+tm6o6VFX7zz777D5jSFLTei30SfYmObC0tNRnDElqmiN6SWqcs24kqXG2biSpcbZuJKlxtm4kqXG2biSpcb1+YKqqDgGHBoPBVX3mkGZl5zV3/vj216+9tMck2sxs3UhS42zdSFLjnHUjSY2zdSNJjbPQS1Lj7NFLUuPs0UtS42zdSFLjLPSS1LiFuGastNH5CVgtMk/GSlLjXOtGmrLx0b20CGzdSOtkQddG4clYSWqchV6SGmehl6TGWeglqXFOr5SkxrnWjSQ1ztaNJDXOQi9JjbPQS1LjLPSS1DgLvSQ1zkIvSY2z0EtS42ZS6JM8LcnRJK+bxfElSd11KvRJbkpyMsmXTtu+J8mXkxxLcs3YQ78P3DbNoJKk9ek6or8Z2DO+IckW4AbgEmA3sC/J7iQXAw8AJ6eYU5K0Tp0uPFJV9yTZedrmC4FjVXUcIMmtwOXA04GnMSz+/53kcFX9aGqJJUlrMskVps4BHhq7fwJ4ZVVdDZDkrcC3lyvySfYD+wF27NgxQQxJ0kpmNuumqm6uqr9b4fEDVTWoqsH27dtnFUOSNr1JCv3DwHlj988dbevMZYolafYmKfRHgAuSnJ9kK3AFcHAtB3CZYkmava7TK28B7gVekuREkiur6ofA1cBdwIPAbVV1/1qe3BG9JM1e11k3+5bZfhg4vN4nr6pDwKHBYHDVeo8hSVqZlxKUpMZ5KUFJapyLmklS42zdSFLjbN1IUuNs3UhS4yz0ktQ4e/SS1Dh79JLUOFs3ktQ4C70kNc4evSQ1zh69JDXO1o0kNc5CL0mNs9BLUuM8GStJjfNkrCQ1ztaNJDXOQi9JjbPQS1LjLPSS1DgLvSQ17qw+nzzJXmDvrl27+owhdbLzmjv7jiCti9MrJalxtm4kqXEWeklqnIVekhrX68lYaTMZP5n79Wsv7TGJNhtH9JLUOAu9JDXOQi9JjZt6oU/y0iTvT3J7krdP+/iSpLXpVOiT3JTkZJIvnbZ9T5IvJzmW5BqAqnqwqt4GvAn4pelHliStRdcR/c3AnvENSbYANwCXALuBfUl2jx67DLgTODy1pJKkdek0vbKq7kmy87TNFwLHquo4QJJbgcuBB6rqIHAwyZ3AR6YXV5ov17dRCyaZR38O8NDY/RPAK5NcBLwBeCorjOiT7Af2A+zYsWOCGJKklUz9A1NVdTdwd4f9DgAHAAaDQU07hyRpaJJZNw8D543dP3e0rbMke5McWFpamiCGJGklkxT6I8AFSc5PshW4Aji4lgO4TLEkzV7X6ZW3APcCL0lyIsmVVfVD4GrgLuBB4Laqun8tT+6IXpJmr+usm33LbD/MBFMoq+oQcGgwGFy13mNIklbmpQSlHriSpebJSwlKUuNcj146jR+SUmt6HdF7MlaSZs/WjSQ1ztaN1DNPzGrWnHUjYV9ebbN1I0mN81KCktQ4C70kNc4evTYt+/LaLHot9K51Iz2RM3A0C7ZuJKlxFnpJapyFXpIaZ6GXpMa5qJkkNc5PxkpS42zdSFLjXL1SWlDOqde0WOi1qfhpWG1Gtm4kqXGO6NU8R/Ha7JxeKUmNc3qlJDXOHr0kNc5CL0mNs9BLUuOcdaNmbJYPGG2W71PTY6FXk5xSKT3OQq8NbbMU9M3yfWo2LPRSI2zpaDkzKfRJXg9cCjwTuLGqPj6L55Ekra5zoU9yE/A64GRVvWxs+x7gfcAW4INVdW1V3QHckeTZwJ8CFnppBmzpqIu1TK+8GdgzviHJFuAG4BJgN7Avye6xXf5w9LgkqSedC31V3QN897TNFwLHqup4VT0G3ApcnqF3Ax+rqn860/GS7E9yNMnRRx99dL35JUmrmPQDU+cAD43dPzHa9jvAa4E3Jnnbmb6wqg5U1aCqBtu3b58whiRpOTM5GVtV1wHXrbZfkr3A3l27ds0ihiSJyUf0DwPnjd0/d7StE1evlKTZm7TQHwEuSHJ+kq3AFcDByWNJkqZlLdMrbwEuArYlOQG8q6puTHI1cBfD6ZU3VdX9azimrRutmVMKpbXpXOirat8y2w8Dh9fz5FV1CDg0GAyuWs/XS5JW1+sSCI7opdlwOQSN67XQO6KXZm+5Vpe/ADYPLzwiSY2zdaMNwROw0vrZutFCsbcsTZ/r0UublL9UNw9bN5Kmyl8gi8fWjZ7k9H64P6zSxmbrRr1w1CfNj4VevXNGjTRbvc6jT7I3yYGlpaU+Y0hS0+zRa2E50pemw0/GSlLjLPSS1DgLvSQ1zg9MSVqW02Db4MlYSU/gSfD2OI9ec2MB2dgc3W9cFnpJm8Zm/WVloddMOYrfGPz/1DYLvVa1WUdBUiss9JIm4l8Di89Cr6nwh11aXC5qJkmNcx691sR+vcC/4DYaWzeSFp4DjMlY6CX1yiI+exb6TcYfKm0Utoemx9UrJalxjug3sUlH9464pI3BQi/Aoq3F0OV9uNw+fbUiN0I7dOqtmyQvSnJjktunfWxJ0tp1GtEnuQl4HXCyql42tn0P8D5gC/DBqrq2qo4DV1roJS3CaNe/Vru3bm4Grgc+fGpDki3ADcDFwAngSJKDVfXAtENK0lpY3J+oU+umqu4Bvnva5guBY1V1vKoeA24FLp9yPknShCbp0Z8DPDR2/wRwTpLnJnk/8Iokf7DcFyfZn+RokqOPPvroBDEkSSuZ+qybqvoO8LYO+x0ADgAMBoOadg5J0tAkhf5h4Lyx++eOtnWWZC+wd9euXRPE2NwW4WSX1IXv1f5M0ro5AlyQ5PwkW4ErgINrOUBVHaqq/WefffYEMSRJK+k6vfIW4CJgW5ITwLuq6sYkVwN3MZxeeVNV3b+WJ3dEPx/OQNBmMOv3+TT/Ipn3XzedCn1V7Vtm+2Hg8Hqf3PXoJWn2XNRMkhrX61o3tm6kzWmR24ld2iqLnP9Meh3RezJWkmbPEf0CczqaNB8bbYS+Vo7oJalxnoyVpMZZ6CWpcfboJW1Kk1zNaqOxRy9JjbN1I0mNs9BLUuPs0S+AafUBnXcv9WtRfwbt0UtS42zdSFLjLPSS1DgLvSQ1rtdCn2RvkgNLS0t9xpCkpnkyVpIaZ+tGkhpnoZekxlnoJalxFnpJapyFXpIat+HXulnUtSX61so62pIm5/RKSWqcrRtJapyFXpIaZ6GXpMZZ6CWpcRZ6SWqchV6SGmehl6TGTf0DU0meBvw58Bhwd1X95bSfQ5LUXacRfZKbkpxM8qXTtu9J8uUkx5JcM9r8BuD2qroKuGzKeSVJa9S1dXMzsGd8Q5ItwA3AJcBuYF+S3cC5wEOj3f5vOjElSevVqdBX1T3Ad0/bfCFwrKqOV9VjwK3A5cAJhsW+8/ElSbMzSY/+HB4fucOwwL8SuA64PsmlwKHlvjjJfmA/wI4dOyaI0Z8uC6pNa9E1FymTtF5TPxlbVf8J/HaH/Q4ABwAGg0FNO4ckaWiS1srDwHlj988dbessyd4kB5aWliaIIUlaySSF/ghwQZLzk2wFrgAOruUALlMsSbPXdXrlLcC9wEuSnEhyZVX9ELgauAt4ELitqu5fy5M7opek2evUo6+qfctsPwwcXu+TV9Uh4NBgMLhqvceQJK2s1+mPjuglafa8lKAkNc4PNElS42zdSFLjUtX/Z5WSPAp8A9gGfLvnOGtl5vnZiLnNPD8bMfekmV9YVdtX22khCv0pSY5W1aDvHGth5vnZiLnNPD8bMfe8Mtujl6TGWeglqXGLVugP9B1gHcw8Pxsxt5nnZyPmnkvmherRS5Kmb9FG9JKkKet7Hv1zknwiyVdH/332Mvu9J8n9SR5Mcl2SzDvrWJaumXck+fgo8wNJds436ROydMo82veZo4Xrrp9nxmWyrJo7ycuT3Dt6f3wxyW/2lPVM108ef/ypST46evyzfb4fxjKtlvl3R+/dLyb5VJIX9pHztEwrZh7b7zeSVJKFmIXTJXeSN41e7/uTfGSqAaqqt3/Ae4BrRrevAd59hn1+EfgMsGX0717gokXOPHrsbuDi0e2nAz+16JlHj78P+AhwfZ/vjTW8P14MXDC6/TPAI8Cz5pxzC/A14EXAVuCfgd2n7fMO4P2j21cAH+35te2S+ZdPvW+Bt2+EzKP9ngHcA9wHDPrMvIbX+gLg88CzR/d/epoZ+m7dXA58aHT7Q8Drz7BPAT/J8AV6KvAU4FtzSXdmq2YeXST9rKr6BEBVfb+q/mt+EZ+ky+tMkp8Hngd8fE65VrNq7qr6SlV9dXT734CTwKofIJmy5a6fPG78e7kd+NU+/zKlQ+aq+vTY+/Y+Hr8WdF+6vM4Afwy8G/ifeYZbQZfcVwE3VNV/AFTVyWkG6LvQP6+qHhnd/neGReYJqupe4NMMR2qPAHdV1YPzi/gkq2ZmOMr8XpK/TvL5JH+SZMv8Ij7JqpmT/ATwZ8DvzTPYKrq81j+W5EKGA4KvzTrYac50/eRzltunhtdyWAKeO5d0Z9Yl87grgY/NNNHqVs2c5OeA86pqkS6y3OW1fjHw4iSfSXJfkj3TDDD1a8aeLskngeef4aF3jt+pqkrypClASXYBL+Xx0cQnkry6qv5h6mEff86JMjN8XV8NvAL4JvBR4K3AjdNN+rgpZH4HcLiqTsxzoDmF3KeO8wLgL4C3VNWPpptyc0vyZmAAvKbvLCsZDVbey/BnbaM5i2H75iKGte6eJD9bVd+b1sFnqqpeu9xjSb6V5AVV9cjoB/VMf678OnBfVX1/9DUfA14FzKzQTyHzCeALVXV89DV3AL/ADAv9FDK/Cnh1kncwPKewNcn3q2rZE17TMIXcJHkmcCfwzqq6b0ZRV9Ll+smn9jmR5CzgbOA784l3Rp2u+ZzktQx/6b6mqv53TtmWs1rmZwAvA+4eDVaeDxxMcllVHZ1byifr8lqfAD5bVT8A/jXJVxgW/iPTCNB36+Yg8JbR7bcAf3uGfb4JvCbJWUmewnBU0WfrpkvmI8CzkpzqFf8K8MAcsi1n1cxV9VtVtaOqdjJs33x41kW+g1VzZ3i94r9hmPf2OWYb1+X6yePfyxuBv6/RWbeerJo5ySuADwCXTbtnvE4rZq6qparaVlU7R+/j+xhm77PIQ7f3xx0MR/Mk2cawlXN8agl6Phv9XOBTwFeBTwLPGW0fAB8cO2P9AYbF/QHgvYueeXT/YuCLwL8ANwNbFz3z2P5vZTFm3XR5f7wZ+AHwhbF/L+8h668BX2F4fuCdo21/xLDQwHBCwV8Bx4B/BF60AK/vapk/yXDiw6nX9eCiZz5t37tZgFk3HV/rMGw7PTCqGVdM8/n9ZKwkNa7v1o0kacYs9JLUOAu9JDXOQi9JjbPQS1LjLPSS1DgLvSQ1zkIvSY37f6Ie9kXWoTyuAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] @@ -1489,7 +1932,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 152561\n" + "delta curv\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 152561\n" ] }, { @@ -1514,7 +1974,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1559,7 +2019,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1571,7 +2031,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 400905\n" + "thcut,pzcut,dcacut 400905\n" ] }, { @@ -1596,7 +2056,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1641,7 +2101,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1653,7 +2113,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "thcut,pzcut,curvcut 400905\n" + "delta curv\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 400905\n" ] }, { @@ -1678,7 +2155,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1721,7 +2198,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 57, "metadata": {}, "outputs": [ { diff --git a/RecoPixelVertexing/PixelVertexFinding/interface/pixelVertexHeterogeneousProduct.h b/RecoPixelVertexing/PixelVertexFinding/interface/pixelVertexHeterogeneousProduct.h index ff3624cdafd65..691c3e9ef429c 100644 --- a/RecoPixelVertexing/PixelVertexFinding/interface/pixelVertexHeterogeneousProduct.h +++ b/RecoPixelVertexing/PixelVertexFinding/interface/pixelVertexHeterogeneousProduct.h @@ -16,7 +16,7 @@ namespace pixelVertexHeterogeneousProduct { float * z_d; float * zerr_d; float * chi2_d; - uint16_t * sortInd; + uint16_t * sortInd_d; int32_t * ivtx_d; // this should be indexed with the original tracks, not the reduced set (oops) }; @@ -24,18 +24,13 @@ namespace pixelVertexHeterogeneousProduct { struct VerticesOnCPU { VerticesOnCPU() = default; - explicit VerticesOnCPU(uint32_t nvtx, uint32_t ntrks) : - z(nvtx), - zerr(nvtx), - ivtx(ntrks), - nVertices(nvtx) - { } - - std::vector> z,zerr, chi2; - std::vector> sortInd; - std::vector> ivtx; + float const *z, *zerr, *chi2; + int16_t const * sortInd; + int32_t const * ivtx; + uint16_t const * itrk; uint32_t nVertices=0; + uint32_t nTracks=0; VerticesOnGPU const * gpu_d = nullptr; }; diff --git a/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py b/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py index 77a9f367b9d9b..ea9e4b1e4e037 100644 --- a/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py +++ b/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py @@ -20,3 +20,6 @@ ) +from Configuration.ProcessModifiers.gpu_cff import gpu +from RecoPixelVertexing.PixelVertexFinding.pixelVertexHeterogeneousProducer_cfi import pixelVertexHeterogeneousProducer as _pixelVertexHeterogeneousProducer +gpu.toReplaceWith(pixelVertices, _pixelVertexHeterogeneousProducer) diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 0b8c31235abea..3bd1e522cb776 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -22,12 +22,22 @@ #include "HeterogeneousCore/Producer/interface/HeterogeneousEDProducer.h" #include "RecoPixelVertexing/PixelVertexFinding/interface/pixelVertexHeterogeneousProduct.h" +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" #include "gpuVertexFinder.h" class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer> { public: + + using Input = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; + using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + + using GPUProduct = pixelVertexHeterogeneousProduct::GPUProduct; + using CPUProduct = pixelVertexHeterogeneousProduct::CPUProduct; + using Output = pixelVertexHeterogeneousProduct::HeterogeneousPixelVertices; + + explicit PixelVertexHeterogeneousProducer(const edm::ParameterSet&); ~PixelVertexHeterogeneousProducer() override = default; @@ -46,14 +56,20 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer token_Tracks; - const edm::EDGetTokenT token_BeamSpot; + const float m_ptMin; - reco::TrackRefVector m_trks; + const bool enableConversion_; + const bool enableTransfer_; + const edm::EDGetTokenT gpuToken_; + edm::EDGetTokenT token_Tracks_; + edm::EDGetTokenT token_BeamSpot_; + gpuVertexFinder::Producer m_gpuAlgo; @@ -72,6 +88,10 @@ void PixelVertexHeterogeneousProducer::fillDescriptions(edm::ConfigurationDescri desc.add("PtMin", 0.5); desc.add("TrackCollection", edm::InputTag("pixelTracks")); desc.add("beamSpot", edm::InputTag("offlineBeamSpot")); + desc.add("src", edm::InputTag("pixelTracksHitQuadruplets")); + desc.add("gpuEnableConversion", true); + desc.add("gpuEnableTransfer", true); + HeterogeneousEDProducer::fillPSetDescription(desc); auto label = "pixelVertexHeterogeneousProducer"; descriptions.add(label, desc); @@ -80,19 +100,26 @@ void PixelVertexHeterogeneousProducer::fillDescriptions(edm::ConfigurationDescri PixelVertexHeterogeneousProducer::PixelVertexHeterogeneousProducer(const edm::ParameterSet& conf) : HeterogeneousEDProducer(conf) - ,m_ptMin (conf.getParameter("PtMin") ) // 0.5 GeV - , trackCollName ( conf.getParameter("TrackCollection") ) - , token_Tracks ( consumes(trackCollName) ) - , token_BeamSpot ( consumes(conf.getParameter("beamSpot") ) ) + , m_ptMin(conf.getParameter("PtMin")) // 0.5 GeV + , enableConversion_(conf.getParameter("gpuEnableConversion")) + , enableTransfer_(enableConversion_ || conf.getParameter("gpuEnableTransfer")) + , gpuToken_(consumes(conf.getParameter("src"))) , m_gpuAlgo( conf.getParameter("minT") ,conf.getParameter("eps") ,conf.getParameter("errmax") ,conf.getParameter("chi2max") + ,enableTransfer_ ) { - // Register my product - produces(); - + produces(); + if (enableConversion_) { + token_Tracks_ = consumes(conf.getParameter("TrackCollection")); + token_BeamSpot_ =consumes(conf.getParameter("beamSpot") ); + // Register my product + produces(); + } else { + produces(); // dummy + } } @@ -103,34 +130,15 @@ void PixelVertexHeterogeneousProducer::acquireGPUCuda( cuda::stream_t<> &cudaStream) { // First fish the pixel tracks out of the event - edm::Handle trackCollection; - e.getByToken(token_Tracks,trackCollection); - const reco::TrackCollection tracks = *(trackCollection.product()); - if (verbose_) edm::LogInfo("PixelVertexHeterogeneousProducer") << ": Found " << tracks.size() << " tracks in TrackCollection called " << trackCollName << "\n"; + edm::Handle gh; + e.getByToken(gpuToken_, gh); + auto const & gTuples = *gh; + // std::cout << "Vertex Producers: tuples from gpu " << gTuples.nTuples << std::endl; - // on gpu beamspot already subtracted at hit level... - math::XYZPoint bs(0.,0.,0.); - edm::Handle bsHandle; - e.getByToken(token_BeamSpot,bsHandle); + tuples_ = gh.product(); - if (bsHandle.isValid()) bs = math::XYZPoint(bsHandle->x0(),bsHandle->y0(), bsHandle->z0() ); + m_gpuAlgo.produce(cudaStream.id(),gTuples,m_ptMin); - // Second, make a collection of pointers to the tracks we want for the vertex finder - // fill z,ez - std::vector z,ez2,pt2; - assert(m_trks.empty()); - for (unsigned int i=0; i(); + e.put(std::move(output), heterogeneous::DisableTransfer{}); + + if (!enableConversion_) return; + + edm::Handle trackCollection; + e.getByToken(token_Tracks_,trackCollection); + const reco::TrackCollection tracks = *(trackCollection.product()); + if (verbose_) std::cout << "PixelVertexHeterogeneousProducer" << ": Found " << tracks.size() << " tracks in TrackCollection" << "\n"; + edm::Handle bsHandle; - e.getByToken(token_BeamSpot,bsHandle); + e.getByToken(token_BeamSpot_,bsHandle); auto vertexes = std::make_unique(); float x0=0,y0=0,z0=0,dxdz=0,dydz=0; - std::vector itrk; + std::vector itrk; if(!bsHandle.isValid()) { edm::LogWarning("PixelVertexHeterogeneousProducer") << "No beamspot found. Using returning vertexes with (0,0,Z) "; } else { @@ -158,6 +176,9 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } // fill legacy data format + if (verbose_) std::cout << "found " << gpuProduct.nVertices << " vertices on GPU using " << gpuProduct.nTracks << " tracks"<< std::endl; + if (verbose_) std::cout << "original tuple size " << (*tuples_).indToEdm.size() << std::endl; + std::set uind; // fort verifing index consistency for (int j=int(gpuProduct.nVertices)-1; j>=0; --j) { auto i = gpuProduct.sortInd[j]; // on gpu sorted in ascending order.... @@ -171,25 +192,31 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( z +=z0; reco::Vertex::Error err; err(2,2) = 1.f/gpuProduct.zerr[i]; - // err(2,2) *= 4.; // artifically inflate error + err(2,2) *= 2.; // artifically inflate error //Copy also the tracks (no intention to be efficient....) - for (auto k=0U; k0); + if (nt==0) { std::cout << "vertex " << i << " with no tracks..." << std::endl; continue;} (*vertexes).emplace_back(reco::Vertex::Point(x,y,z), err, gpuProduct.chi2[i], nt-1, nt ); auto & v = (*vertexes).back(); - for (auto k: itrk) { - v.add(reco::TrackBaseRef(m_trks[k])); + for (auto it: itrk) { + assert(it< (*tuples_).indToEdm.size()); + auto k = (*tuples_).indToEdm[it]; + assert(ksize() << " vertexes\n"; @@ -202,10 +229,8 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( std::cout << "Vertex number " << i << " has " << (*vertexes)[i].tracksSize() << " tracks with a position of " << (*vertexes)[i].z() << " +- " << std::sqrt( (*vertexes)[i].covariance(2,2) ) << " chi2 " << (*vertexes)[i].normalizedChi2() << std::endl; } - } - if(vertexes->empty() && bsHandle.isValid()){ const reco::BeamSpot & bs = *bsHandle; @@ -242,7 +267,6 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } e.put(std::move(vertexes)); - m_trks.clear(); } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h index 1f02b1f84134c..c1ee35ce74ba6 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h @@ -13,49 +13,10 @@ namespace gpuVertexFinder { - __global__ - void sortByPt2(int nt, - OnGPU * pdata - ) { - auto & __restrict__ data = *pdata; - float const * __restrict__ ptt2 = data.ptt2; - uint32_t const & nv = *data.nv; - - int32_t const * __restrict__ iv = data.iv; - float * __restrict__ ptv2 = data.ptv2; - uint16_t * __restrict__ sortInd = data.sortInd; - - if (nv<1) return; - - // can be done asynchronoisly at the end of previous event - for (int i = threadIdx.x; i < nv; i += blockDim.x) { - ptv2[i]=0; - } - __syncthreads(); - - - for (int i = threadIdx.x; i < nt; i += blockDim.x) { - if (iv[i]>9990) continue; - atomicAdd(&ptv2[iv[i]], ptt2[i]); - } - __syncthreads(); - - if (1==nv) { - if (threadIdx.x==0) sortInd[0]=0; - return; - } - __shared__ uint16_t ws[1024]; - radixSort(ptv2,sortInd,ws,nv); - - assert(ptv2[sortInd[nv-1]]>=ptv2[sortInd[nv-2]]); - assert(ptv2[sortInd[1]]>=ptv2[sortInd[0]]); - } - - // this algo does not really scale as it works in a single block... // enough for <10K tracks we have __global__ - void clusterTracks(int nt, + void clusterTracks( OnGPU * pdata, int minT, // min number of neighbours to be "core" float eps, // max absolute distance to cluster @@ -66,17 +27,17 @@ namespace gpuVertexFinder { constexpr bool verbose = false; // in principle the compiler should optmize out if false - if(verbose && 0==threadIdx.x) printf("params %d %f\n",minT,eps); + if(verbose && 0==threadIdx.x) printf("params %d %f %f %f\n",minT,eps,errmax,chi2max); auto er2mx = errmax*errmax; auto & __restrict__ data = *pdata; + auto nt = *data.ntrks; float const * __restrict__ zt = data.zt; float const * __restrict__ ezt2 = data.ezt2; - float * __restrict__ zv = data.zv; - float * __restrict__ wv = data.wv; - float * __restrict__ chi2 = data.chi2; - uint32_t & nv = *data.nv; + + uint32_t & nvFinal = *data.nvFinal; + uint32_t & nvIntermediate = *data.nvIntermediate; uint8_t * __restrict__ izt = data.izt; int32_t * __restrict__ nn = data.nn; @@ -86,10 +47,9 @@ namespace gpuVertexFinder { assert(zt); using Hist=HistoContainer; - constexpr auto wss = Hist::totbins(); __shared__ Hist hist; - __shared__ typename Hist::Counter ws[wss]; - for (auto j=threadIdx.x; j=minT) { auto old = atomicInc(&foundClusters, 0xffffffff); iv[i] = -(old + 1); - zv[old]=0; - wv[old]=0; - chi2[old]=0; } else { // noise iv[i] = -9998; } @@ -220,52 +177,10 @@ namespace gpuVertexFinder { iv[i] = - iv[i] - 1; } - // only for test - __shared__ int noise; - if(verbose && 0==threadIdx.x) noise = 0; - - __syncthreads(); - - // compute cluster location - for (int i = threadIdx.x; i < nt; i += blockDim.x) { - if (iv[i]>9990) { - if (verbose) atomicAdd(&noise, 1); - continue; - } - assert(iv[i]>=0); - assert(iv[i]0.f); - zv[i]/=wv[i]; - nn[i]=-1; // ndof - } - __syncthreads(); - - - // compute chi2 - for (int i = threadIdx.x; i < nt; i += blockDim.x) { - if (iv[i]>9990) continue; - - auto c2 = zv[iv[i]]-zt[i]; c2 *=c2/ezt2[i]; - // remove outliers ???? if (c2> cut) {iv[i] = 9999; continue;}???? - atomicAdd(&chi2[iv[i]],c2); - atomicAdd(&nn[iv[i]],1); - } - __syncthreads(); - for (int i = threadIdx.x; i < foundClusters; i += blockDim.x) if(nn[i]>0) wv[i] *= float(nn[i])/chi2[i]; - - if(verbose && 0==threadIdx.x) printf("found %d proto clusters ",foundClusters); - if(verbose && 0==threadIdx.x) printf("and %d noise\n",noise); - - nv = foundClusters; + nvIntermediate = nvFinal = foundClusters; + + if(verbose && 0==threadIdx.x) printf("found %d proto vertices\n",foundClusters); + } } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h new file mode 100644 index 0000000000000..1dfc84c1657ef --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h @@ -0,0 +1,101 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_fitVertices_H +#define RecoPixelVertexing_PixelVertexFinding_fitVertices_H + +#include +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" + + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + + __global__ + void fitVertices( + OnGPU * pdata, + float chi2Max // for outlier rejection + ) { + + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + auto & __restrict__ data = *pdata; + auto nt = *data.ntrks; + float const * __restrict__ zt = data.zt; + float const * __restrict__ ezt2 = data.ezt2; + float * __restrict__ zv = data.zv; + float * __restrict__ wv = data.wv; + float * __restrict__ chi2 = data.chi2; + uint32_t & nvFinal = *data.nvFinal; + uint32_t & nvIntermediate = *data.nvIntermediate; + + int32_t * __restrict__ nn = data.nn; + int32_t * __restrict__ iv = data.iv; + + assert(pdata); + assert(zt); + + assert(nvFinal<=nvIntermediate); + nvFinal = nvIntermediate; + auto foundClusters = nvFinal; + + // zero + for (int i = threadIdx.x; i < foundClusters; i += blockDim.x) { + zv[i]=0; + wv[i]=0; + chi2[i]=0; + } + + // only for test + __shared__ int noise; + if(verbose && 0==threadIdx.x) noise = 0; + + __syncthreads(); + + // compute cluster location + for (int i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i]>9990) { + if (verbose) atomicAdd(&noise, 1); + continue; + } + assert(iv[i]>=0); + assert(iv[i]0.f); + zv[i]/=wv[i]; + nn[i]=-1; // ndof + } + __syncthreads(); + + + // compute chi2 + for (int i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i]>9990) continue; + + auto c2 = zv[iv[i]]-zt[i]; c2 *=c2/ezt2[i]; + if (c2 > chi2Max ) {iv[i] = 9999; continue;} + atomicAdd(&chi2[iv[i]],c2); + atomicAdd(&nn[iv[i]],1); + } + __syncthreads(); + for (int i = threadIdx.x; i < foundClusters; i += blockDim.x) if(nn[i]>0) wv[i] *= float(nn[i])/chi2[i]; + + if(verbose && 0==threadIdx.x) printf("found %d proto clusters ",foundClusters); + if(verbose && 0==threadIdx.x) printf("and %d noise\n",noise); + + } + +} + +#endif diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h new file mode 100644 index 0000000000000..5d61feebe0d58 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h @@ -0,0 +1,59 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_sortByPt2_H +#define RecoPixelVertexing_PixelVertexFinding_sortByPt2_H + + +#include +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" + + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + __global__ + void sortByPt2( + OnGPU * pdata + ) { + auto & __restrict__ data = *pdata; + auto nt = *data.ntrks; + float const * __restrict__ ptt2 = data.ptt2; + uint32_t const & nvFinal = *data.nvFinal; + + int32_t const * __restrict__ iv = data.iv; + float * __restrict__ ptv2 = data.ptv2; + uint16_t * __restrict__ sortInd = data.sortInd; + + if (nvFinal<1) return; + + // can be done asynchronoisly at the end of previous event + for (int i = threadIdx.x; i < nvFinal; i += blockDim.x) { + ptv2[i]=0; + } + __syncthreads(); + + + for (int i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i]>9990) continue; + atomicAdd(&ptv2[iv[i]], ptt2[i]); + } + __syncthreads(); + + if (1==nvFinal) { + if (threadIdx.x==0) sortInd[0]=0; + return; + } + __shared__ uint16_t ws[1024]; + radixSort(ptv2,sortInd,ws,nvFinal); + + assert(ptv2[sortInd[nvFinal-1]]>=ptv2[sortInd[nvFinal-2]]); + assert(ptv2[sortInd[1]]>=ptv2[sortInd[0]]); + } + +} + +#endif diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h new file mode 100644 index 0000000000000..dfe08d304685f --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -0,0 +1,129 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_splitVertices_H +#define RecoPixelVertexing_PixelVertexFinding_splitVertices_H + +#include +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" + + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + + __global__ + void splitVertices( + OnGPU * pdata, + float maxChi2 + ) { + + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + + auto & __restrict__ data = *pdata; + auto nt = *data.ntrks; + float const * __restrict__ zt = data.zt; + float const * __restrict__ ezt2 = data.ezt2; + float * __restrict__ zv = data.zv; + float * __restrict__ wv = data.wv; + float const * __restrict__ chi2 = data.chi2; + uint32_t & nvFinal = *data.nvFinal; + + int32_t const * __restrict__ nn = data.nn; + int32_t * __restrict__ iv = data.iv; + + assert(pdata); + assert(zt); + + // one vertex per block + auto kv = blockIdx.x; + + if (kv>= nvFinal) return; + if (nn[kv]<4) return; + if (chi2[kv]tuples_d; + auto const * fit = tracks->helix_fit_results_d; + auto const * quality = tracks->quality_d; + + auto idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx>= tuples.nbins()) return; + if (tuples.size(idx)==0) { + return; + } + + if(quality[idx] != pixelTuplesHeterogeneousProduct::loose ) return; + + auto const & fittedTrack = fit[idx]; + + if (fittedTrack.par(2)>>(ntrks,onGPU_d,minT,eps,errmax,chi2max); + assert(onGPU_d);assert(tracks.gpu_d); + cudaCheck(cudaMemsetAsync(onGPU.ntrks, 0, sizeof(uint32_t),stream)); + auto blockSize = 128; + auto numberOfBlocks = (CAConstants::maxTuples() + blockSize - 1) / blockSize; + loadTracks<<>>(tracks.gpu_d,onGPU_d, ptMin); + cudaCheck(cudaGetLastError()); + + clusterTracks<<<1,1024-256,0,stream>>>(onGPU_d,minT,eps,errmax,chi2max); cudaCheck(cudaGetLastError()); - sortByPt2<<<1,256,0,stream>>>(ntrks,onGPU_d); + fitVertices<<<1,1024-256,0,stream>>>(onGPU_d,50.); cudaCheck(cudaGetLastError()); - cudaCheck(cudaMemcpyAsync(&gpuProduct.nVertices, onGPU.nv, sizeof(uint32_t), - cudaMemcpyDeviceToHost, stream)); - - gpuProduct.ivtx.resize(ntrks); - cudaCheck(cudaMemcpyAsync(gpuProduct.ivtx.data(),onGPU.iv,sizeof(int32_t)*ntrks, - cudaMemcpyDeviceToHost, stream)); + splitVertices<<<1024,128,0,stream>>>(onGPU_d,9.f); + cudaCheck(cudaGetLastError()); + fitVertices<<<1,1024-256,0,stream>>>(onGPU_d,5000.); + cudaCheck(cudaGetLastError()); + + sortByPt2<<<1,256,0,stream>>>(onGPU_d); + cudaCheck(cudaGetLastError()); + + if(enableTransfer) { + cudaCheck(cudaMemcpyAsync(&gpuProduct.nVertices, onGPU.nvFinal, sizeof(uint32_t), + cudaMemcpyDeviceToHost, stream)); + cudaCheck(cudaMemcpyAsync(&gpuProduct.nTracks, onGPU.ntrks, sizeof(uint32_t), + cudaMemcpyDeviceToHost, stream)); + } } - Producer::GPUProduct const & Producer::fillResults(cudaStream_t stream) { + Producer::OnCPU const & Producer::fillResults(cudaStream_t stream) { + + if(!enableTransfer) return gpuProduct; // finish copy + gpuProduct.ivtx.resize(gpuProduct.nTracks); + cudaCheck(cudaMemcpyAsync(gpuProduct.ivtx.data(),onGPU.iv,sizeof(int32_t)*gpuProduct.nTracks, + cudaMemcpyDeviceToHost, stream)); + gpuProduct.itrk.resize(gpuProduct.nTracks); + cudaCheck(cudaMemcpyAsync(gpuProduct.itrk.data(),onGPU.itrk,sizeof(int16_t)*gpuProduct.nTracks, + cudaMemcpyDeviceToHost, stream)); + gpuProduct.z.resize(gpuProduct.nVertices); cudaCheck(cudaMemcpyAsync(gpuProduct.z.data(),onGPU.zv,sizeof(float)*gpuProduct.nVertices, cudaMemcpyDeviceToHost, stream)); diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h index ded6759a940bd..8f005052da95c 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h @@ -3,6 +3,7 @@ #include +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" #include "RecoPixelVertexing/PixelVertexFinding/interface/pixelVertexHeterogeneousProduct.h" @@ -12,7 +13,9 @@ namespace gpuVertexFinder { static constexpr uint32_t MAXTRACKS = 16000; static constexpr uint32_t MAXVTX= 1024; - + + uint32_t * ntrks; // number of "selected tracks" + uint16_t * itrk; // index of original track float * zt; // input track z at bs float * ezt2; // input error^2 on the above float * ptt2; // input pt^2 on the above @@ -22,7 +25,8 @@ namespace gpuVertexFinder { float * wv; // output weight (1/error^2) on the above float * chi2; // vertices chi2 float * ptv2; // vertices pt^2 - uint32_t * nv; // the number of vertices + uint32_t * nvFinal; // the number of vertices + uint32_t * nvIntermediate; // the number of vertices after splitting pruning etc. int32_t * iv; // vertex index for each associated track uint16_t * sortInd; // sorted index (by pt2) @@ -33,10 +37,25 @@ namespace gpuVertexFinder { }; + struct OnCPU { + OnCPU() = default; + + std::vector> z,zerr, chi2; + std::vector> sortInd; + std::vector> ivtx; + std::vector> itrk; + + uint32_t nVertices=0; + uint32_t nTracks=0; + OnGPU const * gpu_d = nullptr; + }; + class Producer { public: - - using GPUProduct = pixelVertexHeterogeneousProduct::GPUProduct; + + using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + + using OnCPU = gpuVertexFinder::OnCPU; using OnGPU = gpuVertexFinder::OnGPU; @@ -44,31 +63,28 @@ namespace gpuVertexFinder { int iminT, // min number of neighbours to be "core" float ieps, // max absolute distance to cluster float ierrmax, // max error to be "seed" - float ichi2max // max normalized distance to cluster + float ichi2max, // max normalized distance to cluster + bool ienableTransfer ) : minT(iminT), eps(ieps), errmax(ierrmax), - chi2max(ichi2max) + chi2max(ichi2max), + enableTransfer(ienableTransfer) {} ~Producer() { deallocateOnGPU();} - - void produce(cudaStream_t stream, - float const * zt, - float const * ezt2, - float const * ptt2, - uint32_t ntrks - ); - - GPUProduct const & fillResults(cudaStream_t stream); + + void produce(cudaStream_t stream, TuplesOnCPU const & tuples, float ptMin); + + OnCPU const & fillResults(cudaStream_t stream); void allocateOnGPU(); void deallocateOnGPU(); private: - GPUProduct gpuProduct; + OnCPU gpuProduct; OnGPU onGPU; OnGPU * onGPU_d=nullptr; @@ -76,6 +92,7 @@ namespace gpuVertexFinder { float eps; // max absolute distance to cluster float errmax; // max error to be "seed" float chi2max; // max normalized distance to cluster + const bool enableTransfer; }; diff --git a/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml b/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml index ad1f03999fbea..dc3b98f8456a5 100644 --- a/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml @@ -21,5 +21,6 @@ + diff --git a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu index a92c116702231..d1f508ca98798 100644 --- a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu +++ b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu @@ -4,6 +4,11 @@ #include #include "RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h" +#include "RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h" +#include "RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h" +#include "RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h" + + using namespace gpuVertexFinder; #include @@ -81,7 +86,8 @@ int main() { } auto current_device = cuda::device::current::get(); - + + auto ntrks_d = cuda::memory::device::make_unique(current_device, 1); auto zt_d = cuda::memory::device::make_unique(current_device, 64000); auto ezt2_d = cuda::memory::device::make_unique(current_device, 64000); auto ptt2_d = cuda::memory::device::make_unique(current_device, 64000); @@ -96,11 +102,13 @@ int main() { auto iv_d = cuda::memory::device::make_unique(current_device, 64000); auto nv_d = cuda::memory::device::make_unique(current_device, 1); + auto nv2_d = cuda::memory::device::make_unique(current_device, 1); auto onGPU_d = cuda::memory::device::make_unique(current_device, 1); OnGPU onGPU; + onGPU.ntrks = ntrks_d.get(); onGPU.zt = zt_d.get(); onGPU.ezt2 = ezt2_d.get(); onGPU.ptt2 = ptt2_d.get(); @@ -109,7 +117,8 @@ int main() { onGPU.chi2 = chi2_d.get(); onGPU.ptv2 = ptv2_d.get(); onGPU.sortInd = ind_d.get(); - onGPU.nv = nv_d.get(); + onGPU.nvFinal = nv_d.get(); + onGPU.nvIntermediate = nv2_d.get(); onGPU.izt = izt_d.get(); onGPU.nn = nn_d.get(); onGPU.iv = iv_d.get(); @@ -131,7 +140,8 @@ int main() { gen(ev); std::cout << ev.zvert.size() << ' ' << ev.ztrack.size() << std::endl; - + auto nt = ev.ztrack.size(); + cuda::memory::copy(onGPU.ntrks,&nt,sizeof(uint32_t)); cuda::memory::copy(onGPU.zt,ev.ztrack.data(),sizeof(float)*ev.ztrack.size()); cuda::memory::copy(onGPU.ezt2,ev.eztrack.data(),sizeof(float)*ev.eztrack.size()); cuda::memory::copy(onGPU.ptt2,ev.pttrack.data(),sizeof(float)*ev.eztrack.size()); @@ -143,51 +153,101 @@ int main() { if ( (i%4) == 0 ) cuda::launch(clusterTracks, { 1, 512+256 }, - ev.ztrack.size(), onGPU_d.get(),kk,eps, + onGPU_d.get(),kk,eps, 0.02f,12.0f ); if ( (i%4) == 1 ) cuda::launch(clusterTracks, { 1, 512+256 }, - ev.ztrack.size(), onGPU_d.get(),kk,eps, + onGPU_d.get(),kk,eps, 0.02f,9.0f ); if ( (i%4) == 2 ) cuda::launch(clusterTracks, { 1, 512+256 }, - ev.ztrack.size(), onGPU_d.get(),kk,eps, + onGPU_d.get(),kk,eps, 0.01f,9.0f ); if ( (i%4) == 3 ) cuda::launch(clusterTracks, { 1, 512+256 }, - ev.ztrack.size(), onGPU_d.get(),kk,0.7f*eps, + onGPU_d.get(),kk,0.7f*eps, 0.01f,9.0f ); - + cudaCheck(cudaGetLastError()); cudaDeviceSynchronize(); + + cuda::launch(fitVertices, + { 1,1024-256 }, + onGPU_d.get(),50.f + ); + cudaCheck(cudaGetLastError()); + + uint32_t nv; + cuda::memory::copy(&nv, onGPU.nvFinal, sizeof(uint32_t)); + if (nv==0) { + std::cout << "NO VERTICES???" << std::endl; + continue; + } + float chi2[2*nv]; // make space for splitting... + float zv[2*nv]; + float wv[2*nv]; + float ptv2[2*nv]; + int32_t nn[2*nv]; + uint16_t ind[2*nv]; + + cuda::memory::copy(&nn, onGPU.nn, nv*sizeof(int32_t)); + cuda::memory::copy(&chi2, onGPU.chi2, nv*sizeof(float)); + for (auto j=0U; j0) chi2[j]/=float(nn[j]); + { + auto mx = std::minmax_element(chi2,chi2+nv); + std::cout << "after fit min max chi2 " << nv << " " << *mx.first << ' ' << *mx.second << std::endl; + } + + cuda::launch(fitVertices, + { 1,1024-256 }, + onGPU_d.get(), 50.f + ); + cuda::memory::copy(&nv, onGPU.nvFinal, sizeof(uint32_t)); + cuda::memory::copy(&nn, onGPU.nn, nv*sizeof(int32_t)); + cuda::memory::copy(&chi2, onGPU.chi2, nv*sizeof(float)); + for (auto j=0U; j0) chi2[j]/=float(nn[j]); + { + auto mx = std::minmax_element(chi2,chi2+nv); + std::cout << "before splitting min max chi2 " << nv << " " << *mx.first << ' ' << *mx.second << std::endl; + } + + cuda::launch(splitVertices, + { 1024, 64 }, + onGPU_d.get(), + 9.f + ); + cuda::memory::copy(&nv, onGPU.nvIntermediate, sizeof(uint32_t)); + std::cout << "after split " << nv << std::endl; + + cuda::launch(fitVertices, + { 1,1024-256 }, + onGPU_d.get(),5000.f + ); + cudaCheck(cudaGetLastError()); + + cuda::launch(sortByPt2, { 1, 256 }, - ev.ztrack.size(), onGPU_d.get() + onGPU_d.get() ); - uint32_t nv; - cuda::memory::copy(&nv, onGPU.nv, sizeof(uint32_t)); + cuda::memory::copy(&nv, onGPU.nvFinal, sizeof(uint32_t)); if (nv==0) { std::cout << "NO VERTICES???" << std::endl; continue; } - float zv[nv]; - float wv[nv]; - float chi2[nv]; - float ptv2[nv]; - int32_t nn[nv]; - uint16_t ind[nv]; + cuda::memory::copy(&zv, onGPU.zv, nv*sizeof(float)); cuda::memory::copy(&wv, onGPU.wv, nv*sizeof(float)); cuda::memory::copy(&chi2, onGPU.chi2, nv*sizeof(float)); @@ -195,15 +255,16 @@ int main() { cuda::memory::copy(&nn, onGPU.nn, nv*sizeof(int32_t)); cuda::memory::copy(&ind, onGPU.sortInd, nv*sizeof(uint16_t)); for (auto j=0U; j0) chi2[j]/=float(nn[j]); - { - auto mx = std::minmax_element(wv,wv+nv); - std::cout << "min max error " << 1./std::sqrt(*mx.first) << ' ' << 1./std::sqrt(*mx.second) << std::endl; + auto mx = std::minmax_element(chi2,chi2+nv); + std::cout << "min max chi2 " << nv << " " << *mx.first << ' ' << *mx.second << std::endl; } + { - auto mx = std::minmax_element(chi2,chi2+nv); - std::cout << "min max chi2 " << *mx.first << ' ' << *mx.second << std::endl; + auto mx = std::minmax_element(wv,wv+nv); + std::cout << "min max error " << 1./std::sqrt(*mx.first) << ' ' << 1./std::sqrt(*mx.second) << std::endl; } + { auto mx = std::minmax_element(ptv2,ptv2+nv); std::cout << "min max ptv2 " << *mx.first << ' ' << *mx.second << std::endl; @@ -212,16 +273,15 @@ int main() { } float dd[nv]; - uint32_t ii=0; - for (auto zr : zv) { + for (auto kv=0U; kv