From d15d0dd917c4ec385a38c796a888989aa1b90b02 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 5 Oct 2018 11:47:46 +0200 Subject: [PATCH 01/94] use gpu vertices --- .../Configuration/python/RecoPixelVertexing_cff.py | 4 ++-- .../PixelVertexFinding/python/PixelVertexes_cfi.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py b/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py index 6ba1498313cb7..fa7ff20af2ede 100644 --- a/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py +++ b/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py @@ -4,6 +4,6 @@ # # 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 * recopixelvertexing = cms.Sequence(pixelTracksSequence*pixelVertices) 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) From fc8ffad054c07bb3e13fabce05350ac0a6dcf776 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 5 Oct 2018 16:16:15 +0200 Subject: [PATCH 02/94] add vertex spitting --- .../PixelVertexFinding/src/gpuClusterTracks.h | 94 +------------ .../PixelVertexFinding/src/gpuFitVertices.h | 100 ++++++++++++++ .../PixelVertexFinding/src/gpuSortByPt2.h | 58 ++++++++ .../PixelVertexFinding/src/gpuSplitVertices.h | 124 ++++++++++++++++++ .../PixelVertexFinding/src/gpuVertexFinder.cu | 13 ++ .../PixelVertexFinding/src/gpuVertexFinder.h | 1 + .../PixelVertexFinding/test/BuildFile.xml | 1 + .../test/gpuVertexFinder_t.cu | 91 ++++++++++--- 8 files changed, 374 insertions(+), 108 deletions(-) create mode 100644 RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h create mode 100644 RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h create mode 100644 RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h index ae17401d06625..82dffbcd62dca 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h @@ -14,45 +14,6 @@ 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__ @@ -74,10 +35,9 @@ namespace gpuVertexFinder { auto & __restrict__ data = *pdata; 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 & nv2 = *data.nv2; uint8_t * __restrict__ izt = data.izt; int32_t * __restrict__ nn = data.nn; @@ -195,9 +155,6 @@ namespace gpuVertexFinder { if (nn[i]>=minT) { auto old = atomicAdd(&foundClusters, 1); iv[i] = -(old + 1); - zv[old]=0; - wv[old]=0; - chi2[old]=0; } else { // noise iv[i] = -9998; } @@ -221,52 +178,7 @@ 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; + nv2 = nv = foundClusters; } } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h new file mode 100644 index 0000000000000..6f294b64cdde4 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h @@ -0,0 +1,100 @@ +#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(int nt, + OnGPU * pdata + ) { + + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + + auto & __restrict__ data = *pdata; + 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 & nv2 = *data.nv2; + + int32_t * __restrict__ nn = data.nn; + int32_t * __restrict__ iv = data.iv; + + assert(pdata); + assert(zt); + + assert(nv<=nv2); + nv = nv2; + auto foundClusters = nv2; + + // 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]; + // 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); + + } + +} + +#endif diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h new file mode 100644 index 0000000000000..c3e4f9470db74 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h @@ -0,0 +1,58 @@ +#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(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]]); + } + +} + +#endif diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h new file mode 100644 index 0000000000000..e423c7f9e936a --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -0,0 +1,124 @@ +#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(int nt, + OnGPU * pdata, + float maxChi2 + ) { + + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + + auto & __restrict__ data = *pdata; + 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 & nv = *data.nv; + + 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>= nv) return; + if (nn[kv]<4) return; + if (chi2[kv]>>(ntrks,onGPU_d,minT,eps,errmax,chi2max); cudaCheck(cudaGetLastError()); + fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d); + cudaCheck(cudaGetLastError()); + + splitVertices<<<1024,128,0,stream>>>(ntrks,onGPU_d,9.f); + cudaCheck(cudaGetLastError()); + fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d); + cudaCheck(cudaGetLastError()); + sortByPt2<<<1,256,0,stream>>>(ntrks,onGPU_d); cudaCheck(cudaGetLastError()); diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h index ded6759a940bd..2b1c580d3ed47 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h @@ -23,6 +23,7 @@ namespace gpuVertexFinder { float * chi2; // vertices chi2 float * ptv2; // vertices pt^2 uint32_t * nv; // the number of vertices + uint32_t * nv2; // the number of vertices after sipping pruning etc. int32_t * iv; // vertex index for each associated track uint16_t * sortInd; // sorted index (by pt2) diff --git a/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml b/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml index ad1f03999fbea..00fc00b35a4c6 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..ef52a403dd8cd 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 @@ -96,6 +101,7 @@ 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); @@ -110,6 +116,7 @@ int main() { onGPU.ptv2 = ptv2_d.get(); onGPU.sortInd = ind_d.get(); onGPU.nv = nv_d.get(); + onGPU.nv2 = nv2_d.get(); onGPU.izt = izt_d.get(); onGPU.nn = nn_d.get(); onGPU.iv = iv_d.get(); @@ -167,14 +174,69 @@ int main() { ev.ztrack.size(), onGPU_d.get(),kk,0.7f*eps, 0.01f,9.0f ); - + cudaCheck(cudaGetLastError()); cudaDeviceSynchronize(); + + cuda::launch(fitVertices, + { 1,1024-256 }, + ev.ztrack.size(), onGPU_d.get() + ); + cudaCheck(cudaGetLastError()); + + uint32_t nv; + cuda::memory::copy(&nv, onGPU.nv, 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 }, + ev.ztrack.size(), onGPU_d.get() + ); + cuda::memory::copy(&nv, onGPU.nv, 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 }, + ev.ztrack.size(), onGPU_d.get(), + 9.f + ); + cuda::memory::copy(&nv, onGPU.nv2, sizeof(uint32_t)); + std::cout << "after split " << nv << std::endl; + + cuda::launch(fitVertices, + { 1,1024-256 }, + ev.ztrack.size(), onGPU_d.get() + ); + cudaCheck(cudaGetLastError()); + + cuda::launch(sortByPt2, { 1, 256 }, ev.ztrack.size(), onGPU_d.get() ); - uint32_t nv; cuda::memory::copy(&nv, onGPU.nv, sizeof(uint32_t)); if (nv==0) { @@ -182,12 +244,7 @@ int main() { 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 +252,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 +270,15 @@ int main() { } float dd[nv]; - uint32_t ii=0; - for (auto zr : zv) { + for (auto kv=0U; kv Date: Fri, 5 Oct 2018 17:25:08 +0200 Subject: [PATCH 03/94] fix iterations --- .../PixelVertexFinding/src/gpuSplitVertices.h | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h index e423c7f9e936a..39dda6e2b9fa1 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -21,7 +21,7 @@ namespace gpuVertexFinder { float maxChi2 ) { - constexpr bool verbose = false; // in principle the compiler should optmize out if false + constexpr bool verbose = true; // in principle the compiler should optmize out if false auto & __restrict__ data = *pdata; @@ -68,10 +68,6 @@ namespace gpuVertexFinder { __shared__ float znew[2], wnew[2]; // the new vertices - znew[0]=0; znew[1]=0; - wnew[0]=0; wnew[1]=0; - - __syncthreads(); assert(nq==nn[kv]+1); @@ -80,6 +76,12 @@ namespace gpuVertexFinder { // kt-min.... bool more = true; while(__syncthreads_or(more) ) { + more = false; + if(0==threadIdx.x) { + znew[0]=0; znew[1]=0; + wnew[0]=0; wnew[1]=0; + } + __syncthreads(); for (auto k = threadIdx.x; k Date: Sun, 7 Oct 2018 14:26:24 +0200 Subject: [PATCH 04/94] apply outlier rejection, tune error --- .../src/PixelVertexHeterogeneousProducer.cc | 2 +- .../PixelVertexFinding/src/gpuFitVertices.h | 5 +++-- .../PixelVertexFinding/src/gpuSplitVertices.h | 10 ++++++---- .../PixelVertexFinding/src/gpuVertexFinder.cu | 4 ++-- .../PixelVertexFinding/test/gpuVertexFinder_t.cu | 6 +++--- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 0b8c31235abea..70cdaed2f2959 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -171,7 +171,7 @@ 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; k9990) continue; auto c2 = zv[iv[i]]-zt[i]; c2 *=c2/ezt2[i]; - // remove outliers ???? if (c2> cut) {iv[i] = 9999; continue;}???? + if (c2 > chi2Max ) {iv[i] = 9999; continue;} atomicAdd(&chi2[iv[i]],c2); atomicAdd(&nn[iv[i]],1); } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h index 39dda6e2b9fa1..131b1f96c5c0b 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -28,7 +28,7 @@ namespace gpuVertexFinder { float const * __restrict__ zt = data.zt; float const * __restrict__ ezt2 = data.ezt2; float * __restrict__ zv = data.zv; - // float * __restrict__ wv = data.wv; + float * __restrict__ wv = data.wv; float const * __restrict__ chi2 = data.chi2; uint32_t & nv = *data.nv; @@ -105,11 +105,13 @@ namespace gpuVertexFinder { } // quality cut - auto chi2Dist = (znew[0]-znew[1])*(znew[0]-znew[1])/(1.f/wnew[0]+1.f/wnew[1]); + auto dist2 = (znew[0]-znew[1])*(znew[0]-znew[1]); - if(verbose && 0==threadIdx.x) printf("inter %d %f\n",20-maxiter,chi2Dist); + auto chi2Dist = dist2/(1.f/wnew[0]+1.f/wnew[1]); + + if(verbose && 0==threadIdx.x) printf("inter %d %f %f\n",20-maxiter,chi2Dist, dist2*wv[kv]); - if (chi2Dist<2) return; + if (chi2Dist<4) return; // get a new global vertex __shared__ uint32_t igv; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu index 74ae3d04114bb..a8b7cdcc78f5d 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu @@ -68,12 +68,12 @@ namespace gpuVertexFinder { assert(onGPU_d); clusterTracks<<<1,1024-256,0,stream>>>(ntrks,onGPU_d,minT,eps,errmax,chi2max); cudaCheck(cudaGetLastError()); - fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d); + fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d,50.); cudaCheck(cudaGetLastError()); splitVertices<<<1024,128,0,stream>>>(ntrks,onGPU_d,9.f); cudaCheck(cudaGetLastError()); - fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d); + fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d,5000.); cudaCheck(cudaGetLastError()); sortByPt2<<<1,256,0,stream>>>(ntrks,onGPU_d); diff --git a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu index ef52a403dd8cd..ae7014fb161ae 100644 --- a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu +++ b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu @@ -179,7 +179,7 @@ int main() { cuda::launch(fitVertices, { 1,1024-256 }, - ev.ztrack.size(), onGPU_d.get() + ev.ztrack.size(), onGPU_d.get(),50.f ); cudaCheck(cudaGetLastError()); @@ -206,7 +206,7 @@ int main() { cuda::launch(fitVertices, { 1,1024-256 }, - ev.ztrack.size(), onGPU_d.get() + ev.ztrack.size(), onGPU_d.get(), 50.f ); cuda::memory::copy(&nv, onGPU.nv, sizeof(uint32_t)); cuda::memory::copy(&nn, onGPU.nn, nv*sizeof(int32_t)); @@ -227,7 +227,7 @@ int main() { cuda::launch(fitVertices, { 1,1024-256 }, - ev.ztrack.size(), onGPU_d.get() + ev.ztrack.size(), onGPU_d.get(),5000.f ); cudaCheck(cudaGetLastError()); From 5e9d7cfc5a93f446061b8a1321b5e32f830ee6a5 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 19 Oct 2018 17:29:20 +0200 Subject: [PATCH 05/94] fix duplicate cleaning --- .../src/PixelTrackCleanerBySharedHits.cc | 79 +++++++++++++++---- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 7 +- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 2 + 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index 588f94daa7aa5..e2bd3ad3d341a 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -27,34 +27,82 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) auto kill = [&](unsigned int i) { delete trackHitPairs[i].first; trackHitPairs[i].first=nullptr;}; - for (auto iTrack1 = 0U; iTrack1 < size; iTrack1++) { + auto iTrack1 = 0U; + auto iTrack2 = 0U; + auto track1 = trackHitPairs[iTrack1].first; + auto track2 = trackHitPairs[iTrack1].first; + auto cleanTrack = [&](){ + if (track1->pt() > track2->pt()) { kill(iTrack2); return false; } +// if (track1->chi2() < track2->chi2()) { kill(iTrack2); return false; } + kill(iTrack1); + return true; + }; - auto track1 = trackHitPairs[iTrack1].first; + // first loop: only first two hits.... + for (iTrack1 = 0U; iTrack1 < size; iTrack1++) { + track1 = trackHitPairs[iTrack1].first; if (!track1) continue; + auto const & recHits1 = trackHitPairs[iTrack1].second; + for (iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) + { + track2 = trackHitPairs[iTrack2].first; + if (!track2) continue; + auto const & recHits2 = trackHitPairs[iTrack2].second; + auto commonRecHits = 0U; + // first loop: only first two hits.... + for (auto j=0; j<2; j++) { + if (recHits1[j] == recHits2[j]) ++commonRecHits; + } + if (commonRecHits > 1) { + if(cleanTrack()) break; + } + } // tk2 + } // tk1 + + + // second loop: first and third hits.... + for (iTrack1 = 0U; iTrack1 < size; iTrack1++) { + track1 = trackHitPairs[iTrack1].first; + if (!track1) continue; + auto const & recHits1 = trackHitPairs[iTrack1].second; + for (iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) + { + track2 = trackHitPairs[iTrack2].first; + if (!track2) continue; + auto const & recHits2 = trackHitPairs[iTrack2].second; + auto commonRecHits = 0U; + // first loop: only first two hits.... + for (auto j=0; j<3; j+=2) { + if (recHits1[j] == recHits2[j]) ++commonRecHits; + } + if (commonRecHits > 1) { + if(cleanTrack()) break; + } + } // tk2 + } // tk1 + + + // final loop: all the rest + for (iTrack1 = 0U; iTrack1 < size; iTrack1++) { + track1 = trackHitPairs[iTrack1].first; + if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; auto s1 = recHits1.size(); - for (auto iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) + for (iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) { - auto track2 = trackHitPairs[iTrack2].first; + track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; - auto s2 = recHits2.size(); - auto f2=0U; + auto s2 = recHits1.size(); auto commonRecHits = 0U; + auto f2=0U; for (auto iRecHit1 = 0U; iRecHit1 < s1; ++iRecHit1) { for (auto iRecHit2 = f2; iRecHit2 < s2; ++iRecHit2) { if (recHits1[iRecHit1] == recHits2[iRecHit2]) { ++commonRecHits; f2=iRecHit2+1; break;} // if a hit is common, no other can be the same! } if (commonRecHits > 1) break; } - - auto cleanTrack = [&](){ - if (track1->pt() > track2->pt()) { kill(iTrack2); return false; } - kill(iTrack1); - return true; - }; - if(useQuadrupletAlgo_) { if(commonRecHits >= 1) { if (s1 > s2) kill(iTrack2); @@ -66,8 +114,9 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) else if (commonRecHits > 1) { if(cleanTrack()) break; } - } - } + } // tk2 + } //tk1 trackHitPairs.erase(std::remove_if(trackHitPairs.begin(),trackHitPairs.end(),[&](TrackWithTTRHs & v){ return nullptr==v.first;}),trackHitPairs.end()); + std::cout << "Q after clean " << trackHitPairs.size() << std::endl; } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 708e7ec92a78b..a042e66698ca5 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -118,6 +118,8 @@ void CAHitQuadrupletGeneratorGPU::fillResults( std::array barrels; std::array phits; + + int nbad=0; // loop over quadruplets for (unsigned int quadId = 0; quadId < numberOfFoundQuadruplets; ++quadId) { auto isBarrel = [](const unsigned id) -> bool { @@ -139,7 +141,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( barrels[i] = isBarrel(ahit.geographicalId().subdetId()); } - if (bad) continue; + if (bad) { nbad++; continue;} // TODO: // - if we decide to always do the circle fit for 4 hits, we don't @@ -198,4 +200,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); } // end loop over quads + + std::cout << "Q Final quads " << result[index].size() << ' ' << nbad << std::endl; + } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index d6d6e2bdf24ff..5acd563535248 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -410,6 +410,8 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) 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]; } + std::cout << "Q Produced " << quadsInterface.size() << " quadruplets" << std::endl; + return quadsInterface; } From 2e7910f56636f7ea7d0b2ba1e379da9d45d6b1b2 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 20 Oct 2018 18:58:06 +0200 Subject: [PATCH 06/94] sort and clean --- .../src/PixelTrackCleanerBySharedHits.cc | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index e2bd3ad3d341a..6737e3836466f 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -4,6 +4,7 @@ #include "DataFormats/TrackReco/interface/Track.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include using namespace std; using namespace reco; @@ -25,72 +26,78 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) unsigned int size = trackHitPairs.size(); if (size <= 1) return; - auto kill = [&](unsigned int i) { delete trackHitPairs[i].first; trackHitPairs[i].first=nullptr;}; + // sort (stabilize cleaning) + float pt[size]; + unsigned int ind[size]; + for (auto i = 0U; i < size; ++i) {ind[i]=i; pt[i]=trackHitPairs[i].first->pt();} + std::sort(ind,ind+size,[&](unsigned int i, unsigned int j){return pt[i]>pt[j];}); + assert(pt[ind[0]]>=pt[ind[size-1]]); + + int killed=0; + auto kill = [&](unsigned int k) { assert(trackHitPairs[k].first); killed++; delete trackHitPairs[k].first; trackHitPairs[k].first=nullptr;}; auto iTrack1 = 0U; auto iTrack2 = 0U; auto track1 = trackHitPairs[iTrack1].first; auto track2 = trackHitPairs[iTrack1].first; auto cleanTrack = [&](){ - if (track1->pt() > track2->pt()) { kill(iTrack2); return false; } -// if (track1->chi2() < track2->chi2()) { kill(iTrack2); return false; } - kill(iTrack1); - return true; + auto mpt = track1->pt(); // larger pt as sorted + if (mpt<2.) { // tuned + kill(iTrack2); return false; // lower pt + }else{ + if (track1->chi2() < track2->chi2()) { kill(iTrack2); return false; } + } + kill(iTrack1); + return true; }; // first loop: only first two hits.... - for (iTrack1 = 0U; iTrack1 < size; iTrack1++) { + for (auto i = 0U; i < size; ++i) { + iTrack1 = ind[i]; track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; - for (iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) - { + for (auto j = i+1; j < size; ++j) { + iTrack2 = ind[j]; track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; - auto commonRecHits = 0U; - // first loop: only first two hits.... - for (auto j=0; j<2; j++) { - if (recHits1[j] == recHits2[j]) ++commonRecHits; - } - if (commonRecHits > 1) { - if(cleanTrack()) break; - } + if (recHits1[0] != recHits2[0]) continue; + if (recHits1[1] != recHits2[1]) continue; + if(cleanTrack()) break; } // tk2 } // tk1 // second loop: first and third hits.... - for (iTrack1 = 0U; iTrack1 < size; iTrack1++) { + for (auto i = 0U; i < size; ++i) { + iTrack1 = ind[i]; track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; - for (iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) - { + if (recHits1.size()<3) continue; + for (auto j = i+1; j < size; ++j) { + iTrack2 = ind[j]; track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; - auto commonRecHits = 0U; - // first loop: only first two hits.... - for (auto j=0; j<3; j+=2) { - if (recHits1[j] == recHits2[j]) ++commonRecHits; - } - if (commonRecHits > 1) { - if(cleanTrack()) break; - } + if (recHits2.size()<3) continue; + if (recHits1[0] != recHits2[0]) continue; + if (recHits1[2] != recHits2[2]) continue; + if(cleanTrack()) break; } // tk2 } // tk1 - // final loop: all the rest - for (iTrack1 = 0U; iTrack1 < size; iTrack1++) { + for (auto i = 0U; i < size; ++i) { + iTrack1 = ind[i]; track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; auto s1 = recHits1.size(); - for (iTrack2 = iTrack1 + 1U; iTrack2 < size; iTrack2++) - { + for (auto j = i+1; j < size; ++j) { + iTrack2 = ind[j]; track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; @@ -118,5 +125,5 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) } //tk1 trackHitPairs.erase(std::remove_if(trackHitPairs.begin(),trackHitPairs.end(),[&](TrackWithTTRHs & v){ return nullptr==v.first;}),trackHitPairs.end()); - std::cout << "Q after clean " << trackHitPairs.size() << std::endl; + std::cout << "Q after clean " << trackHitPairs.size() << ' ' << killed << std::endl; } From 2d155f14cb0ea3497724d41358dc383d16c3e28b Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 26 Oct 2018 16:56:29 +0200 Subject: [PATCH 07/94] fishbone works --- .../src/PixelTrackCleanerBySharedHits.cc | 62 ++++++++----------- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 25 +++++++- .../PixelTriplets/plugins/GPUCACell.h | 3 + 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index 6737e3836466f..921de115c4271 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -5,6 +5,7 @@ #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "DataFormats/TrackerCommon/interface/TrackerTopology.h" #include +#include using namespace std; using namespace reco; @@ -27,78 +28,68 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) if (size <= 1) return; // sort (stabilize cleaning) - float pt[size]; + uint16_t score[size]; unsigned int ind[size]; - for (auto i = 0U; i < size; ++i) {ind[i]=i; pt[i]=trackHitPairs[i].first->pt();} - std::sort(ind,ind+size,[&](unsigned int i, unsigned int j){return pt[i]>pt[j];}); - assert(pt[ind[0]]>=pt[ind[size-1]]); + for (auto i = 0U; i < size; ++i) { + ind[i]=i; + score[i]= 32000-std::min(32000, int(trackHitPairs[i].first->chi2()*100.f)); // chi2: smaller is better + if (trackHitPairs[i].second.size()==4) score[i]+=32001; // s4 always better than s3 + } + std::sort(ind,ind+size,[&](unsigned int i, unsigned int j){return score[i]>score[j];}); int killed=0; auto kill = [&](unsigned int k) { assert(trackHitPairs[k].first); killed++; delete trackHitPairs[k].first; trackHitPairs[k].first=nullptr;}; - auto iTrack1 = 0U; - auto iTrack2 = 0U; - auto track1 = trackHitPairs[iTrack1].first; - auto track2 = trackHitPairs[iTrack1].first; - auto cleanTrack = [&](){ - auto mpt = track1->pt(); // larger pt as sorted - if (mpt<2.) { // tuned - kill(iTrack2); return false; // lower pt - }else{ - if (track1->chi2() < track2->chi2()) { kill(iTrack2); return false; } - } - kill(iTrack1); - return true; - }; + // sorted: first is always better! // first loop: only first two hits.... for (auto i = 0U; i < size; ++i) { - iTrack1 = ind[i]; - track1 = trackHitPairs[iTrack1].first; + auto iTrack1 = ind[i]; + auto track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; for (auto j = i+1; j < size; ++j) { - iTrack2 = ind[j]; - track2 = trackHitPairs[iTrack2].first; + auto iTrack2 = ind[j]; + auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; if (recHits1[0] != recHits2[0]) continue; if (recHits1[1] != recHits2[1]) continue; - if(cleanTrack()) break; + kill(iTrack2); } // tk2 } // tk1 // second loop: first and third hits.... for (auto i = 0U; i < size; ++i) { - iTrack1 = ind[i]; - track1 = trackHitPairs[iTrack1].first; + auto iTrack1 = ind[i]; + auto track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; if (recHits1.size()<3) continue; for (auto j = i+1; j < size; ++j) { - iTrack2 = ind[j]; - track2 = trackHitPairs[iTrack2].first; + auto iTrack2 = ind[j]; + auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; if (recHits2.size()<3) continue; if (recHits1[0] != recHits2[0]) continue; if (recHits1[2] != recHits2[2]) continue; - if(cleanTrack()) break; + kill(iTrack2); } // tk2 } // tk1 // final loop: all the rest for (auto i = 0U; i < size; ++i) { - iTrack1 = ind[i]; - track1 = trackHitPairs[iTrack1].first; + auto iTrack1 = ind[i]; + auto track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; auto s1 = recHits1.size(); for (auto j = i+1; j < size; ++j) { - iTrack2 = ind[j]; - track2 = trackHitPairs[iTrack2].first; + auto iTrack2 = ind[j]; + auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; auto s2 = recHits1.size(); @@ -112,14 +103,11 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) } if(useQuadrupletAlgo_) { if(commonRecHits >= 1) { - if (s1 > s2) kill(iTrack2); - else if(s1 < s2) { kill(iTrack1); break;} - else if(s1 == 3) { if(cleanTrack()) break; } // same number of hits - else if(commonRecHits > 1) { if(cleanTrack()) break; }// same number of hits, size != 3 (i.e. == 4) + if(s1 == 3 || commonRecHits > 1) { kill(iTrack2); } } } else if (commonRecHits > 1) { - if(cleanTrack()) break; + kill(iTrack2); } } // tk2 } //tk1 diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 5acd563535248..b179e370532f2 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -10,6 +10,8 @@ #include "CAHitQuadrupletGeneratorGPU.h" #include "GPUCACell.h" #include "gpuPixelDoublets.h" +#include"gpuFishbone.h" +using namespace gpuPixelDoublets; using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; using namespace Eigen; @@ -157,6 +159,10 @@ void kernel_checkOverflows(GPU::SimpleVector *foundNtuplets, GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, uint32_t nHits, uint32_t maxNumberOfDoublets) { + __shared__ uint32_t killedCell; + killedCell=0; + __syncthreads(); + auto idx = threadIdx.x + blockIdx.x * blockDim.x; #ifdef GPU_DEBUG if (0==idx) @@ -166,11 +172,15 @@ void kernel_checkOverflows(GPU::SimpleVector *foundNtuplets, auto &thisCell = cells[idx]; if (thisCell.theOuterNeighbors.full()) //++tooManyNeighbors[thisCell.theLayerPairId]; printf("OuterNeighbors overflow %d in %d\n", idx, thisCell.theLayerPairId); + if (thisCell.theDoubletId<0) atomicInc(&killedCell,maxNumberOfDoublets); } if (idx < nHits) { if (isOuterHitOfCell[idx].full()) // ++tooManyOuterHitOfCell; printf("OuterHitOfCell overflow %d\n", idx); } + + __syncthreads(); +// if (threadIdx.x==0) printf("number of killed cells %d\n",killedCell); } @@ -196,12 +206,13 @@ kernel_connect(GPU::SimpleVector *foundNtuplets, 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) @@ -331,7 +342,17 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, auto nhits = hh.nHits; assert(nhits <= PixelGPUConstants::maxNumberOfHits); auto blockSize = 64; - auto numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; + + auto numberOfBlocks = (nhits + blockSize - 1)/blockSize; + + fishbone<<>>( + hh.gpu_d, + device_theCells_, device_nCells_, + device_isOuterHitOfCell_, + nhits + ); + + numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; kernel_connect<<>>( d_foundNtupletsVec_[regionIndex], // needed only to be reset, ready for next kernel hh.gpu_d, diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 772b802282d31..b29597394fd37 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -49,6 +49,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; } From aad5235d92fb4657f4e742db64cd5f555fbbed55 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 26 Oct 2018 16:56:35 +0200 Subject: [PATCH 08/94] fishbone works --- .../PixelTriplets/plugins/gpuFishbone.h | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h new file mode 100644 index 0000000000000..0b7581fae852f --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -0,0 +1,74 @@ +#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 "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, + GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, + uint32_t nHits) { + auto idx = threadIdx.x + blockIdx.x * blockDim.x; + if (idx>=nHits) return; + auto const & vc = isOuterHitOfCell[idx]; + auto s = vc.size(); + if (s<2) return; + auto const & hh = *hhp; + // if alligned kill one of the two. + auto const & c0 = cells[vc[0]]; + // auto d0 = c0.get_outer_detId(hh); + auto xo = c0.get_outer_x(hh); + auto yo = c0.get_outer_y(hh); + auto zo = c0.get_outer_z(hh); + float x[256], y[256],z[256], n[256]; + uint16_t d[256]; + for (uint32_t ic=0; ic0.99999f*n[ic]*n[jc]) { + // alligned (kill closest) + if (n[ic] Date: Fri, 26 Oct 2018 17:57:44 +0200 Subject: [PATCH 09/94] fishbone works --- RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 0b7581fae852f..50f2a35a4d63e 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -55,7 +55,7 @@ namespace gpuPixelDoublets { if (d[ic]==d[jc]) continue; auto cos12 = x[ic]*x[jc]+y[ic]*y[jc]+z[ic]*z[jc]; // assert(cos12*cos12<1.01f*n1*n2); - if (cos12*cos12>0.99999f*n[ic]*n[jc]) { + if (cos12*cos12>0.999999f*n[ic]*n[jc]) { // alligned (kill closest) if (n[ic] Date: Sun, 28 Oct 2018 17:35:55 +0100 Subject: [PATCH 10/94] add layerid --- .../interface/phase1PixelTopology.h | 24 ++++++++++++++++ .../test/phase1PixelTopology_t.cpp | 8 ++++++ .../plugins/CAHitQuadrupletGeneratorGPU.cu | 3 +- .../PixelTriplets/plugins/gpuFishbone.h | 28 +++++++++++++++---- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h index 37c97a92a3eaa..76edccf3553d4 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,29 @@ namespace phase1PixelTopology { }; + template + constexpr auto make_array_helper(Function f, std::index_sequence) + -> std::array::type, sizeof...(Indices)> + { + return {{ f(Indices)... }}; + } + + template + constexpr auto make_array(Function f) + -> std::array::type, N> + { + return make_array_helper(f, std::make_index_sequence{}); + } + + + constexpr uint8_t findLayer(uint32_t detId) { + for (uint8_t i=0; i<11; ++i) if (detId layer = make_array(findLayer); + + // this is for the ROC n<512 (upgrade 1024) constexpr inline uint16_t divu52(uint16_t n) { diff --git a/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp b/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp index b9f9a4f3b3487..43128fd02e7f6 100644 --- a/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp +++ b/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp @@ -141,5 +141,13 @@ int main() { assert(std::get<1>(ori)==bp); } + using namespace phase1PixelTopology; + for (auto i=0U; i=layerStart[layer[i]]); + assert(i const * __restrict__ isOuterHitOfCell, - uint32_t nHits) { + uint32_t nHits, + uint8_t const * __restrict__ layer) { auto idx = threadIdx.x + blockIdx.x * blockDim.x; if (idx>=nHits) return; auto const & vc = isOuterHitOfCell[idx]; @@ -37,9 +39,10 @@ namespace gpuPixelDoublets { auto yo = c0.get_outer_y(hh); auto zo = c0.get_outer_z(hh); float x[256], y[256],z[256], n[256]; - uint16_t d[256]; + uint16_t d[256]; bool kill[256]; for (uint32_t ic=0; ic0.999999f*n[ic]*n[jc]) { + if (cos12*cos12 >= 0.999999f*n[ic]*n[jc]) { // alligned (kill closest) if (n[ic] Date: Sun, 28 Oct 2018 18:16:11 +0100 Subject: [PATCH 11/94] copy layer on gpu --- RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu | 8 ++++++++ RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.h | 1 + .../plugins/siPixelRecHitsHeterogeneousProduct.h | 5 +++++ .../src/PixelTrackCleanerBySharedHits.cc | 2 +- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu | 3 +-- RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h | 9 +++++---- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu index c63466f157a1b..3a80ac31c8ead 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu @@ -81,6 +81,14 @@ namespace pixelgpudetails { // 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))); 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/siPixelRecHitsHeterogeneousProduct.h b/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h index 9d0fe7a279799..e250ee0cb0469 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h +++ b/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h @@ -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/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index 921de115c4271..5648d7211deef 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -92,7 +92,7 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; - auto s2 = recHits1.size(); + auto s2 = recHits2.size(); auto commonRecHits = 0U; auto f2=0U; for (auto iRecHit1 = 0U; iRecHit1 < s1; ++iRecHit1) { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 582ff3c7ff46d..b179e370532f2 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -349,8 +349,7 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, hh.gpu_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, - nhits, - phase1PixelTopology::layer.data() + nhits ); numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 5872c626e08ee..f75528bf4ea58 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -24,14 +24,15 @@ namespace gpuPixelDoublets { GPUCACell::Hits const * __restrict__ hhp, GPUCACell * cells, uint32_t const * __restrict__ nCells, GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, - uint32_t nHits, - uint8_t const * __restrict__ layer) { + uint32_t nHits) { + auto const & hh = *hhp; + uint8_t const * __restrict__ layer = hh.phase1TopologyLayer_d; + auto idx = threadIdx.x + blockIdx.x * blockDim.x; if (idx>=nHits) return; auto const & vc = isOuterHitOfCell[idx]; auto s = vc.size(); if (s<2) return; - auto const & hh = *hhp; // if alligned kill one of the two. auto const & c0 = cells[vc[0]]; // auto d0 = c0.get_outer_detId(hh); @@ -63,7 +64,7 @@ namespace gpuPixelDoublets { // if (phase1PixelTopology::layer[d[ic]]!=phase1PixelTopology::layer[d[jc]]) continue; auto cos12 = x[ic]*x[jc]+y[ic]*y[jc]+z[ic]*z[jc]; // assert(cos12*cos12<1.01f*n1*n2); - if (cos12*cos12 >= 0.999999f*n[ic]*n[jc]) { + if (cos12*cos12 >= 0.9999f*n[ic]*n[jc]) { // alligned (kill closest) if (n[ic] Date: Mon, 29 Oct 2018 12:57:17 +0100 Subject: [PATCH 12/94] efficient --- .../interface/phase1PixelTopology.h | 39 ++++++++++++++++++- .../PixelTriplets/plugins/GPUCACell.h | 2 +- .../PixelTriplets/plugins/gpuFishbone.h | 35 ++++++++--------- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h index 76edccf3553d4..0ab487d0477e9 100644 --- a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h +++ b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h @@ -45,12 +45,49 @@ namespace phase1PixelTopology { } + 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 = make_array(findLayer); + constexpr uint8_t findLayerFromCompact(uint32_t detId) { + detId*=maxModuleStride; + for (uint8_t i=0; i<11; ++i) if (detId layer = make_array(findLayerFromCompact); + + constexpr bool validateLayerIndex() { + bool res=true; + for (auto i=0U; i=layerStart[layer[j]]); + res &=(i const * __restrict__ isOuterHitOfCell, uint32_t nHits) { auto const & hh = *hhp; - uint8_t const * __restrict__ layer = hh.phase1TopologyLayer_d; + uint8_t const * __restrict__ layerp = hh.phase1TopologyLayer_d; + auto layer = [&](uint16_t id) { return __ldg(layerp+id/phase1PixelTopology::maxModuleStride);}; auto idx = threadIdx.x + blockIdx.x * blockDim.x; if (idx>=nHits) return; @@ -40,11 +41,13 @@ namespace gpuPixelDoublets { auto yo = c0.get_outer_y(hh); auto zo = c0.get_outer_z(hh); float x[256], y[256],z[256], n[256]; - uint16_t d[256]; bool kill[256]; + uint16_t d[256]; uint8_t l[256]; + // bool kill[256]; for (uint32_t ic=0; ic= 0.9999f*n[ic]*n[jc]) { // alligned (kill closest) if (n[ic] Date: Mon, 29 Oct 2018 14:39:08 +0100 Subject: [PATCH 13/94] optimize parallelization --- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 6 +++--- .../PixelTriplets/plugins/gpuFishbone.h | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index b179e370532f2..7d821eb295646 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -342,14 +342,14 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, auto nhits = hh.nHits; assert(nhits <= PixelGPUConstants::maxNumberOfHits); auto blockSize = 64; - + auto stride = 4; auto numberOfBlocks = (nhits + blockSize - 1)/blockSize; - + numberOfBlocks *=stride; fishbone<<>>( hh.gpu_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, - nhits + nhits, stride ); numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 1ce4d4e92a1e1..7be83f10bdd00 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -24,12 +24,17 @@ namespace gpuPixelDoublets { GPUCACell::Hits const * __restrict__ hhp, GPUCACell * cells, uint32_t const * __restrict__ nCells, GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, - uint32_t nHits) { + uint32_t nHits, + uint32_t stride) { auto const & hh = *hhp; uint8_t const * __restrict__ layerp = hh.phase1TopologyLayer_d; auto layer = [&](uint16_t id) { return __ldg(layerp+id/phase1PixelTopology::maxModuleStride);}; - auto idx = threadIdx.x + blockIdx.x * blockDim.x; + 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(); @@ -53,7 +58,9 @@ namespace gpuPixelDoublets { z[ic] = ci.get_inner_z(hh) -zo; n[ic] = x[ic]*x[ic]+y[ic]*y[ic]+z[ic]*z[ic]; } - for (uint32_t ic=0; ic Date: Wed, 31 Oct 2018 10:25:38 +0100 Subject: [PATCH 14/94] update notebook to include fishbone --- .../PixelTriplets/test/pixHits.ipynb | 1110 ++++++----------- 1 file changed, 351 insertions(+), 759 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb index ee8295f85a00a..2b2e9de82e87d 100644 --- a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb +++ b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 74, "metadata": {}, "outputs": [ { @@ -29,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 76, "metadata": {}, "outputs": [ { @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ @@ -133,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 78, "metadata": {}, "outputs": [], "source": [ @@ -175,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 79, "metadata": {}, "outputs": [], "source": [ @@ -207,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 80, "metadata": {}, "outputs": [], "source": [ @@ -224,7 +224,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -241,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 82, "metadata": {}, "outputs": [], "source": [ @@ -258,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 83, "metadata": {}, "outputs": [ { @@ -278,7 +278,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 84, "metadata": {}, "outputs": [ { @@ -336,7 +336,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 85, "metadata": {}, "outputs": [], "source": [ @@ -345,7 +345,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 86, "metadata": {}, "outputs": [ { @@ -613,7 +613,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 87, "metadata": {}, "outputs": [ { @@ -714,7 +714,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 88, "metadata": {}, "outputs": [], "source": [ @@ -723,27 +723,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": 89, "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": 91, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAD1lJREFUeJzt3X+s3Xddx/Hnyy2bEVyBbSRkXemwc1ITfnntjEZBdLHbLANcZI1RhEozzDSamFCCf5EsjpiIWVhCqs4yTTanKOlcySDgUo0DOlBYt2ajFMg6SOqcTmPUOXj7x/0ODpfe9vz63u+5n/t8JCf3nM/5cT/ve9rX/dz393POSVUhSWrX9ww9AUlSvwx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuPOHXoCABdddFFt3bp16GlI0rry2c9+9smquvhstxs06JPsAnZt27aNBx98cMipSNK6k+Sr49xu0NZNVd1TVXs3bdo05DQkqWn26CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjDHpJatxCvDJWkjaSrfvu/db5r9xybe/fzxW9JDWul6BP8rwkDyb5+T4eX5I0vrGCPsntSU4lObpifGeSR5McT7Jv5Kp3AXfPc6KSpOmMu6I/AOwcHUhyDnAbcDWwHdidZHuSq4BHgFNznKckaUpjHYytqsNJtq4Y3gEcr6oTAEnuAq4Dng88j+Xw/+8kh6rqm3ObsSRpIrPsurkEeHzk8kngyqq6CSDJrwJPrhbySfYCewG2bNkywzQkSWfS266bqjpQVX97huv3V9VSVS1dfPFZ3zdfkjSlWYL+CeDSkcubuzFJ0gKZJeiPAJcnuSzJecANwMFJHiDJriT7n3766RmmIUk6k3G3V94JPABckeRkkj1V9SxwE3AfcAy4u6oenuSb+wlTktS/cXfd7F5l/BBwaNpvPvqZsZKkfviZsZLUON/rRpIaN2jQezBWkvpn60aSGmfrRpIaZ9BLUuPs0UtS4+zRS1LjbN1IUuMMeklqnD16SWqcPXpJapytG0lqnEEvSY0z6CWpcR6MlaTGeTBWkhpn60aSGmfQS1LjDHpJapxBL0mNc9eNJDXOXTeS1DhbN5LUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc4XTElS43zBlCQ1ztaNJDXOoJekxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMbNPeiTvDzJB5P8VZJ3zvvxJUmTGSvok9ye5FSSoyvGdyZ5NMnxJPsAqupYVd0I/CLwE/OfsiRpEuOu6A8AO0cHkpwD3AZcDWwHdifZ3l33BuBe4NDcZipJmspYQV9Vh4GnVgzvAI5X1Ymqega4C7iuu/3Bqroa+KV5TlaSNLlzZ7jvJcDjI5dPAlcmeR3wZuB8zrCiT7IX2AuwZcuWGaYhSTqTWYL+tKrqfuD+MW63H9gPsLS0VPOehyRp2Sy7bp4ALh25vLkbG5ufMCVJ/Zsl6I8Alye5LMl5wA3AwUkewE+YkqT+jbu98k7gAeCKJCeT7KmqZ4GbgPuAY8DdVfVwf1OVJE1jrB59Ve1eZfwQM2yhTLIL2LVt27ZpH0KSdBZ+OLgkNc73upGkxg0a9O66kaT+2bqRpMbZupGkxhn0ktQ4e/SS1Dh79JLUOFs3ktQ4g16SGmePXpIaZ49ekhpn60aSGmfQS1LjDHpJapwHYyWpcR6MlaTG2bqRpMYZ9JLUOINekhpn0EtS49x1I0mNO3fIb15V9wD3LC0tvWPIeUhS37buu3ew723rRpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxvmCKUlqnG9TLEmNs3UjSY0b9C0QJKlVQ77lwUqu6CWpcQa9JDXOoJekxhn0ktQ4g16SGueuG0mak0XaaTPKFb0kNc6gl6TG9dK6SfJG4FrgAuBPqupjfXwfSdLZjb2iT3J7klNJjq4Y35nk0STHk+wDqKqPVNU7gBuBt8x3ypKkSUyyoj8AfAC447mBJOcAtwFXASeBI0kOVtUj3U1+t7tekpq0qAdgR429oq+qw8BTK4Z3AMer6kRVPQPcBVyXZe8DPlpVn5vfdCVJk5q1R38J8PjI5ZPAlcBvAD8LbEqyrao+uPKOSfYCewG2bNky4zQkqV+jK/ev3HLtgDOZXC8HY6vqVuDWs9xmP7AfYGlpqfqYhyT1YT20a0bNur3yCeDSkcubu7Gx+AlTktS/WYP+CHB5ksuSnAfcABwc985+wpQk9W+S7ZV3Ag8AVyQ5mWRPVT0L3ATcBxwD7q6qh/uZqiRpGmP36Ktq9yrjh4BD03zzJLuAXdu2bZvm7pI0d+v5oOtqBn1Ts6q6B7hnaWnpHUPOQ9LGttrB1fV20HU1vteNJDVu0KB3140k9W/QoHfXjST1z9aNJDXO1o0kNc5dN5Kas9oWyRa3To7Dz4yVtCG1snVyHPboJalxg67ofWWspL5tpJX7atxeKUmNs3UjSY3zYKykJtiiWZ1BL2ld2ahbJGfhwVhJC8/V+mx8wZSkheFqvR+2biQZsI0z6CUtJNs18+P2SklqnCt6aYNyxbxxGPSS1i1/WY3H7ZWSBmVY98/3upGkxtm6kTSTlStyt2cuHoNe0lyN8+lOWlsGvbSODfVCJ0N7fXEfvSQ1zhW9mufL+4fjyn8xGPTSgprlF9RqATvLLzpDe/0y6KUptdof9y+g9viCKS2UjRgyG7FmrS3fj16aM4Nbi8ZdN5LUOINekhrnwVg1aa13iKzlAVJpUga9BjGvPrb98Mn4C2NjsnUjSY1zRS8NYLWV9aKtuBdtPppOU0G/KH/GL8o8pjXN284uQs0bPZQ2ev1ana0bSWpcUyv69arv1fAirLbnaaiVqytmrVeu6CWpcXNf0Sd5GfAeYFNVXT/vxx9Xa6vYcaxFzev95zrO/Nd7jdJKY63ok9ye5FSSoyvGdyZ5NMnxJPsAqupEVe3pY7KSpMmNu6I/AHwAuOO5gSTnALcBVwEngSNJDlbVI/Oe5Jls9L7petmmB4s5p+cs8tykWY21oq+qw8BTK4Z3AMe7FfwzwF3AdXOenyRpRrP06C8BHh+5fBK4MsmFwM3Aq5O8u6p+73R3TrIX2AuwZcuWGaZxduP2XGf5VB5XhPO3CO8f4/OqFsz9YGxV/Stw4xi32w/sB1haWqp5z0OStGyWoH8CuHTk8uZubGwb7ROmZtnx0febgA25cnXVLPVrln30R4DLk1yW5DzgBuDgJA9QVfdU1d5NmzbNMA1J0pmMu73yTuAB4IokJ5PsqapngZuA+4BjwN1V9XB/U5UkTWOs1k1V7V5l/BBwaNpvPkTrZpoWyEZ/AY2tFWl9G/QtEGzdSFL/fK8bSWrcoO9euRF23aynV65OapzaNmKrS1o0tm4kqXG2biSpcbZuFljL7Z2NyJ+FhmLrRpIaZ+tGkhpn0EtS4zZ0j36anmlLfdaWallU/oy1COzRS1LjbN1IUuMMeklqnEEvSY0bNOiT7Eqy/+mnnx5yGpLUNA/GSlLjbN1IUuMMeklqnEEvSY0z6CWpcQa9JDXO7ZWS1Di3V0pS42zdSFLjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUuA394eDrlR84LWkSvmBKkhpn60aSGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUuFTV0HMgyb8AX53y7hcBT85xOuuBNW8M1rwxzFLzS6vq4rPdaCGCfhZJHqyqpaHnsZaseWOw5o1hLWq2dSNJjTPoJalxLQT9/qEnMABr3hiseWPoveZ136OXJJ1ZCyt6SdIZLFTQJ9mZ5NEkx5PsO831L03yiSRfSHJ/ks0j170vydHu9JaR8QNJvpzkn7vTq9aqnnH0VHOS3JzksSTHkvzmWtUzjp5q/vuR5/hrST6yVvWMo6eafybJ57qa/yHJQn2CT081v76r+WiSDyUZ9MOTVkpye5JTSY6ucn2S3Nr9TL6Q5DUj1701yRe701tHxn8kyUPdfW5NkoknVlULcQLOAb4EvAw4D/g8sH3Fbf4SeGt3/vXAn3XnrwU+zvInZj0POAJc0F13ALh+6PrWuOa3AXcA39NdfvHQtfZd84r7fxj4laFrXYPn+THg5d35XwcODF1rnzWzvDB9HPjB7nbvBfYMXeuKmn4KeA1wdJXrrwE+CgT4MeDT3fiLgBPd1xd251/YXfeZ7rbp7nv1pPNapBX9DuB4VZ2oqmeAu4DrVtxmO/DJ7vzfjVy/HThcVc9W1X8BXwB2rsGcZ9VXze8E3ltV3wSoqlM91jCpXp/nJBewHBqLtKLvq+ZiOQABNgFf62n+0+ij5guBZ6rqse52Hwd+occaJlZVh4GnznCT64A7atmngBckeQnwc8DHq+qpqvo3lmvb2V13QVV9qpZT/w7gjZPOa5GC/hKWf1s/52Q3NurzwJu7828Cvj/Jhd34ziTfl+Qi4KeBS0fud3P3Z9L7k5zfz/Sn0lfNPwC8JcmDST6a5PLeKphcn88zLP8n+ERV/cfcZz69vmr+NeBQkpPALwO39DT/afRR85PAuUmee3HR9Xz387/oVvu5nGn85GnGJ7JIQT+O3wFem+SfgNcCTwDfqKqPAYeAfwTuBB4AvtHd593ADwE/yvKfRe9a60nPaJqazwf+p5ZfbfdHwO1rPuvZTFPzc3Z3160309T828A1VbUZ+FPgD9Z81rOZqOZuRXsD8P4knwH+k+9+/nUaixT0T/Cdv503d2PfUlVfq6o3V9Wrgfd0Y//efb25ql5VVVex3Mt6rBv/evdn0v+y/J9hR/+ljK2Xmln+rf/X3fm/AV7RXwkT66tmutXfDuDefkuY2NxrTnIx8Mqq+nT3EH8B/HjPdUyir//PD1TVT1bVDuAwI8//OrHaz+VM45tPMz6ZeR+MmPbE8oGXE8BlfPvgzQ+vuM1FfPsA480s96Fh+cDPhd35VwBHgXO7yy/pvgb4Q+CWoWtdg5pvAd7enX8dcGToWvuuuRu7EfjQ0DWuRc3d6Um+fWByD/DhoWtdg3/bL+6+ng98Anj90LWepvatrH4w9lq+82DsZ7rxFwFfZvlA7Au78y/qrlt5MPaaiec09A9lxQ/hGpZ/Q38JeE839l7gDd3564Evdrf5Y+D8bvx7gUe606eAV4085ieBh7p/LH8OPH/oOteg5hewvKp9iOU/e185dJ1919xdfz+wc+j61vB5flP3HH++q/1lQ9e5BjX/PnAMeBT4raFrPE3NdwJfB/6P5b+s97C8ALmxuz7Abd3P5CFgaeS+bweOd6e3jYwvdfn1JeADdC90neTkK2MlqXGL1KOXJPXAoJekxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXH/D5EFVIGJvGleAAAAAElFTkSuQmCC\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": "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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": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAEKCAYAAAARnO4WAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEUBJREFUeJzt3X+s3XV9x/Hny3bFqQFBmDLgrpAyZrM/dJ6BbnGSRVzRVTazzTYuwkZo1LD/TCxhfyxLljiXmcxAgs0kRJOBzDjXhpqqmwa3IBY2fwC1WhuVEjaGmzUmyzbCe3+cb/Vwubf93nvOvefc83k+khvO+Zzv+fyg7auffr6f7/ebqkKSNP9eMO0OSJLWh4EvSY0w8CWpEQa+JDXCwJekRhj4ktQIA1+SGmHgS1IjJh74Sa5O8sUkdyS5etL1S5JWp1fgJ7kzyVNJHllUviPJ0STHkuztigv4EfBC4MRkuytJWq30ubVCkl9jGOIfrapf7Mo2Ad8ErmEY7IeB3cA3qurZJC8HPlhV7zhT/eeff35t3bp11YOQpBY9/PDDT1fVBX2P39znoKq6P8nWRcVXAseq6jhAknuA66rqse7z/wLOWq7OJHuAPQALCws89NBDffssSQKSfHclx4+zhn8R8PjI+xPARUneluTDwMeA25b7clXtq6pBVQ0uuKD3X1CSpFXqNcNfiar6JPDJSdcrSRrPODP8J4BLRt5f3JX1lmRnkn0nT54coxuSpD7GCfzDwOVJLk2yBdgF7F9JBVV1oKr2nHPOOWN0Q5LUR99tmXcDDwBXJDmR5Maqega4GTgEHAHurapHV9K4M3xJWj+9tmWutcFgUO7SkaSVSfJwVQ36Hu+tFSSpEVMNfJd0JGn9THxb5kpU1QHgwGAwuGma/ZCk9bR1730/fv2d979l3dp1SUeSGmHgS1IjXMOXpEZMNfC98EqS1o9LOpLUCANfkhrhGr4kNcI1fElqhEs6ktQIA1+SGmHgS1IjPGkrSY3wpK0kNcIlHUlqhIEvSY0w8CWpEQa+JDXCXTqS1Ah36UhSI1zSkaRGGPiS1AgDX5IaYeBLUiMMfElqhIEvSY0w8CWpEV54JUmN8MIrSWqESzqS1AgDX5IaYeBLUiMMfElqhIEvSY0w8CWpEQa+JDXCwJekRhj4ktQIA1+SGmHgS1Ij1iTwk7w4yUNJfnMt6pckrVyvwE9yZ5KnkjyyqHxHkqNJjiXZO/LR+4B7J9lRSdJ4+s7w7wJ2jBYk2QTcDlwLbAd2J9me5BrgMeCpCfZTkjSmzX0Oqqr7k2xdVHwlcKyqjgMkuQe4DngJ8GKGfwn8d5KDVfXs4jqT7AH2ACwsLKy2/5KknnoF/jIuAh4feX8CuKqqbgZIcgPw9FJhD1BV+4B9AIPBoMbohySph3EC/7Sq6q4zHZNkJ7Bz27Zta9UNSVJnnF06TwCXjLy/uCvrzSdeSdL6GSfwDwOXJ7k0yRZgF7B/Mt2SJE1a322ZdwMPAFckOZHkxqp6BrgZOAQcAe6tqkdX0rgPMZek9dN3l87uZcoPAgdX23hVHQAODAaDm1ZbhySpH2+tIEmNmGrgu6QjSetnqoHvLh1JWj8u6UhSI1zSkaRGuKQjSY1wSUeSGmHgS1IjDHxJaoQnbSWpEZ60laRGuKQjSY0w8CWpEWv2xCtJ0k9s3XvftLvgSVtJaoUnbSWpEa7hS1IjDHxJaoSBL0mNMPAlqRHu0pGkRrhLR5Ia4ZKOJDXCwJekRhj4ktQIA1+SGmHgS1IjDHxJaoSBL0mN8MIrSWqEF15JUiNc0pGkRhj4ktQIA1+SGmHgS1IjDHxJaoSBL0mN2DztDkjSPNq6975pd+F5nOFLUiMMfElqhIEvSY2YeOAneWWSO5J8Ism7J12/JGl1egV+kjuTPJXkkUXlO5IcTXIsyV6AqjpSVe8Cfg/41cl3WZK0Gn1n+HcBO0YLkmwCbgeuBbYDu5Ns7z57K3AfcHBiPZUkjaVX4FfV/cB/Liq+EjhWVcer6n+Be4DruuP3V9W1wDsm2VlJ0uqNsw//IuDxkfcngKuSXA28DTiL08zwk+wB9gAsLCyM0Q1JUh8Tv/Cqqr4AfKHHcfuAfQCDwaAm3Q9J0nONs0vnCeCSkfcXd2W9+cQrSVo/4wT+YeDyJJcm2QLsAvavpAKfeCVJ66fvtsy7gQeAK5KcSHJjVT0D3AwcAo4A91bVo2vXVUnSOHqt4VfV7mXKDzLG1sskO4Gd27ZtW20VkqSefIi5JDXCe+lIUiOmGvju0pGk9eOSjiQ1wideSdKEzOJTrka5pCNJjXBJR5Ia4S4dSWqEgS9JjXANX5Ia4Rq+JDXCJR1JaoSBL0mN8MIrSRrDrF9sNcqTtpLUCE/aSlIjXMOXpEYY+JLUCANfkhph4EtSIwx8SWqE2zIlqRFTvfCqqg4ABwaDwU3T7IckrcRGuthqlEs6ktQIA1+SGmHgS1IjvHmaJPWwUdftRznDl6RGGPiS1AiXdCRpGfOwjDPKC68kqRFeeCVJI+ZtVj/KNXxJaoSBL0mNMPAlqREGviQ1wsCXpEa4D19SM0Z34Hzn/W+ZYk+mwxm+JDXCGb6kubbcvvp53m+/HANf0txpMcz7cElHkhqxJjP8JL8FvAU4G/hIVX1mLdqRJPXXe4af5M4kTyV5ZFH5jiRHkxxLshegqj5VVTcB7wLePtkuS5JWYyUz/LuA24CPnipIsgm4HbgGOAEcTrK/qh7rDvnj7nNJmjjX6lemd+BX1f1Jti4qvhI4VlXHAZLcA1yX5AjwfuDTVfUvS9WXZA+wB2BhYWHlPZc015bbM2/Ir964a/gXAY+PvD8BXAX8EfBG4Jwk26rqjsVfrKp9wD6AwWBQY/ZD0hwz5CdjTU7aVtWHgA+tRd2SNj5n79MxbuA/AVwy8v7irqyXJDuBndu2bRuzG5JmnRdATd+4+/APA5cnuTTJFmAXsL/vl6vqQFXtOeecc8bshiTpTFayLfNu4AHgiiQnktxYVc8ANwOHgCPAvVX16Arq9Jm2krROVrJLZ/cy5QeBg6tp3GfaSrOtz1p7i3ed3Ki8l47UqPUIbdfnZ8tUA9+TttJ41nOm3fekqzP+2TXVwHdJR5o/zupnl0s6klyTb4SBL82h5WbZhnnbXMOX5oRLKToT1/ClNbTSpZJZWFrxL4755ZKONAF9gnql94+ZhfDXfDHwpQ1gUrNuZ+9tcw1fmgKDV9Mw1YeYe/M0SVo/Uw18SdL6cQ1fTRnnZmDeQkAbnYEvTZjr85pVnrSVVslg10bjSVtJaoRLOjqjeb0AyBm6WmPga01N6y+Lef1LShqHga+54YxdOj0DX1rEfx1oXrlLZ06td2iN095a93Wcmb//atA88fbIWrVphaEzcGl1XNLpYV4DZiONy5m2ND4Df42tRaiu9L7q49R5uuPWqo1JtCXp+bx5miQ1whn+BrdRZ8F9nvI0Tj2Sns/AnzEbaV19OYawNJsM/CmZh2CXtLHMbeCv1f3Nl5u9zlNoO0OX5pMXXo2YtVn3Wu/wkdQWb48sSY2Y2yWd5WzUGa63B5A0riYCfzWBtxYBa/BKmqYNH/iGqCT145W2ktQIA1+SGmHgS1IjDHxJaoSBL0mN2PC7dOaBO40krQdn+JLUCANfkhox8cBPclmSjyT5xKTrliStXq/AT3JnkqeSPLKofEeSo0mOJdkLUFXHq+rGteisJGn1+s7w7wJ2jBYk2QTcDlwLbAd2J9k+0d5Jkiam1y6dqro/ydZFxVcCx6rqOECSe4DrgMf61JlkD7AHYGFhoWd3Z5c7bSTNunHW8C8CHh95fwK4KMnLktwBvDrJLct9uar2VdWgqgYXXHDBGN2QJPUx8X34VfV94F19jp21J15J0jwbZ4b/BHDJyPuLu7LefOKVJK2fcQL/MHB5kkuTbAF2Afsn0y1J0qT13ZZ5N/AAcEWSE0lurKpngJuBQ8AR4N6qenQljSfZmWTfyZMnV9pvSdIK9d2ls3uZ8oPAwdU2XlUHgAODweCm1dYhSerHWytIUiOmGvgu6UjS+plq4LtLR5LWT6pq2n0gyX8A313l188Hnp5gdzYCx9wGx9yGccb8c1XV+8rVmQj8cSR5qKoG0+7HenLMbXDMbVjPMXvSVpIaYeBLUiPmIfD3TbsDU+CY2+CY27BuY97wa/iSpH7mYYYvSerBwJekRkw98JOcl+SzSb7V/ffcZY67vjvmW0muHyl/TZKvd8/V/VCSnK7eJL+Q5IEk/5PkvYvaeN4zeudkzOmOO5bka0l+aaSuDyR5NMmR0brmeLwLST7TjfexJZ7kNndj7j4/O8MbH962FuOdpTEneVWGf8Yf7crfvgZjPW1WJDkryce7zx8c/X2W5Jau/GiS3zhTnRnekfjBrvzjGd6d+LRtLKuqpvoDfADY273eC/z5EsecBxzv/ntu9/rc7rMvA68FAnwauPZ09QI/A/wy8GfAe0fa2AR8G7gM2AJ8Fdg+J2N+c3dcuu892JX/CvDP3dg3Mbwj6tXzOt7usy8A13SvXwK8aJ5/jUfa+ivgb4Db1mK8szRm4OeBy7vXPws8Cbx0guM8Y1YA7wHu6F7vAj7evd7eHX8WcGlXz6bT1QncC+zqXt8BvPt0bZy272v1i7+C/3lHgQu71xcCR5c4Zjfw4ZH3H+7KLgS+sdRxZ6oX+BOeG/ivAw6NvL8FuGUexnzqu4vb78b8MPDTwIuAh4BXzvF4twP/NI+/r5cbc/f6NcA9wA2sbeDPzJgXtflVur8AJjTOM2YFw9vGv657vZnhlbRZfOyp45ars/vO08DmxW0v18bp+j71JR3g5VX1ZPf634CXL3HMks/P7X5OLFHet94+bayF9R7zknVV1QPA5xnOgJ5k+BvpyKpGdHozMV6GM78fJPlkkn9N8hdJNq1yTGcyE2NO8gLgL4HnLF+ukZkY82hjSa5kOGP+9opGcnp9suLHx9Tw2SEngZed5rvLlb8M+EFXx+K2lmtjWRN/pu1SknwOeMUSH906+qaqKsnE94muVb2nsxHGnGQb8EqGj6cE+GyS11fVF1fa3kYYL8Pf768HXg18D/g4w1nvR1bT5gYZ83uAg1V1IhM4PbNBxgxAkguBjwHXV9Wzk+7LRrQugV9Vb1zusyT/nuTCqnqy+wV6aonDngCuHnl/McO12Cf4SVidKj/1XN0+9S5uY6xn9I6asTEvN7bfB75UVT/q+vVphv9kXHHgb5Dxbga+UlXHu359iuHa76oCf4OM+XXA65O8h+E5iy1JflRVq9qUsEHGTJKzgfuAW6vqSz2H11efrDh1zIkkm4FzgO+f4btLlX8feGmSzd0sfvT45dpY1iws6ewHTp2pvx74+yWOOQS8Kcm53Rn6NzFcfngS+GGS13Zn9N858v0+9Y5az2f0rveY9wPv7HY1vBY42dXzPeANSTYn+SngDQwfVzlpszLewwz/8Jy6u+CvA49NbJTPNRNjrqp3VNVCVW1luKzz0dWGfQ8zMebuz+/fMRzrJyY8RuiXFaN9/h3gH2u42L4f2NXtsLkUuJzhyeol6+y+8/muDnj++JdqY3mTOpGx2h+Ga07/AHwL+BxwXlc+AP565Lg/BI51P38wUj4AHmG4RncbP7l6eLl6X8FwHeyHwA+612d3n70Z+GZX161zNOYAt3fHfx0YdOWbGJ74OsIw+D44z+PtPrsG+FpXfhewZd7HPFLnDaztSduZGDPDf7n+H/CVkZ9XTXisz8sK4E+Bt3avXwj8bTfGLwOXjXz31u57R+l2Ii1XZ1d+WVfHsa7Os87UxnI/3lpBkhoxC0s6kqR1YOBLUiMMfElqhIEvSY0w8CVphiX53QxvBPdskrGefWvgS9KMSHJ1krsWFT8CvA24f9z61+VKW0nS6lR3f6tJ3BrDGb4kNcIZviRNWZIHGd4j/yXAeUm+0n30vqo6NKl2DHxJmrKqugqGa/jADVV1w1q045KOJDXCwJekGZbkt5OcYHir6/uSrHqJx5unSVIjnOFLUiMMfElqhIEvSY0w8CWpEQa+JDXCwJekRhj4ktSI/wcg+J8dJV+RxQAAAABJRU5ErkJggg==\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" + } + ], + "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": 92, "metadata": {}, "outputs": [ { @@ -759,86 +1000,94 @@ "483260\n", "931967\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", - " 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", + "[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", - " 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", + " 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", - " 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", + " 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", - " 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", + "[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", - " 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", + " 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", - " 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", + " 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", - " 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", + "[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", - " z4 \n", - "0 -47.847328 \n", - "1 -47.847328 \n", - "2 -47.847328 \n", - "3 -49.587833 \n", - "4 -49.587833 \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", + " 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" ] } ], @@ -873,7 +1122,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 93, "metadata": {}, "outputs": [], "source": [ @@ -960,7 +1209,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 94, "metadata": {}, "outputs": [], "source": [ @@ -1054,674 +1303,17 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 95, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta123 227975\n", - "field\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 227975\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 227975\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta234\n", - "field\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 227975\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 227975\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADx5JREFUeJzt3W2spGddx/Hvz9aWCLIUWxEpy9mmlbgmBuJYEolSBaG1LCXayFYxVZtuwNQ3xoQl1TckJsU3RhKSulEoaKCUGnEPXaw8rfiiKLvIQx9Sui0lbK20BTmikmrl74u5F4bDnrMzZx7uOdf5fpKTM3M/zPzPPTO/c811X3NNqgpJUru+r+8CJEnzZdBLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGnd23wUAnH/++bWystJ3GZK0rRw/fvyJqrrgTNstRdCvrKxw7NixvsuQpG0lyZfG2c6uG0lqXK9Bn2RfkkNra2t9liFJTes16KtqtaoO7Nq1q88yJKlpdt1IUuMMeklqnEEvSY3zZKwkNc6TsZLUuKX4wJS0rFYO3vHtyw/fdGWPlUhbZx+9JDXOFr20zmgrXmqBLXpJapyjbiSpcY66kaTG2XUjSY0z6CWpcQa9JDXOoJekxhn0ktQ4h1dKUuMcXilJjbPrRpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxvmBKUlqnB+YkqTG2XUjSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMY5e6UkNc7ZKyWpcXbdSFLjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaN5egT/L0JMeSvHoety9JGt9YQZ/kHUkeS3L3uuWXJ7k/yYkkB0dWvQm4bZaFSpK2ZtwW/S3A5aMLkpwFvB24AtgLXJNkb5JfBO4FHpthnZKkLTp7nI2q6hNJVtYtvhQ4UVUPASS5FbgKeAbwdIbh/80kR6rqWzOrWJI0kbGCfgPPA748cv0k8JKqugEgyW8CT2wU8kkOAAcAdu/ePUUZ0mKsHLzju64/fNOVPVUiTWaaoN9UVd1yhvWHgEMAg8Gg5lWHNI71IS61ZJpRN48Azx+5fmG3TJK0RKYJ+k8BlyTZk+QcYD9weDZlSZJmZdzhle8F7gJemORkkuuq6ingBuBO4D7gtqq6Z5I7T7IvyaG1tbVJ65YkjWncUTfXbLD8CHBkq3deVavA6mAwuH6rtyFJ2pxTIEhS43oNertuJGn+eg36qlqtqgO7du3qswxJappdN5LUOINekhpn0EtS4zwZK0mN82SsJDXOrhtJapxBL0mNM+glqXGejJWkxnkyVpIaZ9eNJDXOoJekxhn0ktQ4g16SGueoG0lqnKNuJKlxdt1IUuMMeklqnEEvSY0z6CWpcQa9JDXu7D7vPMk+YN/FF1/cZxnaoVYO3tF3CdJC9Br0VbUKrA4Gg+v7rEPaitF/FA/fdGWPlUibs+tGkhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGOR+9JDXO+eglqXF23UhS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqXK/fGSstml8Irp3ISc0kqXG9tuirahVYHQwG1/dZhzSt0XcKD990ZY+VSN/LPnpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc5pitU8pybWTmeLXpIaZ4temjGnLNaysUUvSY0z6CWpcTMP+iQ/nuTmJLcneeOsb1+SNJmxgj7JO5I8luTudcsvT3J/khNJDgJU1X1V9QbgV4GXzr5kSdIkxm3R3wJcProgyVnA24ErgL3ANUn2duteA9wBHJlZpZKkLRlr1E1VfSLJyrrFlwInquohgCS3AlcB91bVYeBwkjuA98yuXGk8yzJ23hE4WgbTDK98HvDlkesngZckuQz4ZeBcNmnRJzkAHADYvXv3FGVIkjYz83H0VXUUODrGdoeAQwCDwaBmXYckaWiaUTePAM8fuX5ht0yStESmCfpPAZck2ZPkHGA/cHiSG0iyL8mhtbW1KcqQJG1m3OGV7wXuAl6Y5GSS66rqKeAG4E7gPuC2qrpnkjuvqtWqOrBr165J65YkjWncUTfXbLD8CA6hlKSl5hQIktS4XoPePnpJmr9eg94+ekmaP+ejlxbET8mqL/bRS1Lj7KOXpMb12nVTVavA6mAwuL7POtSGZZnITFo2dt1IUuMMeklqnKNutK1t1+4aR+BokWzRS1Ljem3RJ9kH7Lv44ov7LEPbzHZtxUt98ZOxktQ4++ilnm30DsW+e82KQa9twe4aaes8GStJjXMKBElqnCdjJalx9tFrqfhBou/wWGhWDHr1zhOt0nx5MlaSGmeLXtoGxnnXY/eONmLQa2nZpSPNhkEvNc6TunJSM2kHMfR3Jr9KUGdkOLTJx3XnsOtGM7FRaIyzXLNhcGsjBr22bNKwNtwXx2OtUQa9JmKASNuPQS/Jbp/GGfQCZvtCt9XfDv8BtMEpECSpcbboJX0X35G1xw9MSRqL3Tjbl188IkmNs+tG38O37lJbDHpJU1nfMLBbZ/kY9A2ZtA/Vlru2yufO9mLQL4FFBrQvUGnnMeh3AMNdy8BRO/3xA1OS1Dhb9NuEM0Vqu5imK3IZWvrLVs8sGPSSlkaLIbsMmgr6Fp4kLfwN0imzGjjga2E6TQV9a+x+Uatm9dz2n8F4DHpJS89Gz3R6HXWTZF+SQ2tra32WIUlN67VFX1WrwOpgMLi+zzoWxbeZ0vz4+tqY4+glqXH20W9gVtMSbLSvfY5Sv3bSa9AWvSQ1btu36Bf9X9l+QGl7mcdrdrvlwLYPeklab6MG4Lwbhsv6D8Cgn8I4T5qd1A8oaTnZRy9JjdsRLfplfTslSYvQbNCP00c3bujb/SJp2hzos8HZbNBL0rRa+drOHR30fnu9pGktU6BvxJOxktS4Hd2il6Q+LLq/3qAfsR3egknSpOy6kaTGGfSS1DiDXpIaN5c++iSvBa4Engn8RVX9/TzuR5J0ZmO36JO8I8ljSe5et/zyJPcnOZHkIEBVfaCqrgfeALxutiVLkiYxSdfNLcDlowuSnAW8HbgC2Atck2TvyCZ/0K2XJPVk7KCvqk8AX1u3+FLgRFU9VFX/A9wKXJWhtwIfqqpPz65cSdKkpj0Z+zzgyyPXT3bLfhd4BXB1kjecbsckB5IcS3Ls8ccfn7IMSdJG5nIytqreBrztDNscAg4BDAaDmkcdkqTpg/4R4Pkj1y/slk3k+PHjTyT50hZrOB94Yov7zpN1Tca6JrestVnXBPLWqep6wTgbTRv0nwIuSbKHYcDvB35t0hupqgu2WkCSY1U12Or+82Jdk7GuyS1rbdY1mUXUNcnwyvcCdwEvTHIyyXVV9RRwA3AncB9wW1XdM59SJUlbMXaLvqqu2WD5EeDIzCqSJM1UC1MgHOq7gA1Y12Ssa3LLWpt1TWbudaXKAS+S1LIWWvSSpE1si6BP8uwkH07yQPf7vNNs86IkdyW5J8nnkrxuZN2eJP/UzcfzviTnLKqubru/S/L1JB9ct/yWJF9M8pnu50VLUlffx+vabpsHklw7svxoN6/SqeP1w1PW8z3zNK1bf27395/ojsfKyLo3d8vvT/KqaeqYVV1JVpJ8c+T43Lzgun4uyaeTPJXk6nXrTvuYLkFd/zdyvA4vuK7fS3Jvl1cfTfKCkXWzPV5VtfQ/wB8DB7vLB4G3nmabHwMu6S7/KPAo8Kzu+m3A/u7yzcAbF1VXt+7lwD7gg+uW3wJc3cfxOkNdvR0v4NnAQ93v87rL53XrjgKDGdVyFvAgcBFwDvBZYO+6bX4HuLm7vB94X3d5b7f9ucCe7nbOWoK6VoC7Z/18mqCuFeAngXePPq83e0z7rKtb9589Hq+fB36gu/zGkcdx5sdrW7TogauAd3WX3wW8dv0GVfWFqnqgu/yvwGPABUkC/AJw+2b7z6uurp6PAt+Y0X2OY8t1LcHxehXw4ar6WlX9O/Bh1k2mNyOnnadpk3pvB17eHZ+rgFur6smq+iJworu9vuuapzPWVVUPV9XngG+t23eej+k0dc3TOHV9vKr+u7v6SYYfOIU5HK/tEvTPqapHu8v/Bjxns42TXMrwv+iDwA8BX6/hmH/4znw8C69rA3/UvXX7kyTnLkFdfR+vjeZPOuWd3dvsP5wy3M50P9+1TXc81hgen3H27aMugD1J/iXJPyT52RnVNG5d89h33rf9tAzn3Ppkht+jMSuT1nUd8KEt7ntGS/Pl4Ek+AvzIaVbdOHqlqirJhkOFkjwX+Evg2qr61rQNnVnVtYE3Mwy8cxgOsXoT8JYlqGvL5lzXr1fVI0l+EPhr4DcYvh3X0KPA7qr6apKfAj6Q5Ceq6j/6LmyJvaB7Tl0EfCzJ56vqwUUWkOT1wAB42bzuY2mCvqpesdG6JF9J8tyqerQL8sc22O6ZwB3AjVX1yW7xV4FnJTm7a/1MNB/PLOra5LZPtW6fTPJO4PeXoK6+j9cjwGUj1y9k2DdPVT3S/f5GkvcwfHu81aAfZ56mU9ucTHI2sIvh8ZnJHE+zrquGHbxPAlTV8SQPMjx3dWxBdW2272Xr9j06g5pO3faWH4uR59RDSY4CL2bYE7CQupK8gmEj6GVV9eTIvpet2/foNMVsl66bw8CpM8/XAn+7foMMR4b8DfDuqjrVv0z35P84cPVm+8+rrs10YXeqX/y1wN2b7zH/upbgeN0JvDLJeRmOynklcGeSs5OcD5Dk+4FXM93x+vY8Td1zZ39X30b1Xg18rDs+h4H93eiXPcAlwD9PUctM6kpyQYZfBkTXQr2E4Ym8RdW1kdM+pn3X1dVzbnf5fOClwL2LqivJi4E/A15TVaONntkfr3mccZ71D8P+x48CDwAfAZ7dLR8Af95dfj3wv8BnRn5e1K27iOEL8QTwfuDcRdXVXf9H4HHgmwz7217VLf8Y8HmGgfVXwDOWpK6+j9dvd/d9AvitbtnTgePA54B7gD9lypEuwC8BX2DYgruxW/YWhi88gKd1f/+J7nhcNLLvjd1+9wNXzPj5vqW6gF/pjs1ngE8D+xZc1093z6P/YvjO557NHtO+6wJ+pnv9fbb7fd2C6/oI8BW+k1eH53W8/GSsJDVuu3TdSJK2yKCXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalx/w8eW0XhBtdajwAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta123 40709\n", - "field\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 40709\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 40709\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta234\n", - "field\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADhtJREFUeJzt3V+MXOdZx/HvD5sUSEOStlEV+Q92ZcfFQjSpRk6rVqgUiuwmTlBVoVi9KMiyFVSjIiGBIxASd6mEgEQEqlUTfFPZmADFTgxuCY1yE6Wx2xTsGFMTgmyrxQ6hRqoQwe3DxUzKaPF6Z3dmPDOvvx9ptXPePXPmsff4l5Nn3jlvqgpJUrt+YNIFSJLGy6CXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNW7lJF88yXZg+0033bTrjjvumGQpkjRzjh8//lpV3bbYfpmGWyB0Op06duzYpMuQpJmS5HhVdRbbz9aNJDVuokGfZHuSuUuXLk2yDElq2kSDvqoOV9Xum2++eZJlSFLTbN1IUuNs3UhS42zdSFLjbN1IUuMMeklq3FR8MnbDhg3LPsa6vU9///GrD98zgqokqS326CWpcbZuJKlxBr0kNc6gl6TGGfSS1Dg/GStJjXPWjSQ1ztaNJDXOoJekxhn0ktQ4g16SGmfQS1LjnF4pSY1zeqUkNc7WjSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxYwn6JDcmOZbk3nEcX5I0uIGCPskTSS4kOTFvfGuS00nOJNnb96PfAA6OslBJ0vIMekW/D9jaP5BkBfAYsA3YDOxIsjnJR4CXgQsjrFOStEwrB9mpqp5Lsm7e8BbgTFW9ApDkAHA/8FbgRrrh/19JjlTV9+YfM8luYDfA2rVrl1u/JGkRAwX9AlYBZ/u2zwF3V9UegCS/CLx2pZAHqKo5YA6g0+nUEHVIkq5imKC/qqrat9g+SbYD2zds2DCuMiTpujfMrJvzwJq+7dW9sYF5m2JJGr9hgv5FYGOS9UluAB4ADi3lAC48IknjN+j0yv3A88CmJOeS7Kyqy8Ae4ChwCjhYVSeX8uJe0UvS+A0662bHAuNHgCMjrUiSNFKuGStJjXPNWElqnDc1k6TG2bqRpMbZupGkxtm6kaTG2bqRpMbZupGkxtm6kaTGGfSS1Dh79JLUOHv0ktQ4WzeS1DiDXpIaZ9BLUuMMeklqnLNuJKlxzrqRpMbZupGkxhn0ktQ4g16SGmfQS1LjDHpJapzTKyWpcU6vlKTG2bqRpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxIw/6JD+e5LNJnkzyy6M+viRpaQYK+iRPJLmQ5MS88a1JTic5k2QvQFWdqqoHgV8APjD6kiVJSzHoFf0+YGv/QJIVwGPANmAzsCPJ5t7P7gOeBo6MrFJJ0rIMFPRV9Rzw+rzhLcCZqnqlqt4ADgD39/Y/VFXbgE+MslhJ0tKtHOK5q4CzfdvngLuTfAj4GPAWrnJFn2Q3sBtg7dq1Q5QhSbqaYYL+iqrqWeDZAfabA+YAOp1OjboOSVLXMLNuzgNr+rZX98YG5m2KJWn8hgn6F4GNSdYnuQF4ADi0lAN4m2JJGr9Bp1fuB54HNiU5l2RnVV0G9gBHgVPAwao6uZQX94peksZvoB59Ve1YYPwIQ0yhrKrDwOFOp7NruceQJF2dt0CQpMa5ZqwkNc41YyWpcbZuJKlxtm4kqXG2biSpcbZuJKlxtm4kqXG2biSpcbZuJKlxBr0kNc4evSQ1zh69JDXO1o0kNc6gl6TGGfSS1DiDXpIa56wbSWqcs24kqXG2biSpcQa9JDXOoJekxhn0ktQ4g16SGuf0SklqnNMrJalxtm4kqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWrcynEcNMnPA/cAPwo8XlVfHMfrSJIWN3DQJ3kCuBe4UFU/0Te+FXgEWAF8rqoerqovAF9Icivwu8A1Cfp1e5/+/uNXH77nWrykJE29pbRu9gFb+weSrAAeA7YBm4EdSTb37fJbvZ9LkiZk4KCvqueA1+cNbwHOVNUrVfUGcAC4P12fAf66qr46unIlSUs17Juxq4CzfdvnemO/Avws8PEkD17piUl2JzmW5NjFixeHLEOStJCxvBlbVY8Cjy6yzxwwB9DpdGocdUiShr+iPw+s6dte3RsbiLcplqTxGzboXwQ2Jlmf5AbgAeDQoE/2NsWSNH4DB32S/cDzwKYk55LsrKrLwB7gKHAKOFhVJ5dwTK/oJWnMBu7RV9WOBcaPAEeW8+JVdRg43Ol0di3n+ZKkxXkLBElqnGvGSlLjXDNWkhpn60aSGmfrRpIaZ+tGkhpn60aSGmfrRpIaZ+tGkhpn60aSGmfQS1Lj7NFLUuPGsvDIoMZ5UzMXCpekLls3ktQ4g16SGmfQS1LjDHpJapyzbiSpcX4yVpIaN9HpldeKUy0lXc/s0UtS4wx6SWqcQS9JjTPoJalxTq+UpMY5vVKSGmfrRpIaZ9BLUuMMeklqnEEvSY0z6CWpcdfFvW76ed8bSdcbr+glqXEjD/ok70ryeJInR31sSdLSDRT0SZ5IciHJiXnjW5OcTnImyV6AqnqlqnaOo1hJ0tINekW/D9jaP5BkBfAYsA3YDOxIsnmk1UmShjZQ0FfVc8Dr84a3AGd6V/BvAAeA+0dcnyRpSMP06FcBZ/u2zwGrkrw9yWeBu5I8tNCTk+xOcizJsYsXLw5RhiTpakY+vbKq/h14cID95oA5gE6nU6OuQ5LUNcwV/XlgTd/26t7YwLxNsSSN3zBB/yKwMcn6JDcADwCHlnIAb1MsSeM36PTK/cDzwKYk55LsrKrLwB7gKHAKOFhVJ5fy4l7RS9L4DdSjr6odC4wfAY4s98Wr6jBwuNPp7FruMSRJV+ctECSpca4ZK0mNc81YSWqcrRtJatxE70efZDuwfcOGDRN5/f5704P3p5fUJls3ktQ4WzeS1LjrunWzHC5FOF38fUiLs3UjSY2zdSNJjTPoJalx9ugXMH/qpSSNyrV+b8kevSQ1ztaNJDXOoJekxhn0ktQ434ztMw1vwA7zJo0fHpJ0Jb4ZK0mNs3UjSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGuc8+iEsdd6689wlTYLz6CWpcbZuJKlxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklq3Mg/MJXkRuCPgDeAZ6vq86N+DUnS4Aa6ok/yRJILSU7MG9+a5HSSM0n29oY/BjxZVbuA+0ZcryRpiQZt3ewDtvYPJFkBPAZsAzYDO5JsBlYDZ3u7fXc0ZUqSlmugoK+q54DX5w1vAc5U1StV9QZwALgfOEc37Ac+viRpfIbp0a/i/67coRvwdwOPAn+Y5B7g8EJPTrIb2A2wdu3aIcqYPuNYZHyhYy50c7Rh9p+/z7W8Gdv1cuO3pf5+ZtX18vucdiN/M7aqvgP80gD7zQFzAJ1Op0ZdhySpa5jWynlgTd/26t7YwJJsTzJ36dKlIcqQJF3NMEH/IrAxyfokNwAPAIeWcgBvUyxJ4zfo9Mr9wPPApiTnkuysqsvAHuAocAo4WFUnl/LiXtFL0vgN1KOvqh0LjB8Bjiz3xavqMHC40+nsWu4xJElX5/RHSWrcRIPe1o0kjZ9rxkpS47yil6TGpWryn1VKchH412U+/R3AayMs51qy9smw9smw9tH7saq6bbGdpiLoh5HkWFV1Jl3Hclj7ZFj7ZFj75DjrRpIaZ9BLUuNaCPq5SRcwBGufDGufDGufkJnv0UuSrq6FK3pJ0lXMdNAvsGbtVLrSurtJ3pbkS0m+0ft+6yRrXEiSNUm+nOTlJCeTfLo3PvX1J/mhJF9J8vVe7b/TG1+f5IXeufOnvTuwTqUkK5J8LclTve2ZqD3Jq0n+IclLSY71xqb+nAFIckuSJ5P8Y5JTSd4/K7VfycwG/VXWrJ1W+5i37i6wF3imqjYCz/S2p9Fl4NeqajPwPuBTvb/rWaj/v4EPV9V7gDuBrUneB3wG+P2q2gD8B7BzgjUu5tN07xD7plmq/aer6s6+qYmzcM4APAL8TVW9G3gP3b//Wan9/6uqmfwC3g8c7dt+CHho0nUtUvM64ETf9mng9t7j24HTk65xwD/HXwEfmbX6gR8Bvkp3ycvXgJVXOpem6Yvugj7PAB8GngIyQ7W/Crxj3tjUnzPAzcC/0HsPc5ZqX+hrZq/oufKatasmVMtyvbOqvtl7/C3gnZMsZhBJ1gF3AS8wI/X3Wh8vAReALwH/DHy7umsqwHSfO38A/Drwvd7225md2gv4YpLjvTWiYTbOmfXAReBPei2zzyW5kdmo/YpmOeibUt3LhKmeApXkrcCfA79aVf/Z/7Nprr+qvltVd9K9Ot4CvHvCJQ0kyb3Ahao6PulalumDVfVeuu3VTyX5qf4fTvE5sxJ4L/DHVXUX8B3mtWmmuPYrmuWgH3rN2inwb0luB+h9vzDhehaU5Afphvznq+ovesMzUz9AVX0b+DLddsctSd5ceGdaz50PAPcleRU4QLd98wizUTtVdb73/QLwl3T/IzsL58w54FxVvdDbfpJu8M9C7Vc0y0E/9Jq1U+AQ8Mne40/S7X1PnSQBHgdOVdXv9f1o6utPcluSW3qPf5juewun6Ab+x3u7TWXtVfVQVa2uqnV0z++/q6pPMAO1J7kxyU1vPgZ+DjjBDJwzVfUt4GySTb2hnwFeZgZqX9Ck3yQY8k2TjwL/RLfn+puTrmeRWvcD3wT+h+4Vw066/dZngG8Afwu8bdJ1LlD7B+n+b+rfAy/1vj46C/UDPwl8rVf7CeC3e+PvAr4CnAH+DHjLpGtd5M/xIeCpWam9V+PXe18n3/z3OQvnTK/OO4FjvfPmC8Cts1L7lb78ZKwkNW6WWzeSpAEY9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNe5/AUj2kP2JNVWnAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 40709\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 40709\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta123 152561\n", - "field\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 152561\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 152561\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta234\n", - "field\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 152561\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADglJREFUeJzt3WGMHPdZx/HvU0dJRWhPLvarxM45sqlwKqTCkiAQUESrOrSOK1pVMSC1EMUqNIDEG4xSCQneBISQWmERWTQyfRM38CLyEbehtDURUgNxQmjiRKaOmyq2ECEpOgSUVqEPL26cri939u7t7M7sc9+PdPLs7Mzu47H3N//7//8zG5mJJKmuN3VdgCRpugx6SSrOoJek4gx6SSrOoJek4gx6SSrOoJek4gx6SSrOoJek4q7pugCAbdu25eLiYtdlSNJcefLJJ1/JzO1X264XQb+4uMjp06e7LkOS5kpEfGOU7ey6kaTiDHpJKq7ToI+I/RFxdHl5ucsyJKm0ToM+M5cy89DCwkKXZUhSaXbdSFJxBr0kFWfQS1JxBr0kFdeLC6YmsXj4kdeXX7zvfR1WIkn9ZItekopzHr0kFec8ekkqzq4bSSpu7gdjhzkwK0lvZItekooz6CWpOINekooz6CWpOINekooz6CWpOINekoorNY9+mHPqJWmF97qRpOK8140kFWcfvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVV/bK2GFeJStpM7NFL0nFGfSSVJxBL0nFGfSSVJxBL0nFGfSSVNxUgj4iro+I0xHx/mm8viRpdCMFfUQ8EBEvR8Szq9bvi4izEXEuIg4PPfU7wENtFipJ2phRW/THgH3DKyJiC3AEuB3YCxyMiL0R8R7gOeDlFuuUJG3QSFfGZuZjEbG4avWtwLnMPA8QEceBA8D3A9ezEv7fioiTmfnd1iqWJI1lklsg3AC8NPT4AnBbZt4DEBEfBV5ZL+Qj4hBwCGDnzp0TlDEeb4cgabOZ2qybzDyWmX99heePZuYgMwfbt2+fVhmStOlNEvQXgR1Dj29s1kmSemSSoH8C2BMRuyLiWuBO4MQ4LxAR+yPi6PLy8gRlSJKuZNTplQ8CXwHeHhEXIuKuzHwNuAd4FHgeeCgzz4zz5pm5lJmHFhYWxq1bkjSiUWfdHFxn/UngZKsVSZJa1ektEOy6kaTp6zTo7bqRpOnzpmaSVJxBL0nF2UcvScVNcguEiWXmErA0GAzu7uL9h2+HAN4SQVJNdt1IUnEGvSQVZx+9JBXnPHpJKs6uG0kqzqCXpOIMekkqzqCXpOKcdSNJxTnrRpKKs+tGkooz6CWpuE5vatY3wzc58wZnkqqwRS9JxTnrRpKKc9aNJBVn140kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFecFU5JUXKf3usnMJWBpMBjc3WUda/G+N5KqsOtGkooz6CWpOINekooz6CWpOINekooz6CWpOINekorzO2NH4Jx6SfPMFr0kFectECSpOL8zVpKKs+tGkooz6CWpOINekooz6CWpOINekorzgqkxefGUpHlji16SijPoJak4g16SijPoJak4B2Mn4MCspHlgi16SijPoJak4g16Simu9jz4ifgj4LWAb8MXM/LO236OP7K+X1Fcjtegj4oGIeDkinl21fl9EnI2IcxFxGCAzn8/MjwEfBn6y/ZIlSeMYtevmGLBveEVEbAGOALcDe4GDEbG3ee4O4BHgZGuVSpI2ZKSgz8zHgG+uWn0rcC4zz2fmd4DjwIFm+xOZeTvwS20WK0ka3yR99DcALw09vgDcFhHvAn4BuI4rtOgj4hBwCGDnzp0TlCFJupLWB2Mz8xRwaoTtjgJHAQaDQbZdhyRpxSTTKy8CO4Ye39iskyT1yCRB/wSwJyJ2RcS1wJ3AiXFeICL2R8TR5eXlCcqQJF3JqNMrHwS+Arw9Ii5ExF2Z+RpwD/Ao8DzwUGaeGefNM3MpMw8tLCyMW3evLR5+5PUfSeraSH30mXlwnfUncQqlJPWat0CQpOI6DXr76CVp+jq9H31mLgFLg8Hg7i7rmCbvgSOpa3bdSFJxfsPUDK03C8eWvqRpso9ekorrNOirzqOXpD6xj16SijPoJam4TgdjI2I/sH/37t1dljF3nLIpaRzOo++B9YLbQJfUBqdXzglvkCZpowz6njHQJbXNoNcbrD7Z2G0kzTcvmJKk4rxgSpKKcx69JBVnH/2ccwqmpKuxRS9Jxdmi11X5W4M03wz6QgxkSWtxeqUkFee9bgR4Ra5UmV03m4BdOtLmZtAXZQtd0iUG/SbmyUDaHJxHL0nF2aLXWOzvl+aPQa8NM/Sl+eB3xqoVhr7UX86jV+sMfalf7LrZZGY902aU0F+vJk8SUjucdSNJxdmiVyecwy/NjkGv3rKvX2qHXTeSVJwtes3MJN01bbXu/S1Bm5FBr7mzXlg7e0dam0EvrWKrX9UY9Jprbc3ecRaQKjPotWkZ7tos/M5YSSrOe91ILbBfX31m143Ka2ta52rjBvoos4U8SWgaDHppk/IEs3kY9JIu4wmgHoNemqJpT/80iDUKg17qwHrBbWta02DQS0V4ktB6DHqpZbO8WneW24zKE07/eJtiSSrOFr2kiXgrif6zRS9JxdmilwqaxjiB/e3zy6CXNsgui6vzRNEPdt1IUnEGvSQVN5Wum4j4APA+4K3ApzPzb6bxPpK6MYt593b7tGfkoI+IB4D3Ay9n5juG1u8DPglsAf48M+/LzIeBhyNiK/DHgEEvqRWTnAA268ljnBb9MeBPgc9cWhERW4AjwHuAC8ATEXEiM59rNvlE87wkzcxmDfT1jBz0mflYRCyuWn0rcC4zzwNExHHgQEQ8D9wHfC4zn2qpVkkdcpbR/Jq0j/4G4KWhxxeA24DfAN4NLETE7sy8f/WOEXEIOASwc+fOCcuQ1HeeKLozlcHYzPwU8KmrbHMUOAowGAxyGnVIkiYP+ovAjqHHNzbrJGnq7IsfzaTz6J8A9kTEroi4FrgTODHqzhGxPyKOLi8vT1iGJGk940yvfBB4F7AtIi4Av5eZn46Ie4BHWZle+UBmnhn1NTNzCVgaDAZ3j1e2pM1kkv59xwbGm3VzcJ31J4GTrVUkSWpVpzc1i4j9wP7du3d3WYYkva5iv3+nQW/XjaRhdrNMh7cplrTpjXuCmbdWv0EvqQR/G1ifffSSNqVpnBj62tLv9H70mbmUmYcWFha6LEOSSvOLRySpOPvoJWkC8zA2YItekorrNOi9140kTZ+DsZJUnF03klScg7GSNAWrB2m7nFdv0EvSOuZhRs0oHIyVpOIcjJWk4hyMlaTi7KOXpBno8oZntuglqTiDXpKKc9aNJBXnrBtJKs6uG0kqzqCXpOIMekkqznn0kjRjs55Tb4tekooz6CWpOINekorzgilJKs4LpiSpOLtuJKk4g16SijPoJak4g16SiovM7LoGIuLfgW9scPdtwCstltMW6xqPdY2vr7VZ13gmqeumzNx+tY16EfSTiIjTmTnouo7VrGs81jW+vtZmXeOZRV123UhScQa9JBVXIeiPdl3AOqxrPNY1vr7WZl3jmXpdc99HL0m6sgoteknSFfQ66CNiX0ScjYhzEXF4jeevi4jPNs//Q0QsDj33u836sxHx3j7UFRGLEfGtiHi6+bl/xnX9dEQ8FRGvRcSHVj33kYj4WvPzkR7V9X9Dx+vEjOv67Yh4LiK+GhFfjIibhp7r8nhdqa4uj9fHIuKZ5r3/PiL2Dj3X5edxzbq6/jwObffBiMiIGAyta/d4ZWYvf4AtwAvAzcC1wD8De1dt8+vA/c3yncBnm+W9zfbXAbua19nSg7oWgWc7PF6LwA8DnwE+NLT+bcD55s+tzfLWrutqnvuvDo/XzwLf1yz/2tC/Y9fHa826enC83jq0fAfw+Wa568/jenV1+nlstnsL8BjwODCY1vHqc4v+VuBcZp7PzO8Ax4EDq7Y5APxFs/xXwM9FRDTrj2fmtzPz68C55vW6rmuarlpXZr6YmV8Fvrtq3/cCX8jMb2bmfwBfAPb1oK5pGqWuL2fm/zQPHwdubJa7Pl7r1TVNo9T1n0MPrwcuDQB2+nm8Ql3TNEpOAPwB8IfA/w6ta/149TnobwBeGnp8oVm35jaZ+RqwDPzAiPt2URfAroj4p4j4u4j4qZZqGrWuaew77dd+c0ScjojHI+IDLdW0kbruAj63wX1nVRd0fLwi4uMR8QLwR8BvjrNvB3VBh5/HiPgRYEdmPsLlWj9efjn4bP0rsDMzX42IHwUejohbVrU4dLmbMvNiRNwMfCkinsnMF2ZZQET8MjAAfmaW73s169TV6fHKzCPAkYj4ReATQKvjFxu1Tl2dfR4j4k3AnwAfnfZ7Qb9b9BeBHUOPb2zWrblNRFwDLACvjrjvzOtqfhV7FSAzn2Sl7+0HZ1jXNPad6mtn5sXmz/PAKeCds6wrIt4N3AvckZnfHmffDurq/HgNOQ5c+o2i8+O1Vl0dfx7fArwDOBURLwI/DpxoBmTbP17TGIhoaTDjGlYGuXbxvcGMW1Zt83EuH/R8qFm+hcsHM87T3uDPJHVtv1QHK4M0F4G3zaquoW2P8cbB2K+zMrC4tVnuQ11bgeua5W3A11hjQGuK/47vZOXDv2fV+k6P1xXq6vp47Rla3g+cbpa7/jyuV1cvPo/N9qf43mBs68dr4r/QNH+Anwf+pflPfW+z7vdZacUAvBn4S1YGK/4RuHlo33ub/c4Ct/ehLuCDwBngaeApYP+M6/oxVvr7/puV33zODO37q02954Bf6UNdwE8AzzT/6Z8B7ppxXX8L/Fvz7/U0cKInx2vNunpwvD459P/7ywwFW8efxzXr6vrzuGrbUzRBP43j5ZWxklRcn/voJUktMOglqTiDXpKKM+glqTiDXpKKM+glqTiDXpKKM+glqbj/BxplWmZvYVoMAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADk1JREFUeJzt3X+sZPVZx/H3p9uASstK3aYtLMul2dW4Nk3VEWK0FlMaF+lCo0TZtgkkDRuK6B/GxE1o0kT/oUZNaErETSG0JkJbEnG3bEsFS9CkKEtTkS2hLISGBeSHxvVXFUkf/5ihHa53d2f2ztxz5nvfr+RmZ86cnfvk3juf+53nPOfcVBWSpHa9rusCJEnzZdBLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGvf6rgsA2LRpUy0tLXVdhiQtlIceeuilqnrzifbrRdAvLS1x8ODBrsuQpIWS5NuT7GfrRpIaZ9BLUuMMeklqnEEvSY2bS9AnOS3JwSTvn8fzS5ImN1HQJ7klyQtJHlm2fUeSx5IcTrJn7KHfBT4/y0IlSSdn0hX9rcCO8Q1JNgA3AhcB24FdSbYneR/wTeCFGdYpSTpJE83RV9X9SZaWbT4POFxVTwIkuR24FHgDcBrD8P9OkgNV9d3lz5lkN7AbYMuWLSdbvyTpBFZzwtRZwNNj948A51fVtQBJrgReWinkAapqL7AXYDAY+Idr1UtLe+763u2nrr+4w0qkkze3M2Or6tZ5PbckaXKrCfpngLPH7m8ebZMW2vgqXmrBasYrHwS2JTk3ySnA5cC+aZ4gyc4ke48ePbqKMiRJxzPRij7JbcAFwKYkR4CPV9XNSa4F7gY2ALdU1aFpPnlV7Qf2DwaDq6YrW1p7y1f69uy1KCadutl1jO0HgAMzrUjqgO0atcxLIEhS4wx6SWpcp0HvwVhJmr9Og76q9lfV7o0bN3ZZhiQ1rRd/SlDqwmoPwHrWrBaFPXpJapw9eklqnD16SWqcrRtJapxBL0mNc+pGmgEncNRnBr3WFa9po/XIqRtJapxTN5LUOFs30ozZr1ffOHUjSY0z6CWpcbZu1DwnbbTeuaKXpMY5XilJjXO8UpIaZ49emiNHLdUH9uglqXGu6NUkJ22k73NFL0mNM+glqXEGvSQ1zjl6SWqcc/SS1DinbtQMJ22klRn00hrx5Cl1xYOxktQ4g16SGmfQS1LjDHpJapwHY7XQnLSRTsyglzrgBI7Wkq0bSWqcl0CQpMZ5CQRJapytG0lqnEEvSY0z6CWpcQa9JDXOOXotHE+SkqZj0Esd8+QpzZutG0lqnEEvSY0z6CWpcQa9JDXOg7FaCE7aSCfPoJd6xAkczYOtG0lqnJcplqTGeZliSWqcrRtJapxBL0mNM+glqXGOV6q31vvsvKOWmhVX9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxjleqV9b7SKU0Dwa9tACcqddq2LqRpMYZ9JLUOINekhpn0EtS4wx6SWqcUzfqnCOV0nwZ9NKCcdRS07J1I0mNm3nQJ/nxJDcluSPJR2f9/JKk6UwU9EluSfJCkkeWbd+R5LEkh5PsAaiqR6vqauDXgJ+bfcmSpGlMuqK/FdgxviHJBuBG4CJgO7AryfbRY5cAdwEHZlapJOmkTBT0VXU/8C/LNp8HHK6qJ6vqZeB24NLR/vuq6iLgQ8d6ziS7kxxMcvDFF188ueolSSe0mqmbs4Cnx+4fAc5PcgHwK8CpHGdFX1V7gb0Ag8GgVlGHJOk4Zj5eWVX3AffN+nnVFmfnZ8NRS01iNVM3zwBnj93fPNomSeqR1QT9g8C2JOcmOQW4HNg3zRMk2Zlk79GjR1dRhiTpeCYdr7wN+BrwY0mOJPlIVb0CXAvcDTwKfL6qDk3zyatqf1Xt3rhx47R1S1pmac9d3/uQxk3Uo6+qXcfYfgBHKCWp17wEgiQ1zqCXpMZ1evXKJDuBnVu3bu2yDK0Re8dSNzpd0XswVpLmz+vRa65cxUvdM+ilBnnGrMZ12rrxhClJmj979JLUOMcrJalxBr0kNc6gl6TGGfSS1DjPjNXMOTsv9UunQV9V+4H9g8Hgqi7rkFrmTL1s3UhS4zwzVlpHXN2vT67oJalxBr0kNc7WjWbCSRupv7yomSQ1zouaSVLj7NFLUuMMeklqnEEvSY0z6CWpcY5XSnoNz55tj0Gvk+bs/GIz0NcP5+glqXHO0UtS42zdSLIN1ziDXlMxENYX+/htcLxSkhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc7xSp2QI5XSYvMSCJLUuE5X9FW1H9g/GAyu6rIOvZYreJ2IJ1ItFnv0ktQ4e/SSJuI7vcXlil6SGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhrnmbHrzLGuUeJZj5oHr4nTDwb9Oma4axb8Oeq/ToM+yU5g59atW7sso0mupCS9yssUN+RYKytXXNL6ZutGUqd89zl/Tt1IUuNc0UtaE67cu+OKXpIaZ9BLUuNs3Uhac06CrS2DfsH5glFL7OPPh60bSWqcK/qecUUjadYM+jkzuCUtt9a5YNBL6qVjHX9ywTQ9e/SS1DiDXpIaZ+umByYZkXSMUtLJMuh7zHCXjs9hh8kY9JIWigug6Rn0kta91id8DPoZ8S2k1B+TvB5n9c5gEV77Tt1IUuMMeklq3FxaN0k+AFwMnA7cXFVfmcfnkaRprOWB3OWfq8u2zsRBn+QW4P3AC1X1jrHtO4AbgA3Ap6vq+qq6E7gzyRnAHwIGPU4LSIvmWP33RXstT9O6uRXYMb4hyQbgRuAiYDuwK8n2sV0+NnpcktSRiVf0VXV/kqVlm88DDlfVkwBJbgcuTfIocD3wpar6+krPl2Q3sBtgy5Yt01cuSWtotav4Lt8FrLZHfxbw9Nj9I8D5wG8CFwIbk2ytqpuW/8eq2gvsBRgMBrXKOiRpRYvWZpmHuRyMrapPAp+cx3NL0kr6EOh9qGElqx2vfAY4e+z+5tE2SVJPrHZF/yCwLcm5DAP+cuCDk/7nJDuBnVu3bl1lGWtnEc6Ck6Rx04xX3gZcAGxKcgT4eFXdnORa4G6G45W3VNWhSZ+zqvYD+weDwVXTld1vfX37Jml9mmbqZtcxth8ADsysIknSTHlRszG2ZSS1qNOgX5Qeva0YSYus04uaVdX+qtq9cePGLsuQpKYtfOtmkmtRzKsN40pf0iLwMsWS1DiDXpIat64Pxtp6kbQedBr0fT5hyl8Cklph60aSGrfwUzfTcqUuab1xRS9JjTPoJalxnQZ9kp1J9h49erTLMiSpaU1N3dh/l6T/z9aNJDXOoJekxhn0ktQ4g16SGmfQS1LjHK+UpMb5F6YkqXG2biSpceviomaeSCVpPXNFL0mNM+glqXEGvSQ1zqCXpMY5Ry9JjXOOXpIaZ+tGkhpn0EtS4wx6SWpcqqrrGkjyIvDtk/zvm4CXZljOrFjXdKxrOn2tC/pbW4t1nVNVbz7RTr0I+tVIcrCqBl3XsZx1Tce6ptPXuqC/ta3numzdSFLjDHpJalwLQb+36wKOwbqmY13T6Wtd0N/a1m1dC9+jlyQdXwsreknScSxc0Cd5U5K/SvL46N8zjrPv6UmOJPlUH+pKck6Sryf5RpJDSa7uSV3vSvK1UU0PJ/n1PtQ12u/LSf41yRfnXM+OJI8lOZxkzwqPn5rkc6PH/y7J0jzrmaKuXxj9TL2S5LK1qGnCun47yTdHP0/3JjmnJ3VdneQfR6/Bv02yvQ91je33q0kqyWyncKpqoT6APwD2jG7vAT5xnH1vAP4c+FQf6gJOAU4d3X4D8BRwZg/q+lFg2+j2mcBzwA93XdfosfcCO4EvzrGWDcATwNtH36N/ALYv2+ca4KbR7cuBz63Bz9QkdS0B7wQ+C1w275qmqOsXgR8a3f5oj75ep4/dvgT4ch/qGu33RuB+4AFgMMsaFm5FD1wKfGZ0+zPAB1baKclPA28BvtKXuqrq5ar6n9HdU1mbd1ST1PWtqnp8dPtZ4AXghCdhzLuuUT33Av8+51rOAw5X1ZNV9TJw+6i+ceP13gG8N0m6rquqnqqqh4HvzrmWaev6alX91+juA8DmntT1b2N3TwPW4iDlJD9fAL8PfAL471kXsIhB/5aqem50+58YhvlrJHkd8EfA7/SpLoAkZyd5GHia4Sr22T7UNVbfeQxXHU/0qa45O4vh9+NVR0bbVtynql4BjgI/0oO6ujBtXR8BvjTXioYmqivJbyR5guG7yt/qQ11Jfgo4u6rm8geue/nHwZPcA7x1hYeuG79TVZVkpd/I1wAHqurILBddM6iLqnoaeGeSM4E7k9xRVc93Xdfoed4G/BlwRVWteoU4q7q0uJJ8GBgA7+m6lldV1Y3AjUk+CHwMuKLLekYL0z8GrpzX5+hl0FfVhcd6LMnzSd5WVc+NgumFFXb7WeDdSa5h2As/Jcl/VNUxD4KsUV3jz/VskkeAdzNsBXRaV5LTgbuA66rqgdXUM8u61sgzwNlj9zePtq20z5Ekrwc2Av/cg7q6MFFdSS5k+Ev9PWMty87rGnM78CdzrWjoRHW9EXgHcN9oYfpWYF+SS6rq4CwKWMTWzT6+/xv4CuAvl+9QVR+qqi1VtcSwffPZ1Yb8LOpKsjnJD45unwH8PPBYD+o6BfgLhl+nVf3SmWVda+hBYFuSc0dfi8sZ1jduvN7LgL+u0RG0juvqwgnrSvKTwJ8Cl1TVWv0Sn6SubWN3LwYe77quqjpaVZuqammUWQ8w/LrNJORf/SQL9cGwL3ovw2/QPcCbRtsHwKdX2P9K1mbq5oR1Ae8DHmZ41P1hYHdP6vow8L/AN8Y+3tV1XaP7fwO8CHyHYW/zl+ZUzy8D32J4bOK60bbfY/iCA/gB4AvAYeDvgbfP+3s3YV0/M/q6/CfDdxiHelLXPcDzYz9P+3pS1w3AoVFNXwV+og91Ldv3PmY8deOZsZLUuEVs3UiSpmDQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUuP8DaiuG2N51LsMAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 152561\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta123 400905\n", - "field\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADqhJREFUeJzt3V+MXOdZx/HvDwcHKS2BNlYV+Q92WCvCVzS1nEpUVS4o2Albl6oCGyQKsmoFYQQXSHVVLspdiwQXUQzBKJbbqrJlhQCuslUKiMi9MMVOlaZ2LNNtSJW1Qu0QZP4IYdw8XOwkHS2e9dmdGY/33e9HWu3MO2fOeV6f9aN3nvPOe1JVSJLa9UOTDkCSNF4meklqnIlekhpnopekxpnoJalxJnpJapyJXpIaZ6KXpMaZ6CWpcXdMOgCAe+65pzZv3jzpMCRpRXn++edfr6p1N9vutkj0mzdv5uzZs5MOQ5JWlCTf7bLdREs3SaaTHL569eokw5Ckpk000VfVl6tq/9133z3JMCSpaY7oJalxjuglqXFOr5Skxlm6kaTGWbqRpMZZupGkxk30C1NJpoHpqampZe9j88Fn3n78ymcfGUFUktQWSzeS1DhLN5LUOBO9JDXORC9JjXMevSQ1zouxktQ4SzeS1DgTvSQ1zkQvSY0z0UtS45x1I0mNc9aNJDXO0o0kNc5EL0mNM9FLUuNM9JLUOBO9JDVu5Ik+yUNJvpbkiSQPjXr/kqSl6ZTokxxJcjnJuQXtO5NcTDKb5GCvuYD/BH4EmBttuJKkpeo6oj8K7OxvSLIGOATsArYBe5NsA75WVbuATwJ/MLpQJUnL0SnRV9Up4I0FzTuA2ap6uaquAceB3VX1Zu/1fwPuHLTPJPuTnE1y9sqVK8sIXZLUxTA1+vXAq33P54D1ST6a5M+ALwKPD3pzVR2uqu1VtX3dunVDhCFJWswdo95hVT0NPN1l2yTTwPTU1NSow5Ak9Qwzor8EbOx7vqHX1plr3UjS+A2T6M8AW5NsSbIW2AOcXMoOXL1Sksav6/TKY8Bp4P4kc0n2VdV14ADwLHABOFFV55dycEf0kjR+nWr0VbV3QPsMMLPcg1ujl6Txcz16SWqcd5iSpMY5opekxrl6pSQ1ztKNJDXO0o0kNc7SjSQ1zkQvSY2zRi9JjbNGL0mNs3QjSY0z0UtS46zRS1LjrNFLUuMs3UhS40z0ktQ4E70kNc5EL0mNc9aNJDXOWTeS1DhLN5LUOBO9JDXORC9JjTPRS1LjTPSS1LixJPokdyU5m+QXxrF/SVJ3nRJ9kiNJLic5t6B9Z5KLSWaTHOx76ZPAiVEGKklanq4j+qPAzv6GJGuAQ8AuYBuwN8m2JB8CXgIujzBOSdIy3dFlo6o6lWTzguYdwGxVvQyQ5DiwG3gHcBfzyf+/k8xU1Zsji1iStCSdEv0A64FX+57PAQ9W1QGAJL8OvD4oySfZD+wH2LRp0xBhSJIWM0yiX1RVHb3J64eTvAZMr1279n3jikOSVrthZt1cAjb2Pd/Qa+vMtW4kafyGSfRngK1JtiRZC+wBTi5lB65eKUnj13V65THgNHB/krkk+6rqOnAAeBa4AJyoqvNLObgjekkav66zbvYOaJ8BZpZ78CTTwPTU1NRydyFJugnXo5ekxrnWjSQ1zlsJSlLjLN1IUuMc0UtS4xzRS1LjvBgrSY0z0UtS46zRS1LjrNFLUuMs3UhS40z0ktQ4a/SS1Dhr9JLUOEs3ktQ4E70kNc5EL0mNM9FLUuOcdSNJjXPWjSQ1ztKNJDXORC9JjTPRS1LjTPSS1DgTvSQ1buSJPslPJXkiyVNJfnPU+5ckLU2nRJ/kSJLLSc4taN+Z5GKS2SQHAarqQlU9CvwS8DOjD1mStBRdR/RHgZ39DUnWAIeAXcA2YG+Sbb3XPgw8A8yMLFJJ0rJ0SvRVdQp4Y0HzDmC2ql6uqmvAcWB3b/uTVbUL+NVRBitJWro7hnjveuDVvudzwINJHgI+CtzJIiP6JPuB/QCbNm0aIgxJ0mKGSfQ3VFXPAc912O4wcBhg+/btNeo4JEnzhpl1cwnY2Pd8Q6+tMxc1k6TxGybRnwG2JtmSZC2wBzg5mrAkSaPSdXrlMeA0cH+SuST7quo6cAB4FrgAnKiq80s5uKtXStL4darRV9XeAe0zOIVSkm5r3nhEkhrnjUckqXGO6CWpcY7oJalxLlMsSY2zdCNJjbN0I0mNs3QjSY2zdCNJjbN0I0mNG/kyxZO0+eAzbz9+5bOPTDASSbp9WKOXpMaZ6CWpcV6MlaTGeTFWkhpn6UaSGmeil6TGmeglqXEmeklqnLNuJKlxzrqRpMZZupGkxpnoJalxJnpJapyJXpIaZ6KXpMaNZT36JB8BHgF+FHiyqr46juMsxrXpJWle5xF9kiNJLic5t6B9Z5KLSWaTHASoqr+qqk8AjwK/PNqQJUlLsZTSzVFgZ39DkjXAIWAXsA3Ym2Rb3ya/33tdkjQhnRN9VZ0C3ljQvAOYraqXq+oacBzYnXmfA75SVd+40f6S7E9yNsnZK1euLDd+SdJNDHsxdj3wat/zuV7bbwM/C3wsyaM3emNVHa6q7VW1fd26dUOGIUkaZCwXY6vqMeCxm22XZBqYnpqaGkcYkiSGH9FfAjb2Pd/Qa+vEtW4kafyGTfRngK1JtiRZC+wBTnZ9s6tXStL4LWV65THgNHB/krkk+6rqOnAAeBa4AJyoqvNd9+mIXpLGr3ONvqr2DmifAWaWc/BbVaP3y1OSVjPXo5ekxnmHKUlqnCN6SWqcq1dKUuMs3UhS4yzdSFLjxrIEwu3MqZaSVhtLN5LUOEs3ktQ4Z91IUuNM9JLUOBO9JDXOi7GS1DgvxkpS4yzdSFLjTPSS1LhV983Yfv3fkgW/KSupTY7oJalxzrqRpMY560aSGmfpRpIaZ6KXpMaZ6CWpcSZ6SWqciV6SGjfyRJ/kviRPJnlq1PuWJC1dp0Sf5EiSy0nOLWjfmeRiktkkBwGq6uWq2jeOYG+lzQefeftHklayrksgHAUeB77wVkOSNcAh4EPAHHAmycmqemnUQU7aoGTvkgmSVoJOI/qqOgW8saB5BzDbG8FfA44Du0ccnyRpSMPU6NcDr/Y9nwPWJ3l3kieA9yb51KA3J9mf5GySs1euXBkiDEnSYka+emVV/SvwaIftDid5DZheu3bt+0Ydx3JYj5fUomFG9JeAjX3PN/TaOnOtG0kav2FG9GeArUm2MJ/g9wC/spQdJJkGpqempoYI4/bQ/2lgVBdpx7FPSatP1+mVx4DTwP1J5pLsq6rrwAHgWeACcKKqzi/l4I7oJWn8Oo3oq2rvgPYZYGa5B1/pI/pBNX1H4pJuJ65HL0mNm+g9Y1f6iH6pBo30/QQgaZwc0UtS41y9UpIaZ+lmQrpcyJWkUbB0I0mNs3QjSY0z0UtS4yaa6JNMJzl89erVSYYhSU2zRi9JjbN0I0mNM9FLUuOcR6//Z+FcfpdlkEbrVi97Yo1ekhpn6UaSGmeil6TGmeglqXEmeklqnLNuVrFbfeXfG6xIk+GsG0lqnKUbSWqciV6SGmeil6TGmeglqXEmeklq3MinVya5C/gT4BrwXFV9adTHkCR112lEn+RIkstJzi1o35nkYpLZJAd7zR8FnqqqTwAfHnG8kqQl6lq6OQrs7G9IsgY4BOwCtgF7k2wDNgCv9jb7/mjClCQtV6dEX1WngDcWNO8AZqvq5aq6BhwHdgNzzCf7zvuXJI3PMDX69fxg5A7zCf5B4DHg8SSPAF8e9OYk+4H9AJs2bRoijNvbwpt4jGI/XZYPGGb75cQk3a5cemMMF2Or6r+A3+iw3WHgMMD27dtr1HFIkuYNU1q5BGzse76h19ZZkukkh69evTpEGJKkxQyT6M8AW5NsSbIW2AOcHE1YkqRR6Tq98hhwGrg/yVySfVV1HTgAPAtcAE5U1fmlHNzVKyVp/DrV6Ktq74D2GWBmuQd3PXpJGj/Xo5ekxjnPXZIaN9FE76wbSRo/SzeS1LhUTf67SkmuAN9d5tvvAV4fYTgrgX1u32rrL9jn5fiJqlp3s41ui0Q/jCRnq2r7pOO4lexz+1Zbf8E+j5MXYyWpcSZ6SWpcC4n+8KQDmAD73L7V1l+wz2Oz4mv0kqTFtTCilyQtYkUn+gH3rG1KkleSfCvJC0nO9treleRvkny79/vHJx3nMG50T+JBfcy8x3rn/MUkD0wu8uUb0OfPJLnUO9cvJHm477VP9fp8McnPTybq5UuyMcnfJ3kpyfkkv9Nrb/Y8L9LnW3+eq2pF/gBrgO8A9wFrgW8C2yYd1xj6+Qpwz4K2PwQO9h4fBD436TiH7OMHgQeAczfrI/Aw8BUgwPuBr086/hH2+TPA791g2229v+87gS29v/s1k+7DEvt7L/BA7/E7gX/q9avZ87xIn2/5eV7JI/pB96xdDXYDn+89/jzwkQnGMrS68T2JB/VxN/CFmvcPwI8luffWRDo6A/o8yG7geFX9T1X9MzDL/N//ilFVr1XVN3qP/4P5pc3X0/B5XqTPg4ztPK/kRH+je9Yu9o+4UhXw1STP9+6zC/Ceqnqt9/hfgPdMJrSxGtTH1s/7gV6p4khfSa6pPifZDLwX+Dqr5Dwv6DPc4vO8khP9avGBqnoA2AX8VpIP9r9Y85/5mp46tRr62POnwE8CPw28BvzRZMMZvSTvAP4C+N2q+vf+11o9zzfo8y0/zys50Q99z9qVoKou9X5fBv6S+Y9y33vrY2zv9+XJRTg2g/rY7Hmvqu9V1fer6k3gz/nBx/Ym+pzkh5lPeF+qqqd7zU2f5xv1eRLneSUn+ubvWZvkriTvfOsx8HPAOeb7+fHeZh8H/noyEY7VoD6eBH6tNyvj/cDVvo/+K9qCGvQvMn+uYb7Pe5LcmWQLsBX4x1sd3zCSBHgSuFBVf9z3UrPneVCfJ3KeJ31lesir2g8zfyX7O8CnJx3PGPp3H/NX4b8JnH+rj8C7gb8Dvg38LfCuScc6ZD+PMf8R9n+Zr0vuG9RH5mdhHOqd828B2ycd/wj7/MVen17s/ae/t2/7T/f6fBHYNen4l9HfDzBflnkReKH383DL53mRPt/y8+w3YyWpcSu5dCNJ6sBEL0mNM9FLUuNM9JLUOBO9JDXORC9JjTPRS1LjTPSS1Lj/A7BN/WkUbQ+yAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 400905\n" - ] - }, - { - "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/UCwAADnlJREFUeJzt3X+M5PVdx/HnqzSg0rJSr2nLj2NpDhvPpqk6QozW1pTGQ3rQKFHQJpAQLoioiTHxEpqY6D/UqAlNSeulJbQmLcUm4l25lgqWoAkoR1ORH6EchIYD5IqJ669qJX37xw5l3N7eztzM7Pc7n30+ks3NfOe7u6/sj9d89vP9zOdSVUiS2vWargNIkubLopekxln0ktQ4i16SGmfRS1LjLHpJapxFL0mNs+glqXEWvSQ17rVdBwDYtm1bLS8vdx1DkhbKQw899FJVvXGj83pR9MvLyxw6dKjrGJK0UJJ8Y5zznLqRpMZ1WvRJdifZt7Ky0mUMSWpap0VfVQeqas/S0lKXMSSpaU7dSFLjLHpJapxFL0mN82KsJDXOi7GS1LhevGBK6qvlvXd+9/YzN17cYRLpxFn00hqj5S61wIuxktQ4i16SGueqG0lqnKtuJKlxTt1IUuMseklqnEUvSY2z6CWpcZ2+YCrJbmD3jh07uowhjfUiqbXn+EpZLQpX3UhS45y6kaTGWfSS1DiLXpIaZ9FLUuMseklqnEUvSY2z6CWpcW5TLEmN8wVTktQ4p24kqXEWvSQ1rtNNzaRFNrrJmRucqc8sem1Z4+xYKbXAqRtJapxFL0mNs+glqXEWvSQ1zqKXpMZZ9JLUOPe6kaTGudeNJDXOqRtJapxFL0mNs+glqXEWvSQ1zqKXpMa5e6W2lHntWOmWxeozR/SS1DiLXpIaZ9FLUuMseklqnEUvSY2z6CWpcRa9JDXOopekxln0ktS4uRR9klOTHEry/nl8fEnS+MYq+iS3JDma5JE1x3cleSLJ4SR7Rx76PeD2WQaVJJ2YcUf0twK7Rg8kOQm4GbgI2AlckWRnkvcBjwFHZ5hTknSCxtrUrKruS7K85vD5wOGqehogyW3ApcDrgFNZLf9vJTlYVd+ZWWJJ0kSm2b3yTODZkftHgAuq6nqAJFcBL61X8kn2AHsAtm/fPkUMSdLxzG2b4qq6dYPH9wH7AAaDQc0rh7TZ3LJYfTNN0T8HnD1y/6zhMalX5rUHvbQoplle+SBwXpJzk5wMXA7sn+QDJNmdZN/KysoUMSRJxzPu8srPAvcDb0tyJMnVVfUycD1wF/A4cHtVPTrJJ6+qA1W1Z2lpadLckqQxjbvq5op1jh8EDs40kSRpptwCQZIa12nRO0cvSfPXadE7Ry9J8+fUjSQ1zqKXpMY5Ry9JjXOOXpIaN7e9biS57436wTl6SWqcc/SS1Djn6CWpcU7dSFLjvBirJrkHvfQqR/SS1DiLXpIa56obSWqcq24kqXFO3UhS4yx6SWqcRS9JjXMdvbRJ3OBMXXFEL0mNc3mlJDWu06mbqjoAHBgMBtd0mUNtcNsD6dicupGkxln0ktQ4i16SGmfRS1LjLHpJapxFL0mN63R5ZZLdwO4dO3Z0GUPadL5KVpvJbYolqXFO3UhS4yx6SWqcRS9JjXObYi0097eRNuaIXpIaZ9FLUuOcupE65pp6zZsjeklqnEUvSY2z6CWpcf6fsZLUOPe6kaTGuepGC8cXSUmTcY5ekhpn0UtS45y6kXrEF09pHhzRS1LjLHpJapxFL0mNs+glqXFejJV6yguzmhVH9JLUOEf0Wgi+GlY6cY7oJalxjuilBbDeXzTO3WscjuglqXEWvSQ1buZTN0l+BPhtYBtwT1V9bNafQ1uDF2Cl2RhrRJ/kliRHkzyy5viuJE8kOZxkL0BVPV5V1wK/DPz07CNLesXy3ju/+yatZ9ypm1uBXaMHkpwE3AxcBOwErkiyc/jYJcCdwMGZJZUknZCxpm6q6r4ky2sOnw8crqqnAZLcBlwKPFZV+4H9Se4EPjO7uJLW4ytptZ5p5ujPBJ4duX8EuCDJe4BfBE7hOCP6JHuAPQDbt2+fIoaktSx9jZr5xdiquhe4d4zz9gH7AAaDQc06hxaTc82zZ+lrmqJ/Djh75P5Zw2PSRCx3ab6mWUf/IHBeknOTnAxcDuyf5AMk2Z1k38rKyhQxJEnHM+7yys8C9wNvS3IkydVV9TJwPXAX8Dhwe1U9Osknr6oDVbVnaWlp0tySpDGNu+rminWOH8QllJLUa25qJm0h42yO5sXb9nRa9El2A7t37NjRZQxJ67D025Cq7lc2DgaDOnToUNcxtIlcabPYTqT0fdKYvSQPVdVgo/PcvVKSGmfRS1LjOi1619FL0vw5R69OOEffrvVW8Ez6vtrYuHP0Lq/UprHctwa/z/3jHL0kNc6il6TG+YIpSb3kuvvZ6bToq+oAcGAwGFzTZQ7Nj/O1mqdxfr58kvBirKQF4Oh+Os7RS1LjHNFr5pyu0Ynqw89Oi389eDFWM9GHX1BJx+bFWElbxnqj9XEGKos80nfqRtJCmfSvx/XO30p/hVr0krSOcZ4kFmF0b9FL0hQWofQteknaZJv95GDRa0NbaS5TmsbxCrzL3yOXV0rSHPRpgNTpK2Or6kBV7VlaWuoyhiQ1zS0QJKlxFr0kNc6LsfoefZpblDQ9R/SS1DiLXpIa59RNQxbhFXqSNp/r6LeAcXbs84lBapfr6CWpcU7dNGpWW7lKWnxejJWkxjmi32IcuUtbjyN6SWqcRS9JjbPoJalxztEvCNe8SzpRFn0PTFPiXlyVtBGnbiSpcY7oF5CjeEmTcEQvSY3rtOiT7E6yb2VlpcsYktQ0NzWTpMY5R99jzsVLmgXn6CWpcRa9JDXOopekxjlHP2duXSCpa47oJalxjuin4Ghd0iKw6Duy3tJJl1RKmjWLfkIWsaRFsyWKfrOnWBytS+oTL8ZKUuMseklq3JaYupnWOFMuTstI6itH9JLUOItekho3l6mbJB8ALgZOAz5ZVV+ex+eRJG1s7KJPcgvwfuBoVb195Pgu4CbgJOATVXVjVd0B3JHkdOCPgYUoel/pKqlFk0zd3ArsGj2Q5CTgZuAiYCdwRZKdI6d8aPi4JKkjY4/oq+q+JMtrDp8PHK6qpwGS3AZcmuRx4Ebgi1X11WN9vCR7gD0A27dvnzz5nLmKRlIrpp2jPxN4duT+EeAC4DeBC4GlJDuq6uNr37Gq9gH7AAaDQU2ZY2xOz0jaauZyMbaqPgJ8ZB4f+3gscUn6XtMur3wOOHvk/lnDY5Kknpi26B8EzktybpKTgcuB/eO+c5LdSfatrKxMGUOStJ5Jlld+FngPsC3JEeD3q+qTSa4H7mJ1eeUtVfXouB+zqg4ABwaDwTWTxX6VO0VK0vFNsurminWOHwQOziyRJGmmOt0CwakbSZq/TnevnMXUzTSc3pG0FbipmSQ1zqKXpMY5Ry9Jjeu06KvqQFXtWVpa6jKGJDXNqRtJapxFL0mNs+glqXFejJWkxnkxVpIal6pN+z8/1g+RfBP4xgm++zbgpRnGmRVzTcZck+lrLuhvthZznVNVb9zopF4U/TSSHKqqQdc51jLXZMw1mb7mgv5m28q5vBgrSY2z6CWpcS0U/b6uA6zDXJMx12T6mgv6m23L5lr4OXpJ0vG1MKKXJB3HwhV9kjck+eskTw7/Pf04556W5EiSj/YhV5Jzknw1ydeSPJrk2p7kemeS+4eZHk7yK33INTzvS0n+NckX5pxnV5InkhxOsvcYj5+S5HPDx/8+yfI880yQ62eHP1MvJ7lsMzKNmet3kjw2/Hm6J8k5Pcl1bZJ/Gv4O/l2SnX3INXLeLyWpJLNdhVNVC/UG/BGwd3h7L/Dh45x7E/AZ4KN9yAWcDJwyvP064BngjB7k+mHgvOHtM4AXgB/sOtfwsfcCu4EvzDHLScBTwFuH36N/BHauOec64OPD25cDn9uEn6lxci0D7wA+DVw270wT5Po54AeGt3+9R1+v00ZuXwJ8qQ+5hue9HrgPeAAYzDLDwo3ogUuBTw1vfwr4wLFOSvITwJuAL/clV1V9u6r+Z3j3FDbnL6pxcn29qp4c3n4eOAps+CKMeeca5rkH+Pc5ZzkfOFxVT1fVt4HbhvlGjeb9PPDeJOk6V1U9U1UPA9+Zc5ZJc32lqv5rePcB4Kye5Pq3kbunAptxkXKcny+APwQ+DPz3rAMsYtG/qapeGN7+Z1bL/P9J8hrgT4Df7VMugCRnJ3kYeJbVUezzfcg1ku98VkcdT/Up15ydyer34xVHhseOeU5VvQysAD/Ug1xdmDTX1cAX55po1Vi5kvxGkqdY/avyt/qQK8mPA2dX1Vz+I+tO/3Pw9SS5G3jzMR66YfROVVWSYz0jXwccrKojsxx0zSAXVfUs8I4kZwB3JPl8Vb3Yda7hx3kL8OfAlVU19QhxVrm0uJJ8EBgA7+46yyuq6mbg5iS/CnwIuLLLPMOB6Z8CV83rc/Sy6KvqwvUeS/JikrdU1QvDYjp6jNN+CnhXkutYnQs/Ocl/VNW6F0E2Kdfox3o+ySPAu1idCug0V5LTgDuBG6rqgWnyzDLXJnkOOHvk/lnDY8c650iS1wJLwL/0IFcXxsqV5EJWn9TfPTJl2XmuEbcBH5trolUb5Xo98Hbg3uHA9M3A/iSXVNWhWQRYxKmb/bz6DHwl8FdrT6iqX6uq7VW1zOr0zaenLflZ5EpyVpLvH94+HfgZ4Ike5DoZ+EtWv05TPenMMtcmehA4L8m5w6/F5azmGzWa9zLgb2p4Ba3jXF3YMFeSHwP+DLikqjbrSXycXOeN3L0YeLLrXFW1UlXbqmp52FkPsPp1m0nJv/JJFuqN1XnRe1j9Bt0NvGF4fAB84hjnX8XmrLrZMBfwPuBhVq+6Pwzs6UmuDwL/C3xt5O2dXeca3v9b4JvAt1id2/z5OeX5BeDrrF6buGF47A9Y/YUD+D7gL4DDwD8Ab533927MXD85/Lr8J6t/YTzak1x3Ay+O/Dzt70mum4BHh5m+AvxoH3KtOfdeZrzqxlfGSlLjFnHqRpI0AYtekhpn0UtS4yx6SWqcRS9JjbPoJalxFr0kNc6il6TG/R//5LBksiMGbQAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 400905\n" - ] - }, - { - "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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "delta234\n", - "field\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut,pzcut,curvcut 400905\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADXtJREFUeJzt3W+oZPdZwPHvY/5spZHbNgllyWa9iVtqU5FYxq1QkVApbtpuIlIk4ptCyGJqwD+I3VKQKAi1IvaFxbBKXNtq0rX6Ym9bKP1jjS9KzV2tMX9Yvdm2ZEPsmpauCtIa8/hizurc2Z07M/fOzDnzzPcDw849c+bMs7+955lnnt9vzkZmIkmq63vaDkCSNF8mekkqzkQvScWZ6CWpOBO9JBVnopek4kz0klSciV6SijPRS1JxV7cdAMANN9yQ6+vrbYchSUvlzJkzL2bmjeP260SiX19fZ3Nzs+0wJGmpRMTXJ9nP1o0kFWeil6TiTPSSVJyJXpKKM9FLUnEmekkqzkQvScWZ6CWpuE58YWov1o9/6orbv/aBdyw4EknqpqVP9KMMvgGY9CWtsrKJfpBJX9IqazXRR8RR4OihQ4cW9pomfUmrptXJ2MzcyMxja2trbYYhSaWtROtmlOGJXCt8SRWtdKIfZltHUkWuo5ek4kz0klScrZsRbONIqsKKXpKKs6KfgNW9pGVmRS9JxZnoJak4WzdTso0jadlY0UtScVb0e2B1L2kZWNFLUnEmekkqztbNjNjGkdRVVvSSVJyJXpKKs3UzB7ZxJHWJFb0kFWeil6TiTPSSVNxcevQR8Urgb4AHM/OT83iNZWG/XlLbJqroI+LhiLgQEU8ObT8SEWcjYisijg889F7g1CwDlSTtzqStm5PAkcENEXEV8GHgTuA24Oci4raIeBvwNHBhhnFKknZpotZNZj4WEetDmw8DW5l5DiAiHgXuBq4DXkk/+f9XRHw6M1+eWcRLzDaOpDbspUd/E/DcwM/ngTdn5gMAEfFu4MVRST4ijgHHAA4ePLiHMCRJO5nbF6Yy8+SYx08AJwB6vV7OK46usrqXtCh7WV75PHDzwM8Hmm2SpA7ZS6J/HHhdRNwSEdcC9wCnZxOWJGlWJl1e+QjwJeD1EXE+Iu7NzJeAB4DPAM8ApzLzqWlePCKORsSJixcvThu3JGlCkdl+e7zX6+Xm5uaunjvY667Afr2kSUXEmczsjdvPSyBIUnEmekkqzuvRd4zLLiXNWqsVvZOxkjR/rSb6zNzIzGNra2tthiFJpdmjl6TiTPSSVJyTsR3mxKykWXAyVpKKa7Wiz8wNYKPX693XZhzLwOpe0m7Zo5ek4kz0klSck7FLyDaOpGlY0UtSca66kaTivASCJBVnj37J2a+XNI49ekkqzkQvScXZuinENo6kK7Gil6TiXF4pScW5vFKSirN1I0nFORlblBOzki6xopek4kz0klScrZsVYBtHWm1W9JJUnIlekorzC1OSVJxfmJKk4pyMXTGDE7ODnKSV6rJHL0nFmeglqTgTvSQVZ6KXpOJM9JJUnKtuBHiZBKkyK3pJKs6KXpcZXmtvhS8tNy+BIEnFeQkESSrO1o3GcqJWWm5OxkpScSZ6SSrORC9Jxdmj11Ts10vLx4pekooz0UtScSZ6SSrORC9JxZnoJak4V91o11yBIy0HK3pJKs5EL0nF2brRTNjGkbrL69FLUnGtVvSZuQFs9Hq9+9qMQ7NldS91iz16SSrOHr3myupeap8VvSQVZ6KXpOJM9JJUnIlekopzMlYL48Ss1A4rekkqzoperbC6lxbHil6SijPRS1JxJnpJKs4evVpnv16aLyt6SSrORC9Jxdm6UafYxpFmz4pekooz0UtScSZ6SSrORC9JxZnoJak4V92oswZX4AxyNY40nZlX9BHxhoh4KCI+ERH3z/r4kqTpTJToI+LhiLgQEU8ObT8SEWcjYisijgNk5jOZ+QvAzwJvmX3IkqRpTNq6OQn8AfCRSxsi4irgw8DbgPPA4xFxOjOfjoi7gPuBj842XMkvVUnTmqiiz8zHgG8NbT4MbGXmucz8LvAocHez/+nMvBP4+VkGK0ma3l4mY28Cnhv4+Tzw5oi4A/gZYB/w6VFPjohjwDGAgwcP7iEMrTKre2m8ma+6ycwvAl+cYL8TwAmAXq+Xs45DktS3l1U3zwM3D/x8oNkmSeqQvST6x4HXRcQtEXEtcA9wejZhSZJmZdLllY8AXwJeHxHnI+LezHwJeAD4DPAMcCozn5rmxSPiaEScuHjx4rRxS5ImFJntt8d7vV5ubm7u6rmjvj2p1ebErFZBRJzJzN64/bzWjSQVZ6KXpOJaTfT26CVp/lq9emVmbgAbvV7vvjbjUD1e+VL6f7ZuJKk4E70kFWeil6Ti/B+mtFK8CJpWkatuJKk4V91oZVnda1XYupEw6as2J2MlqTgremkHVvqqwIpekoprtaKPiKPA0UOHDrUZhrSNl75WNa1W9Jm5kZnH1tbW2gxDkkqzdSNJxZnoJak4V91IExru3bsKR8vCil6SirOil3bJNfZaFl7UTJKKc3mlJBVnj16SirNHL82A/Xp1mYlemqNRbwC+MWiRbN1IUnFW9NKMzeqiaFb9mhUTvbQgXhVTbTHRSy2bVeXuJwCN4hemJKk4vzAlScW56kaSirNHL3WIK3Y0D1b0klSciV6SirN1Iy2BUS0d2zKahBW9JBVnopek4mzdSMW5AkcmemmJef0cTcJLIEhSca1W9Jm5AWz0er372oxDWhWTtHFs9dRj60aSLaDiTPSS5sZPB91gopc0kom6BtfRS1JxJnpJKs7WjbSinIBdHSZ6SVOzd79cbN1IUnEmekkqztaNpE6yPTQ7VvSSVJwVvaSJTLpKZ5L9rNYXy0QvaSHmvZzTN4/RTPSSOm9WSXxV3wy8Hr0kFef16CWtpFWq7m3dSFpaXsZhMiZ6SXsyy2Q7j8Ttm4GJXpImssytHr8wJUnFWdFLapWtlfkz0UvSjHS1vWOil6QRqnzaMNFLKmcvCXpWyX34OG1W+CZ6SdqD3bwxLLrF46obSSrOil7SUqnSN18kE70kTWnZ3mxM9JJW3iISd5tvDvboJak4E70kFWeil6TiTPSSVJyJXpKKM9FLUnEmekkqzkQvScWZ6CWpuMjMtmMgIv4N+Poun34D8OIMw5kV45qOcU2vq7EZ13T2Etf3Z+aN43bqRKLfi4jYzMxe23EMM67pGNf0uhqbcU1nEXHZupGk4kz0klRchUR/ou0ARjCu6RjX9Loam3FNZ+5xLX2PXpK0swoVvSRpJ5nZ+g04ApwFtoDjV3h8H/Dx5vEvA+sDj72v2X4W+KlxxwRuaY6x1Rzz2o7EdRL4KvCV5nb7guN6GLgAPDl0rNcAnwX+pfnz1R2J60Hg+YHxevui4gJuBv4aeBp4CvilLozXmLjaHK9XAH8H/GMT12924XwcE9dJWjwfm8euAv4B+ORuxmvbsSbZaZ635i/zLHArcG0z6LcN7fMe4KHm/j3Ax5v7tzX772sG4NnmeCOPCZwC7mnuPwTc35G4TgLvamO8msd+AngTlyfUD1765QWOA7/TkbgeBH6tpd+v/cCbmn2+D/jngX/H1sZrTFxtjlcA1zX7XEM/Uf1YB87HneI6SYvnY/P4rwJ/zvZEP9F4Dd+60Lo5DGxl5rnM/C7wKHD30D53A3/a3P8E8JMREc32RzPzO5n5VfrvcodHHbN5zlubY9Ac86fbjmvCcZpnXGTmY8C3rvB6g8da9HjtFNekZh5XZr6QmX/fxPcfwDPATVc41kLHa0xck5pHXJmZ/9nsf01zy7bPx1FxjR2hOccFEBEHgHcAf3zpIFOO1zZdSPQ3Ac8N/Hyey385/2+fzHwJuAhcv8NzR22/Hvh2c4xRr9VGXJf8dkQ8ERG/HxH7FhjXTl6bmS809/8VeG1H4gJ4oBmvhyPi1W3EFRHrwI/QrwahI+N1hbigxfGKiKsi4iv023Cfzcwv0/75OCquS9o8Hz8E/Drw8sDj04zXNl1I9Op7H/CDwI/S7/O+t91wLpf9z4tdWab1h8APALcDLwC/t+gAIuI64C+BX87Mfx9+vK3xGhFXq+OVmf+TmbcDB4DDEfFDi3z9UXaIq7XzMSLeCVzIzDOzOmYXEv3z9CeRLjnQbLviPhFxNbAGfHOH547a/k3gVc0xRr1WG3HRfOzOzPwO8Cc0H+EWFNdOvhER+5tj7adf+bQeV2Z+ozlJXwb+iAWPV0RcQz+Z/llm/tXAPq2O16i42h6vgTi+TX/C+Ajtn4+j4mr7fHwLcFdEfI1+K+itEfExphuv7SZp5M/zBlwNnKM/GXFpMuONQ/v8ItsnM04199/I9smMc/QnR0YeE/gLtk9mvKcjce1v/gz6H9s+sKi4Bp63zuWTnr/L9snFD3Ykrv0D93+Ffq9zUf+OAXwE+NAVXq+18RoTV5vjdSPwqmaf7wX+FnhnB87HneJq/Xxs9rmD7ZOxE43XZXFOstO8b8Db6a8QeBZ4f7Ptt4C7mvuvaP6CW/SXQ9068Nz3N887C9y50zGb7bc2x9hqjrmvI3F9Afgn4EngYzSrARYY1yP0P9L/N/3e373N9uuBz9NfLvg54DUdieujzXg9AZxmIJHNOy7gx+m3ZJ5gaLlim+M1Jq42x+uH6S8TfIL+7/dvdOF8HBNXq+fjwON3sD3RTzxegze/GStJxXWhRy9JmiMTvSQVZ6KXpOJM9JJUnIlekooz0UtScSZ6SSrORC9Jxf0vefVR7C2u4CoAAAAASUVORK5CYII=\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" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "thcut2,pzcut2 400905\n" - ] - }, - { - "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" - } - ], + "outputs": [], "source": [ - "for quad in [t1234,t1231,t1212,t1123] :\n", - " plotTriplets(quad,600)" + "#for quad in [t1234,t1231,t1212,t1123] :\n", + "# plotTriplets(quad,600)" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 96, "metadata": {}, "outputs": [ { From cc973f603ad1b20b726b055cd5424845aa91fb73 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 31 Oct 2018 10:42:08 +0100 Subject: [PATCH 15/94] silence it --- .../interface/phase1PixelTopology.h | 2 +- .../src/PixelTrackCleanerBySharedHits.cc | 3 ++- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 2 ++ .../plugins/CAHitQuadrupletGeneratorGPU.cu | 3 ++- .../PixelTriplets/plugins/gpuFishbone.h | 14 +------------- .../PixelVertexFinding/src/gpuSplitVertices.h | 2 +- 6 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h index 0ab487d0477e9..fca49374679bc 100644 --- a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h +++ b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h @@ -87,7 +87,7 @@ namespace phase1PixelTopology { return res; } - static_assert(validateLayerIndex(),"Vincenzo's algo is buggy"); + static_assert(validateLayerIndex(),"layer from detIndex algo is buggy"); // this is for the ROC n<512 (upgrade 1024) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index 5648d7211deef..7484c082b647b 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -113,5 +113,6 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) } //tk1 trackHitPairs.erase(std::remove_if(trackHitPairs.begin(),trackHitPairs.end(),[&](TrackWithTTRHs & v){ return nullptr==v.first;}),trackHitPairs.end()); - std::cout << "Q after clean " << trackHitPairs.size() << ' ' << killed << std::endl; + + LogDebug("PixelTrackCleanerBySharedHits") << "Q after clean " << trackHitPairs.size() << ' ' << killed << std::endl; } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index a042e66698ca5..a4db5661dd90c 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -201,6 +201,8 @@ void CAHitQuadrupletGeneratorGPU::fillResults( } // end loop over quads +#ifdef GPU_DEBUG std::cout << "Q Final quads " << result[index].size() << ' ' << nbad << std::endl; +#endif } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 7d821eb295646..3ecafb409f699 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -431,8 +431,9 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) 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]; } +#ifdef GPU_DEBUG std::cout << "Q Produced " << quadsInterface.size() << " quadruplets" << std::endl; - +#endif return quadsInterface; } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 7be83f10bdd00..c5248c8e78431 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -41,16 +41,13 @@ namespace gpuPixelDoublets { if (s<2) return; // if alligned kill one of the two. auto const & c0 = cells[vc[0]]; - // auto d0 = c0.get_outer_detId(hh); auto xo = c0.get_outer_x(hh); auto yo = c0.get_outer_y(hh); auto zo = c0.get_outer_z(hh); float x[256], y[256],z[256], n[256]; uint16_t d[256]; uint8_t l[256]; - // bool kill[256]; for (uint32_t ic=0; ic= 0.9999f*n[ic]*n[jc]) { - // alligned (kill closest) + // alligned: kill closest if (n[ic] Date: Wed, 31 Oct 2018 11:08:36 +0100 Subject: [PATCH 16/94] mark magic 2 --- RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index ab1bce4ed9ba0..971d686980d4e 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -84,7 +84,7 @@ 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, 2*thetaCut); + bool aligned = areAlignedRZ(r1, z1, ri, zi, ro, zo, ptmin, 2*thetaCut); // FIXME tune cuts return (aligned && haveSimilarCurvature(hh, otherCell, ptmin, region_origin_x, region_origin_y, region_origin_radius, phiCut, From f2439af23fd470aa9318f67241411bf6fd87ced3 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 31 Oct 2018 13:48:30 +0100 Subject: [PATCH 17/94] remove magic 256, reduce it to 128 --- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 4 ++-- .../plugins/CAHitQuadrupletGeneratorGPU.h | 4 ++-- RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h | 4 ++++ RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h | 10 +++++++--- .../PixelTriplets/plugins/gpuPixelDoublets.h | 6 +++--- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 3ecafb409f699..87c9245437dd6 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -156,7 +156,7 @@ void kernelLineFitAllHits(GPU::SimpleVector * foundNtuplets, __global__ void kernel_checkOverflows(GPU::SimpleVector *foundNtuplets, GPUCACell const * __restrict__ cells, uint32_t const * __restrict__ nCells, - GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, + GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, uint32_t nHits, uint32_t maxNumberOfDoublets) { __shared__ uint32_t killedCell; @@ -189,7 +189,7 @@ void kernel_connect(GPU::SimpleVector *foundNtuplets, GPUCACell::Hits const * __restrict__ hhp, GPUCACell * cells, uint32_t const * __restrict__ nCells, - GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, + GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, float ptmin, float region_origin_radius, const float thetaCut, const float phiCut, const float hardPtCut, diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index fdceec9225d76..281ce23eb1dc9 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -157,7 +157,7 @@ class CAHitQuadrupletGeneratorGPU { const float caHardPtCut = 0.f; static constexpr int maxNumberOfQuadruplets_ = 10000; - static constexpr int maxCellsPerHit_ = 256; + static constexpr int maxCellsPerHit_ = GPUCACell::maxCellsPerHit; static constexpr int maxNumberOfLayerPairs_ = 13; static constexpr int maxNumberOfLayers_ = 10; static constexpr int maxNumberOfDoublets_ = 262144; @@ -170,7 +170,7 @@ class CAHitQuadrupletGeneratorGPU { std::vector d_foundNtupletsData_; GPUCACell* device_theCells_ = nullptr; - GPU::VecArray< unsigned int, maxCellsPerHit_>* device_isOuterHitOfCell_ = nullptr; + GPUCACell::OuterHitOfCell* device_isOuterHitOfCell_ = nullptr; uint32_t* device_nCells_ = nullptr; HitsOnCPU const * hitsOnCPU=nullptr; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 971d686980d4e..e5fbc32afe6ac 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -19,6 +19,10 @@ struct Quadruplet { 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; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index c5248c8e78431..8d1e19ca22651 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -23,9 +23,13 @@ namespace gpuPixelDoublets { void fishbone( GPUCACell::Hits const * __restrict__ hhp, GPUCACell * cells, uint32_t const * __restrict__ nCells, - GPU::VecArray< unsigned int, 256> const * __restrict__ isOuterHitOfCell, + GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, uint32_t nHits, uint32_t stride) { + + 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);}; @@ -44,8 +48,8 @@ namespace gpuPixelDoublets { auto xo = c0.get_outer_x(hh); auto yo = c0.get_outer_y(hh); auto zo = c0.get_outer_z(hh); - float x[256], y[256],z[256], n[256]; - uint16_t d[256]; uint8_t l[256]; + float x[maxCellsPerHit], y[maxCellsPerHit],z[maxCellsPerHit], n[maxCellsPerHit]; + uint16_t d[maxCellsPerHit]; uint8_t l[maxCellsPerHit]; for (uint32_t ic=0; ic __device__ @@ -29,7 +29,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, @@ -144,7 +144,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] = { From ae7fc3f8e622a6430057f3eae2464faed9ba6523 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 31 Oct 2018 15:10:44 +0100 Subject: [PATCH 18/94] reduce size --- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h | 2 +- RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index 281ce23eb1dc9..13ea531540d21 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -161,7 +161,7 @@ class CAHitQuadrupletGeneratorGPU { static constexpr int maxNumberOfLayerPairs_ = 13; static constexpr int maxNumberOfLayers_ = 10; static constexpr int maxNumberOfDoublets_ = 262144; - static constexpr int maxNumberOfRegions_ = 2; + static constexpr int maxNumberOfRegions_ = 1; std::vector*> h_foundNtupletsVec_; std::vector h_foundNtupletsData_; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h index ad84188f31c3c..076ef17238120 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h @@ -16,7 +16,7 @@ namespace gpuPixelDoublets { - constexpr uint32_t MaxNumOfDoublets = 1024*1024*128; // was 256; + constexpr uint32_t MaxNumOfDoublets = 1024*1024*256; // not really relevant template __device__ From 9030763831f99d1e19ad2b7db78d174f7ad0ab4f Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 31 Oct 2018 17:57:02 +0100 Subject: [PATCH 19/94] remove duplicate code lines --- RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu | 2 -- 1 file changed, 2 deletions(-) diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu index 3a80ac31c8ead..cc7fb51b11eca 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu @@ -73,8 +73,6 @@ namespace pixelgpudetails { cudaCheck(cudaMalloc((void **) & gpu_.hist_d, sizeof(HitsOnGPU::Hist))); cudaCheck(cudaMalloc((void **) & gpu_.hws_d, 4*HitsOnGPU::Hist::totbins())); 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?) From d79faf56874d990b8c325e9b3fef2e179d8b4393 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Thu, 1 Nov 2018 15:18:17 +0100 Subject: [PATCH 20/94] narrow cut to avoid inefficiency for realistic --- RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 8d1e19ca22651..9f12608e671e6 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -69,7 +69,7 @@ namespace gpuPixelDoublets { if (d[ic]==d[jc] || l[ic]!=l[jc]) continue; auto cos12 = x[ic]*x[jc]+y[ic]*y[jc]+z[ic]*z[jc]; - if (cos12*cos12 >= 0.9999f*n[ic]*n[jc]) { + if (cos12*cos12 >= 0.99999f*n[ic]*n[jc]) { // alligned: kill closest if (n[ic] Date: Fri, 2 Nov 2018 19:48:17 +0100 Subject: [PATCH 21/94] build pentuplets --- .../src/PixelTrackCleanerBySharedHits.cc | 31 ++++++++++++++-- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 12 ++++-- .../PixelTriplets/plugins/GPUCACell.h | 37 ++++++++++++------- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index 7484c082b647b..f75dcec0783e0 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -60,6 +60,25 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) } // tk1 + // third loop: third and forth hits.... (targetting region around eta=2) + for (auto i = 0U; i < size; ++i) { + auto iTrack1 = ind[i]; + auto track1 = trackHitPairs[iTrack1].first; + if (!track1) continue; + auto const & recHits1 = trackHitPairs[iTrack1].second; + if (recHits1.size()<4) continue; + for (auto j = i+1; j < size; ++j) { + auto iTrack2 = ind[j]; + auto track2 = trackHitPairs[iTrack2].first; + if (!track2) continue; + auto const & recHits2 = trackHitPairs[iTrack2].second; + if (recHits2.size()<4) continue; + if (recHits1[3] != recHits2[3]) continue; + if (recHits1[2] != recHits2[2]) continue; + kill(iTrack2); + } // tk2 + } // tk1 + // second loop: first and third hits.... for (auto i = 0U; i < size; ++i) { auto iTrack1 = ind[i]; @@ -73,13 +92,19 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; if (recHits2.size()<3) continue; - if (recHits1[0] != recHits2[0]) continue; - if (recHits1[2] != recHits2[2]) continue; - kill(iTrack2); + if (recHits1[1] == recHits2[2] + && + recHits1[2] == recHits2[3]) kill(iTrack2); + if (recHits2[1] == recHits1[2] + && + recHits2[2] == recHits1[3]) kill(iTrack2); } // tk2 } // tk1 + + + // final loop: all the rest for (auto i = 0U; i < size; ++i) { auto iTrack1 = ind[i]; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 71533c72b060b..ab1bef7473558 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -234,7 +234,7 @@ __global__ void kernel_find_ntuplets( if (cellIndex >= (*nCells) ) return; auto &thisCell = cells[cellIndex]; if (thisCell.theLayerPairId!=0 && thisCell.theLayerPairId!=3 && thisCell.theLayerPairId!=8) return; // inner layer is 0 FIXME - GPU::VecArray stack; + GPUCACell::TmpTuple stack; stack.reset(); thisCell.find_ntuplets(cells, foundNtuplets, stack, minHitsPerNtuplet); assert(stack.size()==0); @@ -431,13 +431,17 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) assert(0==regionIndex); h_foundNtupletsVec_[regionIndex]->set_data(h_foundNtupletsData_[regionIndex]); + uint32_t sizes[7]={0}; std::vector> quadsInterface(h_foundNtupletsVec_[regionIndex]->size()); for (int i = 0; i < h_foundNtupletsVec_[regionIndex]->size(); ++i) { + ++sizes[(*h_foundNtupletsVec_[regionIndex])[i].size()]; for (int j = 0; j<4; ++j) quadsInterface[i][j] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[j]; } -#ifdef GPU_DEBUG - std::cout << "Q Produced " << quadsInterface.size() << " quadruplets" << std::endl; -#endif +//#ifdef GPU_DEBUG + std::cout << "Q Produced " << quadsInterface.size() << " quadruplets: "; + for (auto i=3; i<7; ++i) std::cout << sizes[i] << ' '; + std::cout << std::endl; +//#endif return quadsInterface; } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index b5a7b43997a67..8e8a9a6c013e7 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -12,8 +12,14 @@ #include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" struct Quadruplet { + static constexpr uint32_t capacity() { return 6;} using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; - hindex_type hitId[4]; + static constexpr auto invalid() { return std::numeric_limits::max();} + hindex_type hitId[6]; + uint32_t size() const { + for (auto i=capacity()-1; i>0; --i) if (hitId[i]!=invalid()) return i+1; + return 0; + } }; @@ -27,6 +33,9 @@ class GPUCACell { using Hits = siPixelRecHitsHeterogeneousProduct::HitsOnGPU; using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; + using TmpTuple = GPU::VecArray; + + GPUCACell() = default; #ifdef __CUDACC__ @@ -204,7 +213,7 @@ class GPUCACell { inline void find_ntuplets( GPUCACell const * __restrict__ cells, GPU::SimpleVector *foundNtuplets, - GPU::VecArray &tmpNtuplet, + TmpTuple & tmpNtuplet, const unsigned int minHitsPerNtuplet) const { // the building process for a track ends if: @@ -214,25 +223,27 @@ class GPUCACell { // than a threshold tmpNtuplet.push_back_unsafe(theInnerHitId); - assert(tmpNtuplet.size()<=3); + assert(tmpNtuplet.size()<=5); - 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, minHitsPerNtuplet); } + } else { // if long enough save... + if ((unsigned int)(tmpNtuplet.size()) >= minHitsPerNtuplet-1) { + Quadruplet tmpQuadruplet; + for (unsigned int i = 0; i < tmpNtuplet.size(); ++i) { + tmpQuadruplet.hitId[i] = tmpNtuplet[i]; + } + tmpQuadruplet.hitId[tmpNtuplet.size()] = theOuterHitId; + for (unsigned int i = tmpNtuplet.size()+1; ipush_back(tmpQuadruplet); + } } tmpNtuplet.pop_back(); - assert(tmpNtuplet.size() < 3); + assert(tmpNtuplet.size() < 5); } #endif // __CUDACC__ From 7bea72e466e2e846cea8a22c0cd8a150dad5dc49 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 3 Nov 2018 18:53:08 +0100 Subject: [PATCH 22/94] simplify --- .../src/PixelTrackCleanerBySharedHits.cc | 14 +++++++++----- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 9 +++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index f75dcec0783e0..c54bf38b2a61f 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -53,7 +53,7 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; - if (recHits1[0] != recHits2[0]) continue; +// if (recHits1[0] != recHits2[0]) continue; if (recHits1[1] != recHits2[1]) continue; kill(iTrack2); } // tk2 @@ -73,12 +73,16 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; if (recHits2.size()<4) continue; - if (recHits1[3] != recHits2[3]) continue; - if (recHits1[2] != recHits2[2]) continue; - kill(iTrack2); + if (recHits1[3] == recHits2[3]) kill(iTrack2); + if (recHits1[3] == recHits2[2]) kill(iTrack2); + if (recHits1[2] == recHits2[3]) kill(iTrack2); + // if (recHits1[3] != recHits2[3]) continue; + // if (recHits1[2] != recHits2[2]) continue; +// kill(iTrack2); } // tk2 } // tk1 + /* // second loop: first and third hits.... for (auto i = 0U; i < size; ++i) { auto iTrack1 = ind[i]; @@ -100,7 +104,7 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) recHits2[2] == recHits1[3]) kill(iTrack2); } // tk2 } // tk1 - + */ diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index ab1bef7473558..b76d94ab54262 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -434,8 +434,13 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) uint32_t sizes[7]={0}; std::vector> quadsInterface(h_foundNtupletsVec_[regionIndex]->size()); for (int i = 0; i < h_foundNtupletsVec_[regionIndex]->size(); ++i) { - ++sizes[(*h_foundNtupletsVec_[regionIndex])[i].size()]; - for (int j = 0; j<4; ++j) quadsInterface[i][j] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[j]; + auto sz = (*h_foundNtupletsVec_[regionIndex])[i].size(); + ++sizes[sz]; + quadsInterface[i][0] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[0]; + quadsInterface[i][1] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[1]; + quadsInterface[i][2] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[2]; // [sz-2]; + quadsInterface[i][3] = (*h_foundNtupletsVec_[regionIndex])[i].hitId[3]; // [sz-1]; + } //#ifdef GPU_DEBUG std::cout << "Q Produced " << quadsInterface.size() << " quadruplets: "; From 932fec9e7ab851cefc49163a4691811a38962128 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 4 Nov 2018 14:24:54 +0100 Subject: [PATCH 23/94] align to offline --- .../src/PixelTrackCleanerBySharedHits.cc | 43 +++---------------- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 6 ++- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index c54bf38b2a61f..83c8211f2b3f7 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -53,60 +53,31 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; -// if (recHits1[0] != recHits2[0]) continue; + if (recHits1[0] != recHits2[0]) continue; if (recHits1[1] != recHits2[1]) continue; kill(iTrack2); } // tk2 } // tk1 - // third loop: third and forth hits.... (targetting region around eta=2) + // second loop: only last two hits hit for (auto i = 0U; i < size; ++i) { auto iTrack1 = ind[i]; auto track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; - if (recHits1.size()<4) continue; - for (auto j = i+1; j < size; ++j) { - auto iTrack2 = ind[j]; - auto track2 = trackHitPairs[iTrack2].first; - if (!track2) continue; - auto const & recHits2 = trackHitPairs[iTrack2].second; - if (recHits2.size()<4) continue; - if (recHits1[3] == recHits2[3]) kill(iTrack2); - if (recHits1[3] == recHits2[2]) kill(iTrack2); - if (recHits1[2] == recHits2[3]) kill(iTrack2); - // if (recHits1[3] != recHits2[3]) continue; - // if (recHits1[2] != recHits2[2]) continue; -// kill(iTrack2); - } // tk2 - } // tk1 - - /* - // second loop: first and third hits.... - for (auto i = 0U; i < size; ++i) { - auto iTrack1 = ind[i]; - auto track1 = trackHitPairs[iTrack1].first; - if (!track1) continue; - auto const & recHits1 = trackHitPairs[iTrack1].second; - if (recHits1.size()<3) continue; + auto s1 = recHits1.size(); for (auto j = i+1; j < size; ++j) { auto iTrack2 = ind[j]; auto track2 = trackHitPairs[iTrack2].first; if (!track2) continue; auto const & recHits2 = trackHitPairs[iTrack2].second; - if (recHits2.size()<3) continue; - if (recHits1[1] == recHits2[2] - && - recHits1[2] == recHits2[3]) kill(iTrack2); - if (recHits2[1] == recHits1[2] - && - recHits2[2] == recHits1[3]) kill(iTrack2); + auto s2 = recHits2.size(); + if (recHits1[s1-1] != recHits2[s2-1]) continue; + if (recHits1[s1-2] != recHits2[s2-2]) continue; + kill(iTrack2); } // tk2 } // tk1 - */ - - // final loop: all the rest diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index b76d94ab54262..54b40aebc1c96 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -432,10 +432,13 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) h_foundNtupletsVec_[regionIndex]->set_data(h_foundNtupletsData_[regionIndex]); 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(h_foundNtupletsVec_[regionIndex]->size()); for (int i = 0; i < h_foundNtupletsVec_[regionIndex]->size(); ++i) { auto sz = (*h_foundNtupletsVec_[regionIndex])[i].size(); ++sizes[sz]; + for (auto j=0U; j0){ave+=k; ++nn;} std::cout << "Q Produced " << quadsInterface.size() << " quadruplets: "; for (auto i=3; i<7; ++i) std::cout << sizes[i] << ' '; - std::cout << std::endl; + std::cout << "max/ave " << *std::max_element(ntk.begin(),ntk.end())<<'/'< Date: Sun, 4 Nov 2018 16:08:24 +0100 Subject: [PATCH 24/94] simplify histogrammer: no need of ws in fill --- .../CUDAUtilities/interface/HistoContainer.h | 55 ++++++++++++------- .../CUDAUtilities/test/BuildFile.xml | 15 ++++- .../CUDAUtilities/test/HistoContainer_t.cpp | 12 ++-- .../CUDAUtilities/test/HistoContainer_t.cu | 2 +- .../CUDAUtilities/test/OneHistoContainer_t.cu | 14 ++--- .../plugins/gpuClustering.h | 11 ++-- .../SiPixelRecHits/plugins/PixelRecHits.cu | 2 +- .../siPixelRecHitsHeterogeneousProduct.h | 2 +- .../PixelVertexFinding/src/gpuClusterTracks.h | 11 ++-- .../PixelVertexFinding/test/BuildFile.xml | 2 +- 10 files changed, 71 insertions(+), 55 deletions(-) diff --git a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h index 3a6545bed17df..cc12a018fcc2f 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -38,8 +38,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,12 +46,12 @@ 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 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)); @@ -60,10 +59,9 @@ namespace cudautils { auto nblocks = (totSize + nthreads - 1) / nthreads; countFromVector<<>>(h, nh, v, offsets); cudaCheck(cudaGetLastError()); - size_t wss = Histo::totbins(); + size_t wss = Histo::wsSize(); CubDebugExit(cub::DeviceScan::InclusiveSum(ws, wss, off, off, Histo::totbins(), stream)); - cudaMemsetAsync(ws,0, 4*Histo::totbins(),stream); - fillFromVector<<>>(h, nh, v, offsets,ws); + fillFromVector<<>>(h, nh, v, offsets); cudaCheck(cudaGetLastError()); } @@ -149,8 +147,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 +174,33 @@ 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 count(T t) { uint32_t b = bin(t); assert(b0); + bins[w-1] = j; } @@ -202,31 +211,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 - + + + + + + + - + @@ -25,21 +31,24 @@ + - + + + 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; - 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))); // Feels a bit dumb but constexpr arrays are not supported for device code diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h b/RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h index e250ee0cb0469..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; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h index 608cb0b6f3b9f..94404873064c0 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h @@ -46,10 +46,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 - + From 6a192fd5c09d5e550e74ddfcf62f3e21905f5e5a Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 5 Nov 2018 08:56:27 +0100 Subject: [PATCH 25/94] test cuda_assert --- HeterogeneousCore/CUDAUtilities/test/BuildFile.xml | 8 ++++++++ HeterogeneousCore/CUDAUtilities/test/assert_t.cu | 14 ++++++++++++++ .../PixelTriplets/plugins/GPUCACell.h | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 HeterogeneousCore/CUDAUtilities/test/assert_t.cu diff --git a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml index c201145614559..8fdb380c1b00c 100644 --- a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml +++ b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml @@ -1,5 +1,13 @@ + + + + + + + + diff --git a/HeterogeneousCore/CUDAUtilities/test/assert_t.cu b/HeterogeneousCore/CUDAUtilities/test/assert_t.cu new file mode 100644 index 0000000000000..8228bba96270c --- /dev/null +++ b/HeterogeneousCore/CUDAUtilities/test/assert_t.cu @@ -0,0 +1,14 @@ +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +__global__ +void testIt(int c){ + assert(c==1); +} + +int main(int c, char **) { + + testIt<<<1,1>>>(c); + cudaDeviceSynchronize(); + return c==1; + +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 8e8a9a6c013e7..d6cdddd598935 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -102,7 +102,7 @@ class GPUCACell { return (aligned && haveSimilarCurvature(hh, otherCell, ptmin, region_origin_x, region_origin_y, region_origin_radius, phiCut, - hardPtCut)); + 0.3f)); //hardPtCut)); } __device__ __forceinline__ From fce72dc90f9ccaf939fa37972c18cbe082076b79 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 5 Nov 2018 17:27:29 +0100 Subject: [PATCH 26/94] use more stable and gpu friendly version of circle --- .../src/PixelTrackCleanerBySharedHits.cc | 34 +++-- .../PixelTriplets/interface/CircleEq.h | 122 ++++++++++++++++++ .../PixelTriplets/plugins/GPUCACell.h | 37 +++++- .../PixelTriplets/plugins/gpuFishbone.h | 6 +- .../PixelTriplets/test/BuildFile.xml | 1 + .../PixelTriplets/test/CircleEq_t.cpp | 97 ++++++++++++++ 6 files changed, 281 insertions(+), 16 deletions(-) create mode 100644 RecoPixelVertexing/PixelTriplets/interface/CircleEq.h create mode 100644 RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index 83c8211f2b3f7..bd3687f00e17a 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -31,6 +31,7 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) uint16_t score[size]; unsigned int ind[size]; for (auto i = 0U; i < size; ++i) { + // assert(trackHitPairs[i].second[0]-> detUnit()->index()<96); 0 is on BPIX1 ind[i]=i; score[i]= 32000-std::min(32000, int(trackHitPairs[i].first->chi2()*100.f)); // chi2: smaller is better if (trackHitPairs[i].second.size()==4) score[i]+=32001; // s4 always better than s3 @@ -42,44 +43,51 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) // sorted: first is always better! - // first loop: only first two hits.... + // by doublets inner to outer + for (auto id = 0U; id <3; ++id) { for (auto i = 0U; i < size; ++i) { auto iTrack1 = ind[i]; auto track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; + if (recHits1.size() 0 | +| 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 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/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index d6cdddd598935..eed8c16bf26f4 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -10,6 +10,7 @@ #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 { static constexpr uint32_t capacity() { return 6;} @@ -99,10 +100,13 @@ 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, 2*thetaCut); // FIXME tune cuts - return (aligned && + return (aligned && dcaCut(hh, otherCell, ptmin, region_origin_radius, phiCut, + 0.3f)); //hardPtCut)); +/* haveSimilarCurvature(hh, otherCell, ptmin, region_origin_x, region_origin_y, region_origin_radius, phiCut, 0.3f)); //hardPtCut)); +*/ } __device__ __forceinline__ @@ -122,6 +126,37 @@ class GPUCACell { return tan_12_13_half_mul_distance_13_squared * pMin <= thetaCut * distance_13_squared * radius_diff; } + + __device__ + bool + dcaCut(Hits const & hh, GPUCACell const & otherCell, + const float ptmin, + const float region_origin_radius, const float phiCut, + const float hardPtCut) const { + + auto region_origin_radius_plus_tolerance = region_origin_radius + phiCut; + + auto x1 = otherCell.get_inner_x(hh); + auto y1 = otherCell.get_inner_y(hh); + + auto x2 = get_inner_x(hh); + auto y2 = get_inner_y(hh); + + auto x3 = get_outer_x(hh); + auto y3 = get_outer_y(hh); + + // 87 cm/GeV = 1/(3.8T * 0.3) + // take less than radius given by the hardPtCut and reject everything below + float maxCurv = 1.f/(hardPtCut * 87.f); // FIXME move out and use real MagField + + CircleEq eq(x1,y1,x2,y2,x3,y3); + + if (eq.curvature() > maxCurv) return false; + + return std::abs(eq.dca0()) < region_origin_radius_plus_tolerance*std::abs(eq.curvature()); + + } + __device__ bool haveSimilarCurvature(Hits const & hh, GPUCACell const & otherCell, diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 9f12608e671e6..c333235101f4e 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -66,12 +66,12 @@ namespace gpuPixelDoublets { for (auto jc=ic+1; jc= 0.99999f*n[ic]*n[jc]) { // alligned: kill closest - if (n[ic]n[jc]) { ci.theDoubletId=-1; break; } else { 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..b6784182d4277 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp @@ -0,0 +1,97 @@ +#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(); + 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; +} From f7dbc25917b8dec1b29721a9b58230954a82b316 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 6 Nov 2018 12:16:40 +0100 Subject: [PATCH 27/94] assoc tested --- .../CUDAUtilities/interface/HistoContainer.h | 45 +++++- .../CUDAUtilities/test/BuildFile.xml | 7 + .../CUDAUtilities/test/OneToManyAssoc_t.cu | 133 ++++++++++++++++++ 3 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu diff --git a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h index cc12a018fcc2f..dce266661a9f4 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -49,18 +49,29 @@ namespace cudautils { (*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, 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::wsSize(); - CubDebugExit(cub::DeviceScan::InclusiveSum(ws, wss, off, off, Histo::totbins(), stream)); + launchFinalize(h,ws,stream); fillFromVector<<>>(h, nh, v, offsets); cudaCheck(cudaGetLastError()); } @@ -184,6 +195,23 @@ class HistoContainer { #endif } + __host__ __device__ + __forceinline__ + void countDirect(T b) { + assert(b0); + bins[w-1] = j; + } + + __host__ __device__ __forceinline__ @@ -258,4 +286,13 @@ class HistoContainer { index_type bins[capacity()]; }; + + +template< + typename I, // type stored in the container (usually an index in a vector of the input values) + uint32_t MAXONES, // max number of "ones" + uint32_t MAXMANYS // max number of "manys" +> +using OneToManyAssoc = HistoContainer; + #endif // HeterogeneousCore_CUDAUtilities_HistoContainer_h diff --git a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml index 8fdb380c1b00c..116c94eedde8b 100644 --- a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml +++ b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml @@ -55,6 +55,13 @@ + + + + + + + diff --git a/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu b/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu new file mode 100644 index 0000000000000..f8d4e9eeea015 --- /dev/null +++ b/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu @@ -0,0 +1,133 @@ +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" + +#include +#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; + assoc->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; + assoc->fillDirect(tk[k][j],k); + +} + +__global__ +void verify(Assoc * __restrict__ assoc) { + assert(assoc->size() 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 Date: Tue, 6 Nov 2018 14:34:19 +0100 Subject: [PATCH 28/94] check cosdir --- RecoPixelVertexing/PixelTriplets/interface/CircleEq.h | 10 ++++++++-- RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h b/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h index 0f8878e4f8c18..fa538256ed010 100644 --- a/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h +++ b/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h @@ -3,13 +3,13 @@ /** | 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 (Yp is at the center of the chamber); | +| 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. | -| Alpha > 0 | +| | | 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. | @@ -56,6 +56,12 @@ class CircleEq { 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)); diff --git a/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp index b6784182d4277..cbbcea96d1ee8 100644 --- a/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp +++ b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp @@ -81,6 +81,8 @@ int main() { // 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; From 384465e7f97186b45022ed22a2f85a1cb4ddc5e8 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 6 Nov 2018 16:07:14 +0100 Subject: [PATCH 29/94] clean clode --- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 10 ++++---- .../PixelTriplets/plugins/GPUCACell.h | 24 +++++-------------- .../PixelTriplets/plugins/gpuFishbone.h | 12 +++++----- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 54b40aebc1c96..9077575dcc21a 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -198,8 +198,10 @@ kernel_connect(GPU::SimpleVector *foundNtuplets, 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); // VI tune auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; @@ -215,8 +217,8 @@ kernel_connect(GPU::SimpleVector *foundNtuplets, 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, + region_origin_radius+phiCut, thetaCut, hardCurvCut) ) { cells[otherCell].theOuterNeighbors.push_back(cellIndex); } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index eed8c16bf26f4..8de58ca7a7295 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -87,9 +87,8 @@ 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 + const float region_origin_radius_plus_tolerance, const float thetaCut, + const float hardCurvCut) const { auto ri = get_inner_r(hh); auto zi = get_inner_z(hh); @@ -100,13 +99,8 @@ 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, 2*thetaCut); // FIXME tune cuts - return (aligned && dcaCut(hh, otherCell, ptmin, region_origin_radius, phiCut, - 0.3f)); //hardPtCut)); -/* - haveSimilarCurvature(hh, otherCell, ptmin, region_origin_x, - region_origin_y, region_origin_radius, phiCut, - 0.3f)); //hardPtCut)); -*/ + return (aligned && dcaCut(hh, otherCell, ptmin, region_origin_radius_plus_tolerance, + hardCurvCut)); } __device__ __forceinline__ @@ -131,10 +125,8 @@ class GPUCACell { bool dcaCut(Hits const & hh, GPUCACell const & otherCell, const float ptmin, - const float region_origin_radius, const float phiCut, - const float hardPtCut) const { - - auto region_origin_radius_plus_tolerance = region_origin_radius + phiCut; + 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); @@ -145,10 +137,6 @@ class GPUCACell { auto x3 = get_outer_x(hh); auto y3 = get_outer_y(hh); - // 87 cm/GeV = 1/(3.8T * 0.3) - // take less than radius given by the hardPtCut and reject everything below - float maxCurv = 1.f/(hardPtCut * 87.f); // FIXME move out and use real MagField - CircleEq eq(x1,y1,x2,y2,x3,y3); if (eq.curvature() > maxCurv) return false; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index c333235101f4e..09196a02ef67f 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -49,11 +49,11 @@ namespace gpuPixelDoublets { 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]; + uint16_t d[maxCellsPerHit]; // uint8_t l[maxCellsPerHit]; for (uint32_t ic=0; ic= 0.99999f*n[ic]*n[jc]) { - // alligned: kill closest + if (d[ic]!=d[jc] && cos12*cos12 >= 0.99999f*n[ic]*n[jc]) { + // alligned: kill farthest (prefer consecutive layers) if (n[ic]>n[jc]) { ci.theDoubletId=-1; break; From ada49bd66c4717870563345c6f278423b328197f Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 7 Nov 2018 16:17:06 +0100 Subject: [PATCH 30/94] try to use template errors --- .../SiPixelRecHits/interface/PixelCPEBase.h | 3 +- .../SiPixelRecHits/interface/PixelCPEFast.h | 4 + .../SiPixelRecHits/interface/pixelCPEforGPU.h | 40 +++++- .../SiPixelRecHits/plugins/gpuPixelRecHits.h | 2 +- .../SiPixelRecHits/src/PixelCPEFast.cc | 129 ++++++++++++++---- 5 files changed, 147 insertions(+), 31 deletions(-) diff --git a/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h b/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h index 109d1be432f7f..3b25ccba790ad 100644 --- a/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h +++ b/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h @@ -80,11 +80,12 @@ class PixelCPEBase : public PixelClusterParameterEstimator struct ClusterParam { + ClusterParam(){} ClusterParam(const SiPixelCluster & cl) : theCluster(&cl) {} virtual ~ClusterParam() = default; - const SiPixelCluster * theCluster; + const SiPixelCluster * theCluster = nullptr;; //--- Cluster-level quantities (filled in computeAnglesFrom....) float cotalpha; diff --git a/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h b/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h index 35a3e6bf2a82c..22798e8541dbf 100644 --- a/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h +++ b/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h @@ -19,7 +19,9 @@ class PixelCPEFast final : public PixelCPEBase public: struct ClusterParamGeneric : ClusterParam { + ClusterParamGeneric() {} ClusterParamGeneric(const SiPixelCluster & cl) : ClusterParam(cl){} + // The truncation value pix_maximum is an angle-dependent cutoff on the // individual pixel signals. It should be applied to all pixels in the // cluster [signal_i = fminf(signal_i, pixmax)] before the column and row @@ -52,6 +54,8 @@ class PixelCPEFast final : public PixelCPEBase LocalPoint localPosition (DetParam const & theDetParam, ClusterParam & theClusterParam) const override; LocalError localError (DetParam const & theDetParam, ClusterParam & theClusterParam) const override; + + void errorFromTemplates(DetParam const & theDetParam, ClusterParamGeneric & theClusterParam, float qclus) const; static void collect_edge_charges(ClusterParam & theClusterParam, //!< input, the cluster diff --git a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h index 9697470ffb0be..3f63a0fc85b8a 100644 --- a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h +++ b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h @@ -37,6 +37,8 @@ namespace pixelCPEforGPU { float x0,y0,z0; // the vertex in the local coord of the detector + float sx[3], sy[3]; // the errors... + Frame frame; }; @@ -208,7 +210,7 @@ namespace pixelCPEforGPU { } constexpr inline - void error(CommonParams const & __restrict__ comParams, DetParams const & __restrict__ detParams, ClusParams & cp, uint32_t ic) { + void errorOld(CommonParams const & __restrict__ comParams, DetParams const & __restrict__ detParams, ClusParams & cp, uint32_t ic) { // Edge cluster errors cp.xerr[ic]= 0.0050; cp.yerr[ic]= 0.0085; @@ -227,12 +229,18 @@ namespace pixelCPEforGPU { constexpr float yerr_endcap[] = { 0.00210 }; constexpr float yerr_endcap_def = 0.00210; + auto sx = cp.maxRow[ic] - cp.minRow[ic]; + auto sy = cp.maxCol[ic] - cp.minCol[ic]; + // is edgy ? bool isEdgeX = cp.minRow[ic] == 0 or cp.maxRow[ic] == phase1PixelTopology::lastRowInModule; bool isEdgeY = cp.minCol[ic] == 0 or cp.maxCol[ic] == phase1PixelTopology::lastColInModule; + // is one and big? + bool isBig1X = (0==sx) && phase1PixelTopology::isBigPixX(cp.minRow[ic]); + bool isBig1Y = (0==sy) && phase1PixelTopology::isBigPixY(cp.minCol[ic]); + - if (not isEdgeX) { - auto sx = cp.maxRow[ic] - cp.minRow[ic]; + if (!isEdgeX && !isBig1X ) { if (not detParams.isBarrel) { cp.xerr[ic] = sx commonParams(), cpeParams->detParams(me), clusParams, ic); - pixelCPEforGPU::error(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); + pixelCPEforGPU::errorOld(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); chargeh[h] = clusParams.charge[ic]; diff --git a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc index af7dd7337084e..cda8825d167b0 100644 --- a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc +++ b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc @@ -125,7 +125,83 @@ 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(); + 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(); + + /* + 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; + */ + + + errorFromTemplates(p,cp,15000.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.00088; + 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.00088; + 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 +219,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 +236,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; } From 6acfd9fcb5c477b8f76c7bc331fd9e40a0b4df90 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 7 Nov 2018 17:40:11 +0100 Subject: [PATCH 31/94] retune but still use old params --- RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc index cda8825d167b0..ad23a13d013de 100644 --- a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc +++ b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc @@ -129,7 +129,7 @@ void PixelCPEFast::fillParamsForGpu() { // errors ..... ClusterParamGeneric cp; - auto gvx = p.theOrigin.x(); + auto gvx = p.theOrigin.x() + 80.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 @@ -155,7 +155,7 @@ void PixelCPEFast::fillParamsForGpu() { std::cout << i << ' ' << m*std::sqrt(lape.xx()) <<' '<< m*std::sqrt(lape.yy()) << std::endl; */ - + /* errorFromTemplates(p,cp,15000.f); g.sx[0] = cp.sigmax; g.sx[1] = cp.sx1; @@ -165,11 +165,11 @@ void PixelCPEFast::fillParamsForGpu() { g.sy[1] = cp.sy1; g.sy[2] = cp.sy2; - /* + */ // from run1?? if (i<96) { - g.sx[0] = 0.00088; + g.sx[0] = 0.00120; g.sx[1] = 0.00115; g.sx[2] = 0.0050; @@ -177,7 +177,7 @@ void PixelCPEFast::fillParamsForGpu() { g.sy[1] = 0.00375; g.sy[2] = 0.0085; } else if (g.isBarrel) { - g.sx[0] = 0.00088; + g.sx[0] = 0.00120; g.sx[1] = 0.00115; g.sx[2] = 0.0050; @@ -194,7 +194,7 @@ void PixelCPEFast::fillParamsForGpu() { 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()); From 45f65b9c3a34b2527ece8ea62e1e2e5a67f07944 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Thu, 8 Nov 2018 17:33:20 +0100 Subject: [PATCH 32/94] add AtomicPairCounter and implement manyToOne --- .../interface/AtomicPairCounter.h | 46 +++++++++++++ .../CUDAUtilities/interface/HistoContainer.h | 29 ++++++++ .../CUDAUtilities/test/AtomicPairCounter_t.cu | 66 +++++++++++++++++++ .../CUDAUtilities/test/BuildFile.xml | 3 + .../CUDAUtilities/test/OneToManyAssoc_t.cu | 60 +++++++++++++++-- 5 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h create mode 100644 HeterogeneousCore/CUDAUtilities/test/AtomicPairCounter_t.cu diff --git a/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h b/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h new file mode 100644 index 0000000000000..fb0d634349a29 --- /dev/null +++ b/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h @@ -0,0 +1,46 @@ +#ifndef HeterogeneousCoreCUDAUtilitiesAtomicPairCounter_H +#define HeterogeneousCoreCUDAUtilitiesAtomicPairCounter_H + +#include + +class AtomicPairCounter { +public: + + using c_type = unsigned long long int; + + AtomicPairCounter(){} + AtomicPairCounter(c_type i) { counter.ac=i;} + AtomicPairCounter & operator=(c_type i) { counter.ac=i; return *this;} + + struct Counters { + uint32_t n; // total size + uint32_t m; // number of elements + }; + + union Atomic2 { + Counters counters; + c_type ac; + }; + + 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(c_type i) { + i+=incr; + Atomic2 ret; + ret.ac = atomicAdd(&counter.ac,i); + return ret.counters; + } + +private: + + Atomic2 counter; + +}; + + +#endif diff --git a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h index dce266661a9f4..96b5c5aaeb14c 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -18,6 +18,7 @@ #include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #ifdef __CUDACC__ #include "HeterogeneousCore/CUDAUtilities/interface/prefixScan.h" +#include "HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h" #endif #ifdef __CUDACC__ @@ -212,6 +213,34 @@ class HistoContainer { } +#ifdef __CUDACC__ + __device__ + __forceinline__ + void 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__ 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 116c94eedde8b..57df6834bba83 100644 --- a/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml +++ b/HeterogeneousCore/CUDAUtilities/test/BuildFile.xml @@ -54,6 +54,9 @@ + + + diff --git a/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu b/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu index f8d4e9eeea015..05e4a7c1abc54 100644 --- a/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu +++ b/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu @@ -24,7 +24,8 @@ void count(TK const * __restrict__ tk, Assoc * __restrict__ assoc, uint32_t n) { auto j = i - 4*k; assert(j<4); if (k>=n) return; - assoc->countDirect(tk[k][j]); + if (tk[k][j]countDirect(tk[k][j]); } @@ -35,7 +36,8 @@ void fill(TK const * __restrict__ tk, Assoc * __restrict__ assoc, uint32_t n) { auto j = i - 4*k; assert(j<4); if (k>=n) return; - assoc->fillDirect(tk[k][j],k); + if (tk[k][j]fillDirect(tk[k][j],k); } @@ -45,6 +47,20 @@ void verify(Assoc * __restrict__ assoc) { } +__global__ +void fillBulk(AtomicPairCounter * apc, TK const * __restrict__ tk, Assoc * __restrict__ assoc, uint32_t n) { + auto k = blockIdx.x * blockDim.x + threadIdx.x; + if (k>=n) return; + auto m = tk[k][3]bulkFill(*apc,&tk[k][0],m); +} + +__global__ +void finalizeBulk(AtomicPairCounter const * apc, Assoc * __restrict__ assoc) { + assoc->bulkFinalizeFill(*apc); +} + + int main() { @@ -79,11 +95,16 @@ int main() { while(j>>(dc_d,v_d.get(),a_d.get(),N); + 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 Date: Fri, 9 Nov 2018 12:24:27 +0100 Subject: [PATCH 33/94] tuning cuts --- .../src/PixelTrackCleanerBySharedHits.cc | 43 +- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 4 +- .../PixelTriplets/plugins/GPUCACell.h | 89 +- .../PixelTriplets/test/pixHits.ipynb | 969 +++++++++++++++++- 4 files changed, 947 insertions(+), 158 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc index bd3687f00e17a..3149837902d02 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelTrackCleanerBySharedHits.cc @@ -31,63 +31,54 @@ void PixelTrackCleanerBySharedHits::cleanTracks(TracksWithTTRHs & trackHitPairs) uint16_t score[size]; unsigned int ind[size]; for (auto i = 0U; i < size; ++i) { - // assert(trackHitPairs[i].second[0]-> detUnit()->index()<96); 0 is on BPIX1 ind[i]=i; score[i]= 32000-std::min(32000, int(trackHitPairs[i].first->chi2()*100.f)); // chi2: smaller is better if (trackHitPairs[i].second.size()==4) score[i]+=32001; // s4 always better than s3 - } - std::sort(ind,ind+size,[&](unsigned int i, unsigned int j){return score[i]>score[j];}); + } + std::sort(ind,ind+size,[&](unsigned int i, unsigned int j){return score[i]>score[j];}); - int killed=0; - auto kill = [&](unsigned int k) { assert(trackHitPairs[k].first); killed++; delete trackHitPairs[k].first; trackHitPairs[k].first=nullptr;}; + auto kill = [&](unsigned int k) {delete trackHitPairs[k].first; trackHitPairs[k].first=nullptr;}; // sorted: first is always better! - // by doublets inner to outer - for (auto id = 0U; id <3; ++id) { + // first loop: only first two hits.... for (auto i = 0U; i < size; ++i) { auto iTrack1 = ind[i]; auto track1 = trackHitPairs[iTrack1].first; if (!track1) continue; auto const & recHits1 = trackHitPairs[iTrack1].second; - if (recHits1.size()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())<<'/'<= 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 // the visit of the graph based on the neighborhood connections between cells. diff --git a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb index 2b2e9de82e87d..12027fe01cdd8 100644 --- a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb +++ b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 74, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -29,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -133,7 +133,53 @@ }, { "cell_type": "code", - "execution_count": 78, + "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": 79, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -207,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -224,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -241,7 +294,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -258,7 +311,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -278,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -336,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -345,7 +398,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -613,7 +666,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -714,7 +767,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ @@ -740,7 +793,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -769,7 +822,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 45, "metadata": { "scrolled": true }, @@ -789,7 +842,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 46, "metadata": {}, "outputs": [ { @@ -984,7 +1037,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -999,6 +1052,11 @@ "540108\n", "483260\n", "931967\n", + "pentuplets\n", + "612902\n", + "triplets\n", + "111601\n", + "47552\n", "4230185\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", @@ -1087,7 +1145,44 @@ "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" + "[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" ] } ], @@ -1111,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": 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": 93, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -1159,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", @@ -1185,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", @@ -1209,7 +1369,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1303,17 +1463,742 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 58, "metadata": {}, - "outputs": [], - "source": [ - "#for quad in [t1234,t1231,t1212,t1123] :\n", - "# plotTriplets(quad,600)" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta123 227975\n", + "field\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 227975\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 227975\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta234\n", + "field\n" + ] + }, + { + "data": { + "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": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADXRJREFUeJzt3W+sZHddx/H31113SYq5tuyKpO1yt9mFWBOCYSyJRENE6CIsJdroVkIqNN2AaeITE5Y0PiExQZ8YE4nNxsCCBkrFRPe2G5rSusID1N5FKP2TpbdLSXdTqEW5omlqKl8fzFkZLvfePfP3zHz3/Uo2d+bMmZnvb2bOZ37zO79zNjITSVJdP9F1AZKk6TLoJak4g16SijPoJak4g16SijPoJak4g16SijPoJak4g16SitvZdQEAe/bsyeXl5a7LkKSFcubMmeczc++l1us06CPiMHD4wIEDrK6udlmKJC2ciPhWm/U6HbrJzJXMPLq0tNRlGZJUmmP0klScQS9JxXUa9BFxOCKOr6+vd1mGJJXmGL0kFefQjSQVZ9BLUnEGvSQVNzcHTEnzYvnYfZsuf/qj75hxJdJkuDNWkopz6EaSijPoJak4g16SivPIWEkqzp2xklScQzeSVJxBL0nFGfSSVJxBL0nFGfSSVJzTKyWpOKdXSlJxDt1IUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQV5zx6SSrOefSSVJxDN5JUnEEvScUZ9JJUnEEvScUZ9JJUnEEvScUZ9JJUnEEvScV5ZKwkFeeRsZJUnEM3klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxXn2SkkqzrNXSlJxDt1IUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnFTCfqIuCIiViPindN4fElSe62CPiI+HhHPRcSjG5YfioizEbEWEccGbvoQcM8kC5UkjaZtj/4EcGhwQUTsAD4GvB24HrglIq6PiLcCjwPPTbBOSdKIdrZZKTO/GBHLGxbfAKxl5jmAiLgbuAl4OXAF/fB/ISJOZeYPJlaxJGkorYJ+C1cDzwxcPw+8MTPvAIiI3wWe3yrkI+IocBRg3759Y5QhSdrO1GbdZOaJzLx3m9uPZ2YvM3t79+6dVhmSdNkbJ+gvANcOXL+mWSZJmiPjBP3DwMGI2B8Ru4AjwMnJlCVJmpS20ys/A3wZeG1EnI+I2zLzJeAO4H7gCeCezHxsmCePiMMRcXx9fX3YuiVJLbWddXPLFstPAadGffLMXAFWer3e7aM+hiRpe54CQZKKM+glqbhOg94xekmavk6DPjNXMvPo0tJSl2VIUmkO3UhScQa9JBVn0EtSce6MlaTi3BkrScU5dCNJxRn0klScQS9JxbkzVpKKc2esJBXn0I0kFWfQS1JxBr0kFWfQS1JxzrqRpOKcdSNJxTl0I0nFGfSSVJxBL0nFGfSSVJxBL0nFGfSSVJzz6CWpOOfRS1JxDt1IUnEGvSQVZ9BLUnEGvSQVt7PrAqR5sHzsvq5LkKbGHr0kFWePXmppY6//6Y++o6NKpOHYo5ek4gx6SSrOUyBIUnGeAkGSinPoRpKKM+glqTiDXpKKM+glqTiDXpKKM+glqTiDXpKKM+glqTiDXpKK8+yVumyNew76wft7JkvNM3v0klScQS9JxXn2SkkqzrNXSlJxDt1IUnEGvSQV5/RKaQKcaql5Zo9ekoqzR6/LyrgHSUmLyB69JBVn0EtScQa9JBVn0EtScQa9JBXnrBuVN+uZNs6p17yxRy9JxRn0klScQzfSFDmMo3lgj16SijPoJak4h25Ukue0kX7IoJdmxPF6dcWglzpg6GuWJh70EfFzwO8De4AHM/MvJv0c0mYcrpE212pnbER8PCKei4hHNyw/FBFnI2ItIo4BZOYTmfkB4LeAN02+ZOnytnzsvv//J7XRtkd/Avhz4FMXF0TEDuBjwFuB88DDEXEyMx+PiHcBHwT+arLlShrkEJDaaBX0mfnFiFjesPgGYC0zzwFExN3ATcDjmXkSOBkR9wGfnly5qmicsKrQqzWsNW3jjNFfDTwzcP088MaIeDPwG8Bu4NRWd46Io8BRgH379o1RhhZRhYCWFsXEd8Zm5mngdIv1jgPHAXq9Xk66DklS3zhHxl4Arh24fk2zTJI0R8bp0T8MHIyI/fQD/gjwO8M8QEQcBg4fOHBgjDKkOrYar3eoS+NoO73yM8CXgddGxPmIuC0zXwLuAO4HngDuyczHhnnyzFzJzKNLS0vD1i1JaqntrJtbtlh+im12uErTYO9WGo6nQNBccarhD/mFpkkx6LUQDD1pdJ2ejz4iDkfE8fX19S7LkKTSOu3RZ+YKsNLr9W7vsg7NJ3vx0mT4P0xJUnEGvSQV1+nQjQdMSZPjjCVtpdMevQdMSdL0OXQjScU5j14z4ywaqRv26CWpOINekopz1o2kH+HsnXo8MlYT51j8/NrqvTHQa3NnrFSQX7YaZNBL8ouhOINeI3MsV1oMBr0mwh5hfX6xLy7PRy9JxUVmdl0DvV4vV1dXuy5DQ7IXL7B336WIOJOZvUut5wFTklScY/QCHH/V5PhZmj8GvX7MxiGZwY3V4RoNwwO05oNBX0ibntQovS3DXVpsjtFLUnH26CXNnOP4s+XZKxecwyrqmp/B+ec8+jkzbE/HjUyV2LsfjvPoJUmAY/Rzzd66pEkw6Dviziipva22F7ejdgx6SXNpq1+0/tIdnkE/pHF2lrY5iEm6nLktTEfZoB8lYP3pJ2lQlaGhskE/aNw3q00vo8oHQqqgzTZ7OW2nCx/00/qp509I6fIxjfNEzdOIgUfGDph2uPvlIU3PpLavittpp0GfmSvASq/Xu31Wz1nxTZSk7Sz80E2X/NKQFtest98u9+MZ9FPgF4Ck7cw69A16SRrDInTsDHpJGtIihPsgz14pScUZ9JJUnEEvScUZ9JJUnEEvScUZ9JJUnEEvScUZ9JJUnEEvScVFZnb35M1pioHfBp4c8WH2AM9PrKhu2Zb5U6UdYFvm1ThteXVm7r3USp0G/SRExGpm9rquYxJsy/yp0g6wLfNqFm1x6EaSijPoJam4CkF/vOsCJsi2zJ8q7QDbMq+m3paFH6OXJG2vQo9ekrSNhQj6iLgqIh6IiCebv1dusd7nI+J7EXHvhuUnIuKbEfHV5t/rZ1P5pjWO25b9EfHPEbEWEZ+NiF2zqXzTGtu25dZmnScj4taB5acj4uzA+/Izs6seIuJQ8/xrEXFsk9t3N6/xWvOaLw/c9uFm+dmIuHGWdW9m1LZExHJEvDDwHtw169o31HmpdvxKRHwlIl6KiJs33Lbp56wrY7blfwfek5NjF5OZc/8P+BPgWHP5GPDHW6z3Fvrz8u/dsPwEcHPX7ZhQW+4BjjSX7wI+OM9tAa4CzjV/r2wuX9ncdhrodVT7DuAp4DpgF/A14PoN6/wecFdz+Qjw2eby9c36u4H9zePs6PB9GKcty8CjXdU+QjuWgdcBnxrcprf7nC1aW5rb/muS9SxEjx64Cfhkc/mTwLs3WykzHwS+P6uiRjRyWyIigF8FPnep+89Im7bcCDyQmf+emf8BPAAcmlF927kBWMvMc5n5P8Dd9NszaLB9nwPe0rwHNwF3Z+aLmflNYK15vK6M05Z5csl2ZObTmfkI8IMN9523z9k4bZm4RQn6V2bms83lbwOvHOEx/igiHomIP42I3ROsbVjjtOUVwPcy86Xm+nng6kkWN6Q2bbkaeGbg+saaP9H8PP3DGQfPper6kXWa13yd/nvQ5r6zNE5bAPZHxL9GxD9GxC9Pu9htjPO6LuJ7sp2XRcRqRPxTRIzdmZub/xw8Ir4A/OwmN905eCUzMyKGnSr0YfpBtIv+VKYPAR8Zpc42ptyWmZpyW96TmRci4qeAvwXeS/9nrGbnWWBfZn43It4A/F1E/Hxm/mfXhV3mXt1sG9cBD0XE1zPzqVEfbG6CPjN/bavbIuI7EfGqzHw2Il4FPDfkY1/sdb4YEZ8A/mCMUts837Ta8l3gpyNiZ9Mruwa4MGa525pAWy4Abx64fg39sXky80Lz9/sR8Wn6P3dnFfQXgGs31LXxtby4zvmI2Aks0X8P2tx3lkZuS/YHhF8EyMwzEfEU8BpgdepV/7hxXtctP2cdGeszMrBtnIuI08Av0B/zH8miDN2cBC7uRb8V+Pth7tyE0MUx7ncDj060uuGM3JZmo/wH4OIe+qFfiwlr05b7gbdFxJXNrJy3AfdHxM6I2AMQET8JvJPZvi8PAwebWUy76O+g3Di7YbB9NwMPNe/BSeBIM5NlP3AQ+JcZ1b2ZkdsSEXsjYgdA03s8SH9HZhfatGMrm37OplRnGyO3pWnD7ubyHuBNwONjVdPVXukh92C/AniQ/hkuvwBc1SzvAX85sN6XgH8DXqA/JnZjs/wh4Ov0g+SvgZcvcFuuox8qa8DfALsXoC3vb+pdA97XLLsCOAM8AjwG/BkznrkC/DrwDfo9pTubZR8B3tVcflnzGq81r/l1A/e9s7nfWeDtXb0H47YF+M3m9f8q8BXg8Jy34xeb7eG/6f+6emy7z9kitgX4pSavvtb8vW3cWjwyVpKKW5ShG0nSiAx6SSrOoJek4gx6SSrOoJek4gx6SSrOoJek4gx6SSru/wAo5gbwInhY+wAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 227975\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 227975\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta123 40709\n", + "field\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" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 40709\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta234\n", + "field\n" + ] + }, + { + "data": { + "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": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 40709\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 40709\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD+CAYAAAA09s7qAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEBNJREFUeJzt3X+sZGddx/H3x60tscil0BWxP9htuoIrMRDGkkiEKki3wlKije4GTNWmGzD1H2NCSTUmJCZgTAwkJHUDpaCxpdSIu7BY+bXWP4p2F/nRpSndFkh3rXQLcgUlxcrXP+YUhsveu/Pzztznvl/JzZ05c86Z7z0z87nPPOeZZ1JVSJLa9SPzLkCSNFsGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjZtJ0Cc5N8mRJK+exf4lScMbKuiT3Jzk0ST3rli+K8n9SY4nuWHgpjcBt0+zUEnSeDLMFAhJXgp8C3hfVT2/W7YF+CLwK8AJ4B5gL3AB8EzgKcBjVfWh2ZQuSRrGWcOsVFV3Jdm2YvFlwPGqegggyW3AVcBTgXOBncC3kxyqqu+u3GeSfcA+gHPPPfdFz3ve88b9GyRpUzp69OhjVbX1TOsNFfSruAB4eOD6CeDFVXU9QJLfpt+i/6GQB6iq/cB+gF6vV0eOHJmgFEnafJJ8ZZj1Jgn6NVXVLbPatyRpeJOMujkJXDRw/cJu2dCS7E6yf3l5eYIyJElrmSTo7wF2JNme5GxgD3BglB1U1cGq2re0tDRBGZKktQw7vPJW4G7guUlOJLm2qp4ArgfuBO4Dbq+qY7MrVZI0jmFH3exdZfkh4NBUK5IkTdVcp0Cwj16SZm+uQW8fvSTNnpOaSVLjZjaOXtqott3w4e9d/vJbXzXHSqTpsI9ekho31xZ9VR0EDvZ6vevmWYe0Glv3aoF99JLUOINekhpn0EtS4zwZK0mN8wNTktQ4u24kqXEGvSQ1zqCXpMYZ9JLUOEfdSFLjHHUjSY2z60aSGmfQS1LjDHpJapxfPCLxg9MRS62xRS9JjZtriz7JbmD3pZdeOs8ypKGsbPX7RSTaKBxeKUmNs+tGkhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGOR+9JDXOD0xJUuPsupGkxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mN86sEtWn59YHaLGzRS1LjDHpJapxBL0mN88vBpTEN9vH7ReFaZM51I0mNs+tGkhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxvnl4NpU/EJwbUYGvTQFftuUFtnUu26S/EySm5LckeSN096/JGk0QwV9kpuTPJrk3hXLdyW5P8nxJDcAVNV9VfUG4DeAl0y/ZEnSKIZt0d8C7BpckGQL8E7gSmAnsDfJzu621wAfBg5NrVJJ0liGCvqqugv4+orFlwHHq+qhqvoOcBtwVbf+gaq6EnjdavtMsi/JkSRHTp06NV71kqQzmuRk7AXAwwPXTwAvTnI58GvAOazRoq+q/cB+gF6vVxPUIUlaw9RH3VTVYeDwtPcrSRrPJKNuTgIXDVy/sFs2tCS7k+xfXl6eoAxJ0lomCfp7gB1Jtic5G9gDHBhlB1V1sKr2LS0tTVCGJGktww6vvBW4G3hukhNJrq2qJ4DrgTuB+4Dbq+rY7EqVJI1jqD76qtq7yvJDOIRSkhbaXCc1s49ekmZvrnPdVNVB4GCv17tunnWobU5kps3OaYolqXEGvSQ1zj56SWrcXIPecfSSNHt+8Yg0ZX4JiRaNffSS1DiDXpIa58lYSWqcJ2MlqXF23UhS4wx6SWqcQS9JjTPoJalxjrqRpMY56kaSGucUCGqSc9BL32cfvSQ1zqCXpMYZ9JLUOINekhrn8EpJatxcR91U1UHgYK/Xu26edUiz4peQaBHYdSNJjTPoJalxBr0kNc6gl6TGOQWCmuG0B9Lp2aKXpMYZ9JLUOD8wJUmNcz56SWqcXTeS1DiDXpIa5/BKaZ04743mxRa9JDXOoJekxhn0ktQ4++i1oTntgXRmtuglqXEGvSQ1zq4baQ4caqn15Fw3ktQ457qRpMbZRy9JjTPoJalxBr0kNc6gl6TGGfSS1DjH0WvDcdoDaTS26CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjHF4pzZlTFmvWbNFLUuNs0UsbmO8GNAyDXlogqwW3ga5JGPTSgnKqB03LTII+yWuBVwFPA95dVf84i/uRJJ3Z0EGf5Gbg1cCjVfX8geW7gLcDW4B3VdVbq+qDwAeTnAf8OWDQayK2bqXxjTLq5hZg1+CCJFuAdwJXAjuBvUl2DqzyR93tkqQ5GbpFX1V3Jdm2YvFlwPGqegggyW3AVUnuA94KfKSqPn26/SXZB+wDuPjii0evXE3ypOOZ+e5Go5p0HP0FwMMD1090y34feAVwdZI3nG7DqtpfVb2q6m3dunXCMiRJq5nJydiqegfwjlnsW5I0mklb9CeBiwauX9gtG0qS3Un2Ly8vT1iGJGk1kwb9PcCOJNuTnA3sAQ4Mu3FVHayqfUtLSxOWIUlazSjDK28FLgfOT3IC+JOqeneS64E76Q+vvLmqjs2kUjXLk4vSbI0y6mbvKssPAYemVpHU8R/A+By9pEFznQIhyW5g96WXXjrPMqQm+I9Rq5nrNMX20UvS7DkfvSQ1zqCXpMbZRy9tIp6k3ZzmGvRVdRA42Ov1rptnHVp/njiU1o9fPKKpsKUoLS6DXlNn6G9sPn7tMeilTcpA3zw8GSs1zvMh8mTsJmaLTmfic6QNdt3oh6xsAfoClzY2g74htr4knY5Bv8msd3+t/3yk+TPoBaz9D8CwljY2R91obI7m2Fz8h79xOU2xJDXOrptNwJa3tLkZ9I0y3LVo7PqZH4Ne0sgM7Y3FoNe68V3G5uNjvhgMeo3EF6608cx11E2S3Un2Ly8vz7MMSWqak5otMPtBtZn5/J8eu242OLtSJJ2JQS9pYdhwmQ2Dfgi+hVTrDNi2GfQLYNQXmS9KtcTn8+zNddSNJGn2bNGPyG4caW2zbqHP+jXY4mvcoJ+xFp80kjYW56OXtGms9m6j9UaYH5iStGH5jnk4dt2sI5+UkubBoJ8SQ1yaL1+DqzPoJzDJ6ALHDkvDm+SzJoZ+w0HvAy0tLhs668sPTElS45pt0Q9jZavClr+0OU2rG3ZRM2RTB72kyS16N8ws6lv0v3mlTRf0az1A03rwfGJJ7Zn0NTjPlr999JLUuE3XopekWVnU/voNP9fNoh5YSVoUc+26qaqDVbVvaWlpnmVIUtPsupGkVbQyCMKTsZLUuE3Rom/lv7IkjcMWvSQ1zqCXpMZtiq4bSVpvi9RlbItekhpn0EtS4wx6SWqcQS9JjfNkrCSts/Weo8sWvSQ1rqkW/SINZ5KkRWGLXpIaZ9BLUuMMeklqnEEvSY2betAnuSTJu5PcMe19S5JGN1TQJ7k5yaNJ7l2xfFeS+5McT3IDQFU9VFXXzqJYSdLohm3R3wLsGlyQZAvwTuBKYCewN8nOqVYnSZrYUEFfVXcBX1+x+DLgeNeC/w5wG3DVsHecZF+SI0mOnDp1auiCJUmjmaSP/gLg4YHrJ4ALkjwzyU3AC5O8ebWNq2p/VfWqqrd169YJypAkrWXqn4ytqq8Bbxhlm6NHjz6W5Ctj3uX5wGNjbjtL1jUa6xqNdY1uIWvL2yaq6znDrDRJ0J8ELhq4fmG3bGRVNXaTPsmRquqNu/2sWNdorGs01jW6Ra1tPeqapOvmHmBHku1Jzgb2AAemU5YkaVqGHV55K3A38NwkJ5JcW1VPANcDdwL3AbdX1bHZlSpJGsdQXTdVtXeV5YeAQ1OtaHT753z/q7Gu0VjXaKxrdIta28zrSlXN+j4kSXPkXDeS1DiDXpIatyGCPskzknw0yQPd7/NOs84Lktyd5FiSzyX5zYHbtif5l25Onvd3o4TWpa5uvX9I8o0kH1qx/JYkX0ryme7nBQtS17yP1zXdOg8kuWZg+eFubqUnj9dPTFjPD83VtOL2c7q//3h3PLYN3Pbmbvn9Sa6YpI5p1ZVkW5JvDxyfm9a5rpcm+XSSJ5JcveK20z6mC1DX/w0cr6mOGhyirj9I8oUurz6e5DkDt033eFXVwv8Afwbc0F2+AXjbadb5aWBHd/mngEeAp3fXbwf2dJdvAt64XnV1t70c2A18aMXyW4Cr53G8zlDX3I4X8Azgoe73ed3l87rbDgO9KdWyBXgQuAQ4G/gssHPFOr8H3NRd3gO8v7u8s1v/HGB7t58tC1DXNuDeaT+fRqhrG/BzwPsGn9drPabzrKu77VtzPF6/BPxYd/mNA4/j1I/XhmjR059D573d5fcCr125QlV9saoe6C7/O/AosDVJgF8G7lhr+1nV1dXzceCbU7rPYYxd1wIcryuAj1bV16vqP4GPsmJCvSkZZq6mwXrvAF7eHZ+rgNuq6vGq+hJwvNvfvOuapTPWVVVfrqrPAd9dse0sH9NJ6pqlYer6ZFX9T3f1U/Q/dAozOF4bJeifVVWPdJf/A3jWWisnuYz+f9EHgWcC36j+uH/o5uSZR12r+NPurdtfJDlnAeqa9/E67RxKA9ff073N/uMJw+1M9/MD63THY5n+8Rlm23nUBbA9yb8l+ackvzilmoataxbbznrfT0l/csVPJZlWg2acuq4FPjLmtmc09bluxpXkY8BPnuamGwevVFUlWXVMaJJnA38FXFNV3520oTOtulbxZvqBdzb9sbRvAt6yAHWNbcZ1va6qTib5ceBvgd+i/3ZcfY8AF1fV15K8CPhgkp+tqv+ad2EL7Dndc+oS4BNJPl9VD65nAUleD/SAl83qPhYm6KvqFavdluSrSZ5dVY90Qf7oKus9DfgwcGNVfapb/DXg6UnO6lo/I83JM4261tj3k63bx5O8B/jDBahr3sfrJHD5wPUL6ffNU1Unu9/fTPI39N8ejxv0w8zV9OQ6J5KcBSzRPz5Tm+dpmnVVv4P3cYCqOprkQfrnro6sU11rbXv5im0PT6GmJ/c99mMx8Jx6KMlh4IX0ewLWpa4kr6DfCHpZVT0+sO3lK7Y9PEkxG6Xr5gDw5Jnna4C/X7lC+iND/g54X1V972sMuyf/J4Gr19p+VnWtpQu7J/vFXwvcu/YWs69rAY7XncArk5yX/qicVwJ3JjkryfkASX4UeDWTHa9h5moarPdq4BPd8TkA7OlGv2wHdgD/OkEtU6krydb0vxCIroW6g/6JvPWqazWnfUznXVdXzznd5fOBlwBfWK+6krwQ+EvgNVU12OiZ/vGaxRnnaf/Q73/8OPAA8DHgGd3yHvCu7vLrgf8FPjPw84LutkvovxCPAx8Azlmvurrr/wycAr5Nv7/tim75J4DP0w+svwaeuiB1zft4/W5338eB3+mWnQscBT4HHAPezoQjXYBfBb5IvwV3Y7fsLfRfeABP6f7+493xuGRg2xu77e4Hrpzy832suoBf747NZ4BPA7vXua6f755H/03/nc+xtR7TedcF/EL3+vts9/vada7rY8BX+X5eHZjV8XIKBElq3EbpupEkjcmgl6TGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY37f1HKtV+A2T7/AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta123 152561\n", + "field\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" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 152561\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta234\n", + "field\n" + ] + }, + { + "data": { + "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": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 152561\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 152561\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta123 400905\n", + "field\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" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADORJREFUeJzt3WGMHHUZx/HfjxJKRLyA7SvaciWticWYoCsYjYpRQtEcNUIMoAkooQEhvvCNNZhg9I36wsTEJqQJTcUXFOSF6WmVoFKIL1CuiJRiKkeB0MaIgKlREYI8vripTJfb6+7tzM7ss99Pcunu7Mze02n3N/975j9zjggBAPI6pekCAAD1IugBIDmCHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBIDmCHgCSO7XpAiRp1apVMT093XQZADBW9u/f/2JErD7Zeq0I+unpac3NzTVdBgCMFdvP9bMerRsASI6gB4DkCHoASI6gB4DkGg162zO2dxw7dqzJMgAgtUaDPiJmI2Lr1NRUk2UAQGq0bgAgOYIeAJJrxQVTw5je9vP/P372O59usBIAaCdG9ACQHEEPAMkxvRIAkmu0Rx8Rs5JmO53ODVW8H/16AHgrWjcAkBxBDwDJEfQAkBxBDwDJEfQAkNzYXxnbCzNwAGAB8+gBIDluUwwAydGjB4DkCHoASI6gB4DkCHoASI6gB4Dk0s6jL2NOPYBJxogeAJIj6AEgOYIeAJLjFggAkBy3QACA5GjdAEByBD0AJEfQA0ByE3HBVBkXTwGYNIzoASA5gh4AkiPoASA5gh4AkiPoASA5gh4AkiPoASA5gh4Akpu4C6bKyhdPSVxABSAnRvQAkBz3oweA5LgfPQAkR+sGAJIj6AEgOYIeAJIj6AEguYmeR9+NX0oCICNG9ACQHEEPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQHEEPAMlxwVQPXDwFIAtG9ACQHEEPAMkR9ACQHEEPAMkR9ACQHEEPAMnVMr3S9hmSHpT0zYj4WR3fY5SYaglgnPU1ore90/YLtp/oWr7Z9iHb87a3lV76mqR7qiwUALA8/bZudknaXF5ge4Wk7ZIuk7RJ0tW2N9m+RNKTkl6osE4AwDL11bqJiIdsT3ctvlDSfEQcliTbuyVtkfR2SWdoIfxfsb03It6orGIAwECG6dGfI+n50vMjki6KiFskyfZ1kl7sFfK2t0raKknr1q0bogwAwFJqm3UTEbuWOhEbETsiohMRndWrV9dVBgBMvGGC/qiktaXna4plAIAWGSboH5G00fZ626dJukrSnmrKAgBUpa8eve27JF0saZXtI5Jui4g7bN8i6T5JKyTtjIiDg3xz2zOSZjZs2DBY1Q1iTj2AcdPvrJureyzfK2nvcr95RMxKmu10Ojcs9z0AAEvjFggAkBxBDwDJEfQAkFyjQW97xvaOY8eONVkGAKTWaNBHxGxEbJ2ammqyDABIrZbbFE8KploCGAf06AEgOYIeAJLjZCwAJMfJWABIjtYNACRH0ANAcgQ9ACTHPPqKMKceQFsx6wYAkmPWDQAkR48eAJIj6AEgOYIeAJJj1k0NmIEDoE2YdQMAyTHrBgCSo0cPAMkR9ACQHCdja8aJWQBNY0QPAMkR9ACQHEEPAMnRox8h+vUAmsAFUwCQHBdMAUBy9OgBIDmCHgCSI+gBIDlm3TSEGTgARoURPQAkR9ADQHIEPQAkR9ADQHKcjG0BTswCqBO3QACA5LgFAgAkR+umZWjjAKgaJ2MBIDmCHgCSo3XTYrRxAFSBET0AJMeIfkwwugewXIzoASA5gh4AkiPoASA5gh4AkiPoASA5gh4AkmN65RhiqiWAQXCbYgBIjtsUA0BytG7wFuXWUDdaRcD4IejHHP16ACfDrBsASI6gB4DkCHoASI4efSL06wEshqBPitAHcBytGwBIjqAHgOQIegBIjh79BKOPD0wGgn4CLHVLg0HWATCeaN0AQHIEPQAkR+sGA6GvD4wfRvQAkBxBDwDJEfQAkBxBDwDJEfQAkBxBDwDJVR70tt9t+3bb99q+qer3BwAMpq+gt73T9gu2n+havtn2IdvztrdJUkT8KSJulPQ5SR+uvmQAwCD6vWBql6QfSrrz+ALbKyRtl3SJpCOSHrG9JyKetH25pJsk/bjactEmXDwFjIe+RvQR8ZCkl7sWXyhpPiIOR8RrknZL2lKsvyciLpP0+SqLBQAMbphbIJwj6fnS8yOSLrJ9saTPSlopaW+vjW1vlbRVktatWzdEGWiDXne/ZKQPNK/ye91ExD5J+/pYb4ekHZLU6XSi6jrQDrR3gOYNM+vmqKS1pedrimUAgBYZZkT/iKSNttdrIeCvknRNJVUhJUb3QDP6Cnrbd0m6WNIq20ck3RYRd9i+RdJ9klZI2hkRBwf55rZnJM1s2LBhsKox9gh9YHQc0Xx7vNPpxNzc3LK25Vfg5ULoA/2zvT8iOidbj188glZhpA9Uj3vdAEByjY7o6dGjSvw0ACyu0aCPiFlJs51O54Ym60D79QrxcTpHw4EITaFHj9bqFeLjFO5AGxD0SG+YkTSjcGRA0AMVqPuAwAEHw+BkLCYWN2LDpOBkLFIapr/f77a9DgiMvtE2tG6AZRrmpDAHA4wSQQ8kxIEEZQQ90CIENOpA0AM1qqq9U1UNHDwmE7NugDFDcGNQzLoBGjbKK305SEwmWjfAGCO40Q+CHkiCewChF4IemFD8NDA5CHoAQ+n3imE0h98wBQDJMb0SQE+0d3JgeiUAAj05evQA+jKKgwEHnHoQ9ABOUNc0TUK8OQQ9gJGrI/Q5kPRG0AOoDffsbweCHsDAlgrwQcOdK3rrR9ADaD1G98Mh6AGkxkGCC6YAjJmqgruf98lykOCCKQDptL3vP+oDCK0bAGOr7YHeFgQ9AJT0Onj0GoWPQ3uHu1cCQHKM6AFMjH5G6xkR9AAwAk0eTGjdAEByjOgBoCJtPTFL0APAgMatp0/rBgCS4xYIAFCDNo36Gx3RR8RsRGydmppqsgwASI3WDQAkR9ADQHIEPQAkR9ADQHIEPQAkR9ADQHIEPQAkR9ADQHKOiKZrkO2/SXpumZuvkvRiheVUhboGQ12DaWtdUntry1jXuRGx+mQrtSLoh2F7LiI6TdfRjboGQ12DaWtdUntrm+S6aN0AQHIEPQAklyHodzRdQA/UNRjqGkxb65LaW9vE1jX2PXoAwNIyjOgBAEtoddDb3mz7kO1529sWeX2l7buL139ne7r02teL5YdsX9qGumxP237F9mPF1+0jruujth+1/brtK7teu9b2U8XXtS2q67+l/bVnxHV91faTth+3/Wvb55Zea3J/LVVXk/vrRtsHiu/9W9ubSq81+XlctK6mP4+l9a6wHbY7pWXV7q+IaOWXpBWSnpZ0nqTTJP1R0qaudb4s6fbi8VWS7i4ebyrWXylpffE+K1pQ17SkJxrcX9OS3ivpTklXlpafLelw8edZxeOzmq6reO2fDe6vj0t6W/H4ptK/Y9P7a9G6WrC/3lF6fLmkXxaPm/489qqr0c9jsd6Zkh6S9LCkTl37q80j+gslzUfE4Yh4TdJuSVu61tki6UfF43slfcK2i+W7I+LViHhG0nzxfk3XVaeT1hURz0bE45Le6Nr2Ukn3R8TLEfF3SfdL2tyCuurUT10PRMS/i6cPS1pTPG56f/Wqq0791PWP0tMzJB0/Adjo53GJuurUT05I0rclfVfSf0rLKt9fbQ76cyQ9X3p+pFi26DoR8bqkY5Le2ee2TdQlSett/8H2g7Y/UlFN/dZVx7Z1v/fptudsP2z7MxXVtJy6rpf0i2VuO6q6pIb3l+2bbT8t6XuSvjLItg3UJTX4ebT9PklrI6L7l8tWvr8a/eXgE+gvktZFxEu23y/pp7bP7xpx4ETnRsRR2+dJ+o3tAxHx9CgLsP0FSR1JHxvl9z2ZHnU1ur8iYruk7bavkfQNSZWev1iuHnU19nm0fYqk70u6ru7vJbV7RH9U0trS8zXFskXXsX2qpClJL/W57cjrKn4Ue0mSImK/Fnpv7xphXXVsW+t7R8TR4s/DkvZJumCUddn+pKRbJV0eEa8Osm0DdTW+v0p2Szr+E0Xj+2uxuhr+PJ4p6T2S9tl+VtIHJe0pTshWv7/qOBFR0cmMU7Vwkmu93jyZcX7XOjfrxJOe9xSPz9eJJzMOq7qTP8PUtfp4HVo4SXNU0tmjqqu07i699WTsM1o4sXhW8bgNdZ0laWXxeJWkp7TICa0a/x0v0MKHf2PX8kb31xJ1Nb2/NpYez0iaKx43/XnsVVcrPo/F+vv05snYyvfX0H+hOr8kfUrSn4v/1LcWy76lhVGMJJ0u6SdaOFnxe0nnlba9tdjukKTL2lCXpCskHZT0mKRHJc2MuK4PaKHf9y8t/ORzsLTtl4p65yV9sQ11SfqQpAPFf/oDkq4fcV2/kvTX4t/rMUl7WrK/Fq2rBfvrB6X/3w+oFGwNfx4Xravpz2PXuvtUBH0d+4srYwEguTb36AEAFSDoASA5gh4AkiPoASA5gh4AkiPoASA5gh4AkiPoASC5/wFKINBvKXk+nQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 400905\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta234\n", + "field\n" + ] + }, + { + "data": { + "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": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 400905\n" + ] + }, + { + "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" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut2,pzcut2 400905\n" + ] + }, + { + "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": [ + "for quad in [t1234,t1231,t1212,t1123] :\n", + " plotTriplets(quad,600)" ] }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 57, "metadata": {}, "outputs": [ { From 2d8e41b9880c872e16cb82b17978de8d581b7998 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 9 Nov 2018 17:26:08 +0100 Subject: [PATCH 34/94] few steps toward persistent gputracks, crashes --- .../interface/AtomicPairCounter.h | 8 + .../CUDAUtilities/interface/HistoContainer.h | 3 +- .../CAHitNtupletHeterogeneousEDProducer.cc | 17 +- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 252 +++++------------- .../plugins/CAHitQuadrupletGeneratorGPU.h | 24 +- .../PixelTriplets/plugins/GPUCACell.h | 20 +- .../PixelTriplets/plugins/QuadrupletFit.cu | 180 +++++++++++++ 7 files changed, 298 insertions(+), 206 deletions(-) create mode 100644 RecoPixelVertexing/PixelTriplets/plugins/QuadrupletFit.cu diff --git a/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h b/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h index fb0d634349a29..8b1160e3898d0 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h +++ b/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h @@ -10,7 +10,11 @@ class AtomicPairCounter { AtomicPairCounter(){} AtomicPairCounter(c_type i) { counter.ac=i;} + +#ifdef __CUDACC__ + __device__ __host__ AtomicPairCounter & operator=(c_type i) { counter.ac=i; return *this;} +#endif struct Counters { uint32_t n; // total size @@ -22,6 +26,8 @@ class AtomicPairCounter { c_type ac; }; +#ifdef __CUDACC__ + static constexpr c_type incr = 1UL<<32; __device__ __host__ @@ -36,6 +42,8 @@ class AtomicPairCounter { return ret.counters; } +#endif + private: Atomic2 counter; diff --git a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h index 96b5c5aaeb14c..0ea73640d6449 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -18,8 +18,9 @@ #include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #ifdef __CUDACC__ #include "HeterogeneousCore/CUDAUtilities/interface/prefixScan.h" -#include "HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h" #endif +#include "HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h" + #ifdef __CUDACC__ namespace cudautils { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc index 47417690d54b4..d8a8391b42269 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); @@ -80,7 +83,8 @@ CAHitNtupletHeterogeneousEDProducer::CAHitNtupletHeterogeneousEDProducer( enableConversion_(iConfig.getParameter("gpuEnableConversion")), enableTransfer_(enableConversion_ || iConfig.getParameter("gpuEnableTransfer")) { - produces(); + produces(); + if(enableConversion_) produces(); } void CAHitNtupletHeterogeneousEDProducer::fillDescriptions( @@ -137,12 +141,16 @@ void CAHitNtupletHeterogeneousEDProducer::acquireGPUCuda( << " regions"; GPUGenerator_.hitNtuplets(region, gHits, iSetup, doRiemannFit_, enableTransfer_, cudaStream.id()); + + + } void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) { + auto output = std::make_unique(GPUGenerator_.getOutput()); GPUGenerator_.cleanup(cudaStream.id()); if (not emptyRegions and enableConversion_) { @@ -165,8 +173,11 @@ void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( index++; } localRA_.update(seedingHitSets_->size()); + iEvent.put(std::move(seedingHitSets_)); } - iEvent.put(std::move(seedingHitSets_)); + + iEvent.put(std::move(output), heterogeneous::DisableTransfer{}); + } void CAHitNtupletHeterogeneousEDProducer::produceCPU( diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 5a13343f9aa5d..9a815ac37ace5 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -15,147 +15,10 @@ using namespace gpuPixelDoublets; using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; -using namespace Eigen; +using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; __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, - 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 - - 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]); -} - -__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, - 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 - auto n = hits[helix_start].cols(); - - Rfit::VectorNd rad = (hits[helix_start].block(0, 0, 2, n).colwise().norm()); - - 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); - -#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 -} - -__global__ -void kernelLineFitAllHits(GPU::SimpleVector * foundNtuplets, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - 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); - - // 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; - - // TODO: pass properly error booleans - - helix.cov = 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; - - helix.q = circle_fit[helix_start].q; - helix.chi2_circle = circle_fit[helix_start].chi2; - helix.chi2_line = line_fit[helix_start].chi2; - -#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 -} - -__global__ -void kernel_checkOverflows(GPU::SimpleVector *foundNtuplets, +void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, GPUCACell const * __restrict__ cells, uint32_t const * __restrict__ nCells, GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, uint32_t nHits, uint32_t maxNumberOfDoublets) { @@ -187,7 +50,7 @@ void kernel_checkOverflows(GPU::SimpleVector *foundNtuplets, __global__ void -kernel_connect(GPU::SimpleVector *foundNtuplets, +kernel_connect(AtomicPairCounter * apc, GPUCACell::Hits const * __restrict__ hhp, GPUCACell * cells, uint32_t const * __restrict__ nCells, GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, @@ -205,7 +68,7 @@ kernel_connect(GPU::SimpleVector *foundNtuplets, auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; - if (0==cellIndex) foundNtuplets->reset(); // ready for next kernel + if (0==cellIndex) (*apc)=0; // ready for next kernel if (cellIndex >= (*nCells) ) return; auto const & thisCell = cells[cellIndex]; @@ -225,9 +88,10 @@ kernel_connect(GPU::SimpleVector *foundNtuplets, } } -__global__ void kernel_find_ntuplets( +__global__ +void kernel_find_ntuplets( GPUCACell * const __restrict__ cells, uint32_t const * nCells, - GPU::SimpleVector *foundNtuplets, + TuplesOnGPU::Container * foundNtuplets, AtomicPairCounter * apc, unsigned int minHitsPerNtuplet, unsigned int maxNumberOfDoublets_) { @@ -238,9 +102,9 @@ __global__ void kernel_find_ntuplets( 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()); } __global__ @@ -270,6 +134,12 @@ void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() cudaFree(device_isOuterHitOfCell_); cudaFree(device_nCells_); + //product + cudaFree(gpu_.tuples_d); + cudaFree(gpu_.apc_d); + cudaFree(gpu_d); + cudaFree(tuples_); + // Free Riemann Fit stuff cudaFree(hitsGPU_); cudaFree(hits_covGPU_); @@ -312,6 +182,18 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() cudaCheck(cudaMemcpy(d_foundNtupletsVec_[i], & tmp_foundNtuplets, sizeof(GPU::SimpleVector), cudaMemcpyDefault)); } + + //product + cudaCheck(cudaMalloc(&gpu_.tuples_d, sizeof(TuplesOnGPU::Container))); + cudaCheck(cudaMalloc(&gpu_.apc_d, sizeof(AtomicPairCounter))); + cudaCheck(cudaMemset(gpu_.apc_d, 0, sizeof(AtomicPairCounter))); + + cudaCheck(cudaMalloc(&gpu_d, sizeof(TuplesOnGPU))); + gpu_.me_d = gpu_d; + cudaCheck(cudaMemcpy(gpu_d, &gpu_, sizeof(HitsOnGPU), cudaMemcpyDefault)); + + cudaCheck(cudaMallocHost(&tuples_, sizeof(TuplesOnGPU::Container))); + // 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)))); @@ -319,8 +201,8 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() 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(&fast_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Vector4d))); - cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Vector4d))); + cudaCheck(cudaMalloc(&fast_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Eigen::Vector4d))); + cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Eigen::Vector4d))); cudaCheck(cudaMalloc(&circle_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::circle_fit))); cudaCheck(cudaMemset(circle_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::circle_fit))); @@ -341,7 +223,6 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, assert(regionIndex < maxNumberOfRegions_); assert(0==regionIndex); - h_foundNtupletsVec_[regionIndex]->reset(); auto nhits = hh.nHits; assert(nhits <= PixelGPUConstants::maxNumberOfHits); @@ -358,7 +239,7 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; kernel_connect<<>>( - d_foundNtupletsVec_[regionIndex], // needed only to be reset, ready for next kernel + gpu_.apc_d, // needed only to be reset, ready for next kernel hh.gpu_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, @@ -370,51 +251,31 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, kernel_find_ntuplets<<>>( device_theCells_, device_nCells_, - d_foundNtupletsVec_[regionIndex], + gpu_.tuples_d, + gpu_.apc_d, 4, maxNumberOfDoublets_); cudaCheck(cudaGetLastError()); + + numberOfBlocks = (std::max(int(nhits), maxNumberOfDoublets_) + blockSize - 1)/blockSize; kernel_checkOverflows<<>>( - d_foundNtupletsVec_[regionIndex], + gpu_.tuples_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, nhits, maxNumberOfDoublets_ ); cudaCheck(cudaGetLastError()); - // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(d_foundNtupletsVec_[regionIndex], 10); + // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(gpu_.tuples_d, 10); if (doRiemannFit) { - kernelFastFitAllHits<<>>( - d_foundNtupletsVec_[regionIndex], hh.gpu_d, 4, bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); - cudaCheck(cudaGetLastError()); - - blockSize = 256; - numberOfBlocks = (maxNumberOfQuadruplets_ + blockSize - 1) / blockSize; - - kernelCircleFitAllHits<<>>( - d_foundNtupletsVec_[regionIndex], 4, bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); - cudaCheck(cudaGetLastError()); - - kernelLineFitAllHits<<>>( - d_foundNtupletsVec_[regionIndex], bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); - cudaCheck(cudaGetLastError()); + launchFit(regionIndex, hh, nhits, cudaStream); } 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), + cudaCheck(cudaMemcpyAsync(tuples_,gpu_.tuples_d, + sizeof(TuplesOnGPU::Container), cudaMemcpyDeviceToHost, cudaStream)); } } @@ -428,31 +289,38 @@ void CAHitQuadrupletGeneratorGPU::cleanup(cudaStream_t cudaStream) { } std::vector> -CAHitQuadrupletGeneratorGPU::fetchKernelResult(int regionIndex) +CAHitQuadrupletGeneratorGPU::fetchKernelResult(int) { - assert(0==regionIndex); - h_foundNtupletsVec_[regionIndex]->set_data(h_foundNtupletsData_[regionIndex]); + 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(h_foundNtupletsVec_[regionIndex]->size()); - for (int i = 0; i < h_foundNtupletsVec_[regionIndex]->size(); ++i) { - auto sz = (*h_foundNtupletsVec_[regionIndex])[i].size(); - ++sizes[sz]; - for (auto j=0U; j> quadsInterface; quadsInterface.reserve(10000); + + nTuples_=0; + for (auto i = 0U; i < tuples.totbins(); ++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 + +//#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())<<'/'<& result, @@ -141,6 +152,8 @@ class CAHitQuadrupletGeneratorGPU { }; void launchKernels(const TrackingRegion &, int, HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t); + void launchFit(int regionIndex, HitsOnCPU const & hh, uint32_t nhits, cudaStream_t cudaStream); + std::vector> fetchKernelResult(int); float bField_; @@ -163,6 +176,15 @@ class CAHitQuadrupletGeneratorGPU { static constexpr int maxNumberOfDoublets_ = 262144; static constexpr int maxNumberOfRegions_ = 1; + + // products + TuplesOnGPU * gpu_d = nullptr; // copy of the structure on the gpu itself: this is the "Product" + TuplesOnGPU::Container * tuples_ = nullptr; + uint32_t nTuples_ = 0; + TuplesOnGPU gpu_; + + + std::vector*> h_foundNtupletsVec_; std::vector h_foundNtupletsData_; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 841f134b457e3..56af90a8c7436 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -12,6 +12,10 @@ #include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" #include "RecoPixelVertexing/PixelTriplets/interface/CircleEq.h" + +#include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" + + struct Quadruplet { static constexpr uint32_t capacity() { return 6;} using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; @@ -36,6 +40,7 @@ class GPUCACell { using TmpTuple = GPU::VecArray; + using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; GPUCACell() = default; #ifdef __CUDACC__ @@ -152,7 +157,8 @@ class GPUCACell { __device__ inline void find_ntuplets( GPUCACell const * __restrict__ cells, - GPU::SimpleVector *foundNtuplets, + TuplesOnGPU::Container & foundNtuplets, + AtomicPairCounter & apc, TmpTuple & tmpNtuplet, const unsigned int minHitsPerNtuplet) const { @@ -168,18 +174,14 @@ class GPUCACell { 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) { - Quadruplet tmpQuadruplet; - for (unsigned int i = 0; i < tmpNtuplet.size(); ++i) { - tmpQuadruplet.hitId[i] = tmpNtuplet[i]; - } - tmpQuadruplet.hitId[tmpNtuplet.size()] = theOuterHitId; - for (unsigned int i = tmpNtuplet.size()+1; ipush_back(tmpQuadruplet); + tmpNtuplet.push_back_unsafe(theOuterHitId); + foundNtuplets.bulkFill(apc,tmpNtuplet.data(),tmpNtuplet.size()); + tmpNtuplet.pop_back(); } } tmpNtuplet.pop_back(); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/QuadrupletFit.cu b/RecoPixelVertexing/PixelTriplets/plugins/QuadrupletFit.cu new file mode 100644 index 0000000000000..804e2dc2f8b20 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/QuadrupletFit.cu @@ -0,0 +1,180 @@ +// +// Author: Felice Pantaleo, CERN +// + +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "CAHitQuadrupletGeneratorGPU.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" +#include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" + + +using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; +using namespace Eigen; + +__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, + 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 + + 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]); +} + +__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, + 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 + auto n = hits[helix_start].cols(); + + Rfit::VectorNd rad = (hits[helix_start].block(0, 0, 2, n).colwise().norm()); + + 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); + +#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 +} + +__global__ +void kernelLineFitAllHits(GPU::SimpleVector * foundNtuplets, + float B, + Rfit::helix_fit *results, + Rfit::Matrix3xNd *hits, + Rfit::Matrix3Nd *hits_cov, + Rfit::circle_fit *circle_fit, + 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); + + // 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; + + // TODO: pass properly error booleans + + helix.cov = 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; + + helix.q = circle_fit[helix_start].q; + helix.chi2_circle = circle_fit[helix_start].chi2; + helix.chi2_line = line_fit[helix_start].chi2; + +#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 +} + + +void CAHitQuadrupletGeneratorGPU::launchFit(int regionIndex, HitsOnCPU const & hh, uint32_t nhits, + cudaStream_t cudaStream) +{ + + auto blockSize = 256; + auto numberOfBlocks = (maxNumberOfQuadruplets_ + blockSize - 1) / blockSize; + + kernelFastFitAllHits<<>>( + d_foundNtupletsVec_[regionIndex], hh.gpu_d, 4, bField_, helix_fit_resultsGPU_, + hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, + line_fit_resultsGPU_); + cudaCheck(cudaGetLastError()); + + kernelCircleFitAllHits<<>>( + d_foundNtupletsVec_[regionIndex], 4, bField_, helix_fit_resultsGPU_, + hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, + line_fit_resultsGPU_); + cudaCheck(cudaGetLastError()); + + kernelLineFitAllHits<<>>( + d_foundNtupletsVec_[regionIndex], bField_, helix_fit_resultsGPU_, + hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, + line_fit_resultsGPU_); + cudaCheck(cudaGetLastError()); +} From 0adee78fa24a0deeb58c1e5f5306328f98625dd5 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 9 Nov 2018 19:41:57 +0100 Subject: [PATCH 35/94] Q productions works --- .../CUDAUtilities/interface/HistoContainer.h | 6 ++++++ .../CUDAUtilities/test/OneToManyAssoc_t.cu | 12 ++++-------- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h index 0ea73640d6449..7b993df78e81d 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -79,6 +79,12 @@ namespace cudautils { } + template + __global__ + void finalizeBulk(AtomicPairCounter const * apc, Assoc * __restrict__ assoc) { + assoc->bulkFinalizeFill(*apc); + } + } // namespace cudautils #endif diff --git a/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu b/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu index 05e4a7c1abc54..700c7dfa6bcd4 100644 --- a/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu +++ b/HeterogeneousCore/CUDAUtilities/test/OneToManyAssoc_t.cu @@ -55,10 +55,6 @@ void fillBulk(AtomicPairCounter * apc, TK const * __restrict__ tk, Assoc * __res assoc->bulkFill(*apc,&tk[k][0],m); } -__global__ -void finalizeBulk(AtomicPairCounter const * apc, Assoc * __restrict__ assoc) { - assoc->bulkFinalizeFill(*apc); -} @@ -124,11 +120,11 @@ int main() { auto nThreads = 256; auto nBlocks = (4*N + nThreads - 1) / nThreads; - count<<>>(v_d.get(),a_d.get(),N); + 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); + fill<<>>(v_d.get(),a_d.get(),N); Assoc la; cuda::memory::copy(&la,a_d.get(),sizeof(Assoc)); @@ -150,8 +146,8 @@ int main() { cudaMalloc(&dc_d, sizeof(AtomicPairCounter)); cudaMemset(dc_d, 0, sizeof(AtomicPairCounter)); nBlocks = (N + nThreads - 1) / nThreads; - fillBulk<<>>(dc_d,v_d.get(),a_d.get(),N); - finalizeBulk<<>>(dc_d,a_d.get()); + fillBulk<<>>(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); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 9a815ac37ace5..4b34755de4839 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -190,7 +190,7 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() cudaCheck(cudaMalloc(&gpu_d, sizeof(TuplesOnGPU))); gpu_.me_d = gpu_d; - cudaCheck(cudaMemcpy(gpu_d, &gpu_, sizeof(HitsOnGPU), cudaMemcpyDefault)); + cudaCheck(cudaMemcpy(gpu_d, &gpu_, sizeof(TuplesOnGPU), cudaMemcpyDefault)); cudaCheck(cudaMallocHost(&tuples_, sizeof(TuplesOnGPU::Container))); @@ -256,7 +256,7 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, 4, maxNumberOfDoublets_); cudaCheck(cudaGetLastError()); - + cudautils::finalizeBulk<<>>(gpu_.apc_d,gpu_.tuples_d); numberOfBlocks = (std::max(int(nhits), maxNumberOfDoublets_) + blockSize - 1)/blockSize; kernel_checkOverflows<<>>( From 0198e524ac38d1ee78eec18c897ff6778c9b6981 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 10 Nov 2018 19:02:04 +0100 Subject: [PATCH 36/94] forward hits --- .../PixelTrackFitting/interface/FitResult.h | 95 +++++++++++++++ .../PixelTrackFitting/interface/RiemannFit.h | 110 ++++-------------- .../pixelTrackHeterogeneousProduct.h | 36 ++++++ .../plugins/RiemannFitOnGPU.h | 52 +++++++++ .../src/PixelFitterByRiemannParaboloid.cc | 4 +- .../test/PixelTrackRiemannFit.cc | 2 +- .../plugins/CAHitQuadrupletGeneratorGPU.h | 2 +- 7 files changed, 208 insertions(+), 93 deletions(-) create mode 100644 RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h create mode 100644 RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h create mode 100644 RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h new file mode 100644 index 0000000000000..15f0ec4749ce3 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h @@ -0,0 +1,95 @@ +#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)| + */ + 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))); + +} // namespace RFit +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index 6de1c77bbac12..062f663e19525 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -1,13 +1,7 @@ #ifndef RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h #define RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h -#include - -#include -#include -#include - -#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" #ifndef RFIT_DEBUG #define RFIT_DEBUG 0 @@ -15,71 +9,9 @@ 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))); + + +// using namespace Eigen; template __host__ __device__ void printIt(C* m, const char* prefix = "") @@ -529,7 +461,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); @@ -555,7 +487,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); @@ -577,7 +509,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); @@ -803,7 +735,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 +782,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 +823,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; @@ -951,14 +883,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 +901,7 @@ __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) for (u_int a = 0; a < 6; ++a) { const u_int i = nu[a][0], j = nu[a][1]; @@ -988,9 +920,9 @@ __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; + Eigen::Matrix cm1; // Matrix cm2; - Matrix cm3; + Eigen::Matrix cm3; cm1 = (v.transpose() * C0 * v); // cm2 = (C0.cwiseProduct(t0)).sum(); cm3 = (r0.transpose() * t0 * r0); @@ -1000,7 +932,7 @@ __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, @@ -1182,7 +1114,7 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, // 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); @@ -1217,14 +1149,14 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, 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); + Eigen::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); } #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); @@ -1232,7 +1164,7 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, const double t2 = 1. / sqrt_; J << -t0, v(0) * t1, 0, -c * v(0) * t0 * t2, v0_2 * c * t1 * t2, -sqrt_ * t0; } - Matrix JT = J.transpose().eval(); + Eigen::Matrix JT = J.transpose().eval(); #if RFIT_DEBUG printIt(&J, "line_fit - J:"); #endif diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h b/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h new file mode 100644 index 0000000000000..7a67662b2c00d --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h @@ -0,0 +1,36 @@ +#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 { + using Container = TracksOnGPU::Container; + + 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/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h new file mode 100644 index 0000000000000..70bf5b600c4bf --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h @@ -0,0 +1,52 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_plugins_RiemannFitOnGPU_h +#define RecoPixelVertexing_PixelTrackFitting_plugins_RiemannFitOnGPU_h + +#include "RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h" + +class RiemannFitOnGPU { +public: + using Input = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; + using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; + using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + + using TracksOnGPU = pixelTuplesHeterogeneousProduct::TracksOnGPU; + using TracksOnCPU = pixelTuplesHeterogeneousProduct::TracksOnCPU; + using Output = pixelTrackHeterogeneousProduct::HeterogeneousPixelTracks; + + RiemannFitOnGPU() = default; + ~RiemannFitOnGPU() { deallocateOnGPU();} + + void launchKernels(TuplesOnCPU const & hh, bool transferToCPU, cudaStream_t); + + TracksOnCPU getOutput() const { + return TuplesOnCPU { helix_fit_results_, gpu_d, nTracks_}; + } + + void allocateOnGPU(); + void deallocateOnGPU(); + + +private: + + static constexpr int maxNumberOfTracks_ = 10000; + + + // input + TuplesOnCPU const * tuplesOnCPU; + + // products + TracksOnGPU * gpu_d = nullptr; // copy of the structure on the gpu itself: this is the "Product" + Rfit::helix_fit * helix_fit_results_ = nullptr; + uint32_t nTracks_ = 0; + TracksOnGPU gpu_; + + // 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/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/PixelTrackRiemannFit.cc b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc index b27ed52473388..22d4e4960ec48 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc +++ b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc @@ -200,7 +200,7 @@ void test_helix_fit() { Matrix3Nd hits_cov; unique_ptr helix(new helix_fit[iteration]); // helix_fit* helix = new helix_fit[iteration]; - Matrix score(41, iteration); + Eigen::Matrix score(41, iteration); for (int i = 0; i < iteration; i++) { if (debug2 == 1 && i == (iteration - 1)) { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index 76877c6467400..0c2e3549067f2 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -72,7 +72,7 @@ class CAHitQuadrupletGeneratorGPU { cudaStream_t stream); TuplesOnCPU getOutput() const { - return TuplesOnCPU { tuples_, gpu_d, nTuples_}; + return TuplesOnCPU { hitsOnCPU->gpu_d, tuples_, gpu_d, nTuples_}; } void cleanup(cudaStream_t stream); From fd8f49c7e6b138cf13e43f097c33f91dbf9a697b Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 11 Nov 2018 12:58:28 +0100 Subject: [PATCH 37/94] compiles --- .../pixelTrackHeterogeneousProduct.h | 1 - .../plugins/RiemannFitOnGPU.h | 52 ------------ .../plugins/CAHitQuadrupletGeneratorGPU.cc | 2 +- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 63 +++------------ .../plugins/CAHitQuadrupletGeneratorGPU.h | 35 ++++---- .../PixelTriplets/plugins/RiemannFitOnGPU.cc | 36 +++++++++ .../{QuadrupletFit.cu => RiemannFitOnGPU.cu} | 79 ++++++++----------- .../PixelTriplets/plugins/RiemannFitOnGPU.h | 49 ++++++++++++ 8 files changed, 150 insertions(+), 167 deletions(-) delete mode 100644 RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h create mode 100644 RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc rename RecoPixelVertexing/PixelTriplets/plugins/{QuadrupletFit.cu => RiemannFitOnGPU.cu} (68%) create mode 100644 RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h b/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h index 7a67662b2c00d..4a1763f312ac4 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h @@ -18,7 +18,6 @@ namespace pixelTrackHeterogeneousProduct { }; struct TracksOnCPU { - using Container = TracksOnGPU::Container; Rfit::helix_fit * helix_fit_results; TracksOnGPU const * gpu_d = nullptr; diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h deleted file mode 100644 index 70bf5b600c4bf..0000000000000 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/RiemannFitOnGPU.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef RecoPixelVertexing_PixelTrackFitting_plugins_RiemannFitOnGPU_h -#define RecoPixelVertexing_PixelTrackFitting_plugins_RiemannFitOnGPU_h - -#include "RecoPixelVertexing/PixelTrackFitting/interface/pixelTrackHeterogeneousProduct.h" - -class RiemannFitOnGPU { -public: - using Input = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; - using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; - using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; - - using TracksOnGPU = pixelTuplesHeterogeneousProduct::TracksOnGPU; - using TracksOnCPU = pixelTuplesHeterogeneousProduct::TracksOnCPU; - using Output = pixelTrackHeterogeneousProduct::HeterogeneousPixelTracks; - - RiemannFitOnGPU() = default; - ~RiemannFitOnGPU() { deallocateOnGPU();} - - void launchKernels(TuplesOnCPU const & hh, bool transferToCPU, cudaStream_t); - - TracksOnCPU getOutput() const { - return TuplesOnCPU { helix_fit_results_, gpu_d, nTracks_}; - } - - void allocateOnGPU(); - void deallocateOnGPU(); - - -private: - - static constexpr int maxNumberOfTracks_ = 10000; - - - // input - TuplesOnCPU const * tuplesOnCPU; - - // products - TracksOnGPU * gpu_d = nullptr; // copy of the structure on the gpu itself: this is the "Product" - Rfit::helix_fit * helix_fit_results_ = nullptr; - uint32_t nTracks_ = 0; - TracksOnGPU gpu_; - - // 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/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index b56ac42d75d91..f4cf2a48ddb24 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -73,7 +73,7 @@ void CAHitQuadrupletGeneratorGPU::fillDescriptions(edm::ParameterSetDescription 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)); } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 4b34755de4839..31ed84c4ac451 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -122,13 +122,8 @@ void kernel_print_found_ntuplets(GPU::SimpleVector *foundNtuplets, i void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() { - 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]); - } + + fitter.deallocateOnGPU(); cudaFree(device_theCells_); cudaFree(device_isOuterHitOfCell_); @@ -136,17 +131,11 @@ void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() //product cudaFree(gpu_.tuples_d); + cudaFree(gpu_.helix_fit_results_d); cudaFree(gpu_.apc_d); cudaFree(gpu_d); cudaFree(tuples_); - - // 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_); + cudaFree(helix_fit_results_); } void CAHitQuadrupletGeneratorGPU::allocateOnGPU() @@ -165,53 +154,22 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() 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))); - new(h_foundNtupletsVec_[i]) GPU::SimpleVector(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))); - GPU::SimpleVector tmp_foundNtuplets(maxNumberOfQuadruplets_, d_foundNtupletsData_[i]); - cudaCheck(cudaMemcpy(d_foundNtupletsVec_[i], & tmp_foundNtuplets, sizeof(GPU::SimpleVector), cudaMemcpyDefault)); - } - //product cudaCheck(cudaMalloc(&gpu_.tuples_d, sizeof(TuplesOnGPU::Container))); cudaCheck(cudaMalloc(&gpu_.apc_d, sizeof(AtomicPairCounter))); - cudaCheck(cudaMemset(gpu_.apc_d, 0, sizeof(AtomicPairCounter))); + cudaCheck(cudaMalloc(&gpu_.helix_fit_results_d, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); cudaCheck(cudaMalloc(&gpu_d, sizeof(TuplesOnGPU))); gpu_.me_d = gpu_d; cudaCheck(cudaMemcpy(gpu_d, &gpu_, sizeof(TuplesOnGPU), cudaMemcpyDefault)); cudaCheck(cudaMallocHost(&tuples_, sizeof(TuplesOnGPU::Container))); + cudaCheck(cudaMallocHost(&helix_fit_results_, sizeof(Rfit::helix_fit)*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)))); - - cudaCheck(cudaMalloc(&hits_covGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::Matrix3Nd(12, 12)))); - cudaCheck(cudaMemset(hits_covGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::Matrix3Nd(12, 12)))); + fitter.allocateOnGPU(gpu_.tuples_d, gpu_.helix_fit_results_d); - cudaCheck(cudaMalloc(&fast_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Eigen::Vector4d))); - cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Eigen::Vector4d))); - cudaCheck(cudaMalloc(&circle_fit_resultsGPU_, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::circle_fit))); - cudaCheck(cudaMemset(circle_fit_resultsGPU_, 0x00, 48 * maxNumberOfQuadruplets_ * sizeof(Rfit::circle_fit))); - - 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::launchKernels(const TrackingRegion ®ion, @@ -270,14 +228,19 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(gpu_.tuples_d, 10); if (doRiemannFit) { - launchFit(regionIndex, hh, nhits, cudaStream); + launchFit(hh, nhits, 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)*maxNumberOfQuadruplets_, + cudaMemcpyDeviceToHost, cudaStream)); } + } void CAHitQuadrupletGeneratorGPU::cleanup(cudaStream_t cudaStream) { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index 0c2e3549067f2..8006a9dbe659f 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -23,6 +23,7 @@ #include "RecoPixelVertexing/PixelTriplets/plugins/RecHitsMap.h" #include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +#include "RiemannFitOnGPU.h" #include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" @@ -72,7 +73,7 @@ class CAHitQuadrupletGeneratorGPU { cudaStream_t stream); TuplesOnCPU getOutput() const { - return TuplesOnCPU { hitsOnCPU->gpu_d, tuples_, gpu_d, nTuples_}; + return TuplesOnCPU { hitsOnCPU->gpu_d, tuples_, helix_fit_results_, gpu_d, nTuples_}; } void cleanup(cudaStream_t stream); @@ -85,6 +86,8 @@ class CAHitQuadrupletGeneratorGPU { private: + // cpu stuff + std::unique_ptr theComparitor; class QuantityDependsPtEval { @@ -151,12 +154,21 @@ class CAHitQuadrupletGeneratorGPU { const bool enabled_; }; + // end cpu stuff + + void launchKernels(const TrackingRegion &, int, HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t); - void launchFit(int regionIndex, HitsOnCPU const & hh, uint32_t nhits, cudaStream_t cudaStream); + void launchFit(HitsOnCPU const & hh, uint32_t nhits, cudaStream_t cudaStream) { + fitter.launchKernels(hh, nhits, maxNumberOfQuadruplets_, cudaStream); + } + std::vector> fetchKernelResult(int); - float bField_; + + + RiemannFitOnGPU fitter; + const float extraHitRPhitolerance; @@ -177,35 +189,24 @@ class CAHitQuadrupletGeneratorGPU { static constexpr int maxNumberOfRegions_ = 1; + // products 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; uint32_t nTuples_ = 0; TuplesOnGPU gpu_; - - std::vector*> h_foundNtupletsVec_; - std::vector h_foundNtupletsData_; - - std::vector*> d_foundNtupletsVec_; - std::vector d_foundNtupletsData_; - GPUCACell* device_theCells_ = nullptr; GPUCACell::OuterHitOfCell* device_isOuterHitOfCell_ = nullptr; uint32_t* device_nCells_ = nullptr; + // 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/RiemannFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc new file mode 100644 index 0000000000000..753d9f8f439c4 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc @@ -0,0 +1,36 @@ +#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; + + 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/QuadrupletFit.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu similarity index 68% rename from RecoPixelVertexing/PixelTriplets/plugins/QuadrupletFit.cu rename to RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu index 804e2dc2f8b20..4f92599bda961 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/QuadrupletFit.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -2,47 +2,47 @@ // 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 "CAHitQuadrupletGeneratorGPU.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(GPU::SimpleVector * foundNtuplets, - siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hhp, +void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtuplets, + HitsOnGPU const * __restrict__ hhp, int hits_in_fit, - float B, - Rfit::helix_fit *results, Rfit::Matrix3xNd *hits, Rfit::Matrix3Nd *hits_cov, - Rfit::circle_fit *circle_fit, - Vector4d *fast_fit, - Rfit::line_fit *line_fit) + Rfit::Vector4d *fast_fit) { int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); - if (helix_start >= foundNtuplets->size()) { + if (foundNtuplets->size(helix_start)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 + auto const * hitId = foundNtuplets->begin(helix_start); for (unsigned int i = 0; i < hits_in_fit; ++i) { - auto hit = (*foundNtuplets)[helix_start].hitId[i]; + auto hit = 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); @@ -73,25 +73,19 @@ void kernelFastFitAllHits(GPU::SimpleVector * foundNtuplets, } __global__ -void kernelCircleFitAllHits(GPU::SimpleVector * foundNtuplets, +void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtuplets, int hits_in_fit, - float B, - Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, + double B, + Rfit::Matrix3xNd const * hits, + Rfit::Matrix3Nd const * hits_cov, Rfit::circle_fit *circle_fit, - Vector4d *fast_fit, - Rfit::line_fit *line_fit) + Rfit::Vector4d * fast_fit) { int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); - if (helix_start >= foundNtuplets->size()) { + if (foundNtuplets->size(helix_start)size()); -#endif auto n = hits[helix_start].cols(); Rfit::VectorNd rad = (hits[helix_start].block(0, 0, 2, n).colwise().norm()); @@ -109,25 +103,21 @@ void kernelCircleFitAllHits(GPU::SimpleVector * foundNtuplets, } __global__ -void kernelLineFitAllHits(GPU::SimpleVector * foundNtuplets, - float B, +void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtuplets, + int hits_in_fit, + double B, Rfit::helix_fit *results, Rfit::Matrix3xNd *hits, Rfit::Matrix3Nd *hits_cov, Rfit::circle_fit *circle_fit, - Vector4d *fast_fit, + Rfit::Vector4d *fast_fit, Rfit::line_fit *line_fit) { int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); - if (helix_start >= foundNtuplets->size()) { + if (foundNtuplets->size(helix_start)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); @@ -153,27 +143,24 @@ void kernelLineFitAllHits(GPU::SimpleVector * foundNtuplets, } -void CAHitQuadrupletGeneratorGPU::launchFit(int regionIndex, HitsOnCPU const & hh, uint32_t nhits, - cudaStream_t cudaStream) +void RiemannFitOnGPU::launchKernels(HitsOnCPU const & hh, uint32_t nhits, uint32_t maxNumberOfTuples, cudaStream_t cudaStream) { - auto blockSize = 256; - auto numberOfBlocks = (maxNumberOfQuadruplets_ + blockSize - 1) / blockSize; + auto blockSize = 128; + auto numberOfBlocks = (maxNumberOfConcurrentFits_ + blockSize - 1) / blockSize; kernelFastFitAllHits<<>>( - d_foundNtupletsVec_[regionIndex], hh.gpu_d, 4, bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); + tuples_d, hh.gpu_d, 4, + hitsGPU_, hits_covGPU_, fast_fit_resultsGPU_); cudaCheck(cudaGetLastError()); kernelCircleFitAllHits<<>>( - d_foundNtupletsVec_[regionIndex], 4, bField_, helix_fit_resultsGPU_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, - line_fit_resultsGPU_); + tuples_d, 4, bField_, + hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_); cudaCheck(cudaGetLastError()); kernelLineFitAllHits<<>>( - d_foundNtupletsVec_[regionIndex], bField_, helix_fit_resultsGPU_, + tuples_d, 4, bField_, helix_fit_results_d, hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, line_fit_resultsGPU_); cudaCheck(cudaGetLastError()); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h new file mode 100644 index 0000000000000..1fda2e587059d --- /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_ = 10000; + + // 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 From fd49d01a800c6137f017fadac16c38605b7bf82b Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 11 Nov 2018 15:33:53 +0100 Subject: [PATCH 38/94] runs --- .../PixelTriplets/plugins/BuildFile.xml | 1 + .../plugins/CAHitQuadrupletGeneratorGPU.cu | 20 ++++++++-- .../PixelTriplets/plugins/RiemannFitOnGPU.cc | 2 + .../PixelTriplets/plugins/RiemannFitOnGPU.cu | 40 ++++++++++++++----- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml b/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml index 8956caca42899..853317afc1900 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml +++ b/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml @@ -14,3 +14,4 @@ + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 31ed84c4ac451..bbe526ffad968 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -18,7 +18,7 @@ using HitsOnCPU = siPixelRecHitsHeterogeneousProduct::HitsOnCPU; using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; __global__ -void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, +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, uint32_t maxNumberOfDoublets) { @@ -29,9 +29,20 @@ void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, auto idx = threadIdx.x + blockIdx.x * blockDim.x; #ifdef GPU_DEBUG - if (0==idx) - printf("number of found cells %d\n",*nCells); + 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); + } + + 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>>(gpu_.apc_d,gpu_.tuples_d); numberOfBlocks = (std::max(int(nhits), maxNumberOfDoublets_) + blockSize - 1)/blockSize; kernel_checkOverflows<<>>( - gpu_.tuples_d, + gpu_.tuples_d, gpu_.apc_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, nhits, maxNumberOfDoublets_ diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc index 753d9f8f439c4..0fdca092e5e6d 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc @@ -5,6 +5,8 @@ void RiemannFitOnGPU::allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit: 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)))); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu index 4f92599bda961..400e6c75db160 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -5,7 +5,6 @@ #include "RiemannFitOnGPU.h" #include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" - #include #include @@ -30,7 +29,12 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl Rfit::Matrix3Nd *hits_cov, Rfit::Vector4d *fast_fit) { + + assert(fast_fit); assert(foundNtuplets); + int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); + + if (helix_start>=foundNtuplets->nbins()) return; if (foundNtuplets->size(helix_start)size(helix_start)=foundNtuplets->nbins()) return; + if (foundNtuplets->size(helix_start)size(helix_start)=foundNtuplets->nbins()) return; + if (foundNtuplets->size(helix_start)>>( tuples_d, 4, bField_, helix_fit_results_d, hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, From aef70eb24e2257ae84b291123193706a22b09c8f Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 11 Nov 2018 17:02:55 +0100 Subject: [PATCH 39/94] use less memory --- .../PixelTrackFitting/plugins/BuildFile.xml | 3 + .../plugins/PixelTrackProducerFromCUDA.cc | 45 +++++++- .../PixelTriplets/plugins/RiemannFitOnGPU.cu | 103 ++++++++++-------- .../PixelTriplets/plugins/RiemannFitOnGPU.h | 2 +- 4 files changed, 100 insertions(+), 53 deletions(-) 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/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index 6a3bf229cc67f..b3840c7a99882 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" @@ -11,34 +15,65 @@ * 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 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: + edm::EDGetTokenT gpuToken_; edm::EDGetTokenT srcToken_; bool enabled_; }; PixelTrackProducerFromCUDA::PixelTrackProducerFromCUDA(const edm::ParameterSet& iConfig): - srcToken_(consumes(iConfig.getParameter("src"))) + HeterogeneousEDProducer(iConfig), + gpuToken_(consumes(iConfig.getParameter("src"))) +// srcToken_(consumes(iConfig.getParameter("src"))) { produces(); +// produces(); } void PixelTrackProducerFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { edm::ParameterSetDescription desc; desc.add("src", edm::InputTag("pixelTracksHitQuadruplets")); + + HeterogeneousEDProducer::fillPSetDescription(desc); descriptions.addWithDefaultLabel(desc); } -void PixelTrackProducerFromCUDA::produce(edm::StreamID id, edm::Event& iEvent, const edm::EventSetup& iSetup) const { +void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, + const edm::EventSetup &iSetup, + cuda::stream_t<> &cudaStream) { iEvent.put(std::make_unique(0)); } + +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/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu index 400e6c75db160..bb8db9feb7c16 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -27,12 +27,14 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl int hits_in_fit, Rfit::Matrix3xNd *hits, Rfit::Matrix3Nd *hits_cov, - Rfit::Vector4d *fast_fit) + Rfit::Vector4d *fast_fit, + uint32_t offset) { assert(fast_fit); assert(foundNtuplets); - int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); + 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); @@ -52,7 +54,7 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl 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]; + hits[local_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) { @@ -69,16 +71,16 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl // | 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]; + hits_cov[local_start](i + j * hits_in_fit, i + l * hits_in_fit) = ge[ge_idx]; } } } - fast_fit[helix_start] = Rfit::Fast_fit(hits[helix_start]); + fast_fit[local_start] = Rfit::Fast_fit(hits[local_start]); - assert(fast_fit[helix_start](0)==fast_fit[helix_start](0)); - assert(fast_fit[helix_start](1)==fast_fit[helix_start](1)); - assert(fast_fit[helix_start](2)==fast_fit[helix_start](2)); - assert(fast_fit[helix_start](3)==fast_fit[helix_start](3)); + 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)); } @@ -89,29 +91,32 @@ void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtu Rfit::Matrix3xNd const * __restrict__ hits, Rfit::Matrix3Nd const * __restrict__ hits_cov, Rfit::circle_fit *circle_fit, - Rfit::Vector4d const * __restrict__ fast_fit) + Rfit::Vector4d const * __restrict__ fast_fit, + uint32_t offset) { assert(circle_fit); - int helix_start = (blockIdx.x * blockDim.x + threadIdx.x); + + 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_); - cudaCheck(cudaGetLastError()); + for (uint32_t offset=0; offset>>( + 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_); - 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_); - 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 index 1fda2e587059d..9352a8127b19b 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h @@ -28,7 +28,7 @@ class RiemannFitOnGPU { private: - static constexpr uint32_t maxNumberOfConcurrentFits_ = 10000; + static constexpr uint32_t maxNumberOfConcurrentFits_ = 2000; // fowarded TuplesOnGPU::Container const * tuples_d = nullptr; From 545a326c22db2478d3daaf056f83180f9f60522e Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 11 Nov 2018 17:34:33 +0100 Subject: [PATCH 40/94] use even less memory --- .../PixelTrackFitting/interface/FitResult.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h index 15f0ec4749ce3..73fd9210bc378 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h @@ -84,11 +84,10 @@ struct helix_fit |(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 + float chi2_circle; + float chi2_line; +// Vector4d fast_fit; + int32_t q; //!< particle charge } __attribute__((aligned(16))); } // namespace RFit From 511aa999adee8783fd5d4c7194f114a0ec82f893 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 11 Nov 2018 19:47:09 +0100 Subject: [PATCH 41/94] add quality flag --- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 6 +-- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 43 ++++++++++++++++++- .../plugins/CAHitQuadrupletGeneratorGPU.h | 4 +- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index f4cf2a48ddb24..8866da0baa22f 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -126,7 +126,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( auto isBarrel = [](const unsigned id) -> bool { return id == PixelSubdetector::PixelBarrel; }; - bool bad = false; + bool bad = quality_[quadId] == pixelTuplesHeterogeneousProduct::bad; for (unsigned int i = 0; i < 4; ++i) { auto k = foundQuads[quadId][i]; assert(kget()); } + +__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); + } + + quality[idx] = isNaN ? quality[idx] : pixelTuplesHeterogeneousProduct::loose; + +} + __global__ void kernel_print_found_ntuplets(GPU::SimpleVector *foundNtuplets, int maxPrint) { for (int i = 0; i < std::min(maxPrint, foundNtuplets->size()); ++i) { @@ -170,6 +201,7 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() 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(&gpu_d, sizeof(TuplesOnGPU))); gpu_.me_d = gpu_d; @@ -177,6 +209,7 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() cudaCheck(cudaMallocHost(&tuples_, sizeof(TuplesOnGPU::Container))); cudaCheck(cudaMallocHost(&helix_fit_results_, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); + cudaCheck(cudaMallocHost(&quality_, sizeof(Quality)*maxNumberOfQuadruplets_)); fitter.allocateOnGPU(gpu_.tuples_d, gpu_.helix_fit_results_d); @@ -241,8 +274,11 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, if (doRiemannFit) { launchFit(hh, nhits, cudaStream); + numberOfBlocks = (maxNumberOfQuadruplets_ + blockSize - 1)/blockSize; + kernel_VerifyFit<<>>(gpu_.tuples_d, gpu_.helix_fit_results_d, gpu_.quality_d); } + if (transferToCPU) { cudaCheck(cudaMemcpyAsync(tuples_,gpu_.tuples_d, sizeof(TuplesOnGPU::Container), @@ -251,6 +287,11 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, cudaCheck(cudaMemcpyAsync(helix_fit_results_,gpu_.helix_fit_results_d, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_, cudaMemcpyDeviceToHost, cudaStream)); + + cudaCheck(cudaMemcpyAsync(quality_,gpu_.quality_d, + sizeof(Quality)*maxNumberOfQuadruplets_, + cudaMemcpyDeviceToHost, cudaStream)); + } } @@ -276,7 +317,7 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int) std::vector> quadsInterface; quadsInterface.reserve(10000); nTuples_=0; - for (auto i = 0U; i < tuples.totbins(); ++i) { + 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_; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index 8006a9dbe659f..a4098945b27ca 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -47,6 +47,7 @@ class CAHitQuadrupletGeneratorGPU { using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + using Quality = pixelTuplesHeterogeneousProduct::Quality; using Output = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; static constexpr unsigned int minLayers = 4; @@ -73,7 +74,7 @@ class CAHitQuadrupletGeneratorGPU { cudaStream_t stream); TuplesOnCPU getOutput() const { - return TuplesOnCPU { hitsOnCPU->gpu_d, tuples_, helix_fit_results_, gpu_d, nTuples_}; + return TuplesOnCPU { hitsOnCPU->gpu_d, tuples_, helix_fit_results_, quality_, gpu_d, nTuples_}; } void cleanup(cudaStream_t stream); @@ -194,6 +195,7 @@ class CAHitQuadrupletGeneratorGPU { 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_; From de2333d7cd123d74553031060bef37c8ed6028e5 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 12 Nov 2018 10:39:17 +0100 Subject: [PATCH 42/94] factorize --- .../plugins/PixelTrackProducer.cc | 59 +------------------ .../plugins/PixelTrackProducer.h | 2 - .../plugins/PixelTrackProducerFromCUDA.cc | 27 +++++++-- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 8 ++- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 2 +- 5 files changed, 32 insertions(+), 66 deletions(-) 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 b3840c7a99882..da13a5c4dbfd9 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -10,6 +10,17 @@ #include "FWCore/ParameterSet/interface/ParameterSetDescription.h" #include "RecoTracker/TkHitPairs/interface/RegionsSeedingHitSets.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" + + /** * This class will eventually be the one creating the reco::Track * objects from the output of GPU CA. Now it is just to produce @@ -43,21 +54,29 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer gpuToken_; edm::EDGetTokenT srcToken_; - bool enabled_; + bool enableConversion_; }; PixelTrackProducerFromCUDA::PixelTrackProducerFromCUDA(const edm::ParameterSet& iConfig): HeterogeneousEDProducer(iConfig), - gpuToken_(consumes(iConfig.getParameter("src"))) -// srcToken_(consumes(iConfig.getParameter("src"))) + gpuToken_(consumes(iConfig.getParameter("src"))), + enableConversion_ (iConfig.getParameter("gpuEnableConversion")) { - produces(); + if (enableConversion_) { + srcToken_ = consumes(iConfig.getParameter("src")); + produces(); + produces(); + produces(); + } + produces(); // dummy // produces(); } void PixelTrackProducerFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { edm::ParameterSetDescription desc; desc.add("src", edm::InputTag("pixelTracksHitQuadruplets")); + desc.add("gpuEnableConversion", true); + HeterogeneousEDProducer::fillPSetDescription(desc); descriptions.addWithDefaultLabel(desc); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 8866da0baa22f..f69a1e0464122 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -126,7 +126,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( auto isBarrel = [](const unsigned id) -> bool { return id == PixelSubdetector::PixelBarrel; }; - bool bad = quality_[quadId] == pixelTuplesHeterogeneousProduct::bad; + bool bad = pixelTuplesHeterogeneousProduct::bad == quality_[quadId]; for (unsigned int i = 0; i < 4; ++i) { auto k = foundQuads[quadId][i]; assert(k Date: Mon, 12 Nov 2018 10:39:29 +0100 Subject: [PATCH 43/94] factorize --- .../PixelTrackFitting/plugins/storeTracks.h | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h new file mode 100644 index 0000000000000..643ca04cb3e96 --- /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" + + +void storeTracks(edm::Event& 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 From e028fd1b60eeb7f4ede586ff8418ecc27dea46fd Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 12 Nov 2018 12:26:30 +0100 Subject: [PATCH 44/94] reading correcly tuples --- .../customizePixelTracksForProfiling.py | 1 + .../plugins/PixelTrackProducerFromCUDA.cc | 33 ++++++++++++++++++- .../PixelTrackFitting/plugins/storeTracks.h | 4 +-- .../python/PixelTracks_cff.py | 4 +++ .../CAHitNtupletHeterogeneousEDProducer.cc | 5 ++- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py index 99a3a9321062b..5a22ac63f7f02 100644 --- a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py +++ b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py @@ -30,6 +30,7 @@ def customizePixelTracksForProfilingDisableConversion(process): process.siPixelClustersPreSplitting.gpuEnableConversion = False process.siPixelRecHitsPreSplitting.gpuEnableConversion = False process.pixelTracksHitQuadruplets.gpuEnableConversion = False + process.pixelTracks.gpuEnableConversion = False return process diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index da13a5c4dbfd9..695c9efb88342 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -20,6 +20,9 @@ #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 @@ -30,6 +33,10 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer> { public: + using Input = pixelTuplesHeterogeneousProduct::HeterogeneousPixelTuples; + using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + + using Output = HeterogeneousProductImpl, heterogeneous::GPUCudaProduct >; @@ -43,7 +50,7 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer &cudaStream) override {} + cuda::stream_t<> &cudaStream) override; void produceGPUCuda(edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) override; @@ -82,10 +89,34 @@ void PixelTrackProducerFromCUDA::fillDescriptions(edm::ConfigurationDescriptions descriptions.addWithDefaultLabel(desc); } +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; + + std::cout << "point on gpu " << gTuples.gpu_d << std::endl; + +} + + 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; + + pixeltrackfitting::TracksWithTTRHs tracks; + edm::ESHandle httopo; + iSetup.get().get(httopo); + + // store tracks + storeTracks(iEvent, tracks, *httopo); } diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h index 643ca04cb3e96..48abab5237587 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h @@ -16,8 +16,8 @@ #include "DataFormats/TrackerCommon/interface/TrackerTopology.h" #include "Geometry/Records/interface/TrackerTopologyRcd.h" - -void storeTracks(edm::Event& ev, const pixeltrackfitting::TracksWithTTRHs& tracksWithHits, const TrackerTopology& ttopo) +template +void storeTracks(Ev & ev, const pixeltrackfitting::TracksWithTTRHs& tracksWithHits, const TrackerTopology& ttopo) { auto tracks = std::make_unique(); auto recHits = std::make_unique(); diff --git a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py index 16803a957c928..1d6dea73402be 100644 --- a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py +++ b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py @@ -67,6 +67,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) + pixelTracksSequence = cms.Sequence( pixelTracksTrackingRegions + pixelFitterByHelixProjections + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc index d8a8391b42269..680da249cac73 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc @@ -150,9 +150,6 @@ void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) { - auto output = std::make_unique(GPUGenerator_.getOutput()); - GPUGenerator_.cleanup(cudaStream.id()); - if (not emptyRegions and enableConversion_) { edm::Handle> hregions; iEvent.getByToken(regionToken_, hregions); @@ -176,7 +173,9 @@ void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( iEvent.put(std::move(seedingHitSets_)); } + auto output = std::make_unique(GPUGenerator_.getOutput()); iEvent.put(std::move(output), heterogeneous::DisableTransfer{}); + GPUGenerator_.cleanup(cudaStream.id()); } From 3064dd909b3f8f2e1e30ddc5c02f6b33b30c92e6 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 12 Nov 2018 12:38:40 +0100 Subject: [PATCH 45/94] read hits --- .../plugins/PixelTrackProducerFromCUDA.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index 695c9efb88342..881d599ac79f8 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -59,6 +59,9 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer gpuToken_; edm::EDGetTokenT srcToken_; bool enableConversion_; @@ -98,7 +101,8 @@ void PixelTrackProducerFromCUDA::acquireGPUCuda(const edm::HeterogeneousEvent & auto const & gTuples = *gh; std::cout << "tuples from gpu " << gTuples.nTuples << std::endl; - std::cout << "point on gpu " << gTuples.gpu_d << std::endl; + + tuples_ = gh.product(); } @@ -115,6 +119,14 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, edm::ESHandle httopo; iSetup.get().get(httopo); + + edm::Handle hhitSets; + iEvent.getByToken(srcToken_, hhitSets); + const auto & hitSet = *hhitSets->begin(); + auto b = hitSet.begin(); auto e = hitSet.end(); + std::cout << "reading hitset " << e-b << std::endl; + + // store tracks storeTracks(iEvent, tracks, *httopo); } From e64286eeca83eed14a8314ac8233b7434f36bc6e Mon Sep 17 00:00:00 2001 From: Matti Kortelainen Date: Mon, 12 Nov 2018 17:01:13 +0100 Subject: [PATCH 46/94] Add B-hadron MTV variation to pixel track validation sequence --- .../RecoTrack/python/PostProcessorTracker_cfi.py | 4 ++-- Validation/RecoTrack/python/TrackValidation_cff.py | 12 +++++++++++- Validation/RecoTrack/python/plotting/html.py | 4 ++++ .../RecoTrack/python/plotting/trackingPlots.py | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Validation/RecoTrack/python/PostProcessorTracker_cfi.py b/Validation/RecoTrack/python/PostProcessorTracker_cfi.py index a926b19d4321a..6b5a19f799035 100644 --- a/Validation/RecoTrack/python/PostProcessorTracker_cfi.py +++ b/Validation/RecoTrack/python/PostProcessorTracker_cfi.py @@ -270,9 +270,9 @@ def _addNoFlow(module): ) postProcessorTrackTrackingOnly = postProcessorTrack.clone() -postProcessorTrackTrackingOnly.subDirs.extend(["Tracking/TrackSeeding/*", "Tracking/PixelTrack/*", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*"]) +postProcessorTrackTrackingOnly.subDirs.extend(["Tracking/TrackSeeding/*", "Tracking/PixelTrack/*", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*", "Tracking/PixelTrackBHadron/*"]) postProcessorTrackSummaryTrackingOnly = postProcessorTrackSummary.clone() -postProcessorTrackSummaryTrackingOnly.subDirs.extend(["Tracking/TrackSeeding", "Tracking/PixelTrack", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*"]) +postProcessorTrackSummaryTrackingOnly.subDirs.extend(["Tracking/TrackSeeding", "Tracking/PixelTrack", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*", "Tracking/PixelTrackBHadron/*"]) postProcessorTrackSequenceTrackingOnly = cms.Sequence( postProcessorTrackTrackingOnly+ diff --git a/Validation/RecoTrack/python/TrackValidation_cff.py b/Validation/RecoTrack/python/TrackValidation_cff.py index a90d22e8518cf..85dbf6594a67a 100644 --- a/Validation/RecoTrack/python/TrackValidation_cff.py +++ b/Validation/RecoTrack/python/TrackValidation_cff.py @@ -770,6 +770,14 @@ def _uniqueFirstLayers(layerList): doSimPlots = False, doSimTrackPlots = False, ) +trackValidatorBHadronPixelTrackingOnly = trackValidatorPixelTrackingOnly.clone( + dirName = "Tracking/PixelTrackBHadron/", + label_tp_effic = "trackingParticlesBHadron", + label_tp_effic_refvector = True, + doSimPlots = True, + doRecoTrackPlots = False, # Fake rate is defined wrt. all TPs, and that is already included in trackValidator + dodEdxPlots = False, +) tracksValidationTruthPixelTrackingOnly = tracksValidationTruth.copy() @@ -777,6 +785,7 @@ def _uniqueFirstLayers(layerList): tracksValidationTruthPixelTrackingOnly.replace(quickTrackAssociatorByHits, quickTrackAssociatorByHitsPixelTrackingOnly) tracksValidationTruthPixelTrackingOnly.replace(trackingParticleRecoTrackAsssociation, trackingParticlePixelTrackAsssociation) tracksValidationTruthPixelTrackingOnly.replace(VertexAssociatorByPositionAndTracks, PixelVertexAssociatorByPositionAndTracks) +tracksValidationTruthPixelTrackingOnly += trackingParticlesBHadron _tracksValidationTruthPixelTrackingOnlyGPU = tracksValidationTruthPixelTrackingOnly.copy() _tracksValidationTruthPixelTrackingOnlyGPU.insert(0, tpClusterProducerHeterogeneousPixelTrackingOnly) @@ -788,7 +797,8 @@ def _uniqueFirstLayers(layerList): pixelTracksFromPV + trackValidatorPixelTrackingOnly + trackValidatorFromPVPixelTrackingOnly + - trackValidatorFromPVAllTPPixelTrackingOnly + trackValidatorFromPVAllTPPixelTrackingOnly + + trackValidatorBHadronPixelTrackingOnly ) diff --git a/Validation/RecoTrack/python/plotting/html.py b/Validation/RecoTrack/python/plotting/html.py index d3f593f6a7586..1cac97b736941 100644 --- a/Validation/RecoTrack/python/plotting/html.py +++ b/Validation/RecoTrack/python/plotting/html.py @@ -63,6 +63,8 @@ def _allToBTV(s): return s.replace("All", "BTV-like") def _ptCut(s): return s.replace("Tracks", "Tracks pT > 0.9 GeV").replace("tracks", "tracks pT > 0.9 GeV") +def _allToPixel(s): + return s.replace("All", "Pixel") def _toPixel(s): return s.replace("Tracks", "Pixel tracks") _trackQualityNameOrder = collections.OrderedDict([ @@ -198,6 +200,7 @@ def _toPixel(s): ("pixel", "Pixel tracks"), ("pixelFromPV", _toPixel(_fromPVName)), ("pixelFromPVAllTP", _toPixel(_fromPVAllTPName)), + ("pixelbhadron", _allToPixel(_bhadronName)), # These are for vertices ("genvertex", "Gen vertices"), ("pixelVertices", "Pixel vertices"), @@ -248,6 +251,7 @@ def _sectionNameLegend(): "bhadron_btvLike": _bhadronLegend.replace("All tracks", _btvLegend), "pixelFromPV": _fromPVLegend, "pixelFromPVAllTP": _fromPVAllTPLegend, + "pixelbhadron": _bhadronLegend, } class Table: diff --git a/Validation/RecoTrack/python/plotting/trackingPlots.py b/Validation/RecoTrack/python/plotting/trackingPlots.py index ac81473c843cb..ab02cf89606b6 100644 --- a/Validation/RecoTrack/python/plotting/trackingPlots.py +++ b/Validation/RecoTrack/python/plotting/trackingPlots.py @@ -1347,6 +1347,7 @@ def _appendPixelTrackingPlots(lastDirName, name): _appendPixelTrackingPlots("PixelTrack", "pixel") _appendPixelTrackingPlots("PixelTrackFromPV", "pixelFromPV") _appendPixelTrackingPlots("PixelTrackFromPVAllTP", "pixelFromPVAllTP") +_appendPixelTrackingPlots("PixelTrackBHadron", "pixelbhadron") # MiniAOD From 5695203a413506aa573849eba712d3dadb0666f1 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 12 Nov 2018 18:14:55 +0100 Subject: [PATCH 47/94] fix errors on gpu --- .../plugins/PixelTrackProducerFromCUDA.cc | 65 ++++++++++++++++++- .../PixelTriplets/plugins/RiemannFitOnGPU.cu | 45 ++++++++----- .../src/PixelVertexHeterogeneousProducer.cc | 12 ++-- 3 files changed, 97 insertions(+), 25 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index 881d599ac79f8..d1f1e1eb10d28 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -10,6 +10,9 @@ #include "FWCore/ParameterSet/interface/ParameterSetDescription.h" #include "RecoTracker/TkHitPairs/interface/RegionsSeedingHitSets.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" @@ -115,17 +118,73 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, 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 hhitSets; - iEvent.getByToken(srcToken_, hhitSets); - const auto & hitSet = *hhitSets->begin(); + 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::vector hits; + hits.reserve(4); + + uint32_t nh=0; // current hitset + for (uint32_t it=0; itnTuples; ++it) { + auto q = tuples_->quality[it]; + if (q == pixelTuplesHeterogeneousProduct::bad ) continue; + 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(), region.origin())); + 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); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu index bb8db9feb7c16..d13c428179c94 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -49,34 +49,45 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl auto const * hitId = foundNtuplets->begin(helix_start); for (unsigned int i = 0; i < hits_in_fit; ++i) { auto hit = hitId[i]; - // printf("Hit global_x: %f\n", hhp->xg_d[hit]); + // 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]); + // 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]; - 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] + // ge[] ==> [xx, xy, yy, xz, yz, zz] // in (j,l) notation, we have: - // ge[] ==> [(0,0), (0,1), (0,2), (1,1), (1,2), (2,2)] + // 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 2 | - // | 1 3 4 | - // | 2 4 5 | - auto ge_idx = j + l + (j > 0 and l > 0); + // | 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)); @@ -114,9 +125,8 @@ void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtu fast_fit[local_start], rad, B, true); #ifdef GPU_DEBUG - printf("kernelCircleFitAllHits circle.par(0): %d %f\n", helix_start, circle_fit[local_start].par(0)); - printf("kernelCircleFitAllHits circle.par(1): %d %f\n", helix_start, circle_fit[local_start].par(1)); - printf("kernelCircleFitAllHits circle.par(2): %d %f\n", helix_start, circle_fit[local_start].par(2)); + printf("kernelCircleFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, + circle_fit[local_start].par(0), circle_fit[local_start].par(1), circle_fit[local_start].par(2)); #endif } @@ -162,8 +172,9 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl helix.chi2_line = line_fit[local_start].chi2; #ifdef GPU_DEBUG - printf("kernelLineFitAllHits line.par(0): %d %f\n", helix_start, circle_fit[local_start].par(0)); - printf("kernelLineFitAllHits line.par(1): %d %f\n", helix_start, line_fit[local_start].par(1)); + printf("kernelLineFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, + circle_fit[local_start].par(0), circle_fit[local_start].par(1), circle_fit[local_start].par(2)); + printf("kernelLineFitAllHits line.par(0,1): %d %f,%f\n", helix_start, line_fit[local_start].par(0),line_fit[local_start].par(1)); #endif } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 70cdaed2f2959..129f2b7205b24 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -57,7 +57,7 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer 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"; + if (verbose_) std::cout << "PixelVertexHeterogeneousProducer" << ": Found " << tracks.size() << " tracks in TrackCollection called " << trackCollName << "\n"; // on gpu beamspot already subtracted at hit level... math::XYZPoint bs(0.,0.,0.); @@ -127,7 +127,7 @@ void PixelVertexHeterogeneousProducer::acquireGPUCuda( pt2.push_back(std::min(25.,tracks[i].pt()));pt2.back()*=pt2.back(); } - if (verbose_) edm::LogInfo("PixelVertexHeterogeneousProducer") << ": Selected " << m_trks.size() << " of these tracks for vertexing\n"; + if (verbose_) std::cout << "PixelVertexHeterogeneousProducer" << ": Selected " << m_trks.size() << " of these tracks for vertexing\n" << std::endl; // Third, ship these tracks off to be vertexed m_gpuAlgo.produce(cudaStream.id(),z.data(),ez2.data(),pt2.data(),z.size()); @@ -158,6 +158,7 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } // fill legacy data format + if (verbose_) std::cout << "found " << gpuProduct.nVertices << " vertices on GPU" << 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.... @@ -177,7 +178,7 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( if (gpuProduct.ivtx[k]==int(i)) itrk.push_back(k); } auto nt = itrk.size(); - assert(nt>0); + 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) { @@ -185,12 +186,13 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } itrk.clear(); } + /* assert(uind.size()==(*vertexes).size()); if (!uind.empty()) { assert(0 == *uind.begin()); assert(uind.size()-1 == *uind.rbegin()); } - + */ if (verbose_) { edm::LogInfo("PixelVertexHeterogeneousProducer") << ": Found " << vertexes->size() << " vertexes\n"; for (unsigned int i=0; isize(); ++i) { From bca31204e110db791a3c211016fbe28dc9548e08 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 13 Nov 2018 09:04:54 +0100 Subject: [PATCH 48/94] tip/zip ok --- .../plugins/PixelTrackProducerFromCUDA.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index d1f1e1eb10d28..635f82b4fd73d 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -10,6 +10,8 @@ #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" @@ -65,6 +67,7 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer tBeamSpot; edm::EDGetTokenT gpuToken_; edm::EDGetTokenT srcToken_; bool enableConversion_; @@ -72,6 +75,7 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer(iConfig.getParameter("beamSpot"))), gpuToken_(consumes(iConfig.getParameter("src"))), enableConversion_ (iConfig.getParameter("gpuEnableConversion")) { @@ -87,6 +91,7 @@ PixelTrackProducerFromCUDA::PixelTrackProducerFromCUDA(const edm::ParameterSet& 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); @@ -137,6 +142,15 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, 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); @@ -177,7 +191,7 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, std::unique_ptr track( builder.build(pt, phi, cotTheta, tip, zip, chi2, iCharge, hits, - fieldESH.product(), region.origin())); + fieldESH.product(), bs)); if (!track) continue; // filter??? tracks.emplace_back(track.release(), shits); From 9367675aeb5a1c3c0f580aceee3dbb3140758c70 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 13 Nov 2018 11:53:27 +0100 Subject: [PATCH 49/94] use new version of Rinman fit --- .../SiPixelRecHits/interface/pixelCPEforGPU.h | 4 +- .../PixelTrackFitting/interface/RiemannFit.h | 563 ++++++++++-------- .../src/PixelVertexHeterogeneousProducer.cc | 12 +- .../PixelVertexFinding/src/gpuClusterTracks.h | 11 +- .../PixelVertexFinding/src/gpuFitVertices.h | 2 +- .../PixelVertexFinding/src/gpuSplitVertices.h | 2 +- 6 files changed, 345 insertions(+), 249 deletions(-) diff --git a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h index 3f63a0fc85b8a..d9c0faea4e73a 100644 --- a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h +++ b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h @@ -217,11 +217,11 @@ namespace pixelCPEforGPU { // FIXME these are errors form Run1 constexpr float xerr_barrel_l1[] = { 0.00115, 0.00120, 0.00088 }; - constexpr float xerr_barrel_l1_def = 0.01030; + constexpr float xerr_barrel_l1_def = 0.00200; // 0.01030; constexpr float yerr_barrel_l1[] = { 0.00375, 0.00230, 0.00250, 0.00250, 0.00230, 0.00230, 0.00210, 0.00210, 0.00240 }; constexpr float yerr_barrel_l1_def = 0.00210; constexpr float xerr_barrel_ln[] = { 0.00115, 0.00120, 0.00088 }; - constexpr float xerr_barrel_ln_def = 0.01030; + constexpr float xerr_barrel_ln_def = 0.00200; // 0.01030; constexpr float yerr_barrel_ln[] = { 0.00375, 0.00230, 0.00250, 0.00250, 0.00230, 0.00230, 0.00210, 0.00210, 0.00240 }; constexpr float yerr_barrel_ln_def = 0.00210; constexpr float xerr_endcap[] = { 0.0020, 0.0020 }; diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index 062f663e19525..da0b35d8a2b8a 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -10,9 +10,6 @@ namespace Rfit { - -// using namespace Eigen; - template __host__ __device__ void printIt(C* m, const char* prefix = "") { @@ -39,10 +36,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. */ @@ -51,43 +46,66 @@ __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 + const double XX_0 = 16.f/(0.06); +// const double XX_0 = 1000.*16.f/(0.06); + u_int n = length_values.rows(); + rad_lengths(0) = length_values(0)/XX_0; + for (u_int j = 1; j < n; ++j) { + rad_lengths(j) = std::abs(length_values(j)-length_values(j-1))/XX_0; + } } /*! \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 @@ -96,47 +114,24 @@ __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, u_int n = s_arcs.rows(); double p_t = fast_fit(2) * B; 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.f + 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); + rot(i, i) = sin(theta); + rot(n + i, n + i) = sin(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, j) = i < j ? cos(theta) : -cos(theta); } #if RFIT_DEBUG @@ -144,34 +139,38 @@ __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, @@ -180,24 +179,32 @@ __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, u_int n = p2D.cols(); double p_t = fast_fit(2) * B; 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 = .000225 / p_2 * (1.f + 0.038 * rad_lengths.array().log()).abs2() * rad_lengths.array(); 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(k, l) += (rad(k) - rad(i)) * (rad(l) - rad(i)) * sig2(i) / (sqr(sin(theta))); scatter_cov_rad(l, k) = scatter_cov_rad(k, l); } } @@ -211,10 +218,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. */ @@ -253,12 +258,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, @@ -286,15 +288,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, @@ -326,12 +325,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. */ @@ -341,31 +337,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. */ @@ -377,7 +354,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. @@ -402,50 +378,12 @@ __host__ __device__ inline void par_uvrtopak(circle_fit& circle, const double B, 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 @@ -453,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) @@ -474,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. @@ -496,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. @@ -519,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); @@ -600,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)). @@ -608,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. @@ -921,7 +844,6 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, Cvc.block(0, 3, 3, 1) = t1; Cvc.block(3, 0, 1, 3) = t1.transpose(); Eigen::Matrix cm1; - // Matrix cm2; Eigen::Matrix cm3; cm1 = (v.transpose() * C0 * v); // cm2 = (C0.cwiseProduct(t0)).sum(); @@ -936,7 +858,7 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, { 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:"); @@ -959,7 +881,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 @@ -967,20 +888,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 @@ -991,21 +907,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 @@ -1017,8 +934,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); @@ -1027,9 +947,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); @@ -1038,7 +958,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 @@ -1046,43 +978,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 @@ -1094,16 +1008,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 @@ -1111,7 +1021,6 @@ __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. Eigen::Matrix cm; @@ -1121,8 +1030,8 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, // 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))); @@ -1138,19 +1047,21 @@ __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; - Eigen::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:"); @@ -1160,9 +1071,7 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, { 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; } Eigen::Matrix JT = J.transpose().eval(); #if RFIT_DEBUG @@ -1177,6 +1086,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 @@ -1185,14 +1272,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 @@ -1204,15 +1288,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. */ @@ -1249,4 +1330,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/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 129f2b7205b24..0d3fb4addc623 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -119,16 +119,24 @@ void PixelVertexHeterogeneousProducer::acquireGPUCuda( // fill z,ez std::vector z,ez2,pt2; assert(m_trks.empty()); + auto nok=0; for (unsigned int i=0; i Date: Tue, 13 Nov 2018 12:32:34 +0100 Subject: [PATCH 50/94] fix error^2 --- .../plugins/SiPixelRecHitHeterogeneous.cc | 2 +- .../SiPixelRecHits/plugins/gpuPixelRecHits.h | 4 ++-- RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) 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::Handle Date: Tue, 13 Nov 2018 13:54:54 +0100 Subject: [PATCH 51/94] fix pixel errors --- CondFormats/SiPixelTransient/src/SiPixelTemplate.cc | 2 +- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 10 +++++++++- .../src/PixelVertexHeterogeneousProducer.cc | 6 +++--- .../PixelVertexFinding/src/gpuClusterTracks.h | 6 +----- .../PixelVertexFinding/src/gpuFitVertices.h | 2 +- .../PixelVertexFinding/src/gpuSplitVertices.h | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CondFormats/SiPixelTransient/src/SiPixelTemplate.cc b/CondFormats/SiPixelTransient/src/SiPixelTemplate.cc index e8eddff6a1fca..b41b6062505c6 100755 --- a/CondFormats/SiPixelTransient/src/SiPixelTemplate.cc +++ b/CondFormats/SiPixelTransient/src/SiPixelTemplate.cc @@ -2578,7 +2578,7 @@ int SiPixelTemplate::qbin(int id, float cotalpha, float cotbeta, float locBz, fl auto xxratio = 0.f; { - auto j = std::lower_bound(templ.cotalphaX,templ.cotalphaX+Nxx,cotalpha); + auto j = std::lower_bound(templ.cotalphaX,templ.cotalphaX+Nxx,cota); if (j==templ.cotalphaX+Nxx) { --j; xxratio = 1.f; } else if (j==templ.cotalphaX) { ++j; xxratio = 0.f;} else { xxratio = (cota - (*(j-1)) )/( (*j) - (*(j-1)) ) ; } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index f69a1e0464122..edd19f35d5b69 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -108,12 +108,16 @@ 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; @@ -144,6 +148,8 @@ void CAHitQuadrupletGeneratorGPU::fillResults( } if (bad) { nbad++; quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; continue;} + /* + // this part shall not be run anymore... quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; @@ -201,8 +207,10 @@ void CAHitQuadrupletGeneratorGPU::fillResults( if (fitFastCircleChi2Cut && chi2 > thisMaxChi2) continue; } - result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); + */ + + result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); quality_[quadId] = pixelTuplesHeterogeneousProduct::loose; } // end loop over quads diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 0d3fb4addc623..df61dcb1e3f6b 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -57,7 +57,7 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer Date: Tue, 13 Nov 2018 16:37:40 +0100 Subject: [PATCH 52/94] use error from templates --- CondFormats/SiPixelTransient/src/SiPixelGenError.cc | 2 +- RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h | 2 +- RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc | 2 +- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu | 6 +++++- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h | 6 +++++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CondFormats/SiPixelTransient/src/SiPixelGenError.cc b/CondFormats/SiPixelTransient/src/SiPixelGenError.cc index ed50718f2907e..c7de54d651262 100644 --- a/CondFormats/SiPixelTransient/src/SiPixelGenError.cc +++ b/CondFormats/SiPixelTransient/src/SiPixelGenError.cc @@ -698,7 +698,7 @@ int SiPixelGenError::qbin(int id, float cotalpha, float cotbeta, float locBz, fl auto xxratio = 0.f; { - auto j = std::lower_bound(templ.cotalphaX,templ.cotalphaX+Nxx,cotalpha); + auto j = std::lower_bound(templ.cotalphaX,templ.cotalphaX+Nxx,cota); if (j==templ.cotalphaX+Nxx) { --j; xxratio = 1.f; } else if (j==templ.cotalphaX) { ++j; xxratio = 0.f;} else { xxratio = (cota - (*(j-1)) )/( (*j) - (*(j-1)) ) ; } diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h b/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h index 8d7f722b368e6..1eafdeefa50e8 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h +++ b/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h @@ -126,7 +126,7 @@ namespace gpuPixelRecHits { assert(h < 2000*256); pixelCPEforGPU::position(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); - pixelCPEforGPU::errorOld(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); + pixelCPEforGPU::error(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); chargeh[h] = clusParams.charge[ic]; diff --git a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc index af38457f7dcfa..ca3336dc86e77 100644 --- a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc +++ b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc @@ -156,7 +156,7 @@ void PixelCPEFast::fillParamsForGpu() { */ - errorFromTemplates(p,cp,15000.f); + errorFromTemplates(p,cp,20000.f); g.sx[0] = cp.sigmax; g.sx[1] = cp.sx1; g.sx[2] = cp.sx2; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index e1193cbf5302b..bbdcef40b6a5d 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -144,7 +144,7 @@ void kernel_VerifyFit(TuplesOnGPU::Container const * __restrict__ tuples, 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 < 10000.f); // catch NaN as well + isNaN |= !(fit_results[idx].chi2_line+fit_results[idx].chi2_circle < 1000.f); // catch NaN as well quality[idx] = isNaN ? quality[idx] : pixelTuplesHeterogeneousProduct::loose; } @@ -170,6 +170,8 @@ void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() cudaFree(device_theCells_); cudaFree(device_isOuterHitOfCell_); cudaFree(device_nCells_); + cudaFree(device_cellToTuple_); + cudaFree(device_cellToTuple_apc_); //product cudaFree(gpu_.tuples_d); @@ -196,6 +198,8 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() cudaCheck(cudaMemset(device_isOuterHitOfCell_, 0, PixelGPUConstants::maxNumberOfHits * sizeof(GPU::VecArray))); + cudaCheck(cudaMalloc(&device_cellToTuple_, sizeof(CellToTuple))); + cudaCheck(cudaMalloc(&device_cellToTuple_apc_, sizeof(AtomicPairCounter))); //product cudaCheck(cudaMalloc(&gpu_.tuples_d, sizeof(TuplesOnGPU::Container))); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index a4098945b27ca..84018831532f1 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -199,11 +199,15 @@ class CAHitQuadrupletGeneratorGPU { uint32_t nTuples_ = 0; TuplesOnGPU gpu_; - + // workspace GPUCACell* device_theCells_ = nullptr; GPUCACell::OuterHitOfCell* device_isOuterHitOfCell_ = nullptr; uint32_t* device_nCells_ = nullptr; + using CellToTuple = OneToManyAssoc; // 3.5 should be enough + CellToTuple * device_cellToTuple_ = nullptr; + AtomicPairCounter * device_cellToTuple_apc_ = nullptr; + // input HitsOnCPU const * hitsOnCPU=nullptr; From 6899cd07fd12e4172f417565dfea0ddb8c6d482b Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 13 Nov 2018 19:40:25 +0100 Subject: [PATCH 53/94] dup remover written --- .../CUDAUtilities/interface/GPUVecArray.h | 4 ++ .../CUDAUtilities/interface/HistoContainer.h | 3 +- .../plugins/PixelTrackProducerFromCUDA.cc | 10 ++--- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 8 ++-- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 40 +++++++++++++++++-- .../PixelTriplets/plugins/GPUCACell.h | 24 ++++++----- 6 files changed, 65 insertions(+), 24 deletions(-) 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 7b993df78e81d..61ccc7484d68b 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h +++ b/HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h @@ -223,10 +223,11 @@ class HistoContainer { #ifdef __CUDACC__ __device__ __forceinline__ - void bulkFill(AtomicPairCounter & apc, index_type const * v, uint32_t n) { + 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(0)); if (!enableConversion_) return; - std::cout << "Converting gpu helix in reco tracks" << std::endl; + // std::cout << "Converting gpu helix in reco tracks" << std::endl; edm::ESHandle fieldESH; iSetup.get().get(fieldESH); @@ -140,15 +140,13 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, 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; + // 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; + // std::cout << "beamspot " << bsh.x0() << ' ' << bsh.y0() << ' ' << bsh.z0() << std::endl; GlobalPoint bs(bsh.x0(),bsh.y0(),bsh.z0()); std::vector hits; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index edd19f35d5b69..2d7724cbfbef3 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -109,7 +109,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( auto const & foundQuads = fetchKernelResult(index); unsigned int numberOfFoundQuadruplets = foundQuads.size(); - /* + const QuantityDependsPtEval maxChi2Eval = maxChi2.evaluator(es); // re-used throughout @@ -117,7 +117,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( std::array bc_z; std::array bc_errZ2; - */ + std::array gps; std::array ges; std::array barrels; @@ -148,7 +148,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( } if (bad) { nbad++; quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; continue;} - /* + // this part shall not be run anymore... quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; @@ -208,7 +208,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( continue; } - */ + result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); quality_[quadId] = pixelTuplesHeterogeneousProduct::loose; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index bbdcef40b6a5d..0c09c0ccbe25b 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -50,6 +50,8 @@ void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, AtomicPairCou 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) atomicInc(&killedCell,maxNumberOfDoublets); } if (idx < nHits) { @@ -61,10 +63,40 @@ void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, AtomicPairCou // if (threadIdx.x==0) printf("number of killed cells %d\n",killedCell); } +__global__ +void +kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict__ nCells, + Rfit::helix_fit const * __restrict__ hfit, + pixelTuplesHeterogeneousProduct::Quality * quality + ) { + + 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; + + // find min chi2 + float mc=1000.f; uint16_t im=60000; + for (auto it : thisCell.theTracks) { + 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) { + quality[it] = it==im ? loose : dup; + } +} __global__ void -kernel_connect(AtomicPairCounter * apc, +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, @@ -82,7 +114,7 @@ kernel_connect(AtomicPairCounter * apc, auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; - if (0==cellIndex) (*apc)=0; // ready for next kernel + if (0==cellIndex) { (*apc1)=0; (*apc2)=0; }// ready for next kernel if (cellIndex >= (*nCells) ) return; auto const & thisCell = cells[cellIndex]; @@ -104,7 +136,7 @@ kernel_connect(AtomicPairCounter * apc, __global__ void kernel_find_ntuplets( - GPUCACell * const __restrict__ cells, uint32_t const * nCells, + GPUCACell * __restrict__ cells, uint32_t const * nCells, TuplesOnGPU::Container * foundNtuplets, AtomicPairCounter * apc, unsigned int minHitsPerNtuplet, unsigned int maxNumberOfDoublets_) @@ -245,7 +277,7 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; kernel_connect<<>>( - gpu_.apc_d, // needed only to be reset, ready for next kernel + gpu_.apc_d, device_cellToTuple_apc_, // needed only to be reset, ready for next kernel hh.gpu_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 56af90a8c7436..19e5f5e4299ac 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -38,10 +38,12 @@ class GPUCACell { using Hits = siPixelRecHitsHeterogeneousProduct::HitsOnGPU; using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; - using TmpTuple = GPU::VecArray; + using TmpTuple = GPU::VecArray; using TuplesOnGPU = pixelTuplesHeterogeneousProduct::TuplesOnGPU; + using CellToTuple = pixelTuplesHeterogeneousProduct::CellToTuple; + GPUCACell() = default; #ifdef __CUDACC__ @@ -58,6 +60,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); } @@ -156,7 +159,7 @@ class GPUCACell { __device__ inline void find_ntuplets( - GPUCACell const * __restrict__ cells, + GPUCACell * __restrict__ cells, TuplesOnGPU::Container & foundNtuplets, AtomicPairCounter & apc, TmpTuple & tmpNtuplet, @@ -168,8 +171,8 @@ 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()<=5); + tmpNtuplet.push_back_unsafe(theDoubletId); + assert(tmpNtuplet.size()<=4); if(theOuterNeighbors.size()>0) { // continue for (int j = 0; j < theOuterNeighbors.size(); ++j) { @@ -179,18 +182,21 @@ class GPUCACell { } } else { // if long enough save... if ((unsigned int)(tmpNtuplet.size()) >= minHitsPerNtuplet-1) { - tmpNtuplet.push_back_unsafe(theOuterHitId); - foundNtuplets.bulkFill(apc,tmpNtuplet.data(),tmpNtuplet.size()); - tmpNtuplet.pop_back(); + 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() < 5); + assert(tmpNtuplet.size() < 4); } #endif // __CUDACC__ - GPU::VecArray< unsigned int, 40> theOuterNeighbors; + GPU::VecArray< uint32_t, 40> theOuterNeighbors; + GPU::VecArray< uint16_t, 20> theTracks; int theDoubletId; int theLayerPairId; From ae195a28f822b34c05aa9b3000d8a6426c00375d Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 13 Nov 2018 19:51:36 +0100 Subject: [PATCH 54/94] filter duplicates --- .../plugins/PixelTrackProducerFromCUDA.cc | 2 +- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 12 ++++++------ .../plugins/CAHitQuadrupletGeneratorGPU.cu | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index 9c370f9ac5e1c..d92209b29ab99 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -155,7 +155,7 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, uint32_t nh=0; // current hitset for (uint32_t it=0; itnTuples; ++it) { auto q = tuples_->quality[it]; - if (q == pixelTuplesHeterogeneousProduct::bad ) continue; + if (q != pixelTuplesHeterogeneousProduct::loose ) continue; // FIXME auto const & shits = *(b+nh); auto nHits = shits.size(); hits.resize(nHits); for (unsigned int iHit = 0; iHit < nHits; ++iHit) hits[iHit] = shits[iHit]; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 2d7724cbfbef3..11c345796e657 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -109,14 +109,14 @@ 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; @@ -147,9 +147,9 @@ void CAHitQuadrupletGeneratorGPU::fillResults( } if (bad) { nbad++; quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; continue;} - + if (quality_[quadId] != pixelTuplesHeterogeneousProduct::loose) continue; // FIXME remove dup - + /* // this part shall not be run anymore... quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; @@ -208,10 +208,10 @@ void CAHitQuadrupletGeneratorGPU::fillResults( continue; } - + */ result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); - quality_[quadId] = pixelTuplesHeterogeneousProduct::loose; + // quality_[quadId] = pixelTuplesHeterogeneousProduct::loose; } // end loop over quads // #ifdef GPU_DEBUG diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 0c09c0ccbe25b..ce0d8c5ae1c22 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -312,6 +312,9 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, launchFit(hh, nhits, cudaStream); numberOfBlocks = (maxNumberOfQuadruplets_ + blockSize - 1)/blockSize; kernel_VerifyFit<<>>(gpu_.tuples_d, gpu_.helix_fit_results_d, gpu_.quality_d); + + numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; + kernel_fastDuplicateRemover<<>>(device_theCells_, device_nCells_,gpu_.helix_fit_results_d, gpu_.quality_d); } From e135f215743dfbe65787c22ad7eaba157649817e Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 14 Nov 2018 08:26:14 +0100 Subject: [PATCH 55/94] mae sure algo is stable --- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index ce0d8c5ae1c22..55cafdadcbf53 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -90,7 +90,7 @@ kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict } // mark duplicates for (auto it : thisCell.theTracks) { - quality[it] = it==im ? loose : dup; + if (it!=im) quality[it] = dup; //no race: simple assignment of the same constant } } From 5763c563822f5c6e443156de1edca3d28a1bb5c1 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 14 Nov 2018 11:45:09 +0100 Subject: [PATCH 56/94] fix for absent lape --- RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc | 5 +++-- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc index ca3336dc86e77..eb51dd5a2eaeb 100644 --- a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc +++ b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc @@ -141,8 +141,9 @@ void PixelCPEFast::fillParamsForGpu() { 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); @@ -153,7 +154,7 @@ void PixelCPEFast::fillParamsForGpu() { << std::endl; } std::cout << i << ' ' << m*std::sqrt(lape.xx()) <<' '<< m*std::sqrt(lape.yy()) << std::endl; - */ +#endif errorFromTemplates(p,cp,20000.f); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 55cafdadcbf53..a8e824394350e 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -72,7 +72,7 @@ kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict constexpr auto bad = pixelTuplesHeterogeneousProduct::bad; constexpr auto dup = pixelTuplesHeterogeneousProduct::dup; - constexpr auto loose = pixelTuplesHeterogeneousProduct::loose; + // constexpr auto loose = pixelTuplesHeterogeneousProduct::loose; auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; @@ -90,7 +90,7 @@ kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict } // mark duplicates for (auto it : thisCell.theTracks) { - if (it!=im) quality[it] = dup; //no race: simple assignment of the same constant + if (quality[it]!= bad && it!=im) quality[it] = dup; //no race: simple assignment of the same constant } } From 7ea1bb1c094788fab5b9bfb75410df82874508b3 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 14 Nov 2018 14:32:34 +0100 Subject: [PATCH 57/94] drop quads if sharing cell with pents --- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index a8e824394350e..9335ff801108d 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -66,6 +66,7 @@ void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, AtomicPairCou __global__ void kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict__ nCells, + TuplesOnGPU::Container * foundNtuplets, Rfit::helix_fit const * __restrict__ hfit, pixelTuplesHeterogeneousProduct::Quality * quality ) { @@ -80,10 +81,20 @@ kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict 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 - float mc=1000.f; uint16_t im=60000; for (auto it : thisCell.theTracks) { - if (quality[it]!= bad && hfit[it].chi2_line+hfit[it].chi2_circle < mc) { + 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; } @@ -314,7 +325,7 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, kernel_VerifyFit<<>>(gpu_.tuples_d, gpu_.helix_fit_results_d, gpu_.quality_d); numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; - kernel_fastDuplicateRemover<<>>(device_theCells_, device_nCells_,gpu_.helix_fit_results_d, gpu_.quality_d); + kernel_fastDuplicateRemover<<>>(device_theCells_, device_nCells_,gpu_.tuples_d,gpu_.helix_fit_results_d, gpu_.quality_d); } From dd6ad51e78f00609b3e2ffad763942e1df61e6ec Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 14 Nov 2018 19:37:17 +0100 Subject: [PATCH 58/94] add region cuts --- .../plugins/CAHitQuadrupletGeneratorGPU.cu | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu index 9335ff801108d..d57493668421c 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cu @@ -187,9 +187,15 @@ void kernel_VerifyFit(TuplesOnGPU::Container const * __restrict__ tuples, 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 < 1000.f); // catch NaN as well - quality[idx] = isNaN ? quality[idx] : pixelTuplesHeterogeneousProduct::loose; - + 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__ @@ -213,7 +219,7 @@ void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() cudaFree(device_theCells_); cudaFree(device_isOuterHitOfCell_); cudaFree(device_nCells_); - cudaFree(device_cellToTuple_); +// cudaFree(device_cellToTuple_); cudaFree(device_cellToTuple_apc_); //product @@ -241,7 +247,7 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() cudaCheck(cudaMemset(device_isOuterHitOfCell_, 0, PixelGPUConstants::maxNumberOfHits * sizeof(GPU::VecArray))); - cudaCheck(cudaMalloc(&device_cellToTuple_, sizeof(CellToTuple))); +// cudaCheck(cudaMalloc(&device_cellToTuple_, sizeof(CellToTuple))); cudaCheck(cudaMalloc(&device_cellToTuple_apc_, sizeof(AtomicPairCounter))); //product From 8cc7562978b11fe10ea0ddb7c599646100025024 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 16 Nov 2018 11:17:56 +0100 Subject: [PATCH 59/94] merged, refactorize --- .../plugins/pixelTuplesHeterogeneousProduct.h | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h b/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h new file mode 100644 index 0000000000000..b8a04581b862d --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h @@ -0,0 +1,61 @@ +#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 { + 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 + From 5ed6161d31ed0af778c70a133a4b0a46c71d6335 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 16 Nov 2018 16:28:52 +0100 Subject: [PATCH 60/94] back to previous status --- .../SiPixelRawToClusterHeterogeneous.cc | 8 + .../PixelTrackFitting/interface/RiemannFit.h | 7 +- .../test/PixelTrackRiemannFit.cc | 393 +++++++++++------- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 5 +- .../CAHitQuadrupletGeneratorKernels.cu | 11 + .../plugins/CAHitQuadrupletGeneratorKernels.h | 2 + 6 files changed, 276 insertions(+), 150 deletions(-) diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc index 384a4732b32e1..532a4359c428b 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc @@ -248,6 +248,14 @@ void SiPixelRawToClusterHeterogeneous::fillDescriptions(edm::ConfigurationDescri desc.add("VCaltoElectronOffset_L1", -414); desc.addUntracked("MissCalibrate", true); desc.add("SplitClusters", false); + desc.add("payloadType", "Offline"); + desc.add("maxNumberOfClusters", -1); + desc.add("ElectronPerADCGain", 135.); + desc.add("Phase2Calibration", false); + desc.add("Phase2ReadoutMode", -1); + desc.add("Phase2DigiBaseline", 1200.); + desc.add("Phase2KinkADC", 8); + desc.add("gpuEnableTransfer", true); desc.add("gpuEnableConversion", true); diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index fb8baa649706c..c497f58741280 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -197,16 +197,17 @@ __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, computeRadLenUniformMaterial(s_values*sqrt(1. + 1./(fast_fit(3)*fast_fit(3))), rad_lengths); MatrixNd scatter_cov_rad = MatrixXd::Zero(n, n); VectorNd sig2(n); - sig2 = .000225 / p_2 * (1. + 0.038 * rad_lengths.array().log()).abs2() * rad_lengths.array(); + 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) { - scatter_cov_rad(k, l) += (rad(k) - rad(i)) * (rad(l) - rad(i)) * sig2(i) / (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 diff --git a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc index 22d4e4960ec48..a405ade872abe 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc +++ b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc @@ -6,7 +6,11 @@ #include #include // unique_ptr +#include +#include + #include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +//#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" using namespace std; using namespace Eigen; @@ -164,161 +168,260 @@ 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 = 1; - 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]; - Eigen::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); - - 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; - } + 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 = 1; - 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)); - } + 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; - 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.; + 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; + for (int i = 0; i < 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; + helixRiemann_fit[i] = Rfit::Helix_fit(gen.hits, gen.hits_cov, B_field, return_err); +// 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 (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; + + } + 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/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 0cbd25d5a006c..848b9d58a3da1 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -271,9 +271,10 @@ void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, kernels.launchKernels(hh, gpu_, cudaStream); - if (doRiemannFit) + 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), diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index d354929869421..58601598a1f92 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -269,3 +269,14 @@ void CAHitQuadrupletGeneratorKernels::buildDoublets(HitsOnCPU const & hh, cudaSt 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_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 71a4c08d0e29c..bea99ec620cc8 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h @@ -23,6 +23,8 @@ class CAHitQuadrupletGeneratorKernels { 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(); From 572613bbdb9afdc2521af8d00d6c24a2cc1e95b7 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 17 Nov 2018 10:55:40 +0100 Subject: [PATCH 61/94] prepare vertex finder to read from gpu --- .../CAHitQuadrupletGeneratorKernels.cu | 2 +- .../src/PixelVertexHeterogeneousProducer.cc | 49 ++++++++++++++----- .../PixelVertexFinding/src/gpuClusterTracks.h | 3 +- .../PixelVertexFinding/src/gpuFitVertices.h | 4 +- .../PixelVertexFinding/src/gpuSortByPt2.h | 3 +- .../PixelVertexFinding/src/gpuSplitVertices.h | 3 +- .../PixelVertexFinding/src/gpuVertexFinder.cu | 23 ++++++--- .../PixelVertexFinding/src/gpuVertexFinder.h | 12 ++++- .../test/gpuVertexFinder_t.cu | 25 +++++----- 9 files changed, 88 insertions(+), 36 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index 58601598a1f92..83d36bed9a165 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -119,7 +119,7 @@ kernel_connect(AtomicPairCounter * apc1, AtomicPairCounter * apc2, // just to z // 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.6f; // FIXME VI "tune" + constexpr auto ptmin = 0.9f; // FIXME original "tune" auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index df61dcb1e3f6b..0510194006dfe 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -22,12 +22,17 @@ #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; + explicit PixelVertexHeterogeneousProducer(const edm::ParameterSet&); ~PixelVertexHeterogeneousProducer() override = default; @@ -46,11 +51,18 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer token_Tracks; - const edm::EDGetTokenT token_BeamSpot; + + + const bool enableConversion_; + const edm::EDGetTokenT gpuToken_; + edm::EDGetTokenT token_Tracks; + edm::EDGetTokenT token_BeamSpot; reco::TrackRefVector m_trks; @@ -72,6 +84,9 @@ 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); + HeterogeneousEDProducer::fillPSetDescription(desc); auto label = "pixelVertexHeterogeneousProducer"; descriptions.add(label, desc); @@ -80,19 +95,21 @@ 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")) + , gpuToken_(consumes(conf.getParameter("src"))) , m_gpuAlgo( conf.getParameter("minT") ,conf.getParameter("eps") ,conf.getParameter("errmax") ,conf.getParameter("chi2max") ) { - // Register my product - produces(); - + if (enableConversion_) { + token_Tracks = consumes(conf.getParameter("TrackCollection")); + token_BeamSpot =consumes(conf.getParameter("beamSpot") ); + // Register my product + produces(); + } } @@ -103,10 +120,20 @@ void PixelVertexHeterogeneousProducer::acquireGPUCuda( cuda::stream_t<> &cudaStream) { // First fish the pixel tracks out of the event + edm::Handle gh; + e.getByToken(gpuToken_, gh); + auto const & gTuples = *gh; + std::cout << "tuples from gpu " << gTuples.nTuples << std::endl; + + tuples_ = gh.product(); + + m_gpuAlgo.produce(cudaStream.id(),(*gh)); + + 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 called " << trackCollName << "\n"; + if (verbose_) std::cout << "PixelVertexHeterogeneousProducer" << ": Found " << tracks.size() << " tracks in TrackCollection" << "\n"; // on gpu beamspot already subtracted at hit level... math::XYZPoint bs(0.,0.,0.); diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h index caef7dd9f6e4d..532b65bb71e05 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h @@ -16,7 +16,7 @@ namespace gpuVertexFinder { // 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 @@ -32,6 +32,7 @@ namespace gpuVertexFinder { auto er2mx = errmax*errmax; auto & __restrict__ data = *pdata; + auto nt = *data.ntrks; float const * __restrict__ zt = data.zt; float const * __restrict__ ezt2 = data.ezt2; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h index c2d233913a147..0f8221cb89b6e 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h @@ -16,15 +16,15 @@ namespace gpuVertexFinder { __global__ - void fitVertices(int nt, + 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; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h index c3e4f9470db74..7cd2d5f5711e9 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h @@ -16,10 +16,11 @@ namespace gpuVertexFinder { __global__ - void sortByPt2(int nt, + void sortByPt2( OnGPU * pdata ) { auto & __restrict__ data = *pdata; + auto nt = *data.ntrks; float const * __restrict__ ptt2 = data.ptt2; uint32_t const & nv = *data.nv; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h index 14e9cf4d8fe84..54b35d319ff91 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -16,7 +16,7 @@ namespace gpuVertexFinder { __global__ - void splitVertices(int nt, + void splitVertices( OnGPU * pdata, float maxChi2 ) { @@ -25,6 +25,7 @@ namespace gpuVertexFinder { 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; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu index a8b7cdcc78f5d..ec3154842974e 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu @@ -7,6 +7,8 @@ namespace gpuVertexFinder { void Producer::allocateOnGPU() { + cudaCheck(cudaMalloc(&onGPU.ntrks, sizeof(uint32_t))); + cudaCheck(cudaMalloc(&onGPU.itrk, OnGPU::MAXTRACKS*sizeof(uint16_t))); cudaCheck(cudaMalloc(&onGPU.zt, OnGPU::MAXTRACKS*sizeof(float))); cudaCheck(cudaMalloc(&onGPU.ezt2, OnGPU::MAXTRACKS*sizeof(float))); cudaCheck(cudaMalloc(&onGPU.ptt2, OnGPU::MAXTRACKS*sizeof(float))); @@ -30,6 +32,8 @@ namespace gpuVertexFinder { } void Producer::deallocateOnGPU() { + cudaCheck(cudaFree(onGPU.ntrks)); + cudaCheck(cudaFree(onGPU.itrk)); cudaCheck(cudaFree(onGPU.zt)); cudaCheck(cudaFree(onGPU.ezt2)); cudaCheck(cudaFree(onGPU.ptt2)); @@ -51,13 +55,20 @@ namespace gpuVertexFinder { } + void Producer::produce(cudaStream_t stream, TuplesOnCPU const & tuples) { + + } + + void Producer::produce(cudaStream_t stream, float const * __restrict__ zt, float const * __restrict__ ezt2, float const * __restrict__ ptt2, uint32_t ntrks ) { - + + cudaCheck(cudaMemcpyAsync(onGPU.ntrks,&ntrks,sizeof(uint32_t), + cudaMemcpyHostToDevice,stream)); cudaCheck(cudaMemcpyAsync(onGPU.zt,zt,ntrks*sizeof(float), cudaMemcpyHostToDevice,stream)); cudaCheck(cudaMemcpyAsync(onGPU.ezt2,ezt2,ntrks*sizeof(float), @@ -66,17 +77,17 @@ namespace gpuVertexFinder { cudaMemcpyHostToDevice,stream)); assert(onGPU_d); - clusterTracks<<<1,1024-256,0,stream>>>(ntrks,onGPU_d,minT,eps,errmax,chi2max); + clusterTracks<<<1,1024-256,0,stream>>>(onGPU_d,minT,eps,errmax,chi2max); cudaCheck(cudaGetLastError()); - fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d,50.); + fitVertices<<<1,1024-256,0,stream>>>(onGPU_d,50.); cudaCheck(cudaGetLastError()); - splitVertices<<<1024,128,0,stream>>>(ntrks,onGPU_d,9.f); + splitVertices<<<1024,128,0,stream>>>(onGPU_d,9.f); cudaCheck(cudaGetLastError()); - fitVertices<<<1,1024-256,0,stream>>>(ntrks,onGPU_d,5000.); + fitVertices<<<1,1024-256,0,stream>>>(onGPU_d,5000.); cudaCheck(cudaGetLastError()); - sortByPt2<<<1,256,0,stream>>>(ntrks,onGPU_d); + sortByPt2<<<1,256,0,stream>>>(onGPU_d); cudaCheck(cudaGetLastError()); cudaCheck(cudaMemcpyAsync(&gpuProduct.nVertices, onGPU.nv, sizeof(uint32_t), diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h index 2b1c580d3ed47..548ed9f24791a 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 @@ -36,7 +39,9 @@ namespace gpuVertexFinder { class Producer { public: - + + using TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; + using GPUProduct = pixelVertexHeterogeneousProduct::GPUProduct; using OnGPU = gpuVertexFinder::OnGPU; @@ -54,6 +59,9 @@ namespace gpuVertexFinder { {} ~Producer() { deallocateOnGPU();} + + void produce(cudaStream_t stream, TuplesOnCPU const & tuples); + void produce(cudaStream_t stream, float const * zt, diff --git a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu index ae7014fb161ae..4e3a598e18538 100644 --- a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu +++ b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu @@ -86,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); @@ -107,6 +108,7 @@ int main() { OnGPU onGPU; + onGPU.ntrks = ntrks_d.get(); onGPU.zt = zt_d.get(); onGPU.ezt2 = ezt2_d.get(); onGPU.ptt2 = ptt2_d.get(); @@ -138,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()); @@ -150,28 +153,28 @@ 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()); @@ -179,7 +182,7 @@ int main() { cuda::launch(fitVertices, { 1,1024-256 }, - ev.ztrack.size(), onGPU_d.get(),50.f + onGPU_d.get(),50.f ); cudaCheck(cudaGetLastError()); @@ -206,7 +209,7 @@ int main() { cuda::launch(fitVertices, { 1,1024-256 }, - ev.ztrack.size(), onGPU_d.get(), 50.f + onGPU_d.get(), 50.f ); cuda::memory::copy(&nv, onGPU.nv, sizeof(uint32_t)); cuda::memory::copy(&nn, onGPU.nn, nv*sizeof(int32_t)); @@ -219,7 +222,7 @@ int main() { cuda::launch(splitVertices, { 1024, 64 }, - ev.ztrack.size(), onGPU_d.get(), + onGPU_d.get(), 9.f ); cuda::memory::copy(&nv, onGPU.nv2, sizeof(uint32_t)); @@ -227,14 +230,14 @@ int main() { cuda::launch(fitVertices, { 1,1024-256 }, - ev.ztrack.size(), onGPU_d.get(),5000.f + onGPU_d.get(),5000.f ); cudaCheck(cudaGetLastError()); cuda::launch(sortByPt2, { 1, 256 }, - ev.ztrack.size(), onGPU_d.get() + onGPU_d.get() ); cuda::memory::copy(&nv, onGPU.nv, sizeof(uint32_t)); From 27976937a49133f9522c36e10b9c10b50882c5d6 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 17 Nov 2018 16:13:32 +0100 Subject: [PATCH 62/94] produce vertices --- .../plugins/PixelTrackProducerFromCUDA.cc | 2 + .../plugins/CAHitQuadrupletGeneratorGPU.cc | 4 +- .../plugins/CAHitQuadrupletGeneratorGPU.h | 3 +- .../plugins/pixelTuplesHeterogeneousProduct.h | 4 + .../pixelVertexHeterogeneousProduct.h | 17 ++-- .../src/PixelVertexHeterogeneousProducer.cc | 81 ++++++++----------- .../PixelVertexFinding/src/gpuVertexFinder.cu | 74 +++++++++++------ .../PixelVertexFinding/src/gpuVertexFinder.h | 29 ++++--- 8 files changed, 116 insertions(+), 98 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index d92209b29ab99..1aa698d78cc5c 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -153,9 +153,11 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, 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]; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 848b9d58a3da1..5d78dada48deb 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -123,6 +123,8 @@ void CAHitQuadrupletGeneratorGPU::fillResults( std::array barrels; std::array phits; + indToEdm.clear(); + indToEdm.resize(numberOfFoundQuadruplets,64000); int nbad=0; // loop over quadruplets @@ -211,7 +213,7 @@ void CAHitQuadrupletGeneratorGPU::fillResults( */ result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); - // quality_[quadId] = pixelTuplesHeterogeneousProduct::loose; + indToEdm[quadId] = result[index].size()-1; } // end loop over quads // #ifdef GPU_DEBUG diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index 97a61a34ce814..1b7190bf50755 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -74,7 +74,7 @@ class CAHitQuadrupletGeneratorGPU { cudaStream_t stream); TuplesOnCPU getOutput() const { - return TuplesOnCPU { hitsOnCPU->gpu_d, tuples_, helix_fit_results_, quality_, gpu_d, nTuples_}; + return TuplesOnCPU { std::move(indToEdm), hitsOnCPU->gpu_d, tuples_, helix_fit_results_, quality_, gpu_d, nTuples_}; } void cleanup(cudaStream_t stream); @@ -180,6 +180,7 @@ class CAHitQuadrupletGeneratorGPU { const float caHardPtCut = 0.f; // 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; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h b/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h index b8a04581b862d..a1c4a26f2fff6 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h @@ -36,6 +36,10 @@ namespace pixelTuplesHeterogeneousProduct { }; struct TuplesOnCPU { + + std::vector indToEdm; // index of tuple in reco tracks.... + + using Container = TuplesOnGPU::Container; siPixelRecHitsHeterogeneousProduct::HitsOnGPU const * hitsOnGPU_d = nullptr; // forwarding 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/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 0510194006dfe..17dfc57d7127c 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -33,6 +33,11 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer token_BeamSpot; - reco::TrackRefVector m_trks; - gpuVertexFinder::Producer m_gpuAlgo; bool verbose_ = false; @@ -104,6 +107,7 @@ PixelVertexHeterogeneousProducer::PixelVertexHeterogeneousProducer(const edm::Pa ,conf.getParameter("chi2max") ) { + produces(); if (enableConversion_) { token_Tracks = consumes(conf.getParameter("TrackCollection")); token_BeamSpot =consumes(conf.getParameter("beamSpot") ); @@ -127,45 +131,8 @@ void PixelVertexHeterogeneousProducer::acquireGPUCuda( tuples_ = gh.product(); - m_gpuAlgo.produce(cudaStream.id(),(*gh)); - - - 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"; - - // on gpu beamspot already subtracted at hit level... - math::XYZPoint bs(0.,0.,0.); - edm::Handle bsHandle; - e.getByToken(token_BeamSpot,bsHandle); + m_gpuAlgo.produce(cudaStream.id(),gTuples,m_ptMin); - if (bsHandle.isValid()) bs = math::XYZPoint(bsHandle->x0(),bsHandle->y0(), bsHandle->z0() ); - - // 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()); - auto nok=0; - 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); @@ -184,7 +161,7 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( 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 { @@ -193,7 +170,9 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } // fill legacy data format - if (verbose_) std::cout << "found " << gpuProduct.nVertices << " vertices on GPU" << std::endl; + 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.... @@ -209,15 +188,19 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( err(2,2) = 1.f/gpuProduct.zerr[i]; err(2,2) *= 2.; // artifically inflate error //Copy also the tracks (no intention to be efficient....) - for (auto k=0U; ksize() << " vertexes\n"; for (unsigned int i=0; isize(); ++i) { @@ -239,6 +223,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; } + + std::cout << ": Found " << vertexes->size() << " vertexes" << std::endl; } @@ -279,7 +265,6 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } e.put(std::move(vertexes)); - m_trks.clear(); } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu index ec3154842974e..339d69ee18e92 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu @@ -8,6 +8,7 @@ namespace gpuVertexFinder { void Producer::allocateOnGPU() { cudaCheck(cudaMalloc(&onGPU.ntrks, sizeof(uint32_t))); + cudaCheck(cudaMemset(onGPU.ntrks, 0, sizeof(uint32_t))); cudaCheck(cudaMalloc(&onGPU.itrk, OnGPU::MAXTRACKS*sizeof(uint16_t))); cudaCheck(cudaMalloc(&onGPU.zt, OnGPU::MAXTRACKS*sizeof(float))); cudaCheck(cudaMalloc(&onGPU.ezt2, OnGPU::MAXTRACKS*sizeof(float))); @@ -54,29 +55,47 @@ namespace gpuVertexFinder { } - - void Producer::produce(cudaStream_t stream, TuplesOnCPU const & tuples) { - + + __global__ + void loadTracks(pixelTuplesHeterogeneousProduct::TuplesOnGPU const * tracks, + OnGPU * pdata, + float ptMin + ){ + + auto const & tuples = *tracks->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)>>(tracks.gpu_d,onGPU_d, ptMin); + cudaCheck(cudaGetLastError()); + clusterTracks<<<1,1024-256,0,stream>>>(onGPU_d,minT,eps,errmax,chi2max); cudaCheck(cudaGetLastError()); fitVertices<<<1,1024-256,0,stream>>>(onGPU_d,50.); @@ -92,15 +111,20 @@ namespace gpuVertexFinder { 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)); + 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) { // 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 548ed9f24791a..44484e8d63430 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h @@ -37,12 +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 TuplesOnCPU = pixelTuplesHeterogeneousProduct::TuplesOnCPU; - using GPUProduct = pixelVertexHeterogeneousProduct::GPUProduct; + using OnCPU = gpuVertexFinder::OnCPU; using OnGPU = gpuVertexFinder::OnGPU; @@ -60,24 +73,16 @@ namespace gpuVertexFinder { ~Producer() { deallocateOnGPU();} - void produce(cudaStream_t stream, TuplesOnCPU const & tuples); + void produce(cudaStream_t stream, TuplesOnCPU const & tuples, float ptMin); - - void produce(cudaStream_t stream, - float const * zt, - float const * ezt2, - float const * ptt2, - uint32_t ntrks - ); - - GPUProduct const & fillResults(cudaStream_t stream); + OnCPU const & fillResults(cudaStream_t stream); void allocateOnGPU(); void deallocateOnGPU(); private: - GPUProduct gpuProduct; + OnCPU gpuProduct; OnGPU onGPU; OnGPU * onGPU_d=nullptr; From aa0e4ad101d9ed7023d354e614eba67c94e04b0e Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 17 Nov 2018 17:31:50 +0100 Subject: [PATCH 63/94] maka vertices on gpu only: not scheduled.. --- .../python/customizePixelTracksForProfiling.py | 7 +++---- .../src/PixelVertexHeterogeneousProducer.cc | 11 ++++++----- .../PixelVertexFinding/src/gpuVertexFinder.cu | 10 +++++++--- .../PixelVertexFinding/src/gpuVertexFinder.h | 7 +++++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py index 5a22ac63f7f02..865465b7628b4 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), ) @@ -22,15 +23,12 @@ def customizePixelTracksForProfilingDisableConversion(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 @@ -41,5 +39,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/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 17dfc57d7127c..b259d8f7c7af2 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -65,6 +65,7 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer gpuToken_; edm::EDGetTokenT token_Tracks; edm::EDGetTokenT token_BeamSpot; @@ -89,6 +90,7 @@ void PixelVertexHeterogeneousProducer::fillDescriptions(edm::ConfigurationDescri 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"; @@ -100,11 +102,13 @@ PixelVertexHeterogeneousProducer::PixelVertexHeterogeneousProducer(const edm::Pa HeterogeneousEDProducer(conf) , 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_ ) { produces(); @@ -127,7 +131,7 @@ void PixelVertexHeterogeneousProducer::acquireGPUCuda( edm::Handle gh; e.getByToken(gpuToken_, gh); auto const & gTuples = *gh; - std::cout << "tuples from gpu " << gTuples.nTuples << std::endl; + std::cout << "Vertex Producers: tuples from gpu " << gTuples.nTuples << std::endl; tuples_ = gh.product(); @@ -223,11 +227,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; } - - std::cout << ": Found " << vertexes->size() << " vertexes" << std::endl; - } - + std::cout << ": Found " << vertexes->size() << " vertexes" << std::endl; if(vertexes->empty() && bsHandle.isValid()){ diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu index 339d69ee18e92..21c2d4cebcbfe 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.cu @@ -109,14 +109,18 @@ namespace gpuVertexFinder { sortByPt2<<<1,256,0,stream>>>(onGPU_d); cudaCheck(cudaGetLastError()); - cudaCheck(cudaMemcpyAsync(&gpuProduct.nVertices, onGPU.nv, sizeof(uint32_t), - cudaMemcpyDeviceToHost, stream)); - cudaCheck(cudaMemcpyAsync(&gpuProduct.nTracks, onGPU.ntrks, sizeof(uint32_t), + if(enableTransfer) { + cudaCheck(cudaMemcpyAsync(&gpuProduct.nVertices, onGPU.nv, sizeof(uint32_t), + cudaMemcpyDeviceToHost, stream)); + cudaCheck(cudaMemcpyAsync(&gpuProduct.nTracks, onGPU.ntrks, sizeof(uint32_t), cudaMemcpyDeviceToHost, 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, diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h index 44484e8d63430..08eea1d0373de 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuVertexFinder.h @@ -63,12 +63,14 @@ 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();} @@ -90,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; }; From 989019e8e1677b7af2ed151306b15932e85a8f2f Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 17 Nov 2018 18:06:35 +0100 Subject: [PATCH 64/94] make profiling working --- .../python/customizePixelTracksForProfiling.py | 2 +- .../PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc | 6 ++++-- .../src/PixelVertexHeterogeneousProducer.cc | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py index 865465b7628b4..bdf49ed791d08 100644 --- a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py +++ b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py @@ -5,7 +5,7 @@ def customizePixelTracksForProfiling(process): process.out = cms.OutputModule("AsciiOutputModule", outputCommands = cms.untracked.vstring( - "keep *_pixelTracks_*_*", +# "keep *_pixelTracks_*_*", "keep *_pixelVertices_*_*", ), verbosity = cms.untracked.uint32(0), diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index 1aa698d78cc5c..ac20a699b8f83 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -85,7 +85,9 @@ PixelTrackProducerFromCUDA::PixelTrackProducerFromCUDA(const edm::ParameterSet& produces(); produces(); } - produces(); // dummy + else { + produces(); // dummy + } // produces(); } @@ -118,7 +120,7 @@ void PixelTrackProducerFromCUDA::acquireGPUCuda(const edm::HeterogeneousEvent & void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) { - iEvent.put(std::make_unique(0)); + // iEvent.put(std::make_unique(0)); if (!enableConversion_) return; // std::cout << "Converting gpu helix in reco tracks" << std::endl; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index b259d8f7c7af2..0e622f067d7fb 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -117,6 +117,8 @@ PixelVertexHeterogeneousProducer::PixelVertexHeterogeneousProducer(const edm::Pa token_BeamSpot =consumes(conf.getParameter("beamSpot") ); // Register my product produces(); + } else { + produces(); // dummy } } From db66a1444bba11af2a8cac8dba22120c1c32d1c0 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sat, 17 Nov 2018 19:52:58 +0100 Subject: [PATCH 65/94] minor cleanup --- RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index c497f58741280..1a6bf4bc08719 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -146,8 +146,8 @@ __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, 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); } + tmp(l + n, k + n) = tmp(k + n, l + n); } } // We are interested only in the errors orthogonal to the rotated s-axis @@ -373,7 +373,9 @@ __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; From 79fd0ae51a02a34604851ca0cf56ddc0999dbd66 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 18 Nov 2018 11:32:31 +0100 Subject: [PATCH 66/94] silenced --- .../plugins/PixelTrackProducerFromCUDA.cc | 9 ++++----- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc | 8 ++++---- .../src/PixelVertexHeterogeneousProducer.cc | 3 +-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index ac20a699b8f83..dcf51eac2067e 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -108,9 +108,8 @@ void PixelTrackProducerFromCUDA::acquireGPUCuda(const edm::HeterogeneousEvent & edm::Handle gh; iEvent.getByToken(gpuToken_, gh); - auto const & gTuples = *gh; - std::cout << "tuples from gpu " << gTuples.nTuples << std::endl; - + //auto const & gTuples = *gh; + // std::cout << "tuples from gpu " << gTuples.nTuples << std::endl; tuples_ = gh.product(); @@ -140,7 +139,7 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, 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; + // std::cout << "reading hitset " << e-b << std::endl; // const auto & region = hitSet.region(); // std::cout << "origin " << region.origin() << std::endl; @@ -200,7 +199,7 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, ++nh; } assert(nh==e-b); - std::cout << "processed " << nh << " good tuples " << tracks.size() << std::endl; + // std::cout << "processed " << nh << " good tuples " << tracks.size() << std::endl; // store tracks storeTracks(iEvent, tracks, *httopo); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 5d78dada48deb..fd829b26073ec 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -216,9 +216,9 @@ void CAHitQuadrupletGeneratorGPU::fillResults( indToEdm[quadId] = result[index].size()-1; } // end loop over quads -// #ifdef GPU_DEBUG +#ifdef GPU_DEBUG std::cout << "Q Final quads " << result[index].size() << ' ' << nbad << std::endl; -// #endif +#endif } @@ -327,12 +327,12 @@ CAHitQuadrupletGeneratorGPU::fetchKernelResult(int) quadsInterface.back()[3] = tuples.begin(i)[3]; // [sz-1]; } -//#ifdef GPU_DEBUG +#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())<<'/'< gh; e.getByToken(gpuToken_, gh); auto const & gTuples = *gh; - std::cout << "Vertex Producers: tuples from gpu " << gTuples.nTuples << std::endl; + // std::cout << "Vertex Producers: tuples from gpu " << gTuples.nTuples << std::endl; tuples_ = gh.product(); @@ -230,7 +230,6 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( << " chi2 " << (*vertexes)[i].normalizedChi2() << std::endl; } } - std::cout << ": Found " << vertexes->size() << " vertexes" << std::endl; if(vertexes->empty() && bsHandle.isValid()){ From 68f9162d0fdd5c48286fecad072463d93b051f5e Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 18 Nov 2018 12:11:28 +0100 Subject: [PATCH 67/94] solve conflict --- .../plugins/SiPixelRawToClusterHeterogeneous.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc index 532a4359c428b..b42932c381b0f 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterHeterogeneous.cc @@ -248,15 +248,13 @@ void SiPixelRawToClusterHeterogeneous::fillDescriptions(edm::ConfigurationDescri desc.add("VCaltoElectronOffset_L1", -414); desc.addUntracked("MissCalibrate", true); desc.add("SplitClusters", false); - desc.add("payloadType", "Offline"); - desc.add("maxNumberOfClusters", -1); desc.add("ElectronPerADCGain", 135.); + // Phase 2 clusterizer desc.add("Phase2Calibration", false); desc.add("Phase2ReadoutMode", -1); desc.add("Phase2DigiBaseline", 1200.); desc.add("Phase2KinkADC", 8); - desc.add("gpuEnableTransfer", true); desc.add("gpuEnableConversion", true); From ddad07630e4f6e7b7afffaeb98504a5b64bffedb Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Sun, 18 Nov 2018 14:54:01 +0100 Subject: [PATCH 68/94] resize to avoid overflows --- RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index 4c6b3b8c17723..ada09f40b60b2 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -193,11 +193,11 @@ class GPUCACell { #endif // __CUDACC__ - GPU::VecArray< uint32_t, 40> theOuterNeighbors; - GPU::VecArray< uint16_t, 20> theTracks; + GPU::VecArray< uint32_t, 36> theOuterNeighbors; + GPU::VecArray< uint16_t, 32> theTracks; - int theDoubletId; - int theLayerPairId; + int32_t theDoubletId; + int32_t theLayerPairId; private: float theInnerZ; From a6e3e7dbabd5169320b1f046f6b5d4de6411b717 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 19 Nov 2018 10:07:11 +0100 Subject: [PATCH 69/94] protect and report cell overflow as well --- HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h | 3 ++- .../plugins/CAHitQuadrupletGeneratorKernels.cu | 6 +++++- RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h b/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h index 47592fd2063d6..3df41bb233b1a 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h +++ b/HeterogeneousCore/CUDAUtilities/interface/GPUSimpleVector.h @@ -81,7 +81,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/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index 83d36bed9a165..8a708f9e5c749 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -11,7 +11,8 @@ #include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" #include "GPUCACell.h" #include "gpuPixelDoublets.h" -#include"gpuFishbone.h" +#include "gpuFishbone.h" +#include "CAConstants.h" using namespace gpuPixelDoublets; @@ -46,6 +47,9 @@ void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, AtomicPairCou } #endif + if (0==idx) { + if (*nCells>=CAConstants::maxNumberOfDoublets()) printf("Cells overflow\n"); + } if (idx < (*nCells) ) { auto &thisCell = cells[idx]; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h index 2c65a89afbfdb..76cbc065d360d 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; // not really relevant + constexpr uint32_t MaxNumOfDoublets = CAConstants::maxNumberOfDoublets(); // not really relevant template __device__ @@ -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); From 0935c5e489fda3c8c2c68a83cb37e8eb26a9dcc2 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 19 Nov 2018 14:47:53 +0100 Subject: [PATCH 70/94] more cleanup --- .../CAHitQuadrupletGeneratorKernels.cu | 11 +++-- .../PixelTriplets/plugins/GPUCACell.h | 13 ------ .../plugins/GPUHitsAndDoublets.h | 44 ------------------- 3 files changed, 5 insertions(+), 63 deletions(-) delete mode 100644 RecoPixelVertexing/PixelTriplets/plugins/GPUHitsAndDoublets.h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index 8a708f9e5c749..caddcd73d09e0 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -199,15 +199,14 @@ void kernel_VerifyFit(TuplesOnGPU::Container const * __restrict__ tuples, } __global__ -void kernel_print_found_ntuplets(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) ); - } } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h index ada09f40b60b2..7e3ca7ff0ecec 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -15,19 +15,6 @@ #include "RecoPixelVertexing/PixelTriplets/plugins/pixelTuplesHeterogeneousProduct.h" - -struct Quadruplet { - static constexpr uint32_t capacity() { return 6;} - using hindex_type = siPixelRecHitsHeterogeneousProduct::hindex_type; - static constexpr auto invalid() { return std::numeric_limits::max();} - hindex_type hitId[6]; - uint32_t size() const { - for (auto i=capacity()-1; i>0; --i) if (hitId[i]!=invalid()) return i+1; - return 0; - } -}; - - class GPUCACell { public: 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 From e695b29db8fcf252f09eecf1697ddc5331da64c5 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 19 Nov 2018 16:21:43 +0100 Subject: [PATCH 71/94] remove all cpu stuff from CA on gpu --- .../python/PixelTracks_cff.py | 5 + .../CAHitNtupletHeterogeneousEDProducer.cc | 42 +++---- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 112 +----------------- .../plugins/CAHitQuadrupletGeneratorGPU.h | 84 +------------ .../python/caHitQuadrupletEDProducer_cfi.py | 6 +- 5 files changed, 32 insertions(+), 217 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py index 1d6dea73402be..65050758d219e 100644 --- a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py +++ b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py @@ -46,13 +46,18 @@ trackingRegions = "pixelTracksTrackingRegions" ) + pixelTracksHitQuadruplets = _initialStepCAHitQuadruplets.clone( 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 pixelTracksHitTriplets = _pixelTripletHLTEDProducer.clone( doublets = "pixelTracksHitDoublets", diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc index 680da249cac73..da38f7ba220b8 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletHeterogeneousEDProducer.cc @@ -74,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()), @@ -84,17 +82,19 @@ CAHitNtupletHeterogeneousEDProducer::CAHitNtupletHeterogeneousEDProducer( enableTransfer_(enableConversion_ || iConfig.getParameter("gpuEnableTransfer")) { produces(); - if(enableConversion_) 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); @@ -113,34 +113,18 @@ 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(region, gHits, iSetup, doRiemannFit_, enableTransfer_, cudaStream.id()); + GPUGenerator_.hitNtuplets(gHits, iSetup, doRiemannFit_, enableTransfer_, cudaStream.id()); @@ -150,11 +134,21 @@ void CAHitNtupletHeterogeneousEDProducer::produceGPUCuda( edm::HeterogeneousEvent &iEvent, const edm::EventSetup &iSetup, cuda::stream_t<> &cudaStream) { - 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; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index fd829b26073ec..8a03e93d1387d 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -28,51 +28,20 @@ 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) : 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); } void CAHitQuadrupletGeneratorGPU::initEvent(edm::Event const& ev, edm::EventSetup const& es) { - if (theComparitor) - theComparitor->init(ev, es); fitter.setBField(1 / PixelRecoUtilities::fieldInInvGev(es)); } @@ -82,7 +51,6 @@ CAHitQuadrupletGeneratorGPU::~CAHitQuadrupletGeneratorGPU() { } void CAHitQuadrupletGeneratorGPU::hitNtuplets( - TrackingRegion const& region, HitsOnCPU const& hh, edm::EventSetup const& es, bool doRiemannFit, @@ -90,8 +58,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,15 +76,6 @@ 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; @@ -151,67 +109,6 @@ void CAHitQuadrupletGeneratorGPU::fillResults( if (bad) { nbad++; quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; continue;} if (quality_[quadId] != pixelTuplesHeterogeneousProduct::loose) continue; // FIXME remove dup - /* - // this part shall not be run anymore... - quality_[quadId] = pixelTuplesHeterogeneousProduct::bad; - - // TODO: - // - if we decide to always do the circle fit for 4 hits, we don't - // need ThirdHitPredictionFromCircle for the curvature; then we - // could remove extraHitRPhitolerance configuration parameter - ThirdHitPredictionFromCircle predictionRPhi(gps[0], gps[2], - extraHitRPhitolerance); - const float curvature = predictionRPhi.curvature( - ThirdHitPredictionFromCircle::Vector2D(gps[1].x(), gps[1].y())); - const float abscurv = std::abs(curvature); - const float thisMaxChi2 = maxChi2Eval.value(abscurv); - if (theComparitor) { - SeedingHitSet tmpTriplet(phits[0], phits[1], phits[3]); - if (!theComparitor->compatible(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; - } - - */ - result[index].emplace_back(phits[0], phits[1], phits[2], phits[3]); indToEdm[quadId] = result[index].size()-1; } // end loop over quads @@ -263,14 +160,11 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() } -void CAHitQuadrupletGeneratorGPU::launchKernels(const TrackingRegion ®ion, - int regionIndex, HitsOnCPU const & hh, +void CAHitQuadrupletGeneratorGPU::launchKernels(HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t cudaStream) { - assert(0==regionIndex); - kernels.launchKernels(hh, gpu_, cudaStream); if (doRiemannFit) { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index 1b7190bf50755..df6e1c26970d4 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -67,7 +67,7 @@ class CAHitQuadrupletGeneratorGPU { void buildDoublets(HitsOnCPU const & hh, cudaStream_t stream); - void hitNtuplets(const TrackingRegion ®ion, HitsOnCPU const & hh, + void hitNtuplets(HitsOnCPU const & hh, const edm::EventSetup& es, bool doRiemannFit, bool transferToCPU, @@ -87,78 +87,7 @@ class CAHitQuadrupletGeneratorGPU { private: - // cpu stuff - - 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_; - }; - - // end cpu stuff - - - void launchKernels(const TrackingRegion &, int, HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t); + void launchKernels(HitsOnCPU const & hh, bool doRiemannFit, bool transferToCPU, cudaStream_t); std::vector> fetchKernelResult(int); @@ -167,14 +96,7 @@ class CAHitQuadrupletGeneratorGPU { CAHitQuadrupletGeneratorKernels kernels; RiemannFitOnGPU fitter; - - const float extraHitRPhitolerance; - - const QuantityDependsPt maxChi2; - const bool fitFastCircle; - const bool fitFastCircleChi2Cut; - const bool useBendingCorrection; - + // not really used at the moment const float caThetaCut = 0.00125f; const float caPhiCut = 0.1f; const float caHardPtCut = 0.f; diff --git a/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py index 8497eba9f759f..e8d09a7ec2e4d 100644 --- a/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py +++ b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py @@ -3,6 +3,6 @@ caHitQuadrupletEDProducer = _caHitQuadrupletDefaultEDProducer.clone() -from Configuration.ProcessModifiers.gpu_cff import gpu -from RecoPixelVertexing.PixelTriplets.caHitQuadrupletHeterogeneousEDProducer_cfi import caHitQuadrupletHeterogeneousEDProducer as _caHitQuadrupletHeterogeneousEDProducer -gpu.toReplaceWith(caHitQuadrupletEDProducer, _caHitQuadrupletHeterogeneousEDProducer) +#from Configuration.ProcessModifiers.gpu_cff import gpu +#from RecoPixelVertexing.PixelTriplets.caHitQuadrupletHeterogeneousEDProducer_cfi import caHitQuadrupletHeterogeneousEDProducer as _caHitQuadrupletHeterogeneousEDProducer +#gpu.toReplaceWith(caHitQuadrupletEDProducer, _caHitQuadrupletHeterogeneousEDProducer) From b2445f275012c6b262cbd67420e91df7d3f99dd9 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 19 Nov 2018 17:18:46 +0100 Subject: [PATCH 72/94] fix gpu only wf --- .../Configuration/python/customizePixelTracksForProfiling.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py index bdf49ed791d08..671daa5d89952 100644 --- a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py +++ b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py @@ -5,7 +5,6 @@ def customizePixelTracksForProfiling(process): process.out = cms.OutputModule("AsciiOutputModule", outputCommands = cms.untracked.vstring( -# "keep *_pixelTracks_*_*", "keep *_pixelVertices_*_*", ), verbosity = cms.untracked.uint32(0), @@ -20,9 +19,6 @@ 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")) - # Disable conversions to legacy process.siPixelClustersPreSplitting.gpuEnableConversion = False process.siPixelRecHitsPreSplitting.gpuEnableConversion = False From fb73c7a999687a6892da32ff46c845452051d26c Mon Sep 17 00:00:00 2001 From: Andrea Bocci Date: Wed, 28 Nov 2018 18:41:33 +0100 Subject: [PATCH 73/94] Address code style and quality issues (#203) --- .../CUDAServices/src/CachingHostAllocator.h | 14 +++++++------- .../Producer/interface/HeterogeneousEDProducer.h | 8 ++++---- .../src/SiPixelFedCablingMapGPUWrapper.cc | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/HeterogeneousCore/CUDAServices/src/CachingHostAllocator.h b/HeterogeneousCore/CUDAServices/src/CachingHostAllocator.h index 97be93c79131a..60cdf8bbbb617 100644 --- a/HeterogeneousCore/CUDAServices/src/CachingHostAllocator.h +++ b/HeterogeneousCore/CUDAServices/src/CachingHostAllocator.h @@ -142,18 +142,18 @@ struct CachingHostAllocator bytes(0), bin(INVALID_BIN), device(INVALID_DEVICE_ORDINAL), - associated_stream(0), - ready_event(0) + associated_stream(nullptr), + ready_event(nullptr) {} // Constructor (suitable for searching maps for a range of suitable blocks) BlockDescriptor() : - d_ptr(NULL), + d_ptr(nullptr), bytes(0), bin(INVALID_BIN), device(INVALID_DEVICE_ORDINAL), - associated_stream(0), - ready_event(0) + associated_stream(nullptr), + ready_event(nullptr) {} // Comparison functor for comparing host pointers @@ -348,9 +348,9 @@ struct CachingHostAllocator cudaError_t HostAllocate( void **d_ptr, ///< [out] Reference to pointer to the allocation size_t bytes, ///< [in] Minimum number of bytes for the allocation - cudaStream_t active_stream = 0) ///< [in] The stream to be associated with this allocation + cudaStream_t active_stream = nullptr) ///< [in] The stream to be associated with this allocation { - *d_ptr = NULL; + *d_ptr = nullptr; int device = INVALID_DEVICE_ORDINAL; cudaError_t error = cudaSuccess; diff --git a/HeterogeneousCore/Producer/interface/HeterogeneousEDProducer.h b/HeterogeneousCore/Producer/interface/HeterogeneousEDProducer.h index 4f06571e8a70d..4da113b4f15cd 100644 --- a/HeterogeneousCore/Producer/interface/HeterogeneousEDProducer.h +++ b/HeterogeneousCore/Producer/interface/HeterogeneousEDProducer.h @@ -172,7 +172,7 @@ class HeterogeneousEDProducer: public Devices, public edm::stream::EDProducer("heterogeneousEnabled_")) {} - ~HeterogeneousEDProducer() = default; + ~HeterogeneousEDProducer() override = default; protected: edm::EDGetTokenT consumesHeterogeneous(const edm::InputTag& tag) { @@ -187,11 +187,11 @@ class HeterogeneousEDProducer: public Devices, public edm::stream::EDProducer products; @@ -213,7 +213,7 @@ class HeterogeneousEDProducer: public Devices, public edm::stream::EDProducerfindItem(path); if (pixelRoc != nullptr) { - modToUnpHost[index] = (modules.size() != 0) && (modules.find(pixelRoc->rawId()) == modules.end()); + modToUnpHost[index] = (not modules.empty()) and (modules.find(pixelRoc->rawId()) == modules.end()); } else { // store some dummy number modToUnpHost[index] = true; } From 6110cf4d5a7180f627efd4ded40edceb57f3e65f Mon Sep 17 00:00:00 2001 From: Matti Kortelainen Date: Thu, 29 Nov 2018 02:17:50 -0600 Subject: [PATCH 74/94] Fix MTV validation of initialStepPreSplitting tracks and add B-hadron MTV variation to pixel track validation sequence (#199) - add B-hadron MTV variation to pixel track validation sequence - fix MTV validation of initialStepPreSplitting tracks --- DataFormats/Provenance/src/classes.h | 1 + DataFormats/Provenance/src/classes_def.xml | 1 + .../TrackerHitAssociation/BuildFile.xml | 1 + .../interface/ClusterTPAssociation.h | 10 + .../src/ClusterTPAssociation.cc | 15 + .../RecoTrack/plugins/MultiTrackValidator.cc | 14 + .../python/PostProcessorTracker_cfi.py | 4 +- .../RecoTrack/python/TrackValidation_cff.py | 276 ++++++++++-------- Validation/RecoTrack/python/plotting/html.py | 4 + .../python/plotting/trackingPlots.py | 6 +- .../RecoVertex/python/VertexValidation_cff.py | 8 +- 11 files changed, 210 insertions(+), 130 deletions(-) diff --git a/DataFormats/Provenance/src/classes.h b/DataFormats/Provenance/src/classes.h index e87580521fddc..6630f6f044166 100644 --- a/DataFormats/Provenance/src/classes.h +++ b/DataFormats/Provenance/src/classes.h @@ -35,6 +35,7 @@ #include "DataFormats/Provenance/interface/ESRecordAuxiliary.h" #include "DataFormats/Provenance/interface/ViewTypeChecker.h" #include "FWCore/Utilities/interface/typedefs.h" +#include "FWCore/Utilities/interface/VecArray.h" #include #include #include diff --git a/DataFormats/Provenance/src/classes_def.xml b/DataFormats/Provenance/src/classes_def.xml index 500022fac5cb1..2a521b82b6b2c 100644 --- a/DataFormats/Provenance/src/classes_def.xml +++ b/DataFormats/Provenance/src/classes_def.xml @@ -61,6 +61,7 @@ + diff --git a/SimTracker/TrackerHitAssociation/BuildFile.xml b/SimTracker/TrackerHitAssociation/BuildFile.xml index b63d354eec25b..48e91f5cbee07 100644 --- a/SimTracker/TrackerHitAssociation/BuildFile.xml +++ b/SimTracker/TrackerHitAssociation/BuildFile.xml @@ -1,5 +1,6 @@ + diff --git a/SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h b/SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h index 41bfb42bead5e..e4fcde1f03f70 100644 --- a/SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h +++ b/SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h @@ -4,6 +4,7 @@ #include "DataFormats/Provenance/interface/ProductID.h" #include "DataFormats/Common/interface/HandleBase.h" #include "DataFormats/TrackerRecHit2D/interface/OmniClusterRef.h" +#include "FWCore/Utilities/interface/VecArray.h" #include "SimDataFormats/TrackingAnalysis/interface/TrackingParticle.h" #include "SimDataFormats/TrackingAnalysis/interface/TrackingParticleFwd.h" @@ -32,6 +33,10 @@ class ClusterTPAssociation { void emplace_back(const OmniClusterRef& cluster, const TrackingParticleRef& tp) { checkMappedProductID(tp); + auto foundKeyID = std::find(std::begin(keyProductIDs_), std::end(keyProductIDs_), cluster.id()); + if(foundKeyID == std::end(keyProductIDs_)) { + keyProductIDs_.emplace_back(cluster.id()); + } map_.emplace_back(cluster, tp); } void sortAndUnique() { @@ -54,11 +59,15 @@ class ClusterTPAssociation { const_iterator cend() const { return map_.end(); } range equal_range(const OmniClusterRef& key) const { + checkKeyProductID(key); return std::equal_range(map_.begin(), map_.end(), value_type(key, TrackingParticleRef()), compare); } const map_type& map() const { return map_; } + void checkKeyProductID(const OmniClusterRef& key) const { checkKeyProductID(key.id()); } + void checkKeyProductID(const edm::ProductID& id) const; + void checkMappedProductID(const edm::HandleBase& mappedHandle) const { checkMappedProductID(mappedHandle.id()); } void checkMappedProductID(const TrackingParticleRef& tp) const { checkMappedProductID(tp.id()); } void checkMappedProductID(const edm::ProductID& id) const; @@ -75,6 +84,7 @@ class ClusterTPAssociation { } map_type map_; + edm::VecArray keyProductIDs_; edm::ProductID mappedProductId_; }; diff --git a/SimTracker/TrackerHitAssociation/src/ClusterTPAssociation.cc b/SimTracker/TrackerHitAssociation/src/ClusterTPAssociation.cc index fd66cb18c2cc4..9ee06286e9dd7 100644 --- a/SimTracker/TrackerHitAssociation/src/ClusterTPAssociation.cc +++ b/SimTracker/TrackerHitAssociation/src/ClusterTPAssociation.cc @@ -1,6 +1,21 @@ #include "SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h" #include "FWCore/Utilities/interface/Exception.h" +void ClusterTPAssociation::checkKeyProductID(const edm::ProductID& id) const { + if(std::find(std::begin(keyProductIDs_), std::end(keyProductIDs_), id) == std::end(keyProductIDs_)) { + auto e = cms::Exception("InvalidReference"); + e << "ClusterTPAssociation has OmniClusterRefs with ProductIDs "; + for(size_t i=0; i("doMVAPlots")), simPVMaxZ_(pset.getUntrackedParameter("simPVMaxZ")) { + if(label.empty()) { + // Disable prefetching of everything if there are no track collections + return; + } + const edm::InputTag& label_tp_effic_tag = pset.getParameter< edm::InputTag >("label_tp_effic"); const edm::InputTag& label_tp_fake_tag = pset.getParameter< edm::InputTag >("label_tp_fake"); @@ -211,6 +216,10 @@ MultiTrackValidator::~MultiTrackValidator() {} void MultiTrackValidator::bookHistograms(DQMStore::ConcurrentBooker& ibook, edm::Run const&, edm::EventSetup const& setup, Histograms& histograms) const { + if(label.empty()) { + // Disable histogram booking if there are no track collections + return; + } const auto minColl = -0.5; const auto maxColl = label.size()-0.5; @@ -481,6 +490,11 @@ void MultiTrackValidator::trackDR(const edm::View& trackCollection, void MultiTrackValidator::dqmAnalyze(const edm::Event& event, const edm::EventSetup& setup, const Histograms& histograms) const { + if(label.empty()) { + // Disable if there are no track collections + return; + } + using namespace reco; LogDebug("TrackValidator") << "\n====================================================" << "\n" diff --git a/Validation/RecoTrack/python/PostProcessorTracker_cfi.py b/Validation/RecoTrack/python/PostProcessorTracker_cfi.py index a926b19d4321a..6b5a19f799035 100644 --- a/Validation/RecoTrack/python/PostProcessorTracker_cfi.py +++ b/Validation/RecoTrack/python/PostProcessorTracker_cfi.py @@ -270,9 +270,9 @@ def _addNoFlow(module): ) postProcessorTrackTrackingOnly = postProcessorTrack.clone() -postProcessorTrackTrackingOnly.subDirs.extend(["Tracking/TrackSeeding/*", "Tracking/PixelTrack/*", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*"]) +postProcessorTrackTrackingOnly.subDirs.extend(["Tracking/TrackSeeding/*", "Tracking/PixelTrack/*", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*", "Tracking/PixelTrackBHadron/*"]) postProcessorTrackSummaryTrackingOnly = postProcessorTrackSummary.clone() -postProcessorTrackSummaryTrackingOnly.subDirs.extend(["Tracking/TrackSeeding", "Tracking/PixelTrack", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*"]) +postProcessorTrackSummaryTrackingOnly.subDirs.extend(["Tracking/TrackSeeding", "Tracking/PixelTrack", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*", "Tracking/PixelTrackBHadron/*"]) postProcessorTrackSequenceTrackingOnly = cms.Sequence( postProcessorTrackTrackingOnly+ diff --git a/Validation/RecoTrack/python/TrackValidation_cff.py b/Validation/RecoTrack/python/TrackValidation_cff.py index a90d22e8518cf..bc84c87cf191f 100644 --- a/Validation/RecoTrack/python/TrackValidation_cff.py +++ b/Validation/RecoTrack/python/TrackValidation_cff.py @@ -33,8 +33,10 @@ _trackProd = [] locals()["_algos"+_postfix] = ["generalTracks"] + _cfg.iterationAlgos(_postfix) + ["duplicateMerge"] - locals()["_seedProducers"+_postfix] = _seedProd + _cfg.seedProducers(_postfix) - locals()["_trackProducers"+_postfix] = _trackProd + _cfg.trackProducers(_postfix) + locals()["_seedProducersPreSplitting"+_postfix] = _seedProd + locals()["_trackProducersPreSplitting"+_postfix] = _trackProd + locals()["_seedProducers"+_postfix] = _cfg.seedProducers(_postfix) + locals()["_trackProducers"+_postfix] = _cfg.trackProducers(_postfix) if _eraName != "trackingPhase2PU140": locals()["_electronSeedProducers"+_postfix] = ["tripletElectronSeeds", "pixelPairElectronSeeds", "stripPairElectronSeeds"] @@ -61,7 +63,7 @@ def _algoToSelector(algo): def _addSelectorsByAlgo(algos, modDict): names = [] - seq = cms.Sequence() + task = cms.Task() for algo in algos: if algo == "generalTracks": continue @@ -72,10 +74,10 @@ def _addSelectorsByAlgo(algos, modDict): else: mod = modDict[modName] names.append(modName) - seq += mod - return (names, seq) + task.add(mod) + return (names, task) def _addSelectorsByHp(algos, modDict): - seq = cms.Sequence() + task = cms.Task() names = [] for algo in algos: modName = _algoToSelector(algo) @@ -89,10 +91,10 @@ def _addSelectorsByHp(algos, modDict): else: mod = modDict[modNameHp] names.append(modNameHp) - seq += mod - return (names, seq) + task.add(mod) + return (names, task) def _addSelectorsBySrc(modules, midfix, src, modDict): - seq = cms.Sequence() + task = cms.Task() names = [] for modName in modules: modNameNew = modName.replace("cutsRecoTracks", "cutsRecoTracks"+midfix) @@ -102,10 +104,10 @@ def _addSelectorsBySrc(modules, midfix, src, modDict): else: mod = modDict[modNameNew] names.append(modNameNew) - seq += mod - return (names, seq) + task.add(mod) + return (names, task) def _addSelectorsByOriginalAlgoMask(modules, midfix, algoParam,modDict): - seq = cms.Sequence() + task = cms.Task() names = [] for modName in modules: if modName[-2:] == "Hp": @@ -120,11 +122,11 @@ def _addSelectorsByOriginalAlgoMask(modules, midfix, algoParam,modDict): else: mod = modDict[modNameNew] names.append(modNameNew) - seq += mod - return (names, seq) + task.add(mod) + return (names, task) def _addSeedToTrackProducers(seedProducers,modDict): names = [] - seq = cms.Sequence() + task = cms.Task() for seed in seedProducers: modName = "seedTracks"+seed if modName not in modDict: @@ -133,8 +135,8 @@ def _addSeedToTrackProducers(seedProducers,modDict): else: mod = modDict[modName] names.append(modName) - seq += mod - return (names, seq) + task.add(mod) + return (names, task) _relevantEras = _cfg.allEras() _relevantErasAndFastSim = _relevantEras + [("fastSim", "_fastSim", fastSim)] @@ -146,9 +148,9 @@ def _translateArgs(args, postfix, modDict): else: ret.append(modDict[arg+postfix]) return ret -def _sequenceForEachEra(function, args, names, sequence, modDict, plainArgs=[], modifySequence=None, includeFastSim=False): - if sequence[0] != "_": - raise Exception("Sequence name is expected to begin with _") +def _taskForEachEra(function, args, names, task, modDict, plainArgs=[], modifyTask=None, includeFastSim=False): + if task[0] != "_": + raise Exception("Task name is expected to begin with _") _eras = _relevantErasAndFastSim if includeFastSim else _relevantEras for eraName, postfix, _era in _eras: @@ -156,23 +158,23 @@ def _sequenceForEachEra(function, args, names, sequence, modDict, plainArgs=[], _args.extend(plainArgs) ret = function(*_args, modDict=modDict) if len(ret) != 2: - raise Exception("_sequenceForEachEra is expected to return 2 values, but function returned %d" % len(ret)) + raise Exception("_taskForEachEra is expected to return 2 values, but function returned %d" % len(ret)) modDict[names+postfix] = ret[0] - modDict[sequence+postfix] = ret[1] + modDict[task+postfix] = ret[1] - # The sequence of the first era will be the default one - defaultSequenceName = sequence+_eras[0][0] - defaultSequence = modDict[defaultSequenceName] - modDict[defaultSequenceName[1:]] = defaultSequence # remove leading underscore + # The task of the first era will be the default one + defaultTaskName = task+_eras[0][0] + defaultTask = modDict[defaultTaskName] + modDict[defaultTaskName[1:]] = defaultTask # remove leading underscore - # Optionally modify sequences before applying the era - if modifySequence is not None: + # Optionally modify task before applying the era + if modifyTask is not None: for eraName, postfix, _era in _eras: - modifySequence(modDict[sequence+postfix]) + modifyTask(modDict[task+postfix]) # Apply eras for _eraName, _postfix, _era in _eras[1:]: - _era.toReplaceWith(defaultSequence, modDict[sequence+_postfix]) + _era.toReplaceWith(defaultTask, modDict[task+_postfix]) def _setForEra(module, eraName, era, **kwargs): if eraName == "": for key, value in six.iteritems(kwargs): @@ -242,10 +244,10 @@ def _getMVASelectors(postfix): locals()["_mvaSelectors"+_postfix] = _getMVASelectors(_postfix) # Validation iterative steps -_sequenceForEachEra(_addSelectorsByAlgo, args=["_algos"], names="_selectorsByAlgo", sequence="_tracksValidationSelectorsByAlgo", modDict=globals()) +_taskForEachEra(_addSelectorsByAlgo, args=["_algos"], names="_selectorsByAlgo", task="_tracksValidationSelectorsByAlgo", modDict=globals()) # high purity -_sequenceForEachEra(_addSelectorsByHp, args=["_algos"], names="_selectorsByAlgoHp", sequence="_tracksValidationSelectorsByAlgoHp", modDict=globals()) +_taskForEachEra(_addSelectorsByHp, args=["_algos"], names="_selectorsByAlgoHp", task="_tracksValidationSelectorsByAlgoHp", modDict=globals()) # by originalAlgo for _eraName, _postfix, _era in _relevantEras: @@ -254,9 +256,9 @@ def _getMVASelectors(postfix): locals()["_selectorsByAlgoAndHpNoGenTk"+_postfix] = [n for n in locals()["_selectorsByAlgoAndHp"+_postfix] if n not in ["generalTracks", "cutsRecoTracksHp"]] # For ByOriginalAlgo locals()["_selectorsByAlgoAndHpNoGenTkDupMerge"+_postfix] = [n for n in locals()["_selectorsByAlgoAndHpNoGenTk"+_postfix] if n not in ["cutsRecoTracksDuplicateMerge", "cutsRecoTracksDuplicateMergeHp"]] -_sequenceForEachEra(_addSelectorsByOriginalAlgoMask, modDict = globals(), +_taskForEachEra(_addSelectorsByOriginalAlgoMask, modDict = globals(), args = ["_selectorsByAlgoAndHpNoGenTkDupMerge"], plainArgs = ["ByOriginalAlgo", "originalAlgorithm"], - names = "_selectorsByOriginalAlgo", sequence = "_tracksValidationSelectorsByOriginalAlgo") + names = "_selectorsByOriginalAlgo", task = "_tracksValidationSelectorsByOriginalAlgo") for _eraName, _postfix, _era in _relevantEras: @@ -296,11 +298,11 @@ def _getMVASelectors(postfix): # select tracks with pT > 0.9 GeV (for upgrade fake rates) generalTracksPt09 = cutsRecoTracks_cfi.cutsRecoTracks.clone(ptMin=0.9) # and then the selectors -_sequenceForEachEra(_addSelectorsBySrc, modDict=globals(), - args=[["_generalTracksHp"]], - plainArgs=["Pt09", "generalTracksPt09"], - names="_selectorsPt09", sequence="_tracksValidationSelectorsPt09", - modifySequence=lambda seq:seq.insert(0, generalTracksPt09)) +_taskForEachEra(_addSelectorsBySrc, modDict=globals(), + args=[["_generalTracksHp"]], + plainArgs=["Pt09", "generalTracksPt09"], + names="_selectorsPt09", task="_tracksValidationSelectorsPt09", + modifyTask=lambda task:task.add(generalTracksPt09)) # select tracks from the PV from CommonTools.RecoAlgos.TrackWithVertexRefSelector_cfi import trackWithVertexRefSelector as _trackWithVertexRefSelector @@ -317,20 +319,20 @@ def _getMVASelectors(postfix): rhoVtx = 1e10, # intentionally no dxy cut ) # and then the selectors -_sequenceForEachEra(_addSelectorsBySrc, modDict=globals(), +_taskForEachEra(_addSelectorsBySrc, modDict=globals(), args=[["_generalTracksHp"]], plainArgs=["FromPV", "generalTracksFromPV"], - names="_selectorsFromPV", sequence="_tracksValidationSelectorsFromPV", - modifySequence=lambda seq: seq.insert(0, generalTracksFromPV)) + names="_selectorsFromPV", task="_tracksValidationSelectorsFromPV", + modifyTask=lambda task: task.add(generalTracksFromPV)) # select tracks with pT > 0.9 GeV from the PV generalTracksFromPVPt09 = generalTracksPt09.clone(src="generalTracksFromPV") # and then the selectors -_sequenceForEachEra(_addSelectorsBySrc, modDict=globals(), - args=[["_generalTracksHp"]], - plainArgs=["FromPVPt09", "generalTracksFromPVPt09"], - names="_selectorsFromPVPt09", sequence="_tracksValidationSelectorsFromPVPt09", - modifySequence=lambda seq: seq.insert(0, generalTracksFromPVPt09)) +_taskForEachEra(_addSelectorsBySrc, modDict=globals(), + args=[["_generalTracksHp"]], + plainArgs=["FromPVPt09", "generalTracksFromPVPt09"], + names="_selectorsFromPVPt09", task="_tracksValidationSelectorsFromPVPt09", + modifyTask=lambda task: task.add(generalTracksFromPVPt09)) ## Select conversion TrackingParticles, and define the corresponding associator trackingParticlesConversion = _trackingParticleConversionRefSelector.clone() @@ -447,6 +449,15 @@ def _getMVASelectors(postfix): _setForEra(trackValidatorAllTPEffic, _eraName, _era, label = ["generalTracks", locals()["_generalTracksHp"+_postfix]]) # Built tracks, in the standard sequence mainly for monitoring the track selection MVA +tpClusterProducerPreSplitting = tpClusterProducer.clone(pixelClusterSrc = "siPixelClustersPreSplitting") +quickTrackAssociatorByHitsPreSplitting = quickTrackAssociatorByHits.clone(cluster2TPSrc = "tpClusterProducerPreSplitting") +tpClusterProducerHeterogeneousPreSplitting = tpClusterProducerHeterogeneous.clone( + pixelClusterSrc = "siPixelClustersPreSplitting" +) +from Configuration.ProcessModifiers.gpu_cff import gpu +gpu.toReplaceWith(tpClusterProducerPreSplitting, tpClusterProducerConverter.clone( + src = "tpClusterProducerHeterogeneousPreSplitting" +)) _trackValidatorSeedingBuilding = trackValidator.clone( # common for built tracks and seeds (in trackingOnly) associators = ["quickTrackAssociatorByHits"], UseAssociators = True, @@ -459,11 +470,17 @@ def _getMVASelectors(postfix): dirName = "Tracking/TrackBuilding/", doMVAPlots = True, ) +trackValidatorBuildingPreSplitting = trackValidatorBuilding.clone( + associators = ["quickTrackAssociatorByHitsPreSplitting"], + doMVAPlots = False, + doSummaryPlots = False, +) for _eraName, _postfix, _era in _relevantErasAndFastSim: _setForEra(trackValidatorBuilding, _eraName, _era, label = locals()["_trackProducers"+_postfix]) fastSim.toModify(trackValidatorBuilding, doMVAPlots=False) for _eraName, _postfix, _era in _relevantEras: _setForEra(trackValidatorBuilding, _eraName, _era, mvaLabels = locals()["_mvaSelectors"+_postfix]) + _setForEra(trackValidatorBuildingPreSplitting, _eraName, _era, label = locals()["_trackProducersPreSplitting"+_postfix]) # For conversions @@ -534,31 +551,34 @@ def _uniqueFirstLayers(layerList): # the track selectors -tracksValidationSelectors = cms.Sequence( - tracksValidationSelectorsByAlgo + - tracksValidationSelectorsByAlgoHp + - tracksValidationSelectorsByOriginalAlgo + - cutsRecoTracksBtvLike + - ak4JetTracksAssociatorExplicitAll + +tracksValidationSelectors = cms.Task( + tracksValidationSelectorsByAlgo, + tracksValidationSelectorsByAlgoHp, + tracksValidationSelectorsByOriginalAlgo, + cutsRecoTracksBtvLike, + ak4JetTracksAssociatorExplicitAll, cutsRecoTracksAK4PFJets ) -tracksValidationTruth = cms.Sequence( - tpClusterProducer + - quickTrackAssociatorByHits + - trackingParticleRecoTrackAsssociation + - VertexAssociatorByPositionAndTracks + +tracksValidationTruth = cms.Task( + tpClusterProducer, + tpClusterProducerHeterogeneousPreSplitting, + tpClusterProducerPreSplitting, + quickTrackAssociatorByHits, + quickTrackAssociatorByHitsPreSplitting, + trackingParticleRecoTrackAsssociation, + VertexAssociatorByPositionAndTracks, trackingParticleNumberOfLayersProducer ) fastSim.toModify(tracksValidationTruth, lambda x: x.remove(tpClusterProducer)) -tracksPreValidation = cms.Sequence( - tracksValidationSelectors + - tracksValidationSelectorsPt09 + - tracksValidationSelectorsFromPV + - tracksValidationSelectorsFromPVPt09 + - tracksValidationTruth + - cms.ignore(trackingParticlesSignal) + - cms.ignore(trackingParticlesElectron) + +tracksPreValidation = cms.Task( + tracksValidationSelectors, + tracksValidationSelectorsPt09, + tracksValidationSelectorsFromPV, + tracksValidationSelectorsFromPVPt09, + tracksValidationTruth, + trackingParticlesSignal, + trackingParticlesElectron, trackingParticlesConversion ) fastSim.toReplaceWith(tracksPreValidation, tracksPreValidation.copyAndExclude([ @@ -567,17 +587,19 @@ def _uniqueFirstLayers(layerList): ])) tracksValidation = cms.Sequence( - tracksPreValidation + trackValidator + trackValidatorTPPtLess09 + trackValidatorFromPV + trackValidatorFromPVAllTP + trackValidatorAllTPEffic + trackValidatorBuilding + + trackValidatorBuildingPreSplitting + trackValidatorConversion + - trackValidatorGsfTracks + trackValidatorGsfTracks, + tracksPreValidation ) fastSim.toReplaceWith(tracksValidation, tracksValidation.copyAndExclude([ + trackValidatorBuildingPreSplitting, trackValidatorConversion, trackValidatorGsfTracks, ])) @@ -585,27 +607,27 @@ def _uniqueFirstLayers(layerList): ### Then define stuff for standalone mode (i.e. MTV with RECO+DIGI input) # Select by originalAlgo and algoMask -_sequenceForEachEra(_addSelectorsByOriginalAlgoMask, modDict = globals(), - args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["ByAlgoMask", "algorithmMaskContains"], - names = "_selectorsByAlgoMask", sequence = "_tracksValidationSelectorsByAlgoMaskStandalone") +_taskForEachEra(_addSelectorsByOriginalAlgoMask, modDict = globals(), + args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["ByAlgoMask", "algorithmMaskContains"], + names = "_selectorsByAlgoMask", task = "_tracksValidationSelectorsByAlgoMaskStandalone") # Select pT>0.9 by iteration # Need to avoid generalTracks+HP because those are already included in the standard validator -_sequenceForEachEra(_addSelectorsBySrc, modDict = globals(), - args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["Pt09", "generalTracksPt09"], - names = "_selectorsPt09Standalone", sequence = "_tracksValidationSelectorsPt09Standalone") +_taskForEachEra(_addSelectorsBySrc, modDict = globals(), + args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["Pt09", "generalTracksPt09"], + names = "_selectorsPt09Standalone", task = "_tracksValidationSelectorsPt09Standalone") # Select fromPV by iteration # Need to avoid generalTracks+HP because those are already included in the standard validator -_sequenceForEachEra(_addSelectorsBySrc, modDict = globals(), - args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["FromPV", "generalTracksFromPV"], - names = "_selectorsFromPVStandalone", sequence = "_tracksValidationSelectorsFromPVStandalone") +_taskForEachEra(_addSelectorsBySrc, modDict = globals(), + args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["FromPV", "generalTracksFromPV"], + names = "_selectorsFromPVStandalone", task = "_tracksValidationSelectorsFromPVStandalone") # Select pt>0.9 and fromPV by iteration # Need to avoid generalTracks+HP because those are already included in the standard validator -_sequenceForEachEra(_addSelectorsBySrc, modDict = globals(), - args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["FromPVPt09", "generalTracksFromPVPt09"], - names = "_selectorsFromPVPt09Standalone", sequence = "_tracksValidationSelectorsFromPVPt09Standalone") +_taskForEachEra(_addSelectorsBySrc, modDict = globals(), + args = ["_selectorsByAlgoAndHpNoGenTk"], plainArgs = ["FromPVPt09", "generalTracksFromPVPt09"], + names = "_selectorsFromPVPt09Standalone", task = "_tracksValidationSelectorsFromPVPt09Standalone") # MTV instances trackValidatorStandalone = trackValidator.clone() @@ -632,13 +654,13 @@ def _uniqueFirstLayers(layerList): # sequences tracksPreValidationStandalone = tracksPreValidation.copy() -tracksPreValidationStandalone += trackingParticlesBHadron +tracksPreValidationStandalone.add(trackingParticlesBHadron) fastSim.toReplaceWith(tracksPreValidationStandalone, tracksPreValidation) -tracksValidationSelectorsStandalone = cms.Sequence( - tracksValidationSelectorsByAlgoMaskStandalone + - tracksValidationSelectorsPt09Standalone + - tracksValidationSelectorsFromPVStandalone + +tracksValidationSelectorsStandalone = cms.Task( + tracksValidationSelectorsByAlgoMaskStandalone, + tracksValidationSelectorsPt09Standalone, + tracksValidationSelectorsFromPVStandalone, tracksValidationSelectorsFromPVPt09Standalone ) @@ -659,16 +681,18 @@ def _uniqueFirstLayers(layerList): tracksValidationStandalone = cms.Sequence( ak4PFL1FastL2L3CorrectorChain + - tracksPreValidationStandalone + - tracksValidationSelectorsStandalone + - trackValidatorsStandalone + trackValidatorsStandalone, + tracksPreValidationStandalone, + tracksValidationSelectorsStandalone ) ### TrackingOnly mode (i.e. MTV with DIGI input + tracking-only reconstruction) # selectors tracksValidationSelectorsTrackingOnly = tracksValidationSelectors.copyAndExclude([ak4JetTracksAssociatorExplicitAll,cutsRecoTracksAK4PFJets]) # selectors using track information only (i.e. no PF) -_sequenceForEachEra(_addSeedToTrackProducers, args=["_seedProducers"], names="_seedSelectors", sequence="_tracksValidationSeedSelectorsTrackingOnly", includeFastSim=True, modDict=globals()) +_taskForEachEra(_addSeedToTrackProducers, args=["_seedProducers"], names="_seedSelectors", task="_tracksValidationSeedSelectorsTrackingOnly", includeFastSim=True, modDict=globals()) +_taskForEachEra(_addSeedToTrackProducers, args=["_seedProducersPreSplitting"], names="_seedSelectorsPreSplitting", task="_tracksValidationSeedSelectorsPreSplittingTrackingOnly", modDict=globals()) +tracksValidationSeedSelectorsTrackingOnly.add(tracksValidationSeedSelectorsPreSplittingTrackingOnly) # MTV instances trackValidatorTrackingOnly = trackValidatorStandalone.clone(label = [ x for x in trackValidatorStandalone.label if x != "cutsRecoTracksAK4PFJets"] ) @@ -678,8 +702,16 @@ def _uniqueFirstLayers(layerList): label = _seedSelectors, doSeedPlots = True, ) +trackValidatorSeedingPreSplittingTrackingOnly = trackValidatorSeedingTrackingOnly.clone( + associators = ["quickTrackAssociatorByHitsPreSplitting"], + label = _seedSelectorsPreSplitting, + doSummaryPlots = False, + +) for _eraName, _postfix, _era in _relevantErasAndFastSim: _setForEra(trackValidatorSeedingTrackingOnly, _eraName, _era, label = locals()["_seedSelectors"+_postfix]) +for _eraName, _postfix, _era in _relevantEras: + _setForEra(trackValidatorSeedingPreSplittingTrackingOnly, _eraName, _era, label = locals()["_seedSelectorsPreSplitting"+_postfix]) trackValidatorConversionTrackingOnly = trackValidatorConversion.clone(label = [x for x in trackValidatorConversion.label if x not in ["ckfInOutTracksFromConversions", "ckfOutInTracksFromConversions"]]) @@ -693,41 +725,32 @@ def _uniqueFirstLayers(layerList): trackValidatorsTrackingOnly = _trackValidatorsBase.copy() trackValidatorsTrackingOnly.replace(trackValidatorStandalone, trackValidatorTrackingOnly) trackValidatorsTrackingOnly += trackValidatorSeedingTrackingOnly +trackValidatorsTrackingOnly += trackValidatorSeedingPreSplittingTrackingOnly trackValidatorsTrackingOnly += trackValidatorBuilding +trackValidatorsTrackingOnly += trackValidatorBuildingPreSplitting trackValidatorsTrackingOnly.replace(trackValidatorConversionStandalone, trackValidatorConversionTrackingOnly) trackValidatorsTrackingOnly.remove(trackValidatorGsfTracks) trackValidatorsTrackingOnly.replace(trackValidatorBHadronStandalone, trackValidatorBHadronTrackingOnly) -fastSim.toModify(trackValidatorsTrackingOnly, lambda x: x.remove(trackValidatorConversionTrackingOnly)) -fastSim.toModify(trackValidatorsTrackingOnly, lambda x: x.remove(trackValidatorBHadronTrackingOnly)) +fastSim.toReplaceWith(trackValidatorsTrackingOnly, trackValidatorsTrackingOnly.copyAndExclude([ + trackValidatorBuildingPreSplitting, + trackValidatorSeedingPreSplittingTrackingOnly, + trackValidatorConversionTrackingOnly, + trackValidatorBHadronTrackingOnly +])) tracksValidationTrackingOnly = cms.Sequence( - tracksPreValidationTrackingOnly + - tracksValidationSelectorsStandalone + - tracksValidationSeedSelectorsTrackingOnly + - trackValidatorsTrackingOnly + trackValidatorsTrackingOnly, + tracksPreValidationTrackingOnly, + tracksValidationSelectorsStandalone, + tracksValidationSeedSelectorsTrackingOnly ) ### Pixel tracking only mode (placeholder for now) - -tpClusterProducerHeterogeneousPixelTrackingOnly = tpClusterProducerHeterogeneous.clone( - pixelClusterSrc = "siPixelClustersPreSplitting" -) -tpClusterProducerPixelTrackingOnly = tpClusterProducer.clone( - pixelClusterSrc = "siPixelClustersPreSplitting" -) -from Configuration.ProcessModifiers.gpu_cff import gpu -gpu.toReplaceWith(tpClusterProducerPixelTrackingOnly, tpClusterProducerConverter.clone( - src = "tpClusterProducerHeterogeneousPixelTrackingOnly" -)) - -quickTrackAssociatorByHitsPixelTrackingOnly = quickTrackAssociatorByHits.clone( - cluster2TPSrc = "tpClusterProducerPixelTrackingOnly" -) trackingParticlePixelTrackAsssociation = trackingParticleRecoTrackAsssociation.clone( label_tr = "pixelTracks", - associator = "quickTrackAssociatorByHitsPixelTrackingOnly", + associator = "quickTrackAssociatorByHitsPreSplitting", ) PixelVertexAssociatorByPositionAndTracks = VertexAssociatorByPositionAndTracks.clone( trackAssociation = "trackingParticlePixelTrackAsssociation" @@ -770,25 +793,32 @@ def _uniqueFirstLayers(layerList): doSimPlots = False, doSimTrackPlots = False, ) +trackValidatorBHadronPixelTrackingOnly = trackValidatorPixelTrackingOnly.clone( + dirName = "Tracking/PixelTrackBHadron/", + label_tp_effic = "trackingParticlesBHadron", + label_tp_effic_refvector = True, + doSimPlots = True, + doRecoTrackPlots = False, # Fake rate is defined wrt. all TPs, and that is already included in trackValidator + dodEdxPlots = False, +) tracksValidationTruthPixelTrackingOnly = tracksValidationTruth.copy() -tracksValidationTruthPixelTrackingOnly.replace(tpClusterProducer, tpClusterProducerPixelTrackingOnly) -tracksValidationTruthPixelTrackingOnly.replace(quickTrackAssociatorByHits, quickTrackAssociatorByHitsPixelTrackingOnly) tracksValidationTruthPixelTrackingOnly.replace(trackingParticleRecoTrackAsssociation, trackingParticlePixelTrackAsssociation) tracksValidationTruthPixelTrackingOnly.replace(VertexAssociatorByPositionAndTracks, PixelVertexAssociatorByPositionAndTracks) +tracksValidationTruthPixelTrackingOnly.add(trackingParticlesBHadron) -_tracksValidationTruthPixelTrackingOnlyGPU = tracksValidationTruthPixelTrackingOnly.copy() -_tracksValidationTruthPixelTrackingOnlyGPU.insert(0, tpClusterProducerHeterogeneousPixelTrackingOnly) -gpu.toReplaceWith(tracksValidationTruthPixelTrackingOnly, _tracksValidationTruthPixelTrackingOnlyGPU) - +tracksPreValidationPixelTrackingOnly = cms.Task( + tracksValidationTruthPixelTrackingOnly, + trackingParticlesSignal, + pixelTracksFromPV, +) tracksValidationPixelTrackingOnly = cms.Sequence( - tracksValidationTruthPixelTrackingOnly + - cms.ignore(trackingParticlesSignal) + - pixelTracksFromPV + trackValidatorPixelTrackingOnly + trackValidatorFromPVPixelTrackingOnly + - trackValidatorFromPVAllTPPixelTrackingOnly + trackValidatorFromPVAllTPPixelTrackingOnly + + trackValidatorBHadronPixelTrackingOnly, + tracksPreValidationPixelTrackingOnly ) @@ -799,8 +829,8 @@ def _uniqueFirstLayers(layerList): ) tracksValidationLite = cms.Sequence( cutsRecoTracksHp + - tracksValidationTruth + - trackValidatorLite + trackValidatorLite, + tracksValidationTruth ) ## customization for timing diff --git a/Validation/RecoTrack/python/plotting/html.py b/Validation/RecoTrack/python/plotting/html.py index d3f593f6a7586..1cac97b736941 100644 --- a/Validation/RecoTrack/python/plotting/html.py +++ b/Validation/RecoTrack/python/plotting/html.py @@ -63,6 +63,8 @@ def _allToBTV(s): return s.replace("All", "BTV-like") def _ptCut(s): return s.replace("Tracks", "Tracks pT > 0.9 GeV").replace("tracks", "tracks pT > 0.9 GeV") +def _allToPixel(s): + return s.replace("All", "Pixel") def _toPixel(s): return s.replace("Tracks", "Pixel tracks") _trackQualityNameOrder = collections.OrderedDict([ @@ -198,6 +200,7 @@ def _toPixel(s): ("pixel", "Pixel tracks"), ("pixelFromPV", _toPixel(_fromPVName)), ("pixelFromPVAllTP", _toPixel(_fromPVAllTPName)), + ("pixelbhadron", _allToPixel(_bhadronName)), # These are for vertices ("genvertex", "Gen vertices"), ("pixelVertices", "Pixel vertices"), @@ -248,6 +251,7 @@ def _sectionNameLegend(): "bhadron_btvLike": _bhadronLegend.replace("All tracks", _btvLegend), "pixelFromPV": _fromPVLegend, "pixelFromPVAllTP": _fromPVAllTPLegend, + "pixelbhadron": _bhadronLegend, } class Table: diff --git a/Validation/RecoTrack/python/plotting/trackingPlots.py b/Validation/RecoTrack/python/plotting/trackingPlots.py index f14650a8d92ff..a14dbe41dfc9b 100644 --- a/Validation/RecoTrack/python/plotting/trackingPlots.py +++ b/Validation/RecoTrack/python/plotting/trackingPlots.py @@ -572,6 +572,8 @@ def _trackingSubFoldersFallbackFromPV(subfolder): return subfolder.replace("trackingParticleRecoAsssociation", "trackingParticleRecoAsssociationSignal") def _trackingSubFoldersFallbackConversion(subfolder): return subfolder.replace("quickAssociatorByHits", "quickAssociatorByHitsConversion") +def _trackingSubFoldersFallbackPreSplitting(subfolder): + return subfolder.replace("quickAssociatorByHits", "quickAssociatorByHitsPreSplitting") # Additional "quality" flags than highPurity. In a separate list to # allow customization. @@ -1294,7 +1296,8 @@ def _appendTrackingPlots(lastDirName, name, algoPlots, onlyForPileup=False, only ], **limiters) common = dict(fallbackDqmSubFolders=[ _trackingSubFoldersFallbackSLHC_Phase1PU140, - _trackingSubFoldersFallbackFromPV, _trackingSubFoldersFallbackConversion]) + _trackingSubFoldersFallbackFromPV, _trackingSubFoldersFallbackConversion, + _trackingSubFoldersFallbackPreSplitting]) plotter.append(name, folders, TrackingPlotFolder(*algoPlots, **commonForTPF), **common) extendedPlots = [] if building: @@ -1357,6 +1360,7 @@ def _appendPixelTrackingPlots(lastDirName, name): _appendPixelTrackingPlots("PixelTrack", "pixel") _appendPixelTrackingPlots("PixelTrackFromPV", "pixelFromPV") _appendPixelTrackingPlots("PixelTrackFromPVAllTP", "pixelFromPVAllTP") +_appendPixelTrackingPlots("PixelTrackBHadron", "pixelbhadron") # MiniAOD diff --git a/Validation/RecoVertex/python/VertexValidation_cff.py b/Validation/RecoVertex/python/VertexValidation_cff.py index 3b7a1aef78ea4..91a6c35d2ddbb 100644 --- a/Validation/RecoVertex/python/VertexValidation_cff.py +++ b/Validation/RecoVertex/python/VertexValidation_cff.py @@ -10,17 +10,17 @@ from Validation.RecoTrack.TrackValidation_cff import tracksValidationTruth, tracksValidationTruthPixelTrackingOnly vertexValidationStandalone = cms.Sequence( + vertexValidation, tracksValidationTruth - * vertexValidation ) vertexValidationTrackingOnly = cms.Sequence( + v0Validator + + vertexAnalysisSequenceTrackingOnly, tracksValidationTruth - + v0Validator - + vertexAnalysisSequenceTrackingOnly ) vertexValidationPixelTrackingOnly = cms.Sequence( + vertexAnalysisSequencePixelTrackingOnly, tracksValidationTruthPixelTrackingOnly - + vertexAnalysisSequencePixelTrackingOnly ) From 77bd1140e8ab4289837f484a6226513a53b0dc13 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Thu, 29 Nov 2018 11:59:43 +0100 Subject: [PATCH 75/94] Fix Free issues remove double delete, delete properly on host, do not leak quality --- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 8a03e93d1387d..5dcc63fb1f73c 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -122,17 +122,15 @@ void CAHitQuadrupletGeneratorGPU::fillResults( void CAHitQuadrupletGeneratorGPU::deallocateOnGPU() { - - fitter.deallocateOnGPU(); - kernels.deallocateOnGPU(); - - //product + //product cudaFree(gpu_.tuples_d); cudaFree(gpu_.helix_fit_results_d); + cudaFree(gpu_.quality_d); cudaFree(gpu_.apc_d); cudaFree(gpu_d); - cudaFree(tuples_); - cudaFree(helix_fit_results_); + cudaFreeHost(tuples_); + cudaFreeHost(helix_fit_results_); + cudaFreeHost(quality_); } void CAHitQuadrupletGeneratorGPU::allocateOnGPU() From 94b521e2f2c3fdf442a40aa9e9539133f543b26d Mon Sep 17 00:00:00 2001 From: Andrea Bocci Date: Mon, 3 Dec 2018 11:51:26 +0100 Subject: [PATCH 76/94] Remove stray empty lines for consistency with upstream --- RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py index c2eadae78b54b..e868ff1921965 100644 --- a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py +++ b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py @@ -46,7 +46,6 @@ trackingRegions = "pixelTracksTrackingRegions" ) - pixelTracksHitQuadruplets = _initialStepCAHitQuadruplets.clone( doublets = "pixelTracksHitDoublets", SeedComparitorPSet = dict(clusterShapeCacheSrc = 'siPixelClusterShapeCachePreSplitting') @@ -57,7 +56,6 @@ gpu.toReplaceWith(pixelTracksHitQuadruplets, _caHitQuadrupletHeterogeneousEDProducer) gpu.toModify(pixelTracksHitQuadruplets, trackingRegions = "pixelTracksTrackingRegions") - # for trackingLowPU pixelTracksHitTriplets = _pixelTripletHLTEDProducer.clone( doublets = "pixelTracksHitDoublets", @@ -85,7 +83,6 @@ pixelTracksHitQuadruplets, pixelTracks ) - _pixelTracksTask_lowPU = pixelTracksTask.copy() _pixelTracksTask_lowPU.replace(pixelTracksHitQuadruplets, pixelTracksHitTriplets) trackingLowPU.toReplaceWith(pixelTracksTask, _pixelTracksTask_lowPU) From 76a4ae99ec65d42881b5e55101ee76a3336d1563 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 3 Dec 2018 14:57:27 +0100 Subject: [PATCH 77/94] add test, adress first set of comments --- .../PixelTrackFitting/test/BuildFile.xml | 23 ++++++++++++++++--- .../test/PixelTrackRiemannFit.cc | 15 ++++++++---- .../python/caHitQuadrupletEDProducer_cfi.py | 4 ---- .../src/PixelVertexHeterogeneousProducer.cc | 18 +++++++-------- .../PixelVertexFinding/src/gpuClusterTracks.h | 6 ++--- .../PixelVertexFinding/src/gpuFitVertices.h | 18 +++++++-------- .../PixelVertexFinding/src/gpuSortByPt2.h | 12 +++++----- .../PixelVertexFinding/src/gpuSplitVertices.h | 6 ++--- .../PixelVertexFinding/src/gpuVertexFinder.cu | 10 ++++---- .../PixelVertexFinding/src/gpuVertexFinder.h | 4 ++-- .../test/gpuVertexFinder_t.cu | 12 +++++----- 11 files changed, 74 insertions(+), 54 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml index d6beb57b862b8..41fc56df1a07d 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml @@ -1,3 +1,4 @@ + @@ -11,19 +12,35 @@ + + + + + - - - + + + + + + + + + + + + + + diff --git a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc index a405ade872abe..b630d0ee4ae27 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc +++ b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc @@ -5,6 +5,7 @@ #include #include #include // unique_ptr +#include #include #include @@ -365,7 +366,7 @@ void test_helix_fit(bool getcin) { gen_par(4) = 10.; // R (p_t) gen_par(5) = 1.; // eta } - return_err = 1; + return_err = true; const int iteration = 5000; gen_par = New_par(gen_par, 1, B_field); @@ -382,7 +383,9 @@ void test_helix_fit(bool getcin) { << "CotT: " << true_par(3) << " " << "Zip: " << true_par(4) << " " << std::endl; - for (int i = 0; i < iteration; i++) { + 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); @@ -391,10 +394,13 @@ void test_helix_fit(bool getcin) { // 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; - helixRiemann_fit[i] = Rfit::Helix_fit(gen.hits, gen.hits_cov, B_field, return_err); + 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; + // helixBrokenLine_fit[i] = BrokenLine::Helix_fit(gen.hits, gen.hits_cov, B_field); - + 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 " @@ -414,6 +420,7 @@ void test_helix_fit(bool getcin) { << "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); } diff --git a/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py index e8d09a7ec2e4d..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/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 1d2314e6f42bf..3bd1e522cb776 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -67,8 +67,8 @@ class PixelVertexHeterogeneousProducer : public HeterogeneousEDProducer gpuToken_; - edm::EDGetTokenT token_Tracks; - edm::EDGetTokenT token_BeamSpot; + edm::EDGetTokenT token_Tracks_; + edm::EDGetTokenT token_BeamSpot_; gpuVertexFinder::Producer m_gpuAlgo; @@ -113,8 +113,8 @@ PixelVertexHeterogeneousProducer::PixelVertexHeterogeneousProducer(const edm::Pa { produces(); if (enableConversion_) { - token_Tracks = consumes(conf.getParameter("TrackCollection")); - token_BeamSpot =consumes(conf.getParameter("beamSpot") ); + token_Tracks_ = consumes(conf.getParameter("TrackCollection")); + token_BeamSpot_ =consumes(conf.getParameter("beamSpot") ); // Register my product produces(); } else { @@ -155,13 +155,13 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( if (!enableConversion_) return; edm::Handle trackCollection; - e.getByToken(token_Tracks,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(); @@ -198,7 +198,7 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( if (gpuProduct.ivtx[k]==int(i)) itrk.push_back(gpuProduct.itrk[k]); } auto nt = itrk.size(); - if (nt==0) { std::cout << "vertex " << i << "with no tracks..." << std::endl; continue;} + 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 it: itrk) { @@ -210,13 +210,13 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } itrk.clear(); } - /* + assert(uind.size()==(*vertexes).size()); if (!uind.empty()) { assert(0 == *uind.begin()); assert(uind.size()-1 == *uind.rbegin()); } - */ + if (verbose_) { edm::LogInfo("PixelVertexHeterogeneousProducer") << ": Found " << vertexes->size() << " vertexes\n"; diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h index 532b65bb71e05..c1ee35ce74ba6 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h @@ -36,8 +36,8 @@ namespace gpuVertexFinder { float const * __restrict__ zt = data.zt; float const * __restrict__ ezt2 = data.ezt2; - uint32_t & nv = *data.nv; - uint32_t & nv2 = *data.nv2; + uint32_t & nvFinal = *data.nvFinal; + uint32_t & nvIntermediate = *data.nvIntermediate; uint8_t * __restrict__ izt = data.izt; int32_t * __restrict__ nn = data.nn; @@ -177,7 +177,7 @@ namespace gpuVertexFinder { iv[i] = - iv[i] - 1; } - nv2 = 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 index 0f8221cb89b6e..1dfc84c1657ef 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h @@ -30,8 +30,8 @@ namespace gpuVertexFinder { float * __restrict__ zv = data.zv; float * __restrict__ wv = data.wv; float * __restrict__ chi2 = data.chi2; - uint32_t & nv = *data.nv; - uint32_t & nv2 = *data.nv2; + uint32_t & nvFinal = *data.nvFinal; + uint32_t & nvIntermediate = *data.nvIntermediate; int32_t * __restrict__ nn = data.nn; int32_t * __restrict__ iv = data.iv; @@ -39,20 +39,20 @@ namespace gpuVertexFinder { assert(pdata); assert(zt); - assert(nv<=nv2); - nv = nv2; - auto foundClusters = nv2; + assert(nvFinal<=nvIntermediate); + nvFinal = nvIntermediate; + auto foundClusters = nvFinal; - // zero - for (int i = threadIdx.x; i < foundClusters; i += blockDim.x) { + // 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; + if(verbose && 0==threadIdx.x) noise = 0; __syncthreads(); diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h index 7cd2d5f5711e9..5d61feebe0d58 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h @@ -22,16 +22,16 @@ namespace gpuVertexFinder { auto & __restrict__ data = *pdata; auto nt = *data.ntrks; float const * __restrict__ ptt2 = data.ptt2; - uint32_t const & nv = *data.nv; + 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 (nv<1) return; + if (nvFinal<1) return; // can be done asynchronoisly at the end of previous event - for (int i = threadIdx.x; i < nv; i += blockDim.x) { + for (int i = threadIdx.x; i < nvFinal; i += blockDim.x) { ptv2[i]=0; } __syncthreads(); @@ -43,14 +43,14 @@ namespace gpuVertexFinder { } __syncthreads(); - if (1==nv) { + if (1==nvFinal) { if (threadIdx.x==0) sortInd[0]=0; return; } __shared__ uint16_t ws[1024]; - radixSort(ptv2,sortInd,ws,nv); + radixSort(ptv2,sortInd,ws,nvFinal); - assert(ptv2[sortInd[nv-1]]>=ptv2[sortInd[nv-2]]); + assert(ptv2[sortInd[nvFinal-1]]>=ptv2[sortInd[nvFinal-2]]); assert(ptv2[sortInd[1]]>=ptv2[sortInd[0]]); } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h index 54b35d319ff91..dfe08d304685f 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -31,7 +31,7 @@ namespace gpuVertexFinder { float * __restrict__ zv = data.zv; float * __restrict__ wv = data.wv; float const * __restrict__ chi2 = data.chi2; - uint32_t & nv = *data.nv; + uint32_t & nvFinal = *data.nvFinal; int32_t const * __restrict__ nn = data.nn; int32_t * __restrict__ iv = data.iv; @@ -42,7 +42,7 @@ namespace gpuVertexFinder { // one vertex per block auto kv = blockIdx.x; - if (kv>= nv) return; + if (kv>= nvFinal) return; if (nn[kv]<4) return; if (chi2[kv]0) chi2[j]/=float(nn[j]); @@ -225,7 +225,7 @@ int main() { onGPU_d.get(), 9.f ); - cuda::memory::copy(&nv, onGPU.nv2, sizeof(uint32_t)); + cuda::memory::copy(&nv, onGPU.nvIntermediate, sizeof(uint32_t)); std::cout << "after split " << nv << std::endl; cuda::launch(fitVertices, @@ -240,7 +240,7 @@ int main() { onGPU_d.get() ); - 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; From ce143caa147ce81a1a965826aa39c2fed1687d6e Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 3 Dec 2018 15:40:04 +0100 Subject: [PATCH 78/94] more comments addressed --- .../interface/phase1PixelTopology.h | 8 ++++---- .../CUDAUtilities/interface/AtomicPairCounter.h | 16 ++++++++-------- .../SiPixelRecHits/interface/pixelCPEforGPU.h | 4 ++-- .../SiPixelRecHits/plugins/gpuPixelRecHits.h | 2 +- .../python/customizePixelTracksForProfiling.py | 1 + .../plugins/PixelTrackProducerFromCUDA.cc | 6 +++--- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h index fca49374679bc..68fb60361d40d 100644 --- a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h +++ b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h @@ -31,17 +31,17 @@ namespace phase1PixelTopology { template - constexpr auto make_array_helper(Function f, std::index_sequence) + constexpr auto map_to_array_helper(Function f, std::index_sequence) -> std::array::type, sizeof...(Indices)> { return {{ f(Indices)... }}; } template - constexpr auto make_array(Function f) + constexpr auto map_to_array(Function f) -> std::array::type, N> { - return make_array_helper(f, std::make_index_sequence{}); + return map_to_array_helper(f, std::make_index_sequence{}); } @@ -74,7 +74,7 @@ namespace phase1PixelTopology { constexpr uint32_t layerIndexSize = numberOfModules/maxModuleStride; - constexpr std::array layer = make_array(findLayerFromCompact); + constexpr std::array layer = map_to_array(findLayerFromCompact); constexpr bool validateLayerIndex() { bool res=true; diff --git a/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h b/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h index 8b1160e3898d0..425bc1ef8a701 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h +++ b/HeterogeneousCore/CUDAUtilities/interface/AtomicPairCounter.h @@ -1,7 +1,8 @@ #ifndef HeterogeneousCoreCUDAUtilitiesAtomicPairCounter_H #define HeterogeneousCoreCUDAUtilitiesAtomicPairCounter_H -#include +#include +#include class AtomicPairCounter { public: @@ -11,14 +12,12 @@ class AtomicPairCounter { AtomicPairCounter(){} AtomicPairCounter(c_type i) { counter.ac=i;} -#ifdef __CUDACC__ __device__ __host__ AtomicPairCounter & operator=(c_type i) { counter.ac=i; return *this;} -#endif struct Counters { - uint32_t n; // total size - uint32_t m; // number of elements + 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 { @@ -35,10 +34,11 @@ class AtomicPairCounter { // increment n by 1 and m by i. return previous value __device__ - Counters add(c_type i) { - i+=incr; + Counters add(uint32_t i) { + c_type c = i; + c+=incr; Atomic2 ret; - ret.ac = atomicAdd(&counter.ac,i); + ret.ac = atomicAdd(&counter.ac,c); return ret.counters; } diff --git a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h index d9c0faea4e73a..fa326865ced73 100644 --- a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h +++ b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h @@ -210,7 +210,7 @@ namespace pixelCPEforGPU { } constexpr inline - void errorOld(CommonParams const & __restrict__ comParams, DetParams const & __restrict__ detParams, ClusParams & cp, uint32_t ic) { + void errorFromSize(CommonParams const & __restrict__ comParams, DetParams const & __restrict__ detParams, ClusParams & cp, uint32_t ic) { // Edge cluster errors cp.xerr[ic]= 0.0050; cp.yerr[ic]= 0.0085; @@ -263,7 +263,7 @@ namespace pixelCPEforGPU { constexpr inline - void error(CommonParams const & __restrict__ comParams, DetParams const & __restrict__ detParams, ClusParams & cp, uint32_t ic) { + void errorFromDB(CommonParams const & __restrict__ comParams, DetParams const & __restrict__ detParams, ClusParams & cp, uint32_t ic) { // Edge cluster errors cp.xerr[ic]= 0.0050f; cp.yerr[ic]= 0.0085f; diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h b/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h index 1eafdeefa50e8..6864a046bf1dc 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h +++ b/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h @@ -126,7 +126,7 @@ namespace gpuPixelRecHits { assert(h < 2000*256); pixelCPEforGPU::position(cpeParams->commonParams(), 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]; diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py index 671daa5d89952..15224adb78cc3 100644 --- a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py +++ b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py @@ -5,6 +5,7 @@ def customizePixelTracksForProfiling(process): process.out = cms.OutputModule("AsciiOutputModule", outputCommands = cms.untracked.vstring( + "keep *_pixelTracks_*_*", "keep *_pixelVertices_*_*", ), verbosity = cms.untracked.uint32(0), diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc index dcf51eac2067e..c05fa9172ffe3 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromCUDA.cc @@ -67,7 +67,7 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer tBeamSpot; + edm::EDGetTokenT tBeamSpot_; edm::EDGetTokenT gpuToken_; edm::EDGetTokenT srcToken_; bool enableConversion_; @@ -75,7 +75,7 @@ class PixelTrackProducerFromCUDA: public HeterogeneousEDProducer(iConfig.getParameter("beamSpot"))), + tBeamSpot_(consumes(iConfig.getParameter("beamSpot"))), gpuToken_(consumes(iConfig.getParameter("src"))), enableConversion_ (iConfig.getParameter("gpuEnableConversion")) { @@ -145,7 +145,7 @@ void PixelTrackProducerFromCUDA::produceGPUCuda(edm::HeterogeneousEvent &iEvent, // std::cout << "origin " << region.origin() << std::endl; edm::Handle bsHandle; - iEvent.getByToken( tBeamSpot, 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()); From 13b72770bb07979b055b84bcfc11a9d46dfb18c4 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 3 Dec 2018 15:46:51 +0100 Subject: [PATCH 79/94] silenced --- RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h index 76cbc065d360d..02a175fcc2903 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h @@ -132,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... } From c455a96ee310b7602c60149080ecfb2b96a275e7 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 4 Dec 2018 11:27:35 +0100 Subject: [PATCH 80/94] now works --- .../PixelTrackFitting/interface/RiemannFit.h | 6 +- .../PixelTrackFitting/test/testEigenGPU.cu | 57 +++++++++++-------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index 1a6bf4bc08719..86738583a0097 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -638,10 +638,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 diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu index 7b1125eebc312..f624d4db11115 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,38 +24,41 @@ 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) { @@ -84,7 +88,7 @@ void kernelCircleFit(Rfit::Matrix3xNd * hits, #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); + *fast_fit_input, rad, B, true); } __global__ @@ -192,6 +196,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); @@ -228,6 +233,7 @@ void testFitOneGo(bool errors, double epsilon=1e-6) { cudaCheck(cudaMemcpy(hitsGPU, &hits, sizeof(Rfit::Matrix3xNd(3,4)), cudaMemcpyHostToDevice)); cudaCheck(cudaMemcpy(hits_covGPU, &hits_cov, sizeof(Rfit::Matrix3Nd(12,12)), cudaMemcpyHostToDevice)); + kernelFullFit<<<1, 1>>>(hitsGPU, hits_covGPU, B, errors, circle_fit_resultsGPU, line_fit_resultsGPU); cudaCheck(cudaDeviceSynchronize()); @@ -251,15 +257,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; } From 6d9379f63302e5ef4a9cbebe12777863967a7ae1 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 4 Dec 2018 14:14:05 +0100 Subject: [PATCH 81/94] test of fit on gpu works --- .../PixelTrackFitting/test/testEigenGPU.cu | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu index f624d4db11115..3338b0bce57b1 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu @@ -62,15 +62,17 @@ void kernelFullFit(Rfit::Matrix3xNd * hits, __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)); @@ -86,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, true); + 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__ @@ -98,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) { @@ -127,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); @@ -143,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); @@ -165,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(); @@ -183,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); @@ -226,13 +233,16 @@ 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); From 522cfdfa445a66993b0a233047715a5e7000d202 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 7 Dec 2018 17:42:34 +0100 Subject: [PATCH 82/94] late fishbone --- .../PixelTrackFitting/interface/RiemannFit.h | 3 ++ .../CAHitQuadrupletGeneratorKernels.cu | 37 ++++++++++++++++++- .../PixelTriplets/plugins/gpuFishbone.h | 29 +++++++++------ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index 86738583a0097..b8e99e933d6d3 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -782,9 +782,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]; @@ -828,6 +830,7 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, printIt(&E, "circle_fit - E:"); 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]; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index caddcd73d09e0..bd7b7de6f9f07 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -68,6 +68,25 @@ void kernel_checkOverflows(TuplesOnGPU::Container * foundNtuplets, AtomicPairCou // if (threadIdx.x==0) printf("number of killed cells %d\n",killedCell); } + +__global__ +void +kernel_fishboneCleaner(GPUCACell const * cells, uint32_t const * __restrict__ nCells, + pixelTuplesHeterogeneousProduct::Quality * quality + ) { + + constexpr auto bad = pixelTuplesHeterogeneousProduct::bad; + + auto cellIndex = threadIdx.x + blockIdx.x * blockDim.x; + + if (cellIndex >= (*nCells) ) return; + auto const & thisCell = cells[cellIndex]; + if (thisCell.theDoubletId>=0) return; + + for (auto it : thisCell.theTracks) quality[it] = bad; + +} + __global__ void kernel_fastDuplicateRemover(GPUCACell const * cells, uint32_t const * __restrict__ nCells, @@ -225,12 +244,14 @@ void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... auto stride = 4; auto numberOfBlocks = (nhits + blockSize - 1)/blockSize; numberOfBlocks *=stride; + /* fishbone<<>>( hh.gpu_d, device_theCells_, device_nCells_, device_isOuterHitOfCell_, - nhits, stride + nhits, stride, false ); + */ numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; kernel_connect<<>>( @@ -260,6 +281,17 @@ void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... ); cudaCheck(cudaGetLastError()); + numberOfBlocks = (nhits + blockSize - 1)/blockSize; + numberOfBlocks *=stride; + fishbone<<>>( + hh.gpu_d, + device_theCells_, device_nCells_, + device_isOuterHitOfCell_, + nhits, stride, true + ); + + + // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(gpu_.tuples_d, 10); } @@ -278,6 +310,9 @@ void CAHitQuadrupletGeneratorKernels::classifyTuples(HitsOnCPU const & hh, Tuple 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/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 09196a02ef67f..41e99fa3754fd 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -25,7 +25,7 @@ namespace gpuPixelDoublets { GPUCACell * cells, uint32_t const * __restrict__ nCells, GPUCACell::OuterHitOfCell const * __restrict__ isOuterHitOfCell, uint32_t nHits, - uint32_t stride) { + uint32_t stride, bool checkTrack) { constexpr auto maxCellsPerHit = GPUCACell::maxCellsPerHit; @@ -50,21 +50,26 @@ namespace gpuPixelDoublets { 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 Date: Sat, 8 Dec 2018 16:23:19 +0100 Subject: [PATCH 83/94] make fishbone configurable --- .../plugins/CAHitQuadrupletGeneratorGPU.cc | 3 ++ .../plugins/CAHitQuadrupletGeneratorGPU.h | 1 + .../CAHitQuadrupletGeneratorKernels.cu | 36 +++++++++++-------- .../plugins/CAHitQuadrupletGeneratorKernels.h | 8 ++++- .../PixelTriplets/plugins/GPUCACell.h | 2 +- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 5dcc63fb1f73c..7454ee0205f51 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -29,6 +29,7 @@ constexpr unsigned int CAHitQuadrupletGeneratorGPU::minLayers; CAHitQuadrupletGeneratorGPU::CAHitQuadrupletGeneratorGPU( const edm::ParameterSet &cfg, edm::ConsumesCollector &iC) : + kernels(cfg.getParameter("earlyFishbone"),cfg.getParameter("lateFishbone")), caThetaCut(cfg.getParameter("CAThetaCut")), caPhiCut(cfg.getParameter("CAPhiCut")), caHardPtCut(cfg.getParameter("CAHardPtCut")) @@ -39,6 +40,8 @@ void CAHitQuadrupletGeneratorGPU::fillDescriptions(edm::ParameterSetDescription desc.add("CAThetaCut", 0.00125); desc.add("CAPhiCut", 10); desc.add("CAHardPtCut", 0); + desc.add("earlyFishbone",false); + desc.add("lateFishbone",true); } void CAHitQuadrupletGeneratorGPU::initEvent(edm::Event const& ev, edm::EventSetup const& es) { diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h index df6e1c26970d4..d2bc4e347bf9a 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.h @@ -101,6 +101,7 @@ class CAHitQuadrupletGeneratorGPU { const float caPhiCut = 0.1f; const float caHardPtCut = 0.f; + // 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" diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index bd7b7de6f9f07..92fff26e7de29 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -240,20 +240,24 @@ void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... auto nhits = hh.nHits; assert(nhits <= PixelGPUConstants::maxNumberOfHits); - auto blockSize = 64; - auto stride = 4; - auto numberOfBlocks = (nhits + blockSize - 1)/blockSize; - numberOfBlocks *=stride; - /* - fishbone<<>>( + + 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()); + } - numberOfBlocks = (maxNumberOfDoublets_ + blockSize - 1)/blockSize; + 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, @@ -281,16 +285,18 @@ void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... ); cudaCheck(cudaGetLastError()); - numberOfBlocks = (nhits + blockSize - 1)/blockSize; - numberOfBlocks *=stride; - fishbone<<>>( + 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); } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h index bea99ec620cc8..97b5617a54f89 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.h @@ -18,7 +18,9 @@ class CAHitQuadrupletGeneratorKernels { using HitToTuple = CAConstants::HitToTuple; - CAHitQuadrupletGeneratorKernels() = default; + CAHitQuadrupletGeneratorKernels(bool earlyFishbone, bool lateFishbone) : + earlyFishbone_(earlyFishbone), + lateFishbone_(lateFishbone){} ~CAHitQuadrupletGeneratorKernels() { deallocateOnGPU();} void launchKernels(HitsOnCPU const & hh, TuplesOnGPU & tuples_d, cudaStream_t cudaStream); @@ -40,6 +42,10 @@ class CAHitQuadrupletGeneratorKernels { 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 7e3ca7ff0ecec..dbd4eecbaab3c 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -181,7 +181,7 @@ class GPUCACell { #endif // __CUDACC__ GPU::VecArray< uint32_t, 36> theOuterNeighbors; - GPU::VecArray< uint16_t, 32> theTracks; + GPU::VecArray< uint16_t, 42> theTracks; int32_t theDoubletId; int32_t theLayerPairId; From 909cfb0817f68c89b62b50671f4607cfed6a4c47 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Mon, 10 Dec 2018 14:32:45 +0100 Subject: [PATCH 84/94] fix long standing bug (minor effect) --- .../PixelTrackFitting/interface/RiemannFit.h | 9 +-- .../PixelTrackFitting/test/BuildFile.xml | 13 ++++ .../PixelTrackFitting/test/testRiemannFit.cpp | 69 +++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index b8e99e933d6d3..c7d475c4d48b9 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -3,9 +3,6 @@ #include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" -#ifndef RFIT_DEBUG -#define RFIT_DEBUG 0 -#endif // RFIT_DEBUG namespace Rfit { @@ -13,7 +10,7 @@ namespace Rfit 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) @@ -130,8 +127,8 @@ __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, for (u_int i = 0; i < n; ++i) { rot(i, i) = sin(theta); rot(n + i, n + i) = sin(theta); - u_int j = (i + n); - rot(i, j) = i < j ? cos(theta) : -cos(theta); + rot(i, n+i) = cos(theta); + rot(n + i, i) = -cos(theta); } #if RFIT_DEBUG diff --git a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml index 41fc56df1a07d..b4b5e3a335bcb 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml @@ -15,6 +15,19 @@ + + + + + + + + + + + + + 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; +} + From b8b4299b8359d04a90dbdc4faf7ae4d2c4537bc7 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 11 Dec 2018 15:21:57 +0100 Subject: [PATCH 85/94] compiles and run --- .../PixelTrackFitting/interface/FitResult.h | 52 +- .../PixelTrackFitting/interface/RiemannFit.h | 711 +++++++----------- .../plugins/PixelTrackReconstructionGPU.cc | 16 +- .../plugins/PixelTrackReconstructionGPU.cu | 53 +- .../src/PixelFitterByRiemannParaboloid.cc | 17 +- .../test/PixelTrackRiemannFit.cc | 33 +- .../PixelTrackFitting/test/testEigenGPU.cu | 307 +++----- .../PixelTrackFitting/test/testRiemannFit.cpp | 77 +- .../PixelTrackFitting/test/test_common.h | 11 +- .../PixelTriplets/plugins/RiemannFitOnGPU.cc | 19 +- .../PixelTriplets/plugins/RiemannFitOnGPU.cu | 99 +-- .../PixelTriplets/plugins/RiemannFitOnGPU.h | 20 +- 12 files changed, 604 insertions(+), 811 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h index 73fd9210bc378..ba0f0aa13e1a6 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h @@ -15,23 +15,38 @@ 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; +template +using MatrixNd = Eigen::Matrix; +template +using ArrayNd = Eigen::Array; +template +using Matrix2Nd = Eigen::Matrix; +template +using Matrix3Nd = Eigen::Matrix; +template +using Matrix2xNd = Eigen::Matrix; +template +using Array2xNd = Eigen::Array; +template +using Matrix3xNd = Eigen::Matrix; +template +using MatrixNx3d = Eigen::Matrix; +template +using MatrixNx5d = Eigen::Matrix; +template +using VectorNd = Eigen::Matrix; +template +using Vector2Nd = Eigen::Matrix; +template +using Vector3Nd = Eigen::Matrix; +template +using RowVectorNd = Eigen::Matrix; +template +using RowVector2Nd = Eigen::Matrix; + using Vector2d = Eigen::Vector2d; @@ -45,6 +60,9 @@ using Matrix6d = Eigen::Matrix; using Vector5d = Eigen::Matrix; using Matrix3f = Eigen::Matrix3f; +using Vector3f = Eigen::Vector3f; +using Vector4f = Eigen::Vector4f; +using Vector6f = Eigen::Matrix; using u_int = unsigned int; @@ -58,8 +76,8 @@ struct circle_fit |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; + int32_t q; //!< particle charge + float chi2 = 0.0; }; struct line_fit diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index c7d475c4d48b9..f508ffc22c64c 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -1,7 +1,7 @@ #ifndef RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h #define RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h -#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" +#include "FitResult.h" namespace Rfit @@ -43,6 +43,72 @@ __host__ __device__ inline double cross2D(const Vector2d& a, const Vector2d& b) return a.x() * b.y() - a.y() * b.x(); } +/*! + * load error in CMSSW format to our formalism + * + */ + template + __host__ __device__ void loadCovariance2D(M6x4f const & ge, M2Nd & hits_cov) { + // 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 | + constexpr uint32_t hits_in_fit = 4; // Fixme + for (uint32_t i=0; i< hits_in_fit; ++i) { + auto ge_idx = 0; auto j=0; auto l=0; + hits_cov(i + j * hits_in_fit, i + l * hits_in_fit) = ge.col(i)[ge_idx]; + ge_idx = 2; j=1; l=1; + hits_cov(i + j * hits_in_fit, i + l * hits_in_fit) = ge.col(i)[ge_idx]; + ge_idx = 1; j=1; l=0; + hits_cov(i + l * hits_in_fit, i + j * hits_in_fit) = + hits_cov(i + j * hits_in_fit, i + l * hits_in_fit) = ge.col(i)[ge_idx]; + } + } + + template + __host__ __device__ void loadCovariance(M6x4f const & ge, Matrix3Nd & hits_cov) { + + // 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 | + constexpr uint32_t hits_in_fit = 4; // Fixme + for (uint32_t i=0; i __host__ __device__ inline -void computeRadLenUniformMaterial(const VectorNd &length_values, - VectorNd & rad_lengths) { +void computeRadLenUniformMaterial(const VNd1 &length_values, + VNd2 & 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.; @@ -98,44 +166,39 @@ void computeRadLenUniformMaterial(const VectorNd &length_values, 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) + template +__host__ __device__ inline auto Scatter_cov_line(Matrix2d const * cov_sz, + const V4& fast_fit, + VNd1 const& s_arcs, + VNd2 const& z_values, + const double theta, + const double B, + MatrixNd& ret) { -#if RFIT_DEBUG +#ifdef RFIT_DEBUG Rfit::printIt(&s_arcs, "Scatter_cov_line - s_arcs: "); #endif - u_int n = s_arcs.rows(); + constexpr auto n = N; 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))); - VectorNd rad_lengths_S(n); + VectorNd rad_lengths_S; // 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(); + 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); + VectorNd sig2_S; 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) = sin(theta); - rot(n + i, n + i) = sin(theta); - rot(i, n+i) = cos(theta); - rot(n + i, i) = -cos(theta); - } - -#if RFIT_DEBUG - Rfit::printIt(&rot, "Scatter_cov_line - rot: "); +#ifdef RFIT_DEBUG + Rfit::printIt(cov_sz, "Scatter_cov_line - cov_sz: "); #endif - - Matrix2Nd tmp = rot*cov_sz*rot.transpose(); + Matrix2Nd tmp = Matrix2Nd::Zero(); + for (u_int k = 0; k < n; ++k) { + tmp(k, k) = cov_sz[k](0, 0); + tmp(k + n, k + n) = cov_sz[k](1, 1); + tmp(k, k + n) = tmp(k + n, k) = cov_sz[k](0, 1); + } for (u_int k = 0; k < n; ++k) { for (u_int l = k; l < n; ++l) @@ -149,10 +212,10 @@ __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, } // 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 +#ifdef RFIT_DEBUG Rfit::printIt(&tmp, "Scatter_cov_line - tmp: "); #endif - return tmp.block(n, n, n, n); + ret = tmp.block(n, n, n, n); } /*! @@ -168,18 +231,19 @@ __host__ __device__ inline MatrixNd Scatter_cov_line(Matrix2Nd& cov_sz, \details Only the tangential component is computed (the radial one is negligible). */ -__host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, - const Vector4d& fast_fit, - VectorNd const& rad, - double B) + template + __host__ __device__ inline MatrixNd Scatter_cov_rad(const M2xN& p2D, + const V4& fast_fit, + VectorNd const& rad, + double B) { - u_int n = p2D.cols(); + u_int n = N; 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 theta = atan(fast_fit(3)); theta = theta < 0. ? theta + M_PI : theta; - VectorNd s_values(n); - VectorNd rad_lengths(n); + VectorNd s_values; + VectorNd rad_lengths; const Vector2d o(fast_fit(0), fast_fit(1)); // associated Jacobian, used in weights and errors computation @@ -192,9 +256,8 @@ __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, 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); - VectorNd sig2(n); - sig2 = (1. + 0.038 * rad_lengths.array().log()).abs2() * rad_lengths.array(); + MatrixNd scatter_cov_rad = MatrixNd::Zero(); + VectorNd 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) { @@ -207,7 +270,7 @@ __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, scatter_cov_rad(l, k) = scatter_cov_rad(k, l); } } -#if RFIT_DEBUG +#ifdef RFIT_DEBUG Rfit::printIt(&scatter_cov_rad, "Scatter_cov_rad - scatter_cov_rad: "); #endif return scatter_cov_rad; @@ -221,17 +284,18 @@ __host__ __device__ inline MatrixNd Scatter_cov_rad(const Matrix2xNd& p2D, \return cov_cart covariance matrix in Cartesian coordinates. */ -__host__ __device__ inline Matrix2Nd cov_radtocart(const Matrix2xNd& p2D, - const MatrixNd& cov_rad, - const VectorNd& rad) + template + __host__ __device__ inline Matrix2Nd cov_radtocart(const M2xN& p2D, + const MatrixNd& cov_rad, + const VectorNd& rad) { -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Address of p2D: %p\n", &p2D); #endif printIt(&p2D, "cov_radtocart - p2D:"); u_int n = p2D.cols(); - Matrix2Nd cov_cart = MatrixXd::Zero(2 * n, 2 * n); - VectorNd rad_inv = rad.cwiseInverse(); + Matrix2Nd cov_cart = Matrix2Nd::Zero(); + VectorNd rad_inv = rad.cwiseInverse(); printIt(&rad_inv, "cov_radtocart - rad_inv:"); for (u_int i = 0; i < n; ++i) { @@ -241,7 +305,6 @@ __host__ __device__ inline Matrix2Nd cov_radtocart(const Matrix2xNd& p2D, cov_cart(i + n, j + n) = cov_rad(i, j) * p2D(0, i) * rad_inv(i) * p2D(0, j) * rad_inv(j); cov_cart(i, j + n) = -cov_rad(i, j) * p2D(1, i) * rad_inv(i) * p2D(0, j) * rad_inv(j); cov_cart(i + n, j) = -cov_rad(i, j) * p2D(0, i) * rad_inv(i) * p2D(1, j) * rad_inv(j); - cov_cart(j, i) = cov_cart(i, j); cov_cart(j + n, i + n) = cov_cart(i + n, j + n); cov_cart(j + n, i) = cov_cart(i, j + n); @@ -261,21 +324,22 @@ __host__ __device__ inline Matrix2Nd cov_radtocart(const Matrix2xNd& p2D, \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, - const Matrix2Nd& cov_cart, - const VectorNd& rad) + template + __host__ __device__ inline VectorNd cov_carttorad(const M2xN& p2D, + const Matrix2Nd& cov_cart, + const VectorNd& rad) { u_int n = p2D.cols(); - MatrixNd cov_rad = MatrixXd::Zero(n, n); - const VectorNd rad_inv2 = rad.cwiseInverse().array().square(); + VectorNd cov_rad; + const VectorNd rad_inv2 = rad.cwiseInverse().array().square(); for (u_int i = 0; i < n; ++i) { //!< in case you have (0,0) to avoid dividing by 0 radius if (rad(i) < 1.e-4) - cov_rad(i, i) = cov_cart(i, i); + cov_rad(i) = cov_cart(i, i); else { - cov_rad(i, i) = rad_inv2(i) * (cov_cart(i, i) * sqr(p2D(1, i)) + cov_cart(i + n, i + n) * sqr(p2D(0, i)) - 2. * cov_cart(i, i + n) * p2D(0, i) * p2D(1, i)); + cov_rad(i) = rad_inv2(i) * (cov_cart(i, i) * sqr(p2D(1, i)) + cov_cart(i + n, i + n) * sqr(p2D(0, i)) - 2. * cov_cart(i, i + n) * p2D(0, i) * p2D(1, i)); } } return cov_rad; @@ -293,18 +357,18 @@ __host__ __device__ inline MatrixNd cov_carttorad(const Matrix2xNd& p2D, \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, - const Vector4d& fast_fit, - const VectorNd& rad) +template + __host__ __device__ inline VectorNd cov_carttorad_prefit(const M2xN& p2D, const Matrix2Nd& cov_cart, + V4& fast_fit, + const VectorNd& rad) { u_int n = p2D.cols(); - MatrixNd cov_rad = MatrixXd::Zero(n, n); + VectorNd cov_rad; for (u_int i = 0; i < n; ++i) { //!< in case you have (0,0) to avoid dividing by 0 radius if (rad(i) < 1.e-4) - cov_rad(i, i) = cov_cart(i, i); // TO FIX + cov_rad(i) = cov_cart(i, i); // TO FIX else { Vector2d a = p2D.col(i); @@ -313,7 +377,7 @@ __host__ __device__ inline MatrixNd cov_carttorad_prefit(const Matrix2xNd& p2D, const double y2 = cross2D(a, b); const double tan_c = -y2 / x2; const double tan_c2 = sqr(tan_c); - cov_rad(i, i) = 1. / (1. + tan_c2) * (cov_cart(i, i) + cov_cart(i + n, i + n) * tan_c2 + 2 * cov_cart(i, i + n) * tan_c); + cov_rad(i) = 1. / (1. + tan_c2) * (cov_cart(i, i) + cov_cart(i + n, i + n) * tan_c2 + 2 * cov_cart(i, i + n) * tan_c); } } return cov_rad; @@ -330,7 +394,8 @@ __host__ __device__ inline MatrixNd cov_carttorad_prefit(const Matrix2xNd& p2D, diagonal cov matrix. Further investigation needed. */ -__host__ __device__ inline VectorNd Weight_circle(const MatrixNd& cov_rad_inv) + template + __host__ __device__ inline VectorNd Weight_circle(const MatrixNd& cov_rad_inv) { return cov_rad_inv.colwise().sum().transpose(); } @@ -343,8 +408,8 @@ __host__ __device__ inline VectorNd Weight_circle(const MatrixNd& cov_rad_inv) \param par_uvr result of the circle fit in this form: (X0,Y0,R). \return q int 1 or -1. */ - -__host__ __device__ inline int64_t Charge(const Matrix2xNd& p2D, const Vector3d& par_uvr) +template + __host__ __device__ inline int32_t Charge(const M2xN& p2D, const Vector3d& par_uvr) { return ((p2D(0, 1) - p2D(0, 0)) * (par_uvr.y() - p2D(1, 0)) - (p2D(1, 1) - p2D(1, 0)) * (par_uvr.x() - p2D(0, 0)) > 0)? -1 : 1; } @@ -395,14 +460,14 @@ __host__ __device__ inline void par_uvrtopak(circle_fit& circle, const double B, __host__ __device__ inline Vector3d min_eigen3D(const Matrix3d& A, double& chi2) { -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("min_eigen3D - enter\n"); #endif Eigen::SelfAdjointEigenSolver solver(3); solver.computeDirect(A); int min_index; chi2 = solver.eigenvalues().minCoeff(&min_index); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("min_eigen3D - exit\n"); #endif return solver.eigenvectors().col(min_index); @@ -460,9 +525,9 @@ __host__ __device__ inline Vector2d min_eigen2D(const Matrix2d& A, double& chi2) - computation of error due to multiple scattering. */ -__host__ __device__ inline Vector4d Fast_fit(const Matrix3xNd& hits) +template +__host__ __device__ inline void Fast_fit(const M3xN& hits, V4 & result) { - Vector4d result; u_int n = hits.cols(); // get the number of hits printIt(&hits, "Fast_fit - hits: "); @@ -473,35 +538,26 @@ __host__ __device__ inline Vector4d Fast_fit(const Matrix3xNd& hits) printIt(&b, "Fast_fit - b: "); printIt(&c, "Fast_fit - c: "); // Compute their lengths - const double b2 = b.squaredNorm(); - const double c2 = c.squaredNorm(); - double X0; - double Y0; + auto b2 = b.squaredNorm(); + auto c2 = c.squaredNorm(); // The algebra has been verified (MR). The usual approach has been followed: // * use an orthogonal reference frame passing from the first point. // * build the segments (chords) // * build orthogonal lines through mid points // * make a system and solve for X0 and Y0. // * add the initial point - if (abs(b.x()) > abs(b.y())) - { //!< in case b.x is 0 (2 hits with same x) - const double k = c.x() / b.x(); - const double div = 2. * (k * b.y() - c.y()); - // if aligned TO FIX - Y0 = (k * b2 - c2) / div; - X0 = b2 / (2 * b.x()) - b.y() / b.x() * Y0; - } - else - { - const double k = c.y() / b.y(); - const double div = 2. * (k * b.x() - c.x()); - // if aligned TO FIX - X0 = (k * b2 - c2) / div; - Y0 = b2 / (2 * b.y()) - b.x() / b.y() * X0; - } - - result(0) = X0 + hits(0, 0); - result(1) = Y0 + hits(1, 0); + bool flip = abs(b.x()) < abs(b.y()); + auto bx = flip ? b.y() : b.x(); + auto by = flip ? b.x() : b.y(); + auto cx = flip ? c.y() : c.x(); + auto cy = flip ? c.x() : c.y(); + //!< in case b.x is 0 (2 hits with same x) + auto div = 2. * (cx * by - bx*cy); + // if aligned TO FIX + auto Y0 = (cx*b2 - bx*c2) / div; + auto X0 = (0.5*b2 - Y0*by) / bx; + result(0) = hits(0, 0) + ( flip ? Y0 : X0); + result(1) = hits(1, 0) + ( flip ? X0 : Y0); result(2) = sqrt(sqr(X0) + sqr(Y0)); printIt(&result, "Fast_fit - result: "); @@ -511,16 +567,15 @@ __host__ __device__ inline Vector4d Fast_fit(const Matrix3xNd& hits) printIt(&e, "Fast_fit - e: "); printIt(&d, "Fast_fit - d: "); // Compute the arc-length between first and last point: L = R * theta = R * atan (tan (Theta) ) - const double dr = result(2) * atan2(cross2D(d, e), d.dot(e)); + auto dr = result(2) * atan2(cross2D(d, e), d.dot(e)); // Simple difference in Z between last and first hit - const double dz = hits(2, n - 1) - hits(2, 0); + auto dz = hits(2, n - 1) - hits(2, 0); result(3) = (dr / dz); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Fast_fit: [%f, %f, %f, %f]\n", result(0), result(1), result(2), result(3)); #endif - return result; } /*! @@ -550,73 +605,64 @@ __host__ __device__ inline Vector4d Fast_fit(const Matrix3xNd& hits) \bug further investigation needed for error propagation with multiple scattering. */ - -__host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, - const Matrix2Nd& hits_cov2D, - const Vector4d& fast_fit, - const VectorNd& rad, +template +__host__ __device__ inline circle_fit Circle_fit(const M2xN& hits2D, + const Matrix2Nd& hits_cov2D, + const V4& fast_fit, + const VectorNd& rad, const double B, const bool error = true) { -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - enter\n"); #endif // INITIALIZATION - Matrix2Nd V = hits_cov2D; + Matrix2Nd V = hits_cov2D; u_int n = hits2D.cols(); printIt(&hits2D, "circle_fit - hits2D:"); printIt(&hits_cov2D, "circle_fit - hits_cov2D:"); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - WEIGHT COMPUTATION\n"); #endif // WEIGHT COMPUTATION - VectorNd weight; - MatrixNd G; + VectorNd weight; + MatrixNd G; double renorm; { - MatrixNd cov_rad; - cov_rad = cov_carttorad_prefit(hits2D, V, fast_fit, rad); - printIt(&cov_rad, "circle_fit - cov_rad:"); - // cov_rad = cov_carttorad(hits2D, V); - - MatrixNd scatter_cov_rad = Scatter_cov_rad(hits2D, fast_fit, rad, B); + MatrixNd cov_rad = cov_carttorad_prefit(hits2D, V, fast_fit, rad).asDiagonal(); + MatrixNd scatter_cov_rad = Scatter_cov_rad(hits2D, fast_fit, rad, B); printIt(&scatter_cov_rad, "circle_fit - scatter_cov_rad:"); printIt(&hits2D, "circle_fit - hits2D bis:"); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Address of hits2D: a) %p\n", &hits2D); #endif V += cov_radtocart(hits2D, scatter_cov_rad, rad); printIt(&V, "circle_fit - V:"); cov_rad += scatter_cov_rad; printIt(&cov_rad, "circle_fit - cov_rad:"); - Matrix4d cov_rad4 = cov_rad; - Matrix4d G4; - G4 = cov_rad4.inverse(); - printIt(&G4, "circle_fit - G4:"); - renorm = G4.sum(); - G4 *= 1. / renorm; - printIt(&G4, "circle_fit - G4:"); - G = G4; + G = cov_rad.inverse(); + renorm = G.sum(); + G *= 1. / renorm; weight = Weight_circle(G); } printIt(&weight, "circle_fit - weight:"); // SPACE TRANSFORMATION -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - SPACE TRANSFORMATION\n"); #endif // center -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Address of hits2D: b) %p\n", &hits2D); #endif const Vector2d h_ = hits2D.rowwise().mean(); // centroid printIt(&h_, "circle_fit - h_:"); - Matrix3xNd p3D(3, n); + Matrix3xNd p3D; p3D.block(0, 0, 2, n) = hits2D.colwise() - h_; printIt(&p3D, "circle_fit - p3D: a)"); - Vector2Nd mc(2 * n); // centered hits, used in error computation + Vector2Nd mc; // centered hits, used in error computation mc << p3D.row(0).transpose(), p3D.row(1).transpose(); printIt(&mc, "circle_fit - mc(centered hits):"); @@ -629,25 +675,24 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, p3D.row(2) = p3D.block(0, 0, 2, n).colwise().squaredNorm(); printIt(&p3D, "circle_fit - p3D: b)"); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - COST FUNCTION\n"); #endif // COST FUNCTION // compute - Matrix3d A; // = Matrix3d::Zero(); Vector3d r0; r0.noalias() = p3D * weight; // center of gravity - const Matrix3xNd X = p3D.colwise() - r0; - A.noalias() = X * G * X.transpose(); + const Matrix3xNd X = p3D.colwise() - r0; + Matrix3d A = X * G * X.transpose(); printIt(&A, "circle_fit - A:"); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - MINIMIZE\n"); #endif // minimize double chi2; Vector3d v = min_eigen3D(A, chi2); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - AFTER MIN_EIGEN\n"); #endif printIt(&v, "v BEFORE INVERSION"); @@ -655,21 +700,21 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, printIt(&v, "v AFTER INVERSION"); // This hack to be able to run on GPU where the automatic assignment to a // double from the vector multiplication is not working. -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - AFTER MIN_EIGEN 1\n"); #endif Eigen::Matrix cm; -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - AFTER MIN_EIGEN 2\n"); #endif cm = -v.transpose() * r0; -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - AFTER MIN_EIGEN 3\n"); #endif const double c = cm(0, 0); // const double c = -v.transpose() * r0; -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - COMPUTE CIRCLE PARAMETER\n"); #endif // COMPUTE CIRCLE PARAMETER @@ -687,57 +732,60 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, circle.chi2 = abs(chi2) * renorm * 1. / sqr(2 * v(2) * par_uvr_(2) * s); printIt(&circle.par, "circle_fit - CIRCLE PARAMETERS:"); printIt(&circle.cov, "circle_fit - CIRCLE COVARIANCE:"); -#if RFIT_DEBUG - printf("circle_fit - CIRCLE CHARGE: %ld\n", circle.q); +#ifdef RFIT_DEBUG + printf("circle_fit - CIRCLE CHARGE: %d\n", circle.q); #endif -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - ERROR PROPAGATION\n"); #endif // ERROR PROPAGATION if (error) { -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - ERROR PRPAGATION ACTIVATED\n"); #endif - ArrayNd Vcs_[2][2]; // cov matrix of center & scaled points -#if RFIT_DEBUG + ArrayNd Vcs_[2][2]; // cov matrix of center & scaled points + MatrixNd C[3][3]; // cov matrix of 3D transformed points +#ifdef RFIT_DEBUG printf("circle_fit - ERROR PRPAGATION ACTIVATED 2\n"); #endif { Eigen::Matrix cm; Eigen::Matrix cm2; cm = mc.transpose() * V * mc; - // cm2 = mc * mc.transpose(); const double c = cm(0, 0); - // const double c2 = cm2(0,0); - const Matrix2Nd Vcs = sqr(s) * V + sqr(sqr(s)) * 1. / (4. * q * n) * - (2. * V.squaredNorm() + 4. * c) * // mc.transpose() * V * mc) * - mc * mc.transpose(); + Matrix2Nd Vcs; Vcs. template triangularView() = (sqr(s) * V + + sqr(sqr(s)) * 1. / (4. * q * n) * + (2. * V.squaredNorm() + 4. * c) * // mc.transpose() * V * mc) * + (mc * mc.transpose())); + printIt(&Vcs, "circle_fit - Vcs:"); - Vcs_[0][0] = Vcs.block(0, 0, n, n); + C[0][0] = Vcs.block(0, 0, n, n). template selfadjointView(); Vcs_[0][1] = Vcs.block(0, n, n, n); - Vcs_[1][1] = Vcs.block(n, n, n, n); + C[1][1] = Vcs.block(n, n, n, n). template selfadjointView(); Vcs_[1][0] = Vcs_[0][1].transpose(); printIt(&Vcs, "circle_fit - Vcs:"); } - MatrixNd C[3][3]; // cov matrix of 3D transformed points { - const ArrayNd t0 = (VectorXd::Constant(n, 1.) * p3D.row(0)); - const ArrayNd t1 = (VectorXd::Constant(n, 1.) * p3D.row(1)); - const ArrayNd t00 = p3D.row(0).transpose() * p3D.row(0); - const ArrayNd t01 = p3D.row(0).transpose() * p3D.row(1); - const ArrayNd t11 = p3D.row(1).transpose() * p3D.row(1); - const ArrayNd t10 = t01.transpose(); - C[0][0] = Vcs_[0][0]; + const ArrayNd t0 = (VectorXd::Constant(n, 1.) * p3D.row(0)); + const ArrayNd t1 = (VectorXd::Constant(n, 1.) * p3D.row(1)); + const ArrayNd t00 = p3D.row(0).transpose() * p3D.row(0); + const ArrayNd t01 = p3D.row(0).transpose() * p3D.row(1); + const ArrayNd t11 = p3D.row(1).transpose() * p3D.row(1); + const ArrayNd t10 = t01.transpose(); + Vcs_[0][0] = C[0][0];; C[0][1] = Vcs_[0][1]; C[0][2] = 2. * (Vcs_[0][0] * t0 + Vcs_[0][1] * t1); - C[1][1] = Vcs_[1][1]; + Vcs_[1][1] = C[1][1]; C[1][2] = 2. * (Vcs_[1][0] * t0 + Vcs_[1][1] * t1); - C[2][2] = 2. * (Vcs_[0][0] * Vcs_[0][0] + Vcs_[0][0] * Vcs_[0][1] + Vcs_[1][1] * Vcs_[1][0] + - Vcs_[1][1] * Vcs_[1][1]) + - 4. * (Vcs_[0][0] * t00 + Vcs_[0][1] * t01 + Vcs_[1][0] * t10 + Vcs_[1][1] * t11); + MatrixNd tmp; + tmp. template triangularView() + = ( 2. * (Vcs_[0][0] * Vcs_[0][0] + Vcs_[0][0] * Vcs_[0][1] + Vcs_[1][1] * Vcs_[1][0] + + Vcs_[1][1] * Vcs_[1][1]) + + 4. * (Vcs_[0][0] * t00 + Vcs_[0][1] * t01 + Vcs_[1][0] * t10 + Vcs_[1][1] * t11) ).matrix(); + C[2][2] = tmp. template selfadjointView(); } printIt(&C[0][0], "circle_fit - C[0][0]:"); @@ -755,14 +803,14 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, } printIt(&C0, "circle_fit - C0:"); - const MatrixNd W = weight * weight.transpose(); - const MatrixNd H = MatrixXd::Identity(n, n).rowwise() - weight.transpose(); - const MatrixNx3d s_v = H * p3D.transpose(); + const MatrixNd W = weight * weight.transpose(); + const MatrixNd H = MatrixXd::Identity(n, n).rowwise() - weight.transpose(); + const MatrixNx3d s_v = H * p3D.transpose(); printIt(&W, "circle_fit - W:"); printIt(&H, "circle_fit - H:"); printIt(&s_v, "circle_fit - s_v:"); - MatrixNd D_[3][3]; // cov(s_v) + MatrixNd D_[3][3]; // cov(s_v) { D_[0][0] = (H * C[0][0] * H.transpose()).cwiseProduct(W); D_[0][1] = (H * C[0][1] * H.transpose()).cwiseProduct(W); @@ -787,8 +835,8 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, for (u_int b = a; b < 6; ++b) { const u_int k = nu[b][0], l = nu[b][1]; - VectorNd t0(n); - VectorNd t1(n); + VectorNd t0(n); + VectorNd t1(n); if (l == k) { t0 = 2. * D_[j][l] * s_v.col(l); @@ -865,7 +913,7 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, } printIt(&J3, "circle_fit - J3:"); - const RowVector2Nd Jq = mc.transpose() * s * 1. / n; // var(q) + const RowVector2Nd Jq = mc.transpose() * s * 1. / n; // var(q) printIt(&Jq, "circle_fit - Jq:"); Matrix3d cov_uvr = J3 * Cvc * J3.transpose() * sqr(s_inv) // cov(X0,Y0,R) @@ -875,246 +923,32 @@ __host__ __device__ inline circle_fit Circle_fit(const Matrix2xNd& hits2D, } printIt(&circle.cov, "Circle cov:"); -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("circle_fit - exit\n"); #endif return circle; } -/*! - \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 - uninitialized) and particle charge. - \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 - approximation). - Covariance matrix of the fitted parameter is optionally computed. - Multiple scattering is not handled (yet). - A fast pre-fit is performed in order to evaluate weights and to compute - errors. -*/ - -__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 = 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 - // 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); - 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); - // 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(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.; -// 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 - - // y - p2D.row(1) = hits.row(2); - - // WEIGHT COMPUTATION -#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), theta, B); -#if RFIT_DEBUG - printIt(&cov_with_ms, "line_fit - cov_with_ms: "); -#endif - Matrix4d G; - G = cov_with_ms.inverse(); -#if RFIT_DEBUG - printIt(&G, "line_fit - cov_with_ms.inverse():"); -#endif - double renorm = G.sum(); - G *= 1. / renorm; -#if RFIT_DEBUG - printIt(&G, "line_fit - G4:"); -#endif - - const VectorNd weight = Weight_circle(G); - - // COST FUNCTION - - // compute - // r0 represents the weighted mean of "x" and "y". - const Vector2d r0 = p2D * weight; - // This is the X vector that will be used to build the - // scatter matrix S = X^T * X - const Matrix2xNd X = p2D.colwise() - r0; - Matrix2d A = Matrix2d::Zero(); - A = X * G * X.transpose(); - -#if RFIT_DEBUG - printIt(&A, "Line_fit - A: "); -#endif - - // minimize. v is normalized!! - double chi2; - Vector2d v = min_eigen2D(A, chi2); -#if RFIT_DEBUG - printIt(&v, "Line_fit - v: "); - printf("Line_fit chi2: %e\n", chi2); -#endif - - // This hack to be able to run on GPU where the automatic assignment to a - // double from the vector multiplication is not working. - 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 / 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))); -#endif - - // ERROR PROPAGATION - if (error) - { - const double v0_2 = sqr(v(0)); - const double v1_2 = sqr(v(1)); - - Matrix3d C; // cov(v,c) - { - // The norm is taken from Chernov, properly adapted to the weights case. - double norm = v.transpose() * A * v; -// 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 = norm/(A(0,0) + A(1,1)); - C(0, 0) = sig2 * v1_2; - C(1, 1) = sig2 * v0_2; - 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 - - Eigen::Matrix J; // Jacobian of (v,c) -> (cotan(theta)),Zip) - { - const double t0 = 1. / v(1); - const double t1 = sqr(t0); - J << -t0, v(0) * t1, 0., 0., c * t1, -t0; - } - Eigen::Matrix JT = J.transpose().eval(); -#if RFIT_DEBUG - printIt(&J, "line_fit - J:"); -#endif - line.cov = J * C * JT; - } - -#if RFIT_DEBUG - printIt(&line.cov, "Line cov:"); -#endif - 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) { + template +__host__ __device__ +inline line_fit Line_fit(const M3xN& hits, + const M6xN & hits_ge, + const circle_fit& circle, + const V4& fast_fit, + const double B, + const bool error = true) { + + constexpr uint32_t N = M3xN::ColsAtCompileTime; auto n = hits.cols(); double theta = -circle.q*atan(fast_fit(3)); theta = theta < 0. ? theta + M_PI : theta; + + // Prepare the Rotation Matrix to rotate the points + Eigen::Matrix rot; + rot << sin(theta), cos(theta), -cos(theta), sin(theta); + // PROJECTION ON THE CILINDER // // p2D will be: @@ -1123,16 +957,14 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, // s values will be ordinary x-values // z values will be ordinary y-values - Matrix2xNd p2D(2, n); + Matrix2xNd p2D = Matrix2xNd::Zero(); Eigen::Matrix Jx; - p2D << MatrixXd::Zero(2, n); - Jx << MatrixXd::Zero(2, 6); - -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Line_fit - B: %g\n", B); printIt(&hits, "Line_fit points: "); - printIt(&hits_cov, "Line_fit covs: "); + printIt(&hits_ge, "Line_fit covs: "); + printIt(&rot, "Line_fit rot: "); #endif // x & associated Jacobian // cfr https://indico.cern.ch/event/663159/contributions/2707659/attachments/1517175/2368189/Riemann_fit.pdf @@ -1142,9 +974,8 @@ __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); - Matrix6d Cov(6,6); - Matrix2d Cov_sz_single(2, 2); + Matrix6d Cov = Matrix6d::Zero(); + Matrix2d cov_sz[4]; // FIXME: should be "N" for (u_int i = 0; i < n; ++i) { Vector2d p = hits.block(0, i, 2, 1) - o; @@ -1169,39 +1000,34 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, 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); + Cov(3, 3) = hits_ge.col(i)[0]; // x errors + Cov(4, 4) = hits_ge.col(i)[2]; // y errors + Cov(5, 5) = hits_ge.col(i)[5]; // z errors + Cov(3, 4) = Cov(4, 3) = hits_ge.col(i)[1]; // cov_xy + Cov(3, 5) = Cov(5, 3) = hits_ge.col(i)[3]; // cov_xz + Cov(4, 5) = Cov(5, 4) = hits_ge.col(i)[4]; // cov_yz + Matrix2d tmp = Jx * Cov * Jx.transpose(); + cov_sz[i].noalias() = rot*tmp*rot.transpose(); } // 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:"); + MatrixNd cov_with_ms; + Scatter_cov_line(cov_sz, fast_fit, p2D.row(0), p2D.row(1), theta, B,cov_with_ms); +#ifdef 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; + Matrix2xNd p2D_rot = rot*p2D; -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Fast fit Tan(theta): %g\n", fast_fit(3)); printf("Rotation angle: %g\n", theta); printIt(&rot, "Rotation Matrix:"); @@ -1211,15 +1037,15 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, #endif // Build the A Matrix - Matrix2xNd A(2,n); + Matrix2xNd A; A << MatrixXd::Ones(1, n), p2D_rot.row(0); // rotated s values -#if RFIT_DEBUG +#ifdef 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(); + MatrixNd Vy_inv = cov_with_ms.inverse(); Eigen::Matrix Inv_Cov = A*Vy_inv*A.transpose(); // Compute the Covariance Matrix of the fit parameters @@ -1231,20 +1057,20 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, Eigen::Matrix sol = Cov_params*A*Vy_inv*p2D_rot.row(1).transpose(); -#if RFIT_DEBUG +#ifdef 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(); + Eigen::Matrix J; 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; + VectorNd res = p2D_rot.row(1).transpose() - A.transpose() * sol; double chi2 = res.transpose()*Vy_inv*res; chi2 = chi2 / float(n); @@ -1253,7 +1079,7 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, line.cov << cov_mq; line.chi2 = chi2; -#if RFIT_DEBUG +#ifdef RFIT_DEBUG printf("Common_factor: %g\n", common_factor); printIt(&J, "Jacobian:"); printIt(&sol, "Rotated solutions:"); @@ -1300,19 +1126,22 @@ __host__ __device__ inline line_fit Line_fit(const Matrix3xNd& hits, \bug see Circle_fit(), Line_fit() and Fast_fit() bugs. */ -inline helix_fit Helix_fit(const Matrix3xNd& hits, const Matrix3Nd& hits_cov, const double B, +template +inline helix_fit Helix_fit(const Matrix3xNd& hits, const Eigen::Matrix& hits_ge, const double B, const bool error = true) { u_int n = hits.cols(); - VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + VectorNd<4> rad = (hits.block(0, 0, 2, n).colwise().norm()); // Fast_fit gives back (X0, Y0, R, theta) w/o errors, using only 3 points. - const Vector4d fast_fit = Fast_fit(hits); - + Vector4d fast_fit; + Fast_fit(hits,fast_fit); + Rfit::Matrix2Nd<4> hits_cov = MatrixXd::Zero(2 * n, 2 * n); + Rfit::loadCovariance2D(hits_ge,hits_cov); circle_fit circle = Circle_fit(hits.block(0, 0, 2, n), - hits_cov.block(0, 0, 2 * n, 2 * n), + hits_cov, fast_fit, rad, B, error); - line_fit line = Line_fit(hits, hits_cov, circle, fast_fit, B, error); + line_fit line = Line_fit(hits, hits_ge, circle, fast_fit, B, error); par_uvrtopak(circle, B, error); diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cc index 68343253b670d..2a360b1da58a6 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cc @@ -75,8 +75,7 @@ void PixelTrackReconstructionGPU::run(TracksWithTTRHs& tracks, Rfit::helix_fit * helix_fit_resultsGPU = nullptr; const int points_in_seed = 4; - // We use 3 floats for GlobalPosition and 9 floats for GlobalError (that's what is used by the Riemann fit). - // TODO: optimise dimensions eploiting matrix symmetries + // We use 3 floats for GlobalPosition and 6 floats for GlobalError (that's what is used by the Riemann fit). // Assume a safe maximum of 3K seeds: it will dynamically grow, if needed. int total_seeds = 0; hits_and_covariances.reserve(sizeof(float)*3000*(points_in_seed*12)); @@ -86,15 +85,16 @@ void PixelTrackReconstructionGPU::run(TracksWithTTRHs& tracks, for (unsigned int iHit = 0; iHit < tuplet.size(); ++iHit) { auto const& recHit = tuplet[iHit]; auto point = GlobalPoint(recHit->globalPosition().basicVector() - region.origin().basicVector()); - auto errors = recHit->globalPositionError().matrix4D(); + auto errors = recHit->globalPositionError(); hits_and_covariances.push_back(point.x()); hits_and_covariances.push_back(point.y()); hits_and_covariances.push_back(point.z()); - for (auto j = 0; j < 3; ++j) { - for (auto l = 0; l < 3; ++l) { - hits_and_covariances.push_back(errors(j, l)); - } - } + hits_and_covariances.push_back(errors.cxx()); + hits_and_covariances.push_back(errors.cyx()); + hits_and_covariances.push_back(errors.cyy()); + hits_and_covariances.push_back(errors.czx()); + hits_and_covariances.push_back(errors.czy()); + hits_and_covariances.push_back(errors.czz()); } total_seeds++; } diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cu b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cu index 25b3bc300fbfb..19a91ba8c83b4 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cu +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackReconstructionGPU.cu @@ -12,8 +12,8 @@ KernelFastFitAllHits(float *hits_and_covariances, int cumulative_size, float B, Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, - Rfit::Matrix3Nd *hits_cov, + Rfit::Matrix3xNd<4> *hits, + Eigen::Matrix *hits_ge, Rfit::circle_fit *circle_fit, Vector4d *fast_fit, Rfit::line_fit *line_fit) @@ -36,8 +36,6 @@ KernelFastFitAllHits(float *hits_and_covariances, blockDim.x, blockIdx.x, threadIdx.x, start, cumulative_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 (stack) for (unsigned int i = 0; i < hits_in_fit; ++i) { @@ -45,22 +43,20 @@ KernelFastFitAllHits(float *hits_and_covariances, hits_and_covariances[start + 1], hits_and_covariances[start + 2]; start += 3; - for (auto j = 0; j < 3; ++j) { - for (auto l = 0; l < 3; ++l) { - hits_cov[helix_start](i + j * hits_in_fit, i + l * hits_in_fit) = - hits_and_covariances[start]; - start++; - } - } + hits_ge[helix_start].col(i) << hits_and_covariances[start], + hits_and_covariances[start + 1], hits_and_covariances[start + 2], + hits_and_covariances[start + 3], hits_and_covariances[start + 4], + hits_and_covariances[start + 5]; + start += 6; } - fast_fit[helix_start] = Rfit::Fast_fit(hits[helix_start]); + Rfit::Fast_fit(hits[helix_start],fast_fit[helix_start]); } __global__ void KernelCircleFitAllHits(float *hits_and_covariances, int hits_in_fit, int cumulative_size, float B, Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, Rfit::Matrix3Nd *hits_cov, + Rfit::Matrix3xNd<4> *hits, Eigen::Matrix *hits_ge, Rfit::circle_fit *circle_fit, Vector4d *fast_fit, Rfit::line_fit *line_fit) { @@ -84,11 +80,14 @@ KernelCircleFitAllHits(float *hits_and_covariances, int hits_in_fit, #endif u_int n = hits[helix_start].cols(); - Rfit::VectorNd rad = (hits[helix_start].block(0, 0, 2, n).colwise().norm()); + constexpr uint32_t N = 4; + Rfit::VectorNd rad = (hits[helix_start].block(0, 0, 2, n).colwise().norm()); + Rfit::Matrix2Nd hits_cov = MatrixXd::Zero(2 * n, 2 * n); + Rfit::loadCovariance2D(hits_ge[helix_start],hits_cov); 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), + hits_cov, fast_fit[helix_start], rad, B, true); #ifdef GPU_DEBUG @@ -105,7 +104,7 @@ KernelCircleFitAllHits(float *hits_and_covariances, int hits_in_fit, __global__ void KernelLineFitAllHits(float *hits_and_covariances, int hits_in_fit, int cumulative_size, float B, Rfit::helix_fit *results, - Rfit::Matrix3xNd *hits, Rfit::Matrix3Nd *hits_cov, + Rfit::Matrix3xNd<4> *hits, Eigen::Matrix *hits_ge, Rfit::circle_fit *circle_fit, Vector4d *fast_fit, Rfit::line_fit *line_fit) { @@ -130,7 +129,7 @@ KernelLineFitAllHits(float *hits_and_covariances, int hits_in_fit, #endif line_fit[helix_start] = - Rfit::Line_fit(hits[helix_start], hits_cov[helix_start], + Rfit::Line_fit(hits[helix_start], hits_ge[helix_start], circle_fit[helix_start], fast_fit[helix_start], B, true); par_uvrtopak(circle_fit[helix_start], B, true); @@ -166,13 +165,13 @@ void PixelTrackReconstructionGPU::launchKernelFit( int num_blocks = cumulative_size / (hits_in_fit * 12) / threads_per_block.x + 1; auto numberOfSeeds = cumulative_size / (hits_in_fit * 12); - Rfit::Matrix3xNd *hitsGPU; - cudaCheck(cudaMalloc(&hitsGPU, 48 * numberOfSeeds * sizeof(Rfit::Matrix3xNd(3, 4)))); - cudaCheck(cudaMemset(hitsGPU, 0x00, 48 * numberOfSeeds * sizeof(Rfit::Matrix3xNd(3, 4)))); + Rfit::Matrix3xNd<4> *hitsGPU; + cudaCheck(cudaMalloc(&hitsGPU, 48 * numberOfSeeds * sizeof(Rfit::Matrix3xNd<4>))); + cudaCheck(cudaMemset(hitsGPU, 0x00, 48 * numberOfSeeds * sizeof(Rfit::Matrix3xNd<4>))); - Rfit::Matrix3Nd *hits_covGPU = nullptr; - cudaCheck(cudaMalloc(&hits_covGPU, 48 * numberOfSeeds * sizeof(Rfit::Matrix3Nd(12, 12)))); - cudaCheck(cudaMemset(hits_covGPU, 0x00, 48 * numberOfSeeds * sizeof(Rfit::Matrix3Nd(12, 12)))); + Eigen::Matrix *hits_geGPU = nullptr; + cudaCheck(cudaMalloc(&hits_geGPU, 48 * numberOfSeeds * sizeof(Eigen::Matrix))); + cudaCheck(cudaMemset(hits_geGPU, 0x00, 48 * numberOfSeeds * sizeof(Eigen::Matrix))); Vector4d *fast_fit_resultsGPU = nullptr; cudaCheck(cudaMalloc(&fast_fit_resultsGPU, 48 * numberOfSeeds * sizeof(Vector4d))); @@ -188,24 +187,24 @@ void PixelTrackReconstructionGPU::launchKernelFit( KernelFastFitAllHits<<>>( hits_and_covariancesGPU, hits_in_fit, cumulative_size, B, results, - hitsGPU, hits_covGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, + hitsGPU, hits_geGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); cudaCheck(cudaGetLastError()); KernelCircleFitAllHits<<>>( hits_and_covariancesGPU, hits_in_fit, cumulative_size, B, results, - hitsGPU, hits_covGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, + hitsGPU, hits_geGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); cudaCheck(cudaGetLastError()); KernelLineFitAllHits<<>>( hits_and_covariancesGPU, hits_in_fit, cumulative_size, B, results, - hitsGPU, hits_covGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, + hitsGPU, hits_geGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); cudaCheck(cudaGetLastError()); cudaFree(hitsGPU); - cudaFree(hits_covGPU); + cudaFree(hits_geGPU); cudaFree(fast_fit_resultsGPU); cudaFree(circle_fit_resultsGPU); cudaFree(line_fit_resultsGPU); diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc index 41778e0571438..45ee6266bcc31 100644 --- a/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelFitterByRiemannParaboloid.cc @@ -59,25 +59,20 @@ std::unique_ptr PixelFitterByRiemannParaboloid::run( isBarrel[i] = recHit->detUnit()->type().isBarrel(); } - Eigen::Matrix riemannHits(3, nhits); + assert(nhits==4); + Rfit::Matrix3xNd<4> riemannHits; - Eigen::Matrix riemannHits_cov = - MatrixXd::Zero(3 * nhits, 3 * nhits); + Eigen::Matrix riemannHits_ge = Eigen::Matrix::Zero(); for (unsigned int i = 0; i < nhits; ++i) { riemannHits.col(i) << points[i].x(), points[i].y(), points[i].z(); - const auto& errorMatrix = errors[i].matrix4D(); - - for (auto j = 0; j < 3; ++j) { - for (auto l = 0; l < 3; ++l) { - riemannHits_cov(i + j * nhits, i + l * nhits) = errorMatrix(j, l); - } - } + riemannHits_ge.col(i) << errors[i].cxx(), errors[i].cyx(), errors[i].cyy(), + errors[i].czx(), errors[i].czy(), errors[i].czz(); } float bField = 1 / PixelRecoUtilities::fieldInInvGev(*es_); - helix_fit fittedTrack = Rfit::Helix_fit(riemannHits, riemannHits_cov, bField, useErrors_); + helix_fit fittedTrack = Rfit::Helix_fit(riemannHits, riemannHits_ge, bField, useErrors_); int iCharge = fittedTrack.q; // parameters are: diff --git a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc index b630d0ee4ae27..adcabd7dde508 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc +++ b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc @@ -25,9 +25,10 @@ using Vector6d = Eigen::Matrix; using Vector8d = Eigen::Matrix; }; // namespace Rfit +// quadruplets... struct hits_gen { - Matrix3xNd hits; - Matrix3Nd hits_cov; + Matrix3xNd<4> hits; + Eigen::Matrix hits_ge; Vector5d true_par; }; @@ -71,30 +72,31 @@ void smearing(const Vector5d& err, const bool& isbarrel, double& x, double& y, d } } -void Hits_cov(Matrix3Nd& V, const unsigned int& i, const unsigned int& n, const Matrix3xNd& hits, +template +void Hits_cov(Eigen::Matrix & V, const unsigned int& i, const unsigned int& n, const Matrix3xNd& hits, const Vector5d& err, bool isbarrel) { if (isbarrel) { double R2 = Rfit::sqr(hits(0, i)) + Rfit::sqr(hits(1, i)); - V(i, i) = + V.col(i)[0] = (Rfit::sqr(err[1]) * Rfit::sqr(hits(1, i)) + Rfit::sqr(err[0]) * Rfit::sqr(hits(0, i))) / R2; - V(i + n, i + n) = + V.col(i)[2] = (Rfit::sqr(err[1]) * Rfit::sqr(hits(0, i)) + Rfit::sqr(err[0]) * Rfit::sqr(hits(1, i))) / R2; - V(i, i + n) = V(i + n, i) = + V.col(i)[1] = (Rfit::sqr(err[0]) - Rfit::sqr(err[1])) * hits(1, i) * hits(0, i) / R2; - V(i + 2 * n, i + 2 * n) = Rfit::sqr(err[2]); + V.col(i)[5] = Rfit::sqr(err[2]); } else { - V(i, i) = Rfit::sqr(err[3]); - V(i + n, i + n) = Rfit::sqr(err[3]); - V(i + 2 * n, i + 2 * n) = Rfit::sqr(err[4]); + V.col(i)[0] = Rfit::sqr(err[3]); + V.col(i)[2] = Rfit::sqr(err[3]); + V.col(i)[5] = Rfit::sqr(err[4]); } } hits_gen Hits_gen(const unsigned int& n, const Matrix& gen_par) { hits_gen gen; gen.hits = MatrixXd::Zero(3, n); - gen.hits_cov = MatrixXd::Zero(3 * n, 3 * n); + gen.hits_ge = Eigen::Matrix::Zero(); // err /= 10000.; constexpr double rad[8] = {2.95, 6.8, 10.9, 16., 3.1, 7., 11., 16.2}; // constexpr double R_err[8] = {5./10000, 5./10000, 5./10000, 5./10000, 5./10000, @@ -128,7 +130,7 @@ hits_gen Hits_gen(const unsigned int& n, const Matrix& gen_par) { Vector5d err; err << R_err[i], Rp_err[i], z_err[i], 0, 0; smearing(err, true, gen.hits(0, i), gen.hits(1, i), gen.hits(2, i)); - Hits_cov(gen.hits_cov, i, n, gen.hits, err, true); + Hits_cov(gen.hits_ge, i, n, gen.hits, err, true); } return gen; @@ -371,8 +373,7 @@ void test_helix_fit(bool getcin) { 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; + // Matrix3xNd<4> hits; std::array helixRiemann_fit; // std::array helixBrokenLine_fit; @@ -395,7 +396,7 @@ void test_helix_fit(bool getcin) { // 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); + helixRiemann_fit[i%iteration] = Rfit::Helix_fit(gen.hits, gen.hits_ge, B_field, return_err); delta += std::chrono::high_resolution_clock::now()-start; // helixBrokenLine_fit[i] = BrokenLine::Helix_fit(gen.hits, gen.hits_cov, B_field); @@ -417,7 +418,7 @@ void test_helix_fit(bool getcin) { << "covariance matrix:" << endl << helixRiemann_fit[i].cov << endl << "Initial hits:\n" << gen.hits << endl - << "Initial Covariance:\n" << gen.hits_cov << endl; + << "Initial Covariance:\n" << gen.hits_ge << endl; } std::cout << "elapsted time " << double(std::chrono::duration_cast(delta).count())/1.e6 << std::endl; diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu index 3338b0bce57b1..085b23bba47ed 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu @@ -3,82 +3,61 @@ #include #include + #include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" -#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" #include "test_common.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" using namespace Eigen; -/* -__global__ -void kernelFullFit(Rfit::Matrix3xNd * hits, - Rfit::Matrix3Nd * hits_cov, - double B, - bool errors, - Rfit::circle_fit * circle_fit_resultsGPU, - Rfit::line_fit * line_fit_resultsGPU) { - - printf("hits size: %d,%d\n", hits->rows(), hits->cols()); - Rfit::printIt(hits, "KernelFulFit - input hits: "); - Vector4d fast_fit = Rfit::Fast_fit(*hits); - - u_int n = hits->cols(); - 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); - - (*circle_fit_resultsGPU) = - Rfit::Circle_fit(hits2D_local, hits_cov2D_local, - fast_fit, rad, B, errors); - - (*line_fit_resultsGPU) = Rfit::Line_fit(*hits, *hits_cov, *circle_fit_resultsGPU, fast_fit, errors); +namespace Rfit { + constexpr uint32_t maxNumberOfTracks() { return 5*1024; } + constexpr uint32_t stride() { return maxNumberOfTracks();} + using Matrix3x4d = Eigen::Matrix; + using Map3x4d = Eigen::Map >; + using Matrix6x4f = Eigen::Matrix; + using Map6x4f = Eigen::Map >; + using Map4d = Eigen::Map >; - return; } -*/ __global__ -void kernelFastFit(Rfit::Matrix3xNd * hits, Vector4d * results) { +void kernelFastFit(double * __restrict__ phits, double * __restrict__ presults) { auto i = blockIdx.x*blockDim.x + threadIdx.x; - results[i] = Rfit::Fast_fit(hits[i]); + Rfit::Map3x4d hits(phits+i,3,4); + Rfit::Map4d result(presults+i,4); + Rfit::Fast_fit(hits, result); } __global__ -void kernelCircleFit(Rfit::Matrix3xNd * hits, - Rfit::Matrix3Nd * hits_cov, Vector4d * fast_fit_input, double B, +void kernelCircleFit(double * __restrict__ phits, + float * __restrict__ phits_ge, + double * __restrict__ pfast_fit_input, + double B, Rfit::circle_fit * circle_fit_resultsGPU) { - 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)); - printf("fast_fit_input(1): %f\n", (*fast_fit_input)(1)); - printf("fast_fit_input(2): %f\n", (*fast_fit_input)(2)); - printf("fast_fit_input(3): %f\n", (*fast_fit_input)(3)); + +auto i = blockIdx.x*blockDim.x + threadIdx.x; + Rfit::Map3x4d hits(phits+i,3,4); + Rfit::Map4d fast_fit_input(pfast_fit_input+i,4); + Rfit::Map6x4f hits_ge(phits_ge+i,6,4); + + constexpr uint32_t N = Rfit::Map3x4d::ColsAtCompileTime; + constexpr auto n = N; + + Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + + Rfit::Matrix2Nd hits_cov = MatrixXd::Zero(2 * n, 2 * n); + Rfit::loadCovariance2D(hits_ge,hits_cov); + +#ifdef TEST_DEBUG +if (0==i) { + printf("hits %f, %f\n", hits.block(0,0,2,n)(0,0), hits.block(0,0,2,n)(0,1)); + printf("hits %f, %f\n", hits.block(0,0,2,n)(1,0), hits.block(0,0,2,n)(1,1)); + printf("fast_fit_input(0): %f\n", fast_fit_input(0)); + printf("fast_fit_input(1): %f\n", fast_fit_input(1)); + printf("fast_fit_input(2): %f\n", fast_fit_input(2)); + printf("fast_fit_input(3): %f\n", fast_fit_input(3)); printf("rad(0,0): %f\n", rad(0,0)); printf("rad(1,1): %f\n", rad(1,1)); printf("rad(2,2): %f\n", rad(2,2)); @@ -87,99 +66,125 @@ void kernelCircleFit(Rfit::Matrix3xNd * hits, printf("hits_cov(2,2): %f\n", (*hits_cov)(2,2)); printf("hits_cov(11,11): %f\n", (*hits_cov)(11,11)); printf("B: %f\n", B); +} #endif 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); + Rfit::Circle_fit(hits.block(0,0,2,n), hits_cov, + fast_fit_input, rad, B, true); +#ifdef TEST_DEBUG +if (0==i) { + printf("Circle param %f,%f,%f\n",circle_fit_resultsGPU[i].par(0),circle_fit_resultsGPU[i].par(1),circle_fit_resultsGPU[i].par(2)); +} +#endif } __global__ -void kernelLineFit(Rfit::Matrix3xNd * hits, - Rfit::Matrix3Nd * hits_cov, +void kernelLineFit(double * __restrict__ phits, + float * __restrict__ phits_ge, Rfit::circle_fit * circle_fit, - Vector4d * fast_fit, + double * __restrict__ pfast_fit, Rfit::line_fit * line_fit) { 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); + Rfit::Map3x4d hits(phits+i,3,4); + Rfit::Map4d fast_fit(pfast_fit+i,4); + Rfit::Map6x4f hits_ge(phits_ge+i,6,4); + line_fit[i] = Rfit::Line_fit(hits, hits_ge, circle_fit[i], fast_fit, true); } -void fillHitsAndHitsCov(Rfit::Matrix3xNd & hits, Rfit::Matrix3Nd & hits_cov) { +template +__device__ __host__ +void fillHitsAndHitsCov(M3x4 & hits, M6x4 & hits_ge) { 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; + hits_ge.col(0)[0] = 7.14652e-06; + hits_ge.col(1)[0] = 2.15789e-06; + hits_ge.col(2)[0] = 1.63328e-06; + hits_ge.col(3)[0] = 6.27919e-06; + hits_ge.col(0)[2] = 6.10348e-06; + hits_ge.col(1)[2] = 2.08211e-06; + hits_ge.col(2)[2] = 1.61672e-06; + hits_ge.col(3)[2] = 6.28081e-06; + hits_ge.col(0)[5] = 5.184e-05; + hits_ge.col(1)[5] = 1.444e-05; + hits_ge.col(2)[5] = 6.25e-06; + hits_ge.col(3)[5] = 3.136e-05; + hits_ge.col(0)[1] = -5.60077e-06; + hits_ge.col(1)[1] = -1.11936e-06; + hits_ge.col(2)[1] = -6.24945e-07; + hits_ge.col(3)[1] = -5.28e-06; +} + +__global__ +void kernelFillHitsAndHitsCov(double * __restrict__ phits, + float * phits_ge) { + auto i = blockIdx.x*blockDim.x + threadIdx.x; + Rfit::Map3x4d hits(phits+i,3,4); + Rfit::Map6x4f hits_ge(phits_ge+i,6,4); + hits_ge = MatrixXf::Zero(6,4); + fillHitsAndHitsCov(hits,hits_ge); } void testFit() { constexpr double B = 0.0113921; - Rfit::Matrix3xNd hits(3,4); - Rfit::Matrix3Nd hits_cov = MatrixXd::Zero(12,12); - Rfit::Matrix3xNd * hitsGPU = nullptr;; - Rfit::Matrix3Nd * hits_covGPU = nullptr; - Vector4d * fast_fit_resultsGPU = nullptr; - Vector4d * fast_fit_resultsGPUret = new Vector4d(); + Rfit::Matrix3xNd<4> hits; + Rfit::Matrix6x4f hits_ge = MatrixXf::Zero(6,4); + double * hitsGPU = nullptr;; + float * hits_geGPU = nullptr; + double * fast_fit_resultsGPU = nullptr; + double * fast_fit_resultsGPUret = new double[Rfit::maxNumberOfTracks()*sizeof(Vector4d)]; 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); + fillHitsAndHitsCov(hits, hits_ge); + + std::cout << "sizes " << sizeof(hits) << ' ' << sizeof(hits_ge) + << ' ' << sizeof(Vector4d)<< std::endl; + std::cout << "Generated hits:\n" << hits << std::endl; + std::cout << "Generated cov:\n" << hits_ge << std::endl; // FAST_FIT_CPU - Vector4d fast_fit_results = Rfit::Fast_fit(hits); -#if TEST_DEBUG - std::cout << "Generated hits:\n" << hits << std::endl; -#endif + Vector4d fast_fit_results; Rfit::Fast_fit(hits, fast_fit_results); std::cout << "Fitted values (FastFit, [X0, Y0, R, tan(theta)]):\n" << fast_fit_results << std::endl; // 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))); + cudaCheck(cudaMalloc(&hits_geGPU, Rfit::maxNumberOfTracks()*sizeof(Rfit::Matrix6x4f))); + cudaCheck(cudaMalloc(&fast_fit_resultsGPU, Rfit::maxNumberOfTracks()*sizeof(Vector4d))); + cudaCheck(cudaMalloc((void **)&line_fit_resultsGPU, Rfit::maxNumberOfTracks()*sizeof(Rfit::line_fit))); + cudaCheck(cudaMalloc((void **)&circle_fit_resultsGPU, Rfit::maxNumberOfTracks()*sizeof(Rfit::circle_fit))); + + + kernelFillHitsAndHitsCov<<>>(hitsGPU,hits_geGPU); // FAST_FIT GPU kernelFastFit<<>>(hitsGPU, fast_fit_resultsGPU); cudaDeviceSynchronize(); - cudaMemcpy(fast_fit_resultsGPUret, fast_fit_resultsGPU, sizeof(Vector4d), cudaMemcpyDeviceToHost); - std::cout << "Fitted values (FastFit, [X0, Y0, R, tan(theta)]): GPU\n" << *fast_fit_resultsGPUret << std::endl; - assert(isEqualFuzzy(fast_fit_results, (*fast_fit_resultsGPUret))); + cudaMemcpy(fast_fit_resultsGPUret, fast_fit_resultsGPU, Rfit::maxNumberOfTracks()*sizeof(Vector4d), cudaMemcpyDeviceToHost); + Rfit::Map4d fast_fit(fast_fit_resultsGPUret+10,4); + std::cout << "Fitted values (FastFit, [X0, Y0, R, tan(theta)]): GPU\n" << fast_fit << std::endl; + assert(isEqualFuzzy(fast_fit_results, fast_fit)); // CIRCLE_FIT CPU - u_int n = hits.cols(); - Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + constexpr uint32_t N = Rfit::Map3x4d::ColsAtCompileTime; + constexpr auto n = N; + Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + Rfit::Matrix2Nd hits_cov = MatrixXd::Zero(2 * n, 2 * n); + Rfit::loadCovariance2D(hits_ge,hits_cov); 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); + hits_cov, + fast_fit_results, rad, B, true); std::cout << "Fitted values (CircleFit):\n" << circle_fit_results.par << std::endl; // CIRCLE_FIT GPU - kernelCircleFit<<>>(hitsGPU, hits_covGPU, + kernelCircleFit<<>>(hitsGPU, hits_geGPU, fast_fit_resultsGPU, B, circle_fit_resultsGPU); cudaDeviceSynchronize(); @@ -189,96 +194,30 @@ void testFit() { assert(isEqualFuzzy(circle_fit_results.par, circle_fit_resultsGPUret->par)); // LINE_FIT CPU - Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_cov, circle_fit_results, fast_fit_results, true); + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, true); std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; // LINE_FIT GPU Rfit::line_fit * line_fit_resultsGPUret = new Rfit::line_fit(); - kernelLineFit<<>>(hitsGPU, hits_covGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); + kernelLineFit<<>>(hitsGPU, hits_geGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); cudaDeviceSynchronize(); cudaMemcpy(line_fit_resultsGPUret, line_fit_resultsGPU, sizeof(Rfit::line_fit), cudaMemcpyDeviceToHost); std::cout << "Fitted values (LineFit) GPU:\n" << line_fit_resultsGPUret->par << std::endl; 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); - Rfit::Matrix3Nd hits_cov = MatrixXd::Zero(12,12); - fillHitsAndHitsCov(hits, hits_cov); - - // FAST_FIT_CPU - Vector4d fast_fit_results = Rfit::Fast_fit(hits); - // CIRCLE_FIT CPU - u_int n = hits.cols(); - Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + std::cout << "Fitted cov (CircleFit) CPU:\n" << circle_fit_results.cov << std::endl; + std::cout << "Fitted cov (LineFit): CPU\n" << line_fit_results.cov << std::endl; + std::cout << "Fitted cov (CircleFit) GPU:\n" << circle_fit_resultsGPUret->cov << std::endl; + std::cout << "Fitted cov (LineFit): GPU\n" << line_fit_resultsGPUret->cov << std::endl; - 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, errors); - // LINE_FIT CPU - Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_cov, circle_fit_results, - fast_fit_results, errors); - - // FIT GPU - std::cout << "GPU FIT" << std::endl; - Rfit::Matrix3xNd * hitsGPU = nullptr; // new Rfit::Matrix3xNd(3,4); - Rfit::Matrix3Nd * hits_covGPU = nullptr; - Rfit::line_fit * line_fit_resultsGPU = nullptr; - Rfit::line_fit * line_fit_resultsGPUret = new Rfit::line_fit(); - Rfit::circle_fit * circle_fit_resultsGPU = nullptr; // new Rfit::circle_fit(); - Rfit::circle_fit * circle_fit_resultsGPUret = new Rfit::circle_fit(); - - // 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()); - - cudaCheck(cudaMemcpy(circle_fit_resultsGPUret, circle_fit_resultsGPU, sizeof(Rfit::circle_fit), cudaMemcpyDeviceToHost)); - cudaCheck(cudaMemcpy(line_fit_resultsGPUret, line_fit_resultsGPU, sizeof(Rfit::line_fit), cudaMemcpyDeviceToHost)); - - std::cout << "Fitted values (CircleFit) CPU:\n" << circle_fit_results.par << std::endl; - std::cout << "Fitted values (LineFit): CPU\n" << line_fit_results.par << std::endl; - std::cout << "Fitted values (CircleFit) GPU:\n" << circle_fit_resultsGPUret->par << std::endl; - std::cout << "Fitted values (LineFit): GPU\n" << line_fit_resultsGPUret->par << std::endl; - assert(isEqualFuzzy(circle_fit_results.par, circle_fit_resultsGPUret->par, epsilon)); - assert(isEqualFuzzy(line_fit_results.par, line_fit_resultsGPUret->par, epsilon)); - - cudaCheck(cudaFree(hitsGPU)); - cudaCheck(cudaFree(hits_covGPU)); - cudaCheck(cudaFree(line_fit_resultsGPU)); - cudaCheck(cudaFree(circle_fit_resultsGPU)); - delete line_fit_resultsGPUret; - delete circle_fit_resultsGPUret; - - cudaDeviceReset(); } -*/ int main (int argc, char * argv[]) { 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 index 5467cc27a6e2c..401ab7142a001 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp +++ b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp @@ -4,62 +4,81 @@ #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) { +namespace Rfit { + constexpr uint32_t maxNumberOfTracks() { return 5*1024; } + constexpr uint32_t stride() { return maxNumberOfTracks();} + using Matrix3x4d = Eigen::Matrix; + using Map3x4d = Eigen::Map >; + using Matrix6x4f = Eigen::Matrix; + using Map6x4f = Eigen::Map >; + using Map4d = Eigen::Map >; + +} + +template +void fillHitsAndHitsCov(M3x4 & hits, M6x4 & hits_ge) { 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; + hits_ge.col(0)[0] = 7.14652e-06; + hits_ge.col(1)[0] = 2.15789e-06; + hits_ge.col(2)[0] = 1.63328e-06; + hits_ge.col(3)[0] = 6.27919e-06; + hits_ge.col(0)[2] = 6.10348e-06; + hits_ge.col(1)[2] = 2.08211e-06; + hits_ge.col(2)[2] = 1.61672e-06; + hits_ge.col(3)[2] = 6.28081e-06; + hits_ge.col(0)[5] = 5.184e-05; + hits_ge.col(1)[5] = 1.444e-05; + hits_ge.col(2)[5] = 6.25e-06; + hits_ge.col(3)[5] = 3.136e-05; + hits_ge.col(0)[1] = -5.60077e-06; + hits_ge.col(1)[1] = -1.11936e-06; + hits_ge.col(2)[1] = -6.24945e-07; + hits_ge.col(3)[1] = -5.28e-06; } void testFit() { constexpr double B = 0.0113921; - Rfit::Matrix3xNd hits(3,4); - Rfit::Matrix3Nd hits_cov = MatrixXd::Zero(12,12); + Rfit::Matrix3xNd<4> hits; + Rfit::Matrix6x4f hits_ge = MatrixXf::Zero(6,4); - fillHitsAndHitsCov(hits, hits_cov); - std::cout << "Generated hits:\n" << hits << std::endl; + fillHitsAndHitsCov(hits, hits_ge); + + std::cout << "sizes " << sizeof(hits) << ' ' << sizeof(hits_ge) + << ' ' << sizeof(Vector4d)<< std::endl; + std::cout << "Generated hits:\n" << hits << std::endl; + std::cout << "Generated cov:\n" << hits_ge << std::endl; // FAST_FIT_CPU - Vector4d fast_fit_results = Rfit::Fast_fit(hits); - + Vector4d fast_fit_results; Rfit::Fast_fit(hits, fast_fit_results); 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()); + constexpr uint32_t N = Rfit::Map3x4d::ColsAtCompileTime; + constexpr auto n = N; + Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + Rfit::Matrix2Nd hits_cov = MatrixXd::Zero(2 * n, 2 * n); + Rfit::loadCovariance2D(hits_ge,hits_cov); 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); + hits_cov, + fast_fit_results, rad, B, true); 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); + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, true); std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; + std::cout << "Fitted cov (CircleFit) CPU:\n" << circle_fit_results.cov << std::endl; + std::cout << "Fitted cov (LineFit): CPU\n" << line_fit_results.cov << std::endl; } int main (int argc, char * argv[]) { diff --git a/RecoPixelVertexing/PixelTrackFitting/test/test_common.h b/RecoPixelVertexing/PixelTrackFitting/test/test_common.h index e22fb5cfbf59b..79bb128eeec8a 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/test_common.h +++ b/RecoPixelVertexing/PixelTrackFitting/test/test_common.h @@ -5,14 +5,10 @@ #include #include -#ifndef TEST_DEBUG -#define TEST_DEBUG 0 -#endif - template __host__ __device__ void printIt(C * m) { -#if TEST_DEBUG +#ifdef TEST_DEBUG printf("\nMatrix %dx%d\n", (int)m->rows(), (int)m->cols()); for (u_int r = 0; r < m->rows(); ++r) { for (u_int c = 0; c < m->cols(); ++c) { @@ -22,8 +18,8 @@ void printIt(C * m) { #endif } -template -bool isEqualFuzzy(C a, C b, double epsilon = 1e-6) { +template +bool isEqualFuzzy(C1 a, C2 b, double epsilon = 1e-6) { for (unsigned int i = 0; i < a.rows(); ++i) { for (unsigned int j = 0; j < a.cols(); ++j) { assert(std::abs(a(i,j)-b(i,j)) @@ -37,6 +33,7 @@ bool isEqualFuzzy(double a, double b, double epsilon=1e-6) { return std::abs(a-b) < std::min(std::abs(a), std::abs(b))*epsilon; } + template void fillMatrix(T & t) { std::random_device rd; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc index 0fdca092e5e6d..3b865e5a031c9 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc @@ -1,4 +1,5 @@ #include "RiemannFitOnGPU.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" void RiemannFitOnGPU::allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit::helix_fit * helix_fit_results) { @@ -7,17 +8,17 @@ void RiemannFitOnGPU::allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit: 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(&hitsGPU_, maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd<4>))); + cudaCheck(cudaMemset(hitsGPU_, 0x00, maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd<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(&hits_geGPU_, maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix6x4f))); + cudaCheck(cudaMemset(hits_geGPU_, 0x00, maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix6x4f))); - cudaCheck(cudaMalloc(&fast_fit_resultsGPU_, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d))); - cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 48 * maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d))); + cudaCheck(cudaMalloc(&fast_fit_resultsGPU_, maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d))); + cudaCheck(cudaMemset(fast_fit_resultsGPU_, 0x00, 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(&circle_fit_resultsGPU_, maxNumberOfConcurrentFits_ * sizeof(Rfit::circle_fit))); + cudaCheck(cudaMemset(circle_fit_resultsGPU_, 0x00, 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))); @@ -27,7 +28,7 @@ void RiemannFitOnGPU::allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit: void RiemannFitOnGPU::deallocateOnGPU() { cudaFree(hitsGPU_); - cudaFree(hits_covGPU_); + cudaFree(hits_geGPU_); 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 index d13c428179c94..26648018cbd99 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -25,13 +25,13 @@ __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, + double * __restrict__ phits, + float * __restrict__ phits_ge, + double * __restrict__ pfast_fit, uint32_t offset) { - assert(fast_fit); assert(foundNtuplets); + assert(pfast_fit); assert(foundNtuplets); auto local_start = (blockIdx.x * blockDim.x + threadIdx.x); auto helix_start = local_start + offset; @@ -41,9 +41,9 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl return; } - - hits[local_start].resize(3, hits_in_fit); - hits_cov[local_start].resize(3 * hits_in_fit, 3 * hits_in_fit); + Rfit::Map3x4d hits(phits+local_start,3,4); + Rfit::Map4d fast_fit(pfast_fit+local_start,4); + Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); // Prepare data structure auto const * hitId = foundNtuplets->begin(helix_start); @@ -54,44 +54,16 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl 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]; - + hits.col(i) << hhp->xg_d[hit], hhp->yg_d[hit], hhp->zg_d[hit]; + hits_ge.col(i) << ge[0],ge[1],ge[2],ge[3],ge[4],ge[5]; } - fast_fit[local_start] = Rfit::Fast_fit(hits[local_start]); + Rfit::Fast_fit(hits,fast_fit); // 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)); + assert(fast_fit(0)==fast_fit(0)); + assert(fast_fit(1)==fast_fit(1)); + assert(fast_fit(2)==fast_fit(2)); + assert(fast_fit(3)==fast_fit(3)); } @@ -99,10 +71,10 @@ __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, + double * __restrict__ phits, + float * __restrict__ phits_ge, + double * __restrict__ pfast_fit_input, Rfit::circle_fit *circle_fit, - Rfit::Vector4d const * __restrict__ fast_fit, uint32_t offset) { assert(circle_fit); @@ -115,18 +87,26 @@ void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtu return; } - auto n = hits[local_start].cols(); + Rfit::Map3x4d hits(phits+local_start,3,4); + Rfit::Map4d fast_fit(pfast_fit_input+local_start,4); + Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); + + constexpr uint32_t N = Rfit::Map3x4d::ColsAtCompileTime; + constexpr auto n = N; - Rfit::VectorNd rad = (hits[local_start].block(0, 0, 2, n).colwise().norm()); + Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); + + Rfit::Matrix2Nd hits_cov = Rfit::Matrix2Nd<4>::Zero(); + Rfit::loadCovariance2D(hits_ge,hits_cov); circle_fit[local_start] = - Rfit::Circle_fit(hits[local_start].block(0, 0, 2, n), - hits_cov[local_start].block(0, 0, 2 * n, 2 * n), - fast_fit[local_start], rad, B, true); + Rfit::Circle_fit(hits.block(0, 0, 2, n), + hits_cov, + fast_fit, rad, B, true); #ifdef GPU_DEBUG printf("kernelCircleFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, - circle_fit[local_start].par(0), circle_fit[local_start].par(1), circle_fit[local_start].par(2)); + circle_fit.par(0), circle_fit.par(1), circle_fit.par(2)); #endif } @@ -135,10 +115,10 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl int hits_in_fit, double B, Rfit::helix_fit *results, - Rfit::Matrix3xNd const * __restrict__ hits, - Rfit::Matrix3Nd const * __restrict__ hits_cov, + double * __restrict__ phits, + float * __restrict__ phits_ge, + double * __restrict__ pfast_fit, Rfit::circle_fit * __restrict__ circle_fit, - Rfit::Vector4d const * __restrict__ fast_fit, Rfit::line_fit *line_fit, uint32_t offset) { @@ -153,7 +133,10 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl return; } - line_fit[local_start] = Rfit::Line_fit(hits[local_start], hits_cov[local_start], circle_fit[local_start], fast_fit[local_start], B, true); + Rfit::Map3x4d hits(phits+local_start,3,4); + Rfit::Map4d fast_fit(pfast_fit+local_start,4); + Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); + line_fit[local_start] = Rfit::Line_fit(hits, hits_ge, circle_fit[local_start], fast_fit, true); par_uvrtopak(circle_fit[local_start], B, true); @@ -189,18 +172,18 @@ void RiemannFitOnGPU::launchKernels(HitsOnCPU const & hh, uint32_t nhits, uint32 for (uint32_t offset=0; offset>>( tuples_d, hh.gpu_d, 4, - hitsGPU_, hits_covGPU_, fast_fit_resultsGPU_,offset); + hitsGPU_, hits_geGPU_, fast_fit_resultsGPU_,offset); cudaCheck(cudaGetLastError()); kernelCircleFitAllHits<<>>( tuples_d, 4, bField_, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, offset); + hitsGPU_, hits_geGPU_, fast_fit_resultsGPU_, circle_fit_resultsGPU_, offset); cudaCheck(cudaGetLastError()); kernelLineFitAllHits<<>>( tuples_d, 4, bField_, helix_fit_results_d, - hitsGPU_, hits_covGPU_, circle_fit_resultsGPU_, fast_fit_resultsGPU_, + hitsGPU_, hits_geGPU_, fast_fit_resultsGPU_, circle_fit_resultsGPU_, line_fit_resultsGPU_, offset); cudaCheck(cudaGetLastError()); } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h index 9352a8127b19b..ec1ced3358dfe 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h @@ -8,6 +8,18 @@ namespace siPixelRecHitsHeterogeneousProduct { struct HitsOnCPU; } +namespace Rfit { + constexpr uint32_t maxNumberOfConcurrentFits() { return 2*1024;} + constexpr uint32_t stride() { return maxNumberOfConcurrentFits();} + using Matrix3x4d = Eigen::Matrix; + using Map3x4d = Eigen::Map >; + using Matrix6x4f = Eigen::Matrix; + using Map6x4f = Eigen::Map >; + using Map4d = Eigen::Map >; + +} + + class RiemannFitOnGPU { public: @@ -28,7 +40,7 @@ class RiemannFitOnGPU { private: - static constexpr uint32_t maxNumberOfConcurrentFits_ = 2000; + static constexpr uint32_t maxNumberOfConcurrentFits_ = Rfit::maxNumberOfConcurrentFits(); // fowarded TuplesOnGPU::Container const * tuples_d = nullptr; @@ -38,9 +50,9 @@ class RiemannFitOnGPU { // Riemann Fit internals - Rfit::Matrix3xNd *hitsGPU_ = nullptr; - Rfit::Matrix3Nd *hits_covGPU_ = nullptr; - Eigen::Vector4d *fast_fit_resultsGPU_ = nullptr; + double *hitsGPU_ = nullptr; + float *hits_geGPU_ = nullptr; + double *fast_fit_resultsGPU_ = nullptr; Rfit::circle_fit *circle_fit_resultsGPU_ = nullptr; Rfit::line_fit *line_fit_resultsGPU_ = nullptr; From 0c55b56c1dd39949bff68e2ce940a82bd0be97aa Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 11 Dec 2018 16:24:53 +0100 Subject: [PATCH 86/94] lit fit is wrong --- .../test/testEigenJacobian.cpp | 94 +++++++++++++++++++ .../PixelTriplets/plugins/BuildFile.xml | 3 +- .../CAHitQuadrupletGeneratorKernels.cu | 4 + .../PixelTriplets/plugins/RiemannFitOnGPU.cu | 10 +- .../src/PixelVertexHeterogeneousProducer.cc | 4 +- 5 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp b/RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp new file mode 100644 index 0000000000000..e01aa30efc656 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp @@ -0,0 +1,94 @@ +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" +#include + +using Rfit::Vector5d; +using Rfit::Matrix5d; + + +Vector5d transf(Vector5d p) { + auto sinTheta = 1/std::sqrt(1+p(3)*p(3)); + p(2) = sinTheta/p(2); + return p; +} + +Matrix5d transfFast(Matrix5d cov, Vector5d const & p) { + auto sqr = [](auto x) { return x*x;}; + auto sinTheta = 1/std::sqrt(1+p(3)*p(3)); + auto cosTheta = p(3)*sinTheta; + cov(2,2) = sqr(sinTheta) * ( + cov(2,2)*sqr(1./(p(2)*p(2))) + + cov(3,3)*sqr(cosTheta*sinTheta/p(2)) + ); + cov(3,2) = cov(2,3) = cov(3,3) * cosTheta * sqr(sinTheta) / p(2); + // for (int i=0; i<5; ++i) cov(i,2) *= -sinTheta/(p(2)*p(2)); + // for (int i=0; i<5; ++i) cov(2,i) *= -sinTheta/(p(2)*p(2)); + return cov; + + +} + +Matrix5d Jacobian(Vector5d const & p) { + + Matrix5d J = Matrix5d::Identity(); + + auto sinTheta2 = 1/(1+p(3)*p(3)); + auto sinTheta = std::sqrt(sinTheta2); + J(2,2) = -sinTheta/(p(2)*p(2)); + J(2,3) = -sinTheta2*sinTheta*p(3)/p(2); + return J; +} + +Matrix5d transf(Matrix5d const & cov, Matrix5d const& J) { + + return J*cov*J.transpose(); + +} + +Matrix5d loadCov(Vector5d const & e) { + + Matrix5d cov = Matrix5d::Zero(); + for (int i=0; i<5; ++i) cov(i,i) = e(i)*e(i); + return cov; +} + + +#include +int main() { + + //!<(phi,Tip,pt,cotan(theta)),Zip) + Vector5d par0; par0 << 0.2,0.1,3.5,0.8,0.1; + Vector5d del0; del0 << 0.01,0.01,0.035,-0.03,-0.01; + + Matrix5d J = Jacobian(par0); + + + Vector5d par1 = transf(par0); + Vector5d par2 = transf(par0+del0); + Vector5d del1 = par2-par1; + + Matrix5d cov0 = loadCov(del0); + Matrix5d cov1 = transf(cov0,J); + Matrix5d cov2 = transfFast(cov0,par0); + + // don't ask: guess + std::cout << "par0 " << par0.transpose() << std::endl; + std::cout << "del0 " << del0.transpose() << std::endl; + + + std::cout << "par1 " << par1.transpose() << std::endl; + std::cout << "del1 " << del1.transpose() << std::endl; + std::cout << "del2 " << (J*del0).transpose() << std::endl; + + std::cout << "del1^2 " << (del1.array()*del1.array()).transpose() << std::endl; + std::cout << std::endl; + std::cout << "J\n" << J << std::endl; + + std::cout << "cov0\n" << cov0 << std::endl; + std::cout << "cov1\n" << cov1 << std::endl; + std::cout << "cov2\n" << cov2 << std::endl; + + + return 0; + + +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml b/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml index 77cb6c4da68a4..3c8397cf572f6 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml +++ b/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml @@ -11,7 +11,8 @@ - + + diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index 92fff26e7de29..e5c62f1364337 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -208,6 +208,10 @@ void kernel_VerifyFit(TuplesOnGPU::Container const * __restrict__ tuples, } isNaN |= !(fit_results[idx].chi2_line+fit_results[idx].chi2_circle < 100.f); // catch NaN as well +#ifdef GPU_DEBUG + if (isNaN) printf("NaN or Bad Fit %d %f/%f\n",idx,fit_results[idx].chi2_line,fit_results[idx].chi2_circle); +#endif + // impose "region cuts" (NaN safe) // phi,Tip,pt,cotan(theta)),Zip bool ok = std::abs(fit_results[idx].par(1)) < 0.1f diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu index 26648018cbd99..f0e202faac9e9 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -31,6 +31,8 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl uint32_t offset) { + assert(hits_in_fit==4); // FixMe later template + assert(pfast_fit); assert(foundNtuplets); auto local_start = (blockIdx.x * blockDim.x + threadIdx.x); @@ -94,6 +96,8 @@ void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtu constexpr uint32_t N = Rfit::Map3x4d::ColsAtCompileTime; constexpr auto n = N; + assert(4==n); // later will be templated... + Rfit::VectorNd rad = (hits.block(0, 0, 2, n).colwise().norm()); Rfit::Matrix2Nd hits_cov = Rfit::Matrix2Nd<4>::Zero(); @@ -105,8 +109,8 @@ void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtu fast_fit, rad, B, true); #ifdef GPU_DEBUG - printf("kernelCircleFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, - circle_fit.par(0), circle_fit.par(1), circle_fit.par(2)); +// printf("kernelCircleFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, +// circle_fit[local_start].par(0), circle_fit[local_start].par(1), circle_fit[local_start].par(2)); #endif } @@ -158,6 +162,8 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl printf("kernelLineFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, circle_fit[local_start].par(0), circle_fit[local_start].par(1), circle_fit[local_start].par(2)); printf("kernelLineFitAllHits line.par(0,1): %d %f,%f\n", helix_start, line_fit[local_start].par(0),line_fit[local_start].par(1)); + printf("kernelLineFitAllHits chi2 cov %f/%f %f,%f,%f,%f,%f\n",helix.chi2_circle,helix.chi2_line, + helix.cov(0,0),helix.cov(1,1),helix.cov(2,2),helix.cov(3,3),helix.cov(4,4)); #endif } diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index 3bd1e522cb776..dba26d98754a9 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -210,13 +210,13 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } itrk.clear(); } - + /* assert(uind.size()==(*vertexes).size()); if (!uind.empty()) { assert(0 == *uind.begin()); assert(uind.size()-1 == *uind.rbegin()); } - + */ if (verbose_) { edm::LogInfo("PixelVertexHeterogeneousProducer") << ": Found " << vertexes->size() << " vertexes\n"; From 811cb6343645c6d1cd9c2938809839044b93161c Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 11 Dec 2018 16:40:45 +0100 Subject: [PATCH 87/94] fix stupid bug --- RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu | 2 +- .../src/PixelVertexHeterogeneousProducer.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu index f0e202faac9e9..18dbba5d7f516 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -140,7 +140,7 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl Rfit::Map3x4d hits(phits+local_start,3,4); Rfit::Map4d fast_fit(pfast_fit+local_start,4); Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); - line_fit[local_start] = Rfit::Line_fit(hits, hits_ge, circle_fit[local_start], fast_fit, true); + line_fit[local_start] = Rfit::Line_fit(hits, hits_ge, circle_fit[local_start], fast_fit, B, true); par_uvrtopak(circle_fit[local_start], B, true); diff --git a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc index dba26d98754a9..3bd1e522cb776 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/src/PixelVertexHeterogeneousProducer.cc @@ -210,13 +210,13 @@ void PixelVertexHeterogeneousProducer::produceGPUCuda( } itrk.clear(); } - /* + assert(uind.size()==(*vertexes).size()); if (!uind.empty()) { assert(0 == *uind.begin()); assert(uind.size()-1 == *uind.rbegin()); } - */ + if (verbose_) { edm::LogInfo("PixelVertexHeterogeneousProducer") << ": Found " << vertexes->size() << " vertexes\n"; From 47d28a7d60c757a76d08fddc5e805cc7b2b1fec5 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Tue, 11 Dec 2018 18:32:15 +0100 Subject: [PATCH 88/94] remove default arg --- .../PixelTrackFitting/interface/RiemannFit.h | 22 ++++++++++-- .../PixelTrackFitting/test/testEigenGPU.cu | 7 ++-- .../PixelTrackFitting/test/testRiemannFit.cpp | 2 +- .../PixelTriplets/plugins/RiemannFitOnGPU.cc | 4 --- .../PixelTriplets/plugins/RiemannFitOnGPU.cu | 35 +++++++++---------- .../PixelTriplets/plugins/RiemannFitOnGPU.h | 1 - 6 files changed, 41 insertions(+), 30 deletions(-) diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h index f508ffc22c64c..3e93aab13d00d 100644 --- a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -611,7 +611,7 @@ __host__ __device__ inline circle_fit Circle_fit(const M2xN& hits2D, const V4& fast_fit, const VectorNd& rad, const double B, - const bool error = true) + const bool error) { #ifdef RFIT_DEBUG printf("circle_fit - enter\n"); @@ -930,6 +930,22 @@ __host__ __device__ inline circle_fit Circle_fit(const M2xN& hits2D, } +/*! \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. + */ + template __host__ __device__ inline line_fit Line_fit(const M3xN& hits, @@ -937,7 +953,7 @@ inline line_fit Line_fit(const M3xN& hits, const circle_fit& circle, const V4& fast_fit, const double B, - const bool error = true) { + const bool error) { constexpr uint32_t N = M3xN::ColsAtCompileTime; auto n = hits.cols(); @@ -1128,7 +1144,7 @@ inline line_fit Line_fit(const M3xN& hits, template inline helix_fit Helix_fit(const Matrix3xNd& hits, const Eigen::Matrix& hits_ge, const double B, - const bool error = true) + const bool error) { u_int n = hits.cols(); VectorNd<4> rad = (hits.block(0, 0, 2, n).colwise().norm()); diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu index 085b23bba47ed..a60eeda935d79 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu @@ -81,6 +81,7 @@ if (0==i) { __global__ void kernelLineFit(double * __restrict__ phits, float * __restrict__ phits_ge, + double B, Rfit::circle_fit * circle_fit, double * __restrict__ pfast_fit, Rfit::line_fit * line_fit) @@ -89,7 +90,7 @@ void kernelLineFit(double * __restrict__ phits, Rfit::Map3x4d hits(phits+i,3,4); Rfit::Map4d fast_fit(pfast_fit+i,4); Rfit::Map6x4f hits_ge(phits_ge+i,6,4); - line_fit[i] = Rfit::Line_fit(hits, hits_ge, circle_fit[i], fast_fit, true); + line_fit[i] = Rfit::Line_fit(hits, hits_ge, circle_fit[i], fast_fit, B, true); } template @@ -194,13 +195,13 @@ void testFit() { assert(isEqualFuzzy(circle_fit_results.par, circle_fit_resultsGPUret->par)); // LINE_FIT CPU - Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, true); + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, B, true); std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; // LINE_FIT GPU Rfit::line_fit * line_fit_resultsGPUret = new Rfit::line_fit(); - kernelLineFit<<>>(hitsGPU, hits_geGPU, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); + kernelLineFit<<>>(hitsGPU, hits_geGPU, B, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); cudaDeviceSynchronize(); cudaMemcpy(line_fit_resultsGPUret, line_fit_resultsGPU, sizeof(Rfit::line_fit), cudaMemcpyDeviceToHost); diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp index 401ab7142a001..af4a3e52f46fa 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp +++ b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp @@ -74,7 +74,7 @@ void testFit() { 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_ge, circle_fit_results, fast_fit_results, true); + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, B, true); std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; std::cout << "Fitted cov (CircleFit) CPU:\n" << circle_fit_results.cov << std::endl; diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc index 3b865e5a031c9..fe95e10a48b5a 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc @@ -20,9 +20,6 @@ void RiemannFitOnGPU::allocateOnGPU(TuplesOnGPU::Container const * tuples, Rfit: cudaCheck(cudaMalloc(&circle_fit_resultsGPU_, maxNumberOfConcurrentFits_ * sizeof(Rfit::circle_fit))); cudaCheck(cudaMemset(circle_fit_resultsGPU_, 0x00, 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() { @@ -31,7 +28,6 @@ void RiemannFitOnGPU::deallocateOnGPU() { cudaFree(hits_geGPU_); 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 index 18dbba5d7f516..1bcfb847d2ae8 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -43,9 +43,9 @@ void kernelFastFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl return; } - Rfit::Map3x4d hits(phits+local_start,3,4); - Rfit::Map4d fast_fit(pfast_fit+local_start,4); - Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); + Rfit::Map3x4d hits(phits+local_start); + Rfit::Map4d fast_fit(pfast_fit+local_start); + Rfit::Map6x4f hits_ge(phits_ge+local_start); // Prepare data structure auto const * hitId = foundNtuplets->begin(helix_start); @@ -89,9 +89,9 @@ void kernelCircleFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtu return; } - Rfit::Map3x4d hits(phits+local_start,3,4); - Rfit::Map4d fast_fit(pfast_fit_input+local_start,4); - Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); + Rfit::Map3x4d hits(phits+local_start); + Rfit::Map4d fast_fit(pfast_fit_input+local_start); + Rfit::Map6x4f hits_ge(phits_ge+local_start); constexpr uint32_t N = Rfit::Map3x4d::ColsAtCompileTime; constexpr auto n = N; @@ -123,11 +123,10 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl float * __restrict__ phits_ge, double * __restrict__ pfast_fit, Rfit::circle_fit * __restrict__ circle_fit, - Rfit::line_fit *line_fit, uint32_t offset) { - assert(results); assert(line_fit); + assert(results); assert(circle_fit); auto local_start = (blockIdx.x * blockDim.x + threadIdx.x); auto helix_start = local_start + offset; @@ -137,31 +136,31 @@ void kernelLineFitAllHits(TuplesOnGPU::Container const * __restrict__ foundNtupl return; } - Rfit::Map3x4d hits(phits+local_start,3,4); - Rfit::Map4d fast_fit(pfast_fit+local_start,4); - Rfit::Map6x4f hits_ge(phits_ge+local_start,6,4); - line_fit[local_start] = Rfit::Line_fit(hits, hits_ge, circle_fit[local_start], fast_fit, B, true); + Rfit::Map3x4d hits(phits+local_start); + Rfit::Map4d fast_fit(pfast_fit+local_start); + Rfit::Map6x4f hits_ge(phits_ge+local_start); + auto const & line_fit = Rfit::Line_fit(hits, hits_ge, circle_fit[local_start], fast_fit, B, true); par_uvrtopak(circle_fit[local_start], B, true); // Grab helix_fit from the proper location in the output vector auto & helix = results[helix_start]; - helix.par << circle_fit[local_start].par, line_fit[local_start].par; + helix.par << circle_fit[local_start].par, line_fit.par; // TODO: pass properly error booleans - helix.cov = MatrixXd::Zero(5, 5); + helix.cov = Rfit::Matrix5d::Zero(); helix.cov.block(0, 0, 3, 3) = circle_fit[local_start].cov; - helix.cov.block(3, 3, 2, 2) = line_fit[local_start].cov; + helix.cov.block(3, 3, 2, 2) = line_fit.cov; helix.q = circle_fit[local_start].q; helix.chi2_circle = circle_fit[local_start].chi2; - helix.chi2_line = line_fit[local_start].chi2; + helix.chi2_line = line_fit.chi2; #ifdef GPU_DEBUG printf("kernelLineFitAllHits circle.par(0,1,2): %d %f,%f,%f\n", helix_start, circle_fit[local_start].par(0), circle_fit[local_start].par(1), circle_fit[local_start].par(2)); - printf("kernelLineFitAllHits line.par(0,1): %d %f,%f\n", helix_start, line_fit[local_start].par(0),line_fit[local_start].par(1)); + printf("kernelLineFitAllHits line.par(0,1): %d %f,%f\n", helix_start, line_fit.par(0),line_fit.par(1)); printf("kernelLineFitAllHits chi2 cov %f/%f %f,%f,%f,%f,%f\n",helix.chi2_circle,helix.chi2_line, helix.cov(0,0),helix.cov(1,1),helix.cov(2,2),helix.cov(3,3),helix.cov(4,4)); #endif @@ -190,7 +189,7 @@ void RiemannFitOnGPU::launchKernels(HitsOnCPU const & hh, uint32_t nhits, uint32 kernelLineFitAllHits<<>>( tuples_d, 4, bField_, helix_fit_results_d, hitsGPU_, hits_geGPU_, fast_fit_resultsGPU_, circle_fit_resultsGPU_, - line_fit_resultsGPU_, offset); + offset); cudaCheck(cudaGetLastError()); } } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h index ec1ced3358dfe..fac88ac2c2bd4 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h @@ -54,7 +54,6 @@ class RiemannFitOnGPU { float *hits_geGPU_ = nullptr; double *fast_fit_resultsGPU_ = nullptr; Rfit::circle_fit *circle_fit_resultsGPU_ = nullptr; - Rfit::line_fit *line_fit_resultsGPU_ = nullptr; }; From ff31eaf928a2196b3c7b11583d0184d3a4445184 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 12 Dec 2018 15:12:58 +0100 Subject: [PATCH 89/94] debug occupancy --- .../SiPixelClusterizer/plugins/gpuClustering.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h index 27e7495ad055b..c59ed9c6dc83d 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h @@ -141,6 +141,23 @@ namespace gpuClustering { __syncthreads(); // for hit filling! +#ifdef GPU_DEBUG + // look for anomalous high occupancy + __shared__ uint32_t n40,n60; + n40=n60=0; + __syncthreads(); + for (auto j=threadIdx.x; j60) atomicAdd(&n60,1); + if(hist.size(j)>40) atomicAdd(&n40,1); + } + __syncthreads(); + if (0==threadIdx.x) { + if (n60>0) printf("columns with more than 60 px %d in %d\n",n60,thisModuleId); + else if (n40>0) printf("columns with more than 40 px %d in %d\n",n40,thisModuleId); + } + __syncthreads(); +#endif + // for each pixel, look at all the pixels until the end of the module; // when two valid pixels within +/- 1 in x or y are found, set their id to the minimum; // after the loop, all the pixel in each cluster should have the id equeal to the lowest From 5ee726da40709e9218c93bb30f4ceec24c845add Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Wed, 12 Dec 2018 15:58:03 +0100 Subject: [PATCH 90/94] faster clustering --- .../SiPixelClusterizer/plugins/gpuClustering.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h index c59ed9c6dc83d..dba9fe12f5492 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h @@ -133,10 +133,8 @@ namespace gpuClustering { jmax[k] = hist.end(); #endif -#ifdef GPU_DEBUG __shared__ int nloops; nloops=0; -#endif __syncthreads(); // for hit filling! @@ -164,7 +162,16 @@ namespace gpuClustering { // pixel in the cluster ( clus[i] == i ). bool more = true; while (__syncthreads_or(more)) { - more = false; + if (1==nloops%2) { + for (int j=threadIdx.x, k = 0; j 1) return; // if (std::abs(int(y[m]) - int(y[i])) > 1) return; // binssize is 1 auto old = atomicMin(&clusterId[m], clusterId[i]); @@ -201,9 +206,8 @@ namespace gpuClustering { ++p; for (;p Date: Wed, 12 Dec 2018 16:39:55 +0100 Subject: [PATCH 91/94] apply to vertex as well --- .../PixelVertexFinding/src/gpuClusterTracks.h | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h index c1ee35ce74ba6..dfc9efe28a81d 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuClusterTracks.h @@ -94,14 +94,27 @@ namespace gpuVertexFinder { forEachInBins(hist,izt[i],1,loop); } + + + __shared__ int nloops; + nloops=0; __syncthreads(); + + // cluster seeds only bool more = true; while (__syncthreads_or(more)) { - more=false; - for (int k = threadIdx.x; k < hist.size(); k += blockDim.x) { + if (1==nloops%2) { + for (int i = threadIdx.x; i < nt; i += blockDim.x) { + auto m = iv[i]; + while (m!=iv[m]) m=iv[m]; + iv[i]=m; + } + } else { + more=false; + for (int k = threadIdx.x; k < hist.size(); k += blockDim.x) { auto p = hist.begin()+k; auto i = (*p); auto be = std::min(Hist::bin(izt[i])+1,int(hist.nbins()-1)); @@ -121,7 +134,9 @@ namespace gpuVertexFinder { }; ++p; for (;p Date: Thu, 13 Dec 2018 14:28:51 +0100 Subject: [PATCH 92/94] clean assert --- .../CAHitQuadrupletGeneratorKernels.cu | 19 +++++++++++-------- .../PixelTriplets/plugins/gpuFishbone.h | 3 ++- .../PixelVertexFinding/src/gpuFitVertices.h | 2 +- .../PixelVertexFinding/src/gpuSortByPt2.h | 2 +- .../PixelVertexFinding/src/gpuSplitVertices.h | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu index e5c62f1364337..85dc10ee04587 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorKernels.cu @@ -281,14 +281,6 @@ void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... 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; @@ -302,6 +294,17 @@ void CAHitQuadrupletGeneratorKernels::launchKernels( // here goes algoparms.... cudaCheck(cudaGetLastError()); } +#ifndef NO_CHECK_OVERFLOWS + 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()); +#endif + + // kernel_print_found_ntuplets<<<1, 1, 0, cudaStream>>>(gpu_.tuples_d, 10); } diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h index 41e99fa3754fd..717cbf777fcdb 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -2,12 +2,13 @@ #define RecoLocalTracker_SiPixelRecHits_plugins_gpuFishbone_h #include -#include #include #include #include #include +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + #include "DataFormats/Math/interface/approx_atan2.h" #include "RecoLocalTracker/SiPixelRecHits/plugins/siPixelRecHitsHeterogeneousProduct.h" #include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h index 1dfc84c1657ef..e9124816ab37e 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuFitVertices.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" #include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h index 5d61feebe0d58..d6f2c20a4420d 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSortByPt2.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" #include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" diff --git a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h index dfe08d304685f..56dc6ed41a00f 100644 --- a/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h +++ b/RecoPixelVertexing/PixelVertexFinding/src/gpuSplitVertices.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" #include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" #include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" From 49df121e5b2774a36233339ad62fc9f15e394484 Mon Sep 17 00:00:00 2001 From: Vincenzo Innocente Date: Fri, 14 Dec 2018 19:04:06 +0100 Subject: [PATCH 93/94] fix missing Free --- RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu index 575185fea2044..947cd20d97919 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHits.cu @@ -119,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_)); From 409522515f92ecc264238e238c4a647b23526230 Mon Sep 17 00:00:00 2001 From: Andrea Bocci Date: Tue, 8 Jan 2019 17:04:20 +0100 Subject: [PATCH 94/94] Silence initcheck reports Backported from c46e71620eecf7c25387c09e1b037c35da1396e3 --- .../PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc index 75e3913855013..4963bd68c712c 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitQuadrupletGeneratorGPU.cc @@ -140,11 +140,15 @@ void CAHitQuadrupletGeneratorGPU::allocateOnGPU() { constexpr auto maxNumberOfQuadruplets_ = CAConstants::maxNumberOfQuadruplets(); - //product + // allocate and initialise the GPU memory cudaCheck(cudaMalloc(&gpu_.tuples_d, sizeof(TuplesOnGPU::Container))); + cudaCheck(cudaMemset(gpu_.tuples_d, 0x00, sizeof(TuplesOnGPU::Container))); cudaCheck(cudaMalloc(&gpu_.apc_d, sizeof(AtomicPairCounter))); + cudaCheck(cudaMemset(gpu_.apc_d, 0x00, sizeof(AtomicPairCounter))); cudaCheck(cudaMalloc(&gpu_.helix_fit_results_d, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); + cudaCheck(cudaMemset(gpu_.helix_fit_results_d, 0x00, sizeof(Rfit::helix_fit)*maxNumberOfQuadruplets_)); cudaCheck(cudaMalloc(&gpu_.quality_d, sizeof(Quality)*maxNumberOfQuadruplets_)); + cudaCheck(cudaMemset(gpu_.quality_d, 0x00, sizeof(Quality)*maxNumberOfQuadruplets_)); cudaCheck(cudaMalloc(&gpu_d, sizeof(TuplesOnGPU))); gpu_.me_d = gpu_d;