diff --git a/CUDADataFormats/Common/BuildFile.xml b/CUDADataFormats/Common/BuildFile.xml index b990c1295e31a..f6b68fe69b400 100644 --- a/CUDADataFormats/Common/BuildFile.xml +++ b/CUDADataFormats/Common/BuildFile.xml @@ -1,4 +1,6 @@ + + diff --git a/CUDADataFormats/Common/src/classes.h b/CUDADataFormats/Common/src/classes.h new file mode 100644 index 0000000000000..239e071d513a2 --- /dev/null +++ b/CUDADataFormats/Common/src/classes.h @@ -0,0 +1,7 @@ +#ifndef CUDADataFormats_Common_src_classes_h +#define CUDADataFormats_Common_src_classes_h + +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "DataFormats/Common/interface/Wrapper.h" + +#endif // CUDADataFormats_Common_src_classes_h diff --git a/CUDADataFormats/Common/src/classes_def.xml b/CUDADataFormats/Common/src/classes_def.xml new file mode 100644 index 0000000000000..024d927595914 --- /dev/null +++ b/CUDADataFormats/Common/src/classes_def.xml @@ -0,0 +1,4 @@ + + + + diff --git a/CUDADataFormats/EcalDigi/BuildFile.xml b/CUDADataFormats/EcalDigi/BuildFile.xml new file mode 100644 index 0000000000000..b7d25b0872646 --- /dev/null +++ b/CUDADataFormats/EcalDigi/BuildFile.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CUDADataFormats/EcalDigi/interface/DigisCollection.h b/CUDADataFormats/EcalDigi/interface/DigisCollection.h new file mode 100644 index 0000000000000..f471dbfb9fac8 --- /dev/null +++ b/CUDADataFormats/EcalDigi/interface/DigisCollection.h @@ -0,0 +1,24 @@ +#ifndef CUDADataFormats_EcalDigi_interface_DigisCollection_h +#define CUDADataFormats_EcalDigi_interface_DigisCollection_h + +#include "CUDADataFormats/CaloCommon/interface/Common.h" + +namespace ecal { + + template + struct DigisCollection : public ::calo::common::AddSize { + DigisCollection() = default; + DigisCollection(DigisCollection const &) = default; + DigisCollection &operator=(DigisCollection const &) = default; + + DigisCollection(DigisCollection &&) = default; + DigisCollection &operator=(DigisCollection &&) = default; + + // stride is statically known + typename StoragePolicy::template StorageSelector::type ids; + typename StoragePolicy::template StorageSelector::type data; + }; + +} // namespace ecal + +#endif // CUDADataFormats_EcalDigi_interface_DigisCollection_h diff --git a/CUDADataFormats/EcalDigi/src/classes.h b/CUDADataFormats/EcalDigi/src/classes.h new file mode 100644 index 0000000000000..cd60b775e229b --- /dev/null +++ b/CUDADataFormats/EcalDigi/src/classes.h @@ -0,0 +1,3 @@ +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/EcalDigi/interface/DigisCollection.h" +#include "DataFormats/Common/interface/Wrapper.h" diff --git a/CUDADataFormats/EcalDigi/src/classes_def.xml b/CUDADataFormats/EcalDigi/src/classes_def.xml new file mode 100644 index 0000000000000..6a3adfe4b41c5 --- /dev/null +++ b/CUDADataFormats/EcalDigi/src/classes_def.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/CUDADataFormats/EcalRecHitSoA/BuildFile.xml b/CUDADataFormats/EcalRecHitSoA/BuildFile.xml new file mode 100644 index 0000000000000..a684d9a23f1c6 --- /dev/null +++ b/CUDADataFormats/EcalRecHitSoA/BuildFile.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h b/CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h new file mode 100644 index 0000000000000..731b8b801407f --- /dev/null +++ b/CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h @@ -0,0 +1,45 @@ +#ifndef CUDADataFormats_EcalRecHitSoA_interface_EcalRecHit_h +#define CUDADataFormats_EcalRecHitSoA_interface_EcalRecHit_h + +#include +#include + +#include "CUDADataFormats/CaloCommon/interface/Common.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" + +namespace ecal { + + template + struct RecHit : public ::calo::common::AddSize { + RecHit() = default; + RecHit(const RecHit&) = default; + RecHit& operator=(const RecHit&) = default; + + RecHit(RecHit&&) = default; + RecHit& operator=(RecHit&&) = default; + + typename StoragePolicy::template StorageSelector::type energy; + typename StoragePolicy::template StorageSelector::type time; + // should we remove the following, since already included in "extra" ? + typename StoragePolicy::template StorageSelector::type chi2; + typename StoragePolicy::template StorageSelector::type + extra; // packed uint32_t for timeError, chi2, energyError + typename StoragePolicy::template StorageSelector::type + flagBits; // store rechit condition (see Flags enum) in a bit-wise way + typename StoragePolicy::template StorageSelector::type did; + + template + typename std::enable_if::value, void>::type resize(size_t size) { + energy.resize(size); + time.resize(size); + chi2.resize(size); + extra.resize(size); + flagBits.resize(size); + did.resize(size); + } + }; + +} // namespace ecal + +#endif // CUDADataFormats_EcalRecHitSoA_interface_EcalRecHit_h diff --git a/CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h b/CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h new file mode 100644 index 0000000000000..78c909b029dc1 --- /dev/null +++ b/CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h @@ -0,0 +1,46 @@ +#ifndef CUDADataFormats_EcalRecHitSoA_interface_EcalUncalibratedRecHit_h +#define CUDADataFormats_EcalRecHitSoA_interface_EcalUncalibratedRecHit_h + +#include +#include + +#include "CUDADataFormats/CaloCommon/interface/Common.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h" +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" + +namespace ecal { + + template + struct UncalibratedRecHit : public ::calo::common::AddSize { + UncalibratedRecHit() = default; + UncalibratedRecHit(const UncalibratedRecHit&) = default; + UncalibratedRecHit& operator=(const UncalibratedRecHit&) = default; + + UncalibratedRecHit(UncalibratedRecHit&&) = default; + UncalibratedRecHit& operator=(UncalibratedRecHit&&) = default; + + typename StoragePolicy::template StorageSelector::type amplitudesAll; + typename StoragePolicy::template StorageSelector::type amplitude; + typename StoragePolicy::template StorageSelector::type chi2; + typename StoragePolicy::template StorageSelector::type pedestal; + typename StoragePolicy::template StorageSelector::type jitter; + typename StoragePolicy::template StorageSelector::type jitterError; + typename StoragePolicy::template StorageSelector::type did; + typename StoragePolicy::template StorageSelector::type flags; + + template + typename std::enable_if::value, void>::type resize(size_t size) { + amplitudesAll.resize(size * EcalDataFrame::MAXSAMPLES); + amplitude.resize(size); + pedestal.resize(size); + chi2.resize(size); + did.resize(size); + flags.resize(size); + jitter.resize(size); + jitterError.resize(size); + } + }; + +} // namespace ecal + +#endif // CUDADataFormats_EcalRecHitSoA_interface_EcalUncalibratedRecHit_h diff --git a/CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h b/CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h new file mode 100644 index 0000000000000..87c4252a5e949 --- /dev/null +++ b/CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h @@ -0,0 +1,13 @@ +#ifndef CUDADataFormats_EcalRecHitSoA_interface_RecoTypes_h +#define CUDADataFormats_EcalRecHitSoA_interface_RecoTypes_h + +namespace ecal { + namespace reco { + + using ComputationScalarType = float; + using StorageScalarType = float; + + } // namespace reco +} // namespace ecal + +#endif // CUDADataFormats_EcalRecHitSoA_interface_RecoTypes_h diff --git a/CUDADataFormats/EcalRecHitSoA/src/classes.h b/CUDADataFormats/EcalRecHitSoA/src/classes.h new file mode 100644 index 0000000000000..ef95da461e3ba --- /dev/null +++ b/CUDADataFormats/EcalRecHitSoA/src/classes.h @@ -0,0 +1,4 @@ +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" +#include "DataFormats/Common/interface/Wrapper.h" diff --git a/CUDADataFormats/EcalRecHitSoA/src/classes_def.xml b/CUDADataFormats/EcalRecHitSoA/src/classes_def.xml new file mode 100644 index 0000000000000..27e315b2c2822 --- /dev/null +++ b/CUDADataFormats/EcalRecHitSoA/src/classes_def.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/CUDADataFormats/HcalDigi/BuildFile.xml b/CUDADataFormats/HcalDigi/BuildFile.xml new file mode 100644 index 0000000000000..fb871f16b69f0 --- /dev/null +++ b/CUDADataFormats/HcalDigi/BuildFile.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CUDADataFormats/HcalDigi/interface/DigiCollection.h b/CUDADataFormats/HcalDigi/interface/DigiCollection.h new file mode 100644 index 0000000000000..e2f4bf0848e94 --- /dev/null +++ b/CUDADataFormats/HcalDigi/interface/DigiCollection.h @@ -0,0 +1,160 @@ +#ifndef CUDADataFormats_HcalDigi_interface_DigiCollection_h +#define CUDADataFormats_HcalDigi_interface_DigiCollection_h + +#include "CUDADataFormats/CaloCommon/interface/Common.h" + +namespace hcal { + + // FLAVOR_HE_QIE11 = 1; Phase1 upgrade + struct Flavor1 { + static constexpr int WORDS_PER_SAMPLE = 1; + static constexpr int SAMPLES_PER_WORD = 1; + static constexpr int HEADER_WORDS = 1; + + static constexpr uint8_t adc(uint16_t const* const sample_start) { return (*sample_start & 0xff); } + static constexpr uint8_t tdc(uint16_t const* const sample_start) { return (*sample_start >> 8) & 0x3f; } + static constexpr uint8_t soibit(uint16_t const* const sample_start) { return (*sample_start >> 14) & 0x1; } + }; + + // FLAVOR_HB_QIE11 = 3; Phase1 upgrade + struct Flavor3 { + static constexpr int WORDS_PER_SAMPLE = 1; + static constexpr int SAMPLES_PER_WORD = 1; + static constexpr int HEADER_WORDS = 1; + + static constexpr uint8_t adc(uint16_t const* const sample_start) { return (*sample_start & 0xff); } + static constexpr uint8_t tdc(uint16_t const* const sample_start) { return ((*sample_start >> 8) & 0x3); } + static constexpr uint8_t soibit(uint16_t const* const sample_start) { return ((*sample_start >> 14) & 0x1); } + static constexpr uint8_t capid(uint16_t const* const sample_start) { return ((*sample_start >> 10) & 0x3); } + }; + + // FLAVOR_HB_QIE10 = 5; Phase0 + struct Flavor5 { + static constexpr float WORDS_PER_SAMPLE = 0.5; + static constexpr int SAMPLES_PER_WORD = 2; + static constexpr int HEADER_WORDS = 1; + + static constexpr uint8_t adc(uint16_t const* const sample_start, uint8_t const shifter) { + return ((*sample_start >> shifter * 8) & 0x7f); + } + }; + + template + constexpr uint8_t capid_for_sample(uint16_t const* const dfstart, uint32_t const sample) { + auto const capid_first = (*dfstart >> 8) & 0x3; + return (capid_first + sample) & 0x3; // same as % 4 + } + + template <> + constexpr uint8_t capid_for_sample(uint16_t const* const dfstart, uint32_t const sample) { + return Flavor3::capid(dfstart + Flavor3::HEADER_WORDS + sample * Flavor3::WORDS_PER_SAMPLE); + } + + template + constexpr uint8_t soibit_for_sample(uint16_t const* const dfstart, uint32_t const sample) { + return Flavor::soibit(dfstart + Flavor::HEADER_WORDS + sample * Flavor::WORDS_PER_SAMPLE); + } + + template + constexpr uint8_t adc_for_sample(uint16_t const* const dfstart, uint32_t const sample) { + return Flavor::adc(dfstart + Flavor::HEADER_WORDS + sample * Flavor::WORDS_PER_SAMPLE); + } + + template + constexpr uint8_t tdc_for_sample(uint16_t const* const dfstart, uint32_t const sample) { + return Flavor::tdc(dfstart + Flavor::HEADER_WORDS + sample * Flavor::WORDS_PER_SAMPLE); + } + + template <> + constexpr uint8_t adc_for_sample(uint16_t const* const dfstart, uint32_t const sample) { + // avoid using WORDS_PER_SAMPLE and simply shift + return Flavor5::adc(dfstart + Flavor5::HEADER_WORDS + (sample >> 1), sample % 2); + } + + template + constexpr uint32_t compute_stride(uint32_t const nsamples) { + return static_cast(nsamples * Flavor::WORDS_PER_SAMPLE) + Flavor::HEADER_WORDS; + } + + template + constexpr uint32_t compute_nsamples(uint32_t const nwords) { + if constexpr (Flavor::SAMPLES_PER_WORD >= 1) + return (nwords - Flavor::HEADER_WORDS) * Flavor::SAMPLES_PER_WORD; + else + return (nwords - Flavor::HEADER_WORDS) / Flavor::WORDS_PER_SAMPLE; + } + + // + template + struct DigiCollectionBase : public ::calo::common::AddSize { + DigiCollectionBase() = default; + DigiCollectionBase(DigiCollectionBase const&) = default; + DigiCollectionBase& operator=(DigiCollectionBase const&) = default; + + DigiCollectionBase(DigiCollectionBase&&) = default; + DigiCollectionBase& operator=(DigiCollectionBase&&) = default; + + template + typename std::enable_if::value, void>::type resize(std::size_t size) { + ids.resize(size); + data.resize(size * stride); + } + + template + typename std::enable_if::value, void>::type reserve(std::size_t size) { + ids.reserve(size); + data.reserve(size * stride); + } + + template + typename std::enable_if::value, void>::type clear() { + ids.clear(); + data.clear(); + } + + typename StoragePolicy::template StorageSelector::type ids; + typename StoragePolicy::template StorageSelector::type data; + uint32_t stride{0}; + }; + + template + struct DigiCollection : public DigiCollectionBase { + using DigiCollectionBase::DigiCollectionBase; + }; + + // NOTE: base ctors will not be available + template + struct DigiCollection : public DigiCollectionBase { + DigiCollection() = default; + + DigiCollection(DigiCollection const&) = default; + DigiCollection& operator=(DigiCollection const&) = default; + + DigiCollection(DigiCollection&&) = default; + DigiCollection& operator=(DigiCollection&&) = default; + + template + typename std::enable_if::value, void>::type resize(std::size_t size) { + DigiCollectionBase::resize(size); + npresamples.resize(size); + } + + template + typename std::enable_if::value, void>::type reserve(std::size_t size) { + DigiCollectionBase::reserve(size); + npresamples.reserve(size); + } + + template + typename std::enable_if::value, void>::type clear() { + DigiCollectionBase::clear(); + npresamples.clear(); + } + + // add npresamples member + typename StoragePolicy::template StorageSelector::type npresamples; + }; + +} // namespace hcal + +#endif // CUDADataFormats_HcalDigi_interface_DigiCollection_h diff --git a/CUDADataFormats/HcalDigi/src/classes.h b/CUDADataFormats/HcalDigi/src/classes.h new file mode 100644 index 0000000000000..8c4a20318928e --- /dev/null +++ b/CUDADataFormats/HcalDigi/src/classes.h @@ -0,0 +1,3 @@ +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/HcalDigi/interface/DigiCollection.h" +#include "DataFormats/Common/interface/Wrapper.h" diff --git a/CUDADataFormats/HcalDigi/src/classes_def.xml b/CUDADataFormats/HcalDigi/src/classes_def.xml new file mode 100644 index 0000000000000..71997eb59ba61 --- /dev/null +++ b/CUDADataFormats/HcalDigi/src/classes_def.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CUDADataFormats/HcalRecHitSoA/BuildFile.xml b/CUDADataFormats/HcalRecHitSoA/BuildFile.xml new file mode 100644 index 0000000000000..245701de5fdb0 --- /dev/null +++ b/CUDADataFormats/HcalRecHitSoA/BuildFile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h b/CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h new file mode 100644 index 0000000000000..424b2c0813b4c --- /dev/null +++ b/CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h @@ -0,0 +1,38 @@ +#ifndef CUDADataFormats_HcalRecHitCollectionSoA_interface_RecHitCollection_h +#define CUDADataFormats_HcalRecHitCollectionSoA_interface_RecHitCollection_h + +#include + +#include "CUDADataFormats/CaloCommon/interface/Common.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" + +namespace hcal { + + template + struct RecHitCollection : public ::calo::common::AddSize { + RecHitCollection() = default; + RecHitCollection(const RecHitCollection&) = default; + RecHitCollection& operator=(const RecHitCollection&) = default; + + RecHitCollection(RecHitCollection&&) = default; + RecHitCollection& operator=(RecHitCollection&&) = default; + + typename StoragePolicy::template StorageSelector::type energy; + typename StoragePolicy::template StorageSelector::type chi2; + typename StoragePolicy::template StorageSelector::type energyM0; + typename StoragePolicy::template StorageSelector::type timeM0; + typename StoragePolicy::template StorageSelector::type did; + + template + typename std::enable_if::value, void>::type resize(size_t size) { + energy.resize(size); + chi2.resize(size); + energyM0.resize(size); + timeM0.resize(size); + did.resize(size); + } + }; + +} // namespace hcal + +#endif // RecoLocalCalo_HcalRecAlgos_interface_RecHitCollection_h diff --git a/CUDADataFormats/HcalRecHitSoA/src/classes.h b/CUDADataFormats/HcalRecHitSoA/src/classes.h new file mode 100644 index 0000000000000..a13782165c413 --- /dev/null +++ b/CUDADataFormats/HcalRecHitSoA/src/classes.h @@ -0,0 +1,3 @@ +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h" +#include "DataFormats/Common/interface/Wrapper.h" diff --git a/CUDADataFormats/HcalRecHitSoA/src/classes_def.xml b/CUDADataFormats/HcalRecHitSoA/src/classes_def.xml new file mode 100644 index 0000000000000..71dd18a7daddb --- /dev/null +++ b/CUDADataFormats/HcalRecHitSoA/src/classes_def.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/CUDADataFormats/SiPixelCluster/BuildFile.xml b/CUDADataFormats/SiPixelCluster/BuildFile.xml new file mode 100644 index 0000000000000..5406d1355533f --- /dev/null +++ b/CUDADataFormats/SiPixelCluster/BuildFile.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h b/CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h new file mode 100644 index 0000000000000..acdf1b34a6d79 --- /dev/null +++ b/CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h @@ -0,0 +1,63 @@ +#ifndef CUDADataFormats_SiPixelCluster_interface_SiPixelClustersCUDA_h +#define CUDADataFormats_SiPixelCluster_interface_SiPixelClustersCUDA_h + +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" + +#include + +class SiPixelClustersCUDA { +public: + SiPixelClustersCUDA() = default; + explicit SiPixelClustersCUDA(size_t maxModules, cudaStream_t stream); + ~SiPixelClustersCUDA() = default; + + SiPixelClustersCUDA(const SiPixelClustersCUDA &) = delete; + SiPixelClustersCUDA &operator=(const SiPixelClustersCUDA &) = delete; + SiPixelClustersCUDA(SiPixelClustersCUDA &&) = default; + SiPixelClustersCUDA &operator=(SiPixelClustersCUDA &&) = default; + + void setNClusters(uint32_t nClusters) { nClusters_h = nClusters; } + + uint32_t nClusters() const { return nClusters_h; } + + uint32_t *moduleStart() { return moduleStart_d.get(); } + uint32_t *clusInModule() { return clusInModule_d.get(); } + uint32_t *moduleId() { return moduleId_d.get(); } + uint32_t *clusModuleStart() { return clusModuleStart_d.get(); } + + uint32_t const *moduleStart() const { return moduleStart_d.get(); } + uint32_t const *clusInModule() const { return clusInModule_d.get(); } + uint32_t const *moduleId() const { return moduleId_d.get(); } + uint32_t const *clusModuleStart() const { return clusModuleStart_d.get(); } + + class DeviceConstView { + public: + __device__ __forceinline__ uint32_t moduleStart(int i) const { return __ldg(moduleStart_ + i); } + __device__ __forceinline__ uint32_t clusInModule(int i) const { return __ldg(clusInModule_ + i); } + __device__ __forceinline__ uint32_t moduleId(int i) const { return __ldg(moduleId_ + i); } + __device__ __forceinline__ uint32_t clusModuleStart(int i) const { return __ldg(clusModuleStart_ + i); } + + uint32_t const *moduleStart_; + uint32_t const *clusInModule_; + uint32_t const *moduleId_; + uint32_t const *clusModuleStart_; + }; + + DeviceConstView *view() const { return view_d.get(); } + +private: + cms::cuda::device::unique_ptr moduleStart_d; // index of the first pixel of each module + cms::cuda::device::unique_ptr clusInModule_d; // number of clusters found in each module + cms::cuda::device::unique_ptr moduleId_d; // module id of each module + + // originally from rechits + cms::cuda::device::unique_ptr clusModuleStart_d; // index of the first cluster of each module + + cms::cuda::device::unique_ptr view_d; // "me" pointer + + uint32_t nClusters_h = 0; +}; + +#endif // CUDADataFormats_SiPixelCluster_interface_SiPixelClustersCUDA_h diff --git a/CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h b/CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h new file mode 100644 index 0000000000000..e9dfed7bca7a6 --- /dev/null +++ b/CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h @@ -0,0 +1,37 @@ +#ifndef CUDADataFormats_SiPixelCluster_interface_gpuClusteringConstants_h +#define CUDADataFormats_SiPixelCluster_interface_gpuClusteringConstants_h + +#include +#include + +namespace pixelGPUConstants { +#ifdef GPU_SMALL_EVENTS + // kept for testing and debugging + constexpr uint32_t maxNumberOfHits = 24 * 1024; +#else + // data at pileup 50 has 18300 +/- 3500 hits; 40000 is around 6 sigma away + // tested on MC events with 55-75 pileup events + constexpr uint32_t maxNumberOfHits = 48 * 1024; +#endif +} // namespace pixelGPUConstants + +namespace gpuClustering { +#ifdef GPU_SMALL_EVENTS + // kept for testing and debugging + constexpr uint32_t maxHitsInIter() { return 64; } +#else + // optimized for real data PU 50 + // tested on MC events with 55-75 pileup events + constexpr uint32_t maxHitsInIter() { return 160; } +#endif + constexpr uint32_t maxHitsInModule() { return 1024; } + + constexpr uint16_t maxNumModules = 2000; + constexpr int32_t maxNumClustersPerModules = maxHitsInModule(); + constexpr uint32_t maxNumClusters = pixelGPUConstants::maxNumberOfHits; + constexpr uint16_t invalidModuleId = std::numeric_limits::max() - 1; + static_assert(invalidModuleId > maxNumModules); // invalidModuleId must be > maxNumModules + +} // namespace gpuClustering + +#endif // CUDADataFormats_SiPixelCluster_interface_gpuClusteringConstants_h diff --git a/CUDADataFormats/SiPixelCluster/src/SiPixelClustersCUDA.cc b/CUDADataFormats/SiPixelCluster/src/SiPixelClustersCUDA.cc new file mode 100644 index 0000000000000..ae4a24dbbf83b --- /dev/null +++ b/CUDADataFormats/SiPixelCluster/src/SiPixelClustersCUDA.cc @@ -0,0 +1,19 @@ +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +SiPixelClustersCUDA::SiPixelClustersCUDA(size_t maxModules, cudaStream_t stream) + : moduleStart_d(cms::cuda::make_device_unique(maxModules + 1, stream)), + clusInModule_d(cms::cuda::make_device_unique(maxModules, stream)), + moduleId_d(cms::cuda::make_device_unique(maxModules, stream)), + clusModuleStart_d(cms::cuda::make_device_unique(maxModules + 1, stream)) { + auto view = cms::cuda::make_host_unique(stream); + view->moduleStart_ = moduleStart_d.get(); + view->clusInModule_ = clusInModule_d.get(); + view->moduleId_ = moduleId_d.get(); + view->clusModuleStart_ = clusModuleStart_d.get(); + + view_d = cms::cuda::make_device_unique(stream); + cms::cuda::copyAsync(view_d, view, stream); +} diff --git a/CUDADataFormats/SiPixelCluster/src/classes.h b/CUDADataFormats/SiPixelCluster/src/classes.h new file mode 100644 index 0000000000000..3eee5a1fce009 --- /dev/null +++ b/CUDADataFormats/SiPixelCluster/src/classes.h @@ -0,0 +1,8 @@ +#ifndef CUDADataFormats_SiPixelCluster_src_classes_h +#define CUDADataFormats_SiPixelCluster_src_classes_h + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "DataFormats/Common/interface/Wrapper.h" + +#endif // CUDADataFormats_SiPixelCluster_src_classes_h diff --git a/CUDADataFormats/SiPixelCluster/src/classes_def.xml b/CUDADataFormats/SiPixelCluster/src/classes_def.xml new file mode 100644 index 0000000000000..70decb9f27df7 --- /dev/null +++ b/CUDADataFormats/SiPixelCluster/src/classes_def.xml @@ -0,0 +1,4 @@ + + + + diff --git a/CUDADataFormats/SiPixelDigi/BuildFile.xml b/CUDADataFormats/SiPixelDigi/BuildFile.xml new file mode 100644 index 0000000000000..0806768a9b657 --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/BuildFile.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h b/CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h new file mode 100644 index 0000000000000..bfb15c4ac9f5c --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h @@ -0,0 +1,42 @@ +#ifndef CUDADataFormats_SiPixelDigi_interface_SiPixelDigiErrorsCUDA_h +#define CUDADataFormats_SiPixelDigi_interface_SiPixelDigiErrorsCUDA_h + +#include + +#include "DataFormats/SiPixelRawData/interface/SiPixelErrorCompact.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelFormatterErrors.h" +#include "HeterogeneousCore/CUDAUtilities/interface/SimpleVector.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +class SiPixelDigiErrorsCUDA { +public: + using SiPixelErrorCompactVector = cms::cuda::SimpleVector; + + SiPixelDigiErrorsCUDA() = default; + explicit SiPixelDigiErrorsCUDA(size_t maxFedWords, SiPixelFormatterErrors errors, cudaStream_t stream); + ~SiPixelDigiErrorsCUDA() = default; + + SiPixelDigiErrorsCUDA(const SiPixelDigiErrorsCUDA&) = delete; + SiPixelDigiErrorsCUDA& operator=(const SiPixelDigiErrorsCUDA&) = delete; + SiPixelDigiErrorsCUDA(SiPixelDigiErrorsCUDA&&) = default; + SiPixelDigiErrorsCUDA& operator=(SiPixelDigiErrorsCUDA&&) = default; + + const SiPixelFormatterErrors& formatterErrors() const { return formatterErrors_h; } + + SiPixelErrorCompactVector* error() { return error_d.get(); } + SiPixelErrorCompactVector const* error() const { return error_d.get(); } + + using HostDataError = std::pair>; + HostDataError dataErrorToHostAsync(cudaStream_t stream) const; + + void copyErrorToHostAsync(cudaStream_t stream); + +private: + cms::cuda::device::unique_ptr data_d; + cms::cuda::device::unique_ptr error_d; + cms::cuda::host::unique_ptr error_h; + SiPixelFormatterErrors formatterErrors_h; +}; + +#endif // CUDADataFormats_SiPixelDigi_interface_SiPixelDigiErrorsCUDA_h diff --git a/CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h b/CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h new file mode 100644 index 0000000000000..950f9651cf83b --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h @@ -0,0 +1,85 @@ +#ifndef CUDADataFormats_SiPixelDigi_interface_SiPixelDigisCUDA_h +#define CUDADataFormats_SiPixelDigi_interface_SiPixelDigisCUDA_h + +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" + +class SiPixelDigisCUDA { +public: + SiPixelDigisCUDA() = default; + explicit SiPixelDigisCUDA(size_t maxFedWords, cudaStream_t stream); + ~SiPixelDigisCUDA() = default; + + SiPixelDigisCUDA(const SiPixelDigisCUDA &) = delete; + SiPixelDigisCUDA &operator=(const SiPixelDigisCUDA &) = delete; + SiPixelDigisCUDA(SiPixelDigisCUDA &&) = default; + SiPixelDigisCUDA &operator=(SiPixelDigisCUDA &&) = default; + + void setNModulesDigis(uint32_t nModules, uint32_t nDigis) { + nModules_h = nModules; + nDigis_h = nDigis; + } + + uint32_t nModules() const { return nModules_h; } + uint32_t nDigis() const { return nDigis_h; } + + uint16_t *xx() { return xx_d.get(); } + uint16_t *yy() { return yy_d.get(); } + uint16_t *adc() { return adc_d.get(); } + uint16_t *moduleInd() { return moduleInd_d.get(); } + int32_t *clus() { return clus_d.get(); } + uint32_t *pdigi() { return pdigi_d.get(); } + uint32_t *rawIdArr() { return rawIdArr_d.get(); } + + uint16_t const *xx() const { return xx_d.get(); } + uint16_t const *yy() const { return yy_d.get(); } + uint16_t const *adc() const { return adc_d.get(); } + uint16_t const *moduleInd() const { return moduleInd_d.get(); } + int32_t const *clus() const { return clus_d.get(); } + uint32_t const *pdigi() const { return pdigi_d.get(); } + uint32_t const *rawIdArr() const { return rawIdArr_d.get(); } + + cms::cuda::host::unique_ptr adcToHostAsync(cudaStream_t stream) const; + cms::cuda::host::unique_ptr clusToHostAsync(cudaStream_t stream) const; + cms::cuda::host::unique_ptr pdigiToHostAsync(cudaStream_t stream) const; + cms::cuda::host::unique_ptr rawIdArrToHostAsync(cudaStream_t stream) const; + + class DeviceConstView { + public: + __device__ __forceinline__ uint16_t xx(int i) const { return __ldg(xx_ + i); } + __device__ __forceinline__ uint16_t yy(int i) const { return __ldg(yy_ + i); } + __device__ __forceinline__ uint16_t adc(int i) const { return __ldg(adc_ + i); } + __device__ __forceinline__ uint16_t moduleInd(int i) const { return __ldg(moduleInd_ + i); } + __device__ __forceinline__ int32_t clus(int i) const { return __ldg(clus_ + i); } + + uint16_t const *xx_; + uint16_t const *yy_; + uint16_t const *adc_; + uint16_t const *moduleInd_; + int32_t const *clus_; + }; + + const DeviceConstView *view() const { return view_d.get(); } + +private: + // These are consumed by downstream device code + cms::cuda::device::unique_ptr xx_d; // local coordinates of each pixel + cms::cuda::device::unique_ptr yy_d; // + cms::cuda::device::unique_ptr adc_d; // ADC of each pixel + cms::cuda::device::unique_ptr moduleInd_d; // module id of each pixel + cms::cuda::device::unique_ptr clus_d; // cluster id of each pixel + cms::cuda::device::unique_ptr view_d; // "me" pointer + + // These are for CPU output; should we (eventually) place them to a + // separate product? + cms::cuda::device::unique_ptr pdigi_d; // packed digi (row, col, adc) of each pixel + cms::cuda::device::unique_ptr rawIdArr_d; // DetId of each pixel + + uint32_t nModules_h = 0; + uint32_t nDigis_h = 0; +}; + +#endif // CUDADataFormats_SiPixelDigi_interface_SiPixelDigisCUDA_h diff --git a/CUDADataFormats/SiPixelDigi/src/SiPixelDigiErrorsCUDA.cc b/CUDADataFormats/SiPixelDigi/src/SiPixelDigiErrorsCUDA.cc new file mode 100644 index 0000000000000..eecea35ddd622 --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/src/SiPixelDigiErrorsCUDA.cc @@ -0,0 +1,40 @@ +#include + +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/memsetAsync.h" + +SiPixelDigiErrorsCUDA::SiPixelDigiErrorsCUDA(size_t maxFedWords, SiPixelFormatterErrors errors, cudaStream_t stream) + : data_d(cms::cuda::make_device_unique(maxFedWords, stream)), + error_d(cms::cuda::make_device_unique(stream)), + error_h(cms::cuda::make_host_unique(stream)), + formatterErrors_h(std::move(errors)) { + cms::cuda::memsetAsync(data_d, 0x00, maxFedWords, stream); + + cms::cuda::make_SimpleVector(error_h.get(), maxFedWords, data_d.get()); + assert(error_h->empty()); + assert(error_h->capacity() == static_cast(maxFedWords)); + + cms::cuda::copyAsync(error_d, error_h, stream); +} + +void SiPixelDigiErrorsCUDA::copyErrorToHostAsync(cudaStream_t stream) { + cms::cuda::copyAsync(error_h, error_d, stream); +} + +SiPixelDigiErrorsCUDA::HostDataError SiPixelDigiErrorsCUDA::dataErrorToHostAsync(cudaStream_t stream) const { + // On one hand size() could be sufficient. On the other hand, if + // someone copies the SimpleVector<>, (s)he might expect the data + // buffer to actually have space for capacity() elements. + auto data = cms::cuda::make_host_unique(error_h->capacity(), stream); + + // but transfer only the required amount + if (not error_h->empty()) { + cms::cuda::copyAsync(data, data_d, error_h->size(), stream); + } + auto err = *error_h; + err.set_data(data.get()); + return HostDataError(err, std::move(data)); +} diff --git a/CUDADataFormats/SiPixelDigi/src/SiPixelDigisCUDA.cc b/CUDADataFormats/SiPixelDigi/src/SiPixelDigisCUDA.cc new file mode 100644 index 0000000000000..4e6a3fc2593fd --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/src/SiPixelDigisCUDA.cc @@ -0,0 +1,46 @@ +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +SiPixelDigisCUDA::SiPixelDigisCUDA(size_t maxFedWords, cudaStream_t stream) + : xx_d(cms::cuda::make_device_unique(maxFedWords, stream)), + yy_d(cms::cuda::make_device_unique(maxFedWords, stream)), + adc_d(cms::cuda::make_device_unique(maxFedWords, stream)), + moduleInd_d(cms::cuda::make_device_unique(maxFedWords, stream)), + clus_d(cms::cuda::make_device_unique(maxFedWords, stream)), + view_d(cms::cuda::make_device_unique(stream)), + pdigi_d(cms::cuda::make_device_unique(maxFedWords, stream)), + rawIdArr_d(cms::cuda::make_device_unique(maxFedWords, stream)) { + auto view = cms::cuda::make_host_unique(stream); + view->xx_ = xx_d.get(); + view->yy_ = yy_d.get(); + view->adc_ = adc_d.get(); + view->moduleInd_ = moduleInd_d.get(); + view->clus_ = clus_d.get(); + cms::cuda::copyAsync(view_d, view, stream); +} + +cms::cuda::host::unique_ptr SiPixelDigisCUDA::adcToHostAsync(cudaStream_t stream) const { + auto ret = cms::cuda::make_host_unique(nDigis(), stream); + cms::cuda::copyAsync(ret, adc_d, nDigis(), stream); + return ret; +} + +cms::cuda::host::unique_ptr SiPixelDigisCUDA::clusToHostAsync(cudaStream_t stream) const { + auto ret = cms::cuda::make_host_unique(nDigis(), stream); + cms::cuda::copyAsync(ret, clus_d, nDigis(), stream); + return ret; +} + +cms::cuda::host::unique_ptr SiPixelDigisCUDA::pdigiToHostAsync(cudaStream_t stream) const { + auto ret = cms::cuda::make_host_unique(nDigis(), stream); + cms::cuda::copyAsync(ret, pdigi_d, nDigis(), stream); + return ret; +} + +cms::cuda::host::unique_ptr SiPixelDigisCUDA::rawIdArrToHostAsync(cudaStream_t stream) const { + auto ret = cms::cuda::make_host_unique(nDigis(), stream); + cms::cuda::copyAsync(ret, rawIdArr_d, nDigis(), stream); + return ret; +} diff --git a/CUDADataFormats/SiPixelDigi/src/classes.h b/CUDADataFormats/SiPixelDigi/src/classes.h new file mode 100644 index 0000000000000..fc5d318fad688 --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/src/classes.h @@ -0,0 +1,9 @@ +#ifndef CUDADataFormats_SiPixelDigi_src_classes_h +#define CUDADataFormats_SiPixelDigi_src_classes_h + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "DataFormats/Common/interface/Wrapper.h" + +#endif // CUDADataFormats_SiPixelDigi_src_classes_h diff --git a/CUDADataFormats/SiPixelDigi/src/classes_def.xml b/CUDADataFormats/SiPixelDigi/src/classes_def.xml new file mode 100644 index 0000000000000..ff775afdc2046 --- /dev/null +++ b/CUDADataFormats/SiPixelDigi/src/classes_def.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CUDADataFormats/Track/BuildFile.xml b/CUDADataFormats/Track/BuildFile.xml new file mode 100644 index 0000000000000..e3f9a0910bbd8 --- /dev/null +++ b/CUDADataFormats/Track/BuildFile.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h b/CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h new file mode 100644 index 0000000000000..41936b5fc7077 --- /dev/null +++ b/CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h @@ -0,0 +1,76 @@ +#ifndef CUDADataFormatsTrackTrackHeterogeneous_H +#define CUDADataFormatsTrackTrackHeterogeneous_H + +#include "CUDADataFormats/Track/interface/TrajectoryStateSoA.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" + +#include "CUDADataFormats/Common/interface/HeterogeneousSoA.h" + +namespace trackQuality { + enum Quality : uint8_t { bad = 0, dup, loose, strict, tight, highPurity }; +} + +template +class TrackSoAT { +public: + static constexpr int32_t stride() { return S; } + + using Quality = trackQuality::Quality; + using hindex_type = uint32_t; + using HitContainer = cms::cuda::OneToManyAssoc; + + // Always check quality is at least loose! + // CUDA does not support enums in __lgc ... + eigenSoA::ScalarSoA m_quality; + constexpr Quality quality(int32_t i) const { return (Quality)(m_quality(i)); } + constexpr Quality &quality(int32_t i) { return (Quality &)(m_quality(i)); } + constexpr Quality const *qualityData() const { return (Quality const *)(m_quality.data()); } + constexpr Quality *qualityData() { return (Quality *)(m_quality.data()); } + + // this is chi2/ndof as not necessarely all hits are used in the fit + eigenSoA::ScalarSoA chi2; + + constexpr int nHits(int i) const { return detIndices.size(i); } + + // State at the Beam spot + // phi,tip,1/pt,cotan(theta),zip + TrajectoryStateSoA stateAtBS; + eigenSoA::ScalarSoA eta; + eigenSoA::ScalarSoA pt; + constexpr float charge(int32_t i) const { return std::copysign(1.f, stateAtBS.state(i)(2)); } + constexpr float phi(int32_t i) const { return stateAtBS.state(i)(0); } + constexpr float tip(int32_t i) const { return stateAtBS.state(i)(1); } + constexpr float zip(int32_t i) const { return stateAtBS.state(i)(4); } + + // state at the detector of the outermost hit + // representation to be decided... + // not yet filled on GPU + // TrajectoryStateSoA stateAtOuterDet; + + HitContainer hitIndices; + HitContainer detIndices; + + // total number of tracks (including those not fitted) + uint32_t m_nTracks; +}; + +namespace pixelTrack { + +#ifdef GPU_SMALL_EVENTS + // kept for testing and debugging + constexpr uint32_t maxNumber() { return 2 * 1024; } +#else + // tested on MC events with 55-75 pileup events + constexpr uint32_t maxNumber() { return 32 * 1024; } +#endif + + using TrackSoA = TrackSoAT; + using TrajectoryState = TrajectoryStateSoA; + using HitContainer = TrackSoA::HitContainer; + using Quality = trackQuality::Quality; + +} // namespace pixelTrack + +using PixelTrackHeterogeneous = HeterogeneousSoA; + +#endif // CUDADataFormatsTrackTrackSoA_H diff --git a/CUDADataFormats/Track/interface/TrajectoryStateSoA.h b/CUDADataFormats/Track/interface/TrajectoryStateSoA.h new file mode 100644 index 0000000000000..7cd2e93fb914e --- /dev/null +++ b/CUDADataFormats/Track/interface/TrajectoryStateSoA.h @@ -0,0 +1,59 @@ +#ifndef CUDADataFormatsTrackTrajectoryStateSOA_H +#define CUDADataFormatsTrackTrajectoryStateSOA_H + +#include +#include "HeterogeneousCore/CUDAUtilities/interface/eigenSoA.h" + +template +struct TrajectoryStateSoA { + using Vector5f = Eigen::Matrix; + using Vector15f = Eigen::Matrix; + + using Vector5d = Eigen::Matrix; + using Matrix5d = Eigen::Matrix; + + static constexpr int32_t stride() { return S; } + + eigenSoA::MatrixSoA state; + eigenSoA::MatrixSoA covariance; + + template + __host__ __device__ inline void copyFromCircle( + V3 const& cp, M3 const& ccov, V2 const& lp, M2 const& lcov, float b, int32_t i) { + state(i) << cp.template cast(), lp.template cast(); + state(i)(2) *= b; + auto cov = covariance(i); + cov(0) = ccov(0, 0); + cov(1) = ccov(0, 1); + cov(2) = b * float(ccov(0, 2)); + cov(4) = cov(3) = 0; + cov(5) = ccov(1, 1); + cov(6) = b * float(ccov(1, 2)); + cov(8) = cov(7) = 0; + cov(9) = b * b * float(ccov(2, 2)); + cov(11) = cov(10) = 0; + cov(12) = lcov(0, 0); + cov(13) = lcov(0, 1); + cov(14) = lcov(1, 1); + } + + template + __host__ __device__ inline void copyFromDense(V5 const& v, M5 const& cov, int32_t i) { + state(i) = v.template cast(); + for (int j = 0, ind = 0; j < 5; ++j) + for (auto k = j; k < 5; ++k) + covariance(i)(ind++) = cov(j, k); + } + + template + __host__ __device__ inline void copyToDense(V5& v, M5& cov, int32_t i) const { + v = state(i).template cast(); + for (int j = 0, ind = 0; j < 5; ++j) { + cov(j, j) = covariance(i)(ind++); + for (auto k = j + 1; k < 5; ++k) + cov(k, j) = cov(j, k) = covariance(i)(ind++); + } + } +}; + +#endif // CUDADataFormatsTrackTrajectoryStateSOA_H diff --git a/CUDADataFormats/Track/src/classes.h b/CUDADataFormats/Track/src/classes.h new file mode 100644 index 0000000000000..4843818978cca --- /dev/null +++ b/CUDADataFormats/Track/src/classes.h @@ -0,0 +1,9 @@ +#ifndef CUDADataFormats_Track_src_classes_h +#define CUDADataFormats_Track_src_classes_h + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "DataFormats/Common/interface/Wrapper.h" + +#endif // CUDADataFormats_Track_src_classes_h diff --git a/CUDADataFormats/Track/src/classes_def.xml b/CUDADataFormats/Track/src/classes_def.xml new file mode 100644 index 0000000000000..9c80ae91baf29 --- /dev/null +++ b/CUDADataFormats/Track/src/classes_def.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/CUDADataFormats/Track/test/BuildFile.xml b/CUDADataFormats/Track/test/BuildFile.xml new file mode 100644 index 0000000000000..598b345d4709d --- /dev/null +++ b/CUDADataFormats/Track/test/BuildFile.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/CUDADataFormats/Track/test/TrajectoryStateSOA_t.cpp b/CUDADataFormats/Track/test/TrajectoryStateSOA_t.cpp new file mode 100644 index 0000000000000..d6ff539a642b0 --- /dev/null +++ b/CUDADataFormats/Track/test/TrajectoryStateSOA_t.cpp @@ -0,0 +1 @@ +#include "TrajectoryStateSOA_t.h" diff --git a/CUDADataFormats/Track/test/TrajectoryStateSOA_t.cu b/CUDADataFormats/Track/test/TrajectoryStateSOA_t.cu new file mode 100644 index 0000000000000..d6ff539a642b0 --- /dev/null +++ b/CUDADataFormats/Track/test/TrajectoryStateSOA_t.cu @@ -0,0 +1 @@ +#include "TrajectoryStateSOA_t.h" diff --git a/CUDADataFormats/Track/test/TrajectoryStateSOA_t.h b/CUDADataFormats/Track/test/TrajectoryStateSOA_t.h new file mode 100644 index 0000000000000..c8e92aca2628f --- /dev/null +++ b/CUDADataFormats/Track/test/TrajectoryStateSOA_t.h @@ -0,0 +1,75 @@ +#include "CUDADataFormats/Track/interface/TrajectoryStateSoA.h" + +using Vector5d = Eigen::Matrix; +using Matrix5d = Eigen::Matrix; + +__host__ __device__ Matrix5d loadCov(Vector5d const& e) { + Matrix5d cov; + for (int i = 0; i < 5; ++i) + cov(i, i) = e(i) * e(i); + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < i; ++j) { + double v = 0.3 * std::sqrt(cov(i, i) * cov(j, j)); // this makes the matrix pos defined + cov(i, j) = (i + j) % 2 ? -0.4 * v : 0.1 * v; + cov(j, i) = cov(i, j); + } + } + return cov; +} + +using TS = TrajectoryStateSoA<128>; + +__global__ void testTSSoA(TS* pts, int n) { + assert(n <= 128); + + Vector5d par0; + par0 << 0.2, 0.1, 3.5, 0.8, 0.1; + Vector5d e0; + e0 << 0.01, 0.01, 0.035, -0.03, -0.01; + auto cov0 = loadCov(e0); + + TS& ts = *pts; + + int first = threadIdx.x + blockIdx.x * blockDim.x; + + for (int i = first; i < n; i += blockDim.x * gridDim.x) { + ts.copyFromDense(par0, cov0, i); + Vector5d par1; + Matrix5d cov1; + ts.copyToDense(par1, cov1, i); + Vector5d delV = par1 - par0; + Matrix5d delM = cov1 - cov0; + for (int j = 0; j < 5; ++j) { + assert(std::abs(delV(j)) < 1.e-5); + for (auto k = j; k < 5; ++k) { + assert(cov0(k, j) == cov0(j, k)); + assert(cov1(k, j) == cov1(j, k)); + assert(std::abs(delM(k, j)) < 1.e-5); + } + } + } +} + +#ifdef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/requireDevices.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#endif + +int main() { +#ifdef __CUDACC__ + cms::cudatest::requireDevices(); +#endif + + TS ts; + +#ifdef __CUDACC__ + TS* ts_d; + cudaCheck(cudaMalloc(&ts_d, sizeof(TS))); + testTSSoA<<<1, 64>>>(ts_d, 128); + cudaCheck(cudaGetLastError()); + cudaCheck(cudaMemcpy(&ts, ts_d, sizeof(TS), cudaMemcpyDefault)); + cudaCheck(cudaDeviceSynchronize()); +#else + testTSSoA(&ts, 128); +#endif +} diff --git a/CUDADataFormats/TrackingRecHit/BuildFile.xml b/CUDADataFormats/TrackingRecHit/BuildFile.xml new file mode 100644 index 0000000000000..8dc569d40b6c4 --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/BuildFile.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h b/CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h new file mode 100644 index 0000000000000..f10495abd2ab8 --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h @@ -0,0 +1,142 @@ +#ifndef CUDADataFormats_TrackingRecHit_interface_TrackingRecHit2DHeterogeneous_h +#define CUDADataFormats_TrackingRecHit_interface_TrackingRecHit2DHeterogeneous_h + +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DSOAView.h" +#include "CUDADataFormats/Common/interface/HeterogeneousSoA.h" + +template +class TrackingRecHit2DHeterogeneous { +public: + template + using unique_ptr = typename Traits::template unique_ptr; + + using Hist = TrackingRecHit2DSOAView::Hist; + + TrackingRecHit2DHeterogeneous() = default; + + explicit TrackingRecHit2DHeterogeneous(uint32_t nHits, + pixelCPEforGPU::ParamsOnGPU const* cpeParams, + uint32_t const* hitsModuleStart, + cudaStream_t stream); + + ~TrackingRecHit2DHeterogeneous() = default; + + TrackingRecHit2DHeterogeneous(const TrackingRecHit2DHeterogeneous&) = delete; + TrackingRecHit2DHeterogeneous& operator=(const TrackingRecHit2DHeterogeneous&) = delete; + TrackingRecHit2DHeterogeneous(TrackingRecHit2DHeterogeneous&&) = default; + TrackingRecHit2DHeterogeneous& operator=(TrackingRecHit2DHeterogeneous&&) = default; + + TrackingRecHit2DSOAView* view() { return m_view.get(); } + TrackingRecHit2DSOAView const* view() const { return m_view.get(); } + + auto nHits() const { return m_nHits; } + + auto hitsModuleStart() const { return m_hitsModuleStart; } + auto hitsLayerStart() { return m_hitsLayerStart; } + auto phiBinner() { return m_hist; } + auto iphi() { return m_iphi; } + + // only the local coord and detector index + cms::cuda::host::unique_ptr localCoordToHostAsync(cudaStream_t stream) const; + cms::cuda::host::unique_ptr detIndexToHostAsync(cudaStream_t stream) const; + cms::cuda::host::unique_ptr hitsModuleStartToHostAsync(cudaStream_t stream) const; + +private: + static constexpr uint32_t n16 = 4; + static constexpr uint32_t n32 = 9; + static_assert(sizeof(uint32_t) == sizeof(float)); // just stating the obvious + + unique_ptr m_store16; //! + unique_ptr m_store32; //! + + unique_ptr m_HistStore; //! + unique_ptr m_AverageGeometryStore; //! + + unique_ptr m_view; //! + + uint32_t m_nHits; + + uint32_t const* m_hitsModuleStart; // needed for legacy, this is on GPU! + + // needed as kernel params... + Hist* m_hist; + uint32_t* m_hitsLayerStart; + int16_t* m_iphi; +}; + +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +template +TrackingRecHit2DHeterogeneous::TrackingRecHit2DHeterogeneous(uint32_t nHits, + pixelCPEforGPU::ParamsOnGPU const* cpeParams, + uint32_t const* hitsModuleStart, + cudaStream_t stream) + : m_nHits(nHits), m_hitsModuleStart(hitsModuleStart) { + auto view = Traits::template make_host_unique(stream); + + view->m_nHits = nHits; + m_view = Traits::template make_device_unique(stream); + m_AverageGeometryStore = Traits::template make_device_unique(stream); + view->m_averageGeometry = m_AverageGeometryStore.get(); + view->m_cpeParams = cpeParams; + view->m_hitsModuleStart = hitsModuleStart; + + // if empy do not bother + if (0 == nHits) { + if constexpr (std::is_same::value) { + cms::cuda::copyAsync(m_view, view, stream); + } else { + m_view.reset(view.release()); // NOLINT: std::move() breaks CUDA version + } + return; + } + + // the single arrays are not 128 bit alligned... + // the hits are actually accessed in order only in building + // if ordering is relevant they may have to be stored phi-ordered by layer or so + // this will break 1to1 correspondence with cluster and module locality + // so unless proven VERY inefficient we keep it ordered as generated + m_store16 = Traits::template make_device_unique(nHits * n16, stream); + m_store32 = Traits::template make_device_unique(nHits * n32 + 11, stream); + m_HistStore = Traits::template make_device_unique(stream); + + auto get16 = [&](int i) { return m_store16.get() + i * nHits; }; + auto get32 = [&](int i) { return m_store32.get() + i * nHits; }; + + // copy all the pointers + m_hist = view->m_hist = m_HistStore.get(); + + view->m_xl = get32(0); + view->m_yl = get32(1); + view->m_xerr = get32(2); + view->m_yerr = get32(3); + + view->m_xg = get32(4); + view->m_yg = get32(5); + view->m_zg = get32(6); + view->m_rg = get32(7); + + m_iphi = view->m_iphi = reinterpret_cast(get16(0)); + + view->m_charge = reinterpret_cast(get32(8)); + view->m_xsize = reinterpret_cast(get16(2)); + view->m_ysize = reinterpret_cast(get16(3)); + view->m_detInd = get16(1); + + m_hitsLayerStart = view->m_hitsLayerStart = reinterpret_cast(get32(n32)); + + // transfer view + if constexpr (std::is_same::value) { + cms::cuda::copyAsync(m_view, view, stream); + } else { + m_view.reset(view.release()); // NOLINT: std::move() breaks CUDA version + } +} + +using TrackingRecHit2DGPU = TrackingRecHit2DHeterogeneous; +using TrackingRecHit2DCUDA = TrackingRecHit2DHeterogeneous; +using TrackingRecHit2DCPU = TrackingRecHit2DHeterogeneous; +using TrackingRecHit2DHost = TrackingRecHit2DHeterogeneous; + +#endif // CUDADataFormats_TrackingRecHit_interface_TrackingRecHit2DHeterogeneous_h diff --git a/CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DSOAView.h b/CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DSOAView.h new file mode 100644 index 0000000000000..53297a78a428f --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DSOAView.h @@ -0,0 +1,104 @@ +#ifndef CUDADataFormats_TrackingRecHit_interface_TrackingRecHit2DSOAView_h +#define CUDADataFormats_TrackingRecHit_interface_TrackingRecHit2DSOAView_h + +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" +#include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" + +namespace pixelCPEforGPU { + struct ParamsOnGPU; +} + +class TrackingRecHit2DSOAView { +public: + static constexpr uint32_t maxHits() { return gpuClustering::maxNumClusters; } + using hindex_type = uint32_t; // if above is <=2^32 + + using PhiBinner = + cms::cuda::HistoContainer; + + using Hist = PhiBinner; // FIXME + + using AverageGeometry = phase1PixelTopology::AverageGeometry; + + template + friend class TrackingRecHit2DHeterogeneous; + + __device__ __forceinline__ uint32_t nHits() const { return m_nHits; } + + __device__ __forceinline__ float& xLocal(int i) { return m_xl[i]; } + __device__ __forceinline__ float xLocal(int i) const { return __ldg(m_xl + i); } + __device__ __forceinline__ float& yLocal(int i) { return m_yl[i]; } + __device__ __forceinline__ float yLocal(int i) const { return __ldg(m_yl + i); } + + __device__ __forceinline__ float& xerrLocal(int i) { return m_xerr[i]; } + __device__ __forceinline__ float xerrLocal(int i) const { return __ldg(m_xerr + i); } + __device__ __forceinline__ float& yerrLocal(int i) { return m_yerr[i]; } + __device__ __forceinline__ float yerrLocal(int i) const { return __ldg(m_yerr + i); } + + __device__ __forceinline__ float& xGlobal(int i) { return m_xg[i]; } + __device__ __forceinline__ float xGlobal(int i) const { return __ldg(m_xg + i); } + __device__ __forceinline__ float& yGlobal(int i) { return m_yg[i]; } + __device__ __forceinline__ float yGlobal(int i) const { return __ldg(m_yg + i); } + __device__ __forceinline__ float& zGlobal(int i) { return m_zg[i]; } + __device__ __forceinline__ float zGlobal(int i) const { return __ldg(m_zg + i); } + __device__ __forceinline__ float& rGlobal(int i) { return m_rg[i]; } + __device__ __forceinline__ float rGlobal(int i) const { return __ldg(m_rg + i); } + + __device__ __forceinline__ int16_t& iphi(int i) { return m_iphi[i]; } + __device__ __forceinline__ int16_t iphi(int i) const { return __ldg(m_iphi + i); } + + __device__ __forceinline__ int32_t& charge(int i) { return m_charge[i]; } + __device__ __forceinline__ int32_t charge(int i) const { return __ldg(m_charge + i); } + __device__ __forceinline__ int16_t& clusterSizeX(int i) { return m_xsize[i]; } + __device__ __forceinline__ int16_t clusterSizeX(int i) const { return __ldg(m_xsize + i); } + __device__ __forceinline__ int16_t& clusterSizeY(int i) { return m_ysize[i]; } + __device__ __forceinline__ int16_t clusterSizeY(int i) const { return __ldg(m_ysize + i); } + __device__ __forceinline__ uint16_t& detectorIndex(int i) { return m_detInd[i]; } + __device__ __forceinline__ uint16_t detectorIndex(int i) const { return __ldg(m_detInd + i); } + + __device__ __forceinline__ pixelCPEforGPU::ParamsOnGPU const& cpeParams() const { return *m_cpeParams; } + + __device__ __forceinline__ uint32_t hitsModuleStart(int i) const { return __ldg(m_hitsModuleStart + i); } + + __device__ __forceinline__ uint32_t* hitsLayerStart() { return m_hitsLayerStart; } + __device__ __forceinline__ uint32_t const* hitsLayerStart() const { return m_hitsLayerStart; } + + __device__ __forceinline__ Hist& phiBinner() { return *m_hist; } + __device__ __forceinline__ Hist const& phiBinner() const { return *m_hist; } + + __device__ __forceinline__ AverageGeometry& averageGeometry() { return *m_averageGeometry; } + __device__ __forceinline__ AverageGeometry const& averageGeometry() const { return *m_averageGeometry; } + +private: + // local coord + float *m_xl, *m_yl; + float *m_xerr, *m_yerr; + + // global coord + float *m_xg, *m_yg, *m_zg, *m_rg; + int16_t* m_iphi; + + // cluster properties + int32_t* m_charge; + int16_t* m_xsize; + int16_t* m_ysize; + uint16_t* m_detInd; + + // supporting objects + // m_averageGeometry is corrected for beam spot, not sure where to host it otherwise + AverageGeometry* m_averageGeometry; // owned by TrackingRecHit2DHeterogeneous + pixelCPEforGPU::ParamsOnGPU const* m_cpeParams; // forwarded from setup, NOT owned + uint32_t const* m_hitsModuleStart; // forwarded from clusters + + uint32_t* m_hitsLayerStart; + + PhiBinner* m_hist; // FIXME use a more descriptive name consistently + + uint32_t m_nHits; +}; + +#endif // CUDADataFormats_TrackingRecHit_interface_TrackingRecHit2DSOAView_h diff --git a/CUDADataFormats/TrackingRecHit/src/TrackingRecHit2DHeterogeneous.cc b/CUDADataFormats/TrackingRecHit/src/TrackingRecHit2DHeterogeneous.cc new file mode 100644 index 0000000000000..dd3cf548e11dd --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/src/TrackingRecHit2DHeterogeneous.cc @@ -0,0 +1,20 @@ +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +template <> +cms::cuda::host::unique_ptr TrackingRecHit2DCUDA::localCoordToHostAsync(cudaStream_t stream) const { + auto ret = cms::cuda::make_host_unique(4 * nHits(), stream); + cms::cuda::copyAsync(ret, m_store32, 4 * nHits(), stream); + return ret; +} + +template <> +cms::cuda::host::unique_ptr TrackingRecHit2DCUDA::hitsModuleStartToHostAsync(cudaStream_t stream) const { + auto ret = cms::cuda::make_host_unique(gpuClustering::maxNumModules + 1, stream); + cudaCheck(cudaMemcpyAsync( + ret.get(), m_hitsModuleStart, sizeof(uint32_t) * (gpuClustering::maxNumModules + 1), cudaMemcpyDefault, stream)); + return ret; +} diff --git a/CUDADataFormats/TrackingRecHit/src/classes.h b/CUDADataFormats/TrackingRecHit/src/classes.h new file mode 100644 index 0000000000000..86fef25746efd --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/src/classes.h @@ -0,0 +1,8 @@ +#ifndef CUDADataFormats_SiPixelCluster_src_classes_h +#define CUDADataFormats_SiPixelCluster_src_classes_h + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "DataFormats/Common/interface/Wrapper.h" + +#endif // CUDADataFormats_SiPixelCluster_src_classes_h diff --git a/CUDADataFormats/TrackingRecHit/src/classes_def.xml b/CUDADataFormats/TrackingRecHit/src/classes_def.xml new file mode 100644 index 0000000000000..7e1919de510b3 --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/src/classes_def.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CUDADataFormats/TrackingRecHit/test/BuildFile.xml b/CUDADataFormats/TrackingRecHit/test/BuildFile.xml new file mode 100644 index 0000000000000..74f2818790d0f --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/test/BuildFile.xml @@ -0,0 +1,3 @@ + + + diff --git a/CUDADataFormats/TrackingRecHit/test/TrackingRecHit2DCUDA_t.cpp b/CUDADataFormats/TrackingRecHit/test/TrackingRecHit2DCUDA_t.cpp new file mode 100644 index 0000000000000..3d8413b36ec96 --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/test/TrackingRecHit2DCUDA_t.cpp @@ -0,0 +1,29 @@ +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/requireDevices.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +namespace testTrackingRecHit2D { + + void runKernels(TrackingRecHit2DSOAView* hits); + +} + +int main() { + cms::cudatest::requireDevices(); + + cudaStream_t stream; + cudaCheck(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking)); + + // inner scope to deallocate memory before destroying the stream + { + auto nHits = 200; + TrackingRecHit2DCUDA tkhit(nHits, nullptr, nullptr, stream); + + testTrackingRecHit2D::runKernels(tkhit.view()); + } + + cudaCheck(cudaStreamDestroy(stream)); + + return 0; +} diff --git a/CUDADataFormats/TrackingRecHit/test/TrackingRecHit2DCUDA_t.cu b/CUDADataFormats/TrackingRecHit/test/TrackingRecHit2DCUDA_t.cu new file mode 100644 index 0000000000000..06bd599d074f9 --- /dev/null +++ b/CUDADataFormats/TrackingRecHit/test/TrackingRecHit2DCUDA_t.cu @@ -0,0 +1,31 @@ +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" + +namespace testTrackingRecHit2D { + + __global__ void fill(TrackingRecHit2DSOAView* phits) { + assert(phits); + auto& hits = *phits; + assert(hits.nHits() == 200); + + int i = threadIdx.x; + if (i > 200) + return; + } + + __global__ void verify(TrackingRecHit2DSOAView const* phits) { + assert(phits); + auto const& hits = *phits; + assert(hits.nHits() == 200); + + int i = threadIdx.x; + if (i > 200) + return; + } + + void runKernels(TrackingRecHit2DSOAView* hits) { + assert(hits); + fill<<<1, 1024>>>(hits); + verify<<<1, 1024>>>(hits); + } + +} // namespace testTrackingRecHit2D diff --git a/CUDADataFormats/Vertex/BuildFile.xml b/CUDADataFormats/Vertex/BuildFile.xml new file mode 100644 index 0000000000000..e3f9a0910bbd8 --- /dev/null +++ b/CUDADataFormats/Vertex/BuildFile.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h b/CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h new file mode 100644 index 0000000000000..aacfddc6fe7e2 --- /dev/null +++ b/CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h @@ -0,0 +1,14 @@ +#ifndef CUDADataFormatsVertexZVertexHeterogeneous_H +#define CUDADataFormatsVertexZVertexHeterogeneous_H + +#include "CUDADataFormats/Vertex/interface/ZVertexSoA.h" +#include "CUDADataFormats/Common/interface/HeterogeneousSoA.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" + +using ZVertexHeterogeneous = HeterogeneousSoA; +#ifndef __CUDACC__ +#include "CUDADataFormats/Common/interface/Product.h" +using ZVertexCUDAProduct = cms::cuda::Product; +#endif + +#endif diff --git a/CUDADataFormats/Vertex/interface/ZVertexSoA.h b/CUDADataFormats/Vertex/interface/ZVertexSoA.h new file mode 100644 index 0000000000000..5f0699d5831ec --- /dev/null +++ b/CUDADataFormats/Vertex/interface/ZVertexSoA.h @@ -0,0 +1,26 @@ +#ifndef CUDADataFormatsVertexZVertexSoA_H +#define CUDADataFormatsVertexZVertexSoA_H + +#include +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" + +// SOA for vertices +// These vertices are clusterized and fitted only along the beam line (z) +// to obtain their global coordinate the beam spot position shall be added (eventually correcting for the beam angle as well) +struct ZVertexSoA { + static constexpr uint32_t MAXTRACKS = 32 * 1024; + static constexpr uint32_t MAXVTX = 1024; + + int16_t idv[MAXTRACKS]; // vertex index for each associated (original) track (-1 == not associate) + float zv[MAXVTX]; // output z-posistion of found vertices + float wv[MAXVTX]; // output weight (1/error^2) on the above + float chi2[MAXVTX]; // vertices chi2 + float ptv2[MAXVTX]; // vertices pt^2 + int32_t ndof[MAXTRACKS]; // vertices number of dof (reused as workspace for the number of nearest neighbours FIXME) + uint16_t sortInd[MAXVTX]; // sorted index (by pt2) ascending + uint32_t nvFinal; // the number of vertices + + __host__ __device__ void init() { nvFinal = 0; } +}; + +#endif // CUDADataFormatsVertexZVertexSoA.H diff --git a/CUDADataFormats/Vertex/src/classes.h b/CUDADataFormats/Vertex/src/classes.h new file mode 100644 index 0000000000000..e7fea871f7d39 --- /dev/null +++ b/CUDADataFormats/Vertex/src/classes.h @@ -0,0 +1,8 @@ +#ifndef CUDADataFormats__src_classes_h +#define CUDADataFormats__src_classes_h + +#include "CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h" +#include "CUDADataFormats/Common/interface/Product.h" +#include "DataFormats/Common/interface/Wrapper.h" + +#endif diff --git a/CUDADataFormats/Vertex/src/classes_def.xml b/CUDADataFormats/Vertex/src/classes_def.xml new file mode 100644 index 0000000000000..ea633080af9af --- /dev/null +++ b/CUDADataFormats/Vertex/src/classes_def.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/CalibTracker/Records/interface/SiPixelGainCalibrationForHLTGPURcd.h b/CalibTracker/Records/interface/SiPixelGainCalibrationForHLTGPURcd.h new file mode 100644 index 0000000000000..ad8f0a4032588 --- /dev/null +++ b/CalibTracker/Records/interface/SiPixelGainCalibrationForHLTGPURcd.h @@ -0,0 +1,14 @@ +#ifndef CalibTracker_Records_SiPixelGainCalibrationForHLTGPURcd_h +#define CalibTracker_Records_SiPixelGainCalibrationForHLTGPURcd_h + +#include "CondFormats/DataRecord/interface/SiPixelGainCalibrationForHLTRcd.h" +#include "FWCore/Framework/interface/DependentRecordImplementation.h" +#include "FWCore/Framework/interface/EventSetupRecordImplementation.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" + +class SiPixelGainCalibrationForHLTGPURcd + : public edm::eventsetup::DependentRecordImplementation< + SiPixelGainCalibrationForHLTGPURcd, + edm::mpl::Vector> {}; + +#endif // CalibTracker_Records_SiPixelGainCalibrationForHLTGPURcd_h diff --git a/CalibTracker/Records/src/SiPixelGainCalibrationForHLTGPURcd.cc b/CalibTracker/Records/src/SiPixelGainCalibrationForHLTGPURcd.cc new file mode 100644 index 0000000000000..e6020eca80b1f --- /dev/null +++ b/CalibTracker/Records/src/SiPixelGainCalibrationForHLTGPURcd.cc @@ -0,0 +1,5 @@ +#include "CalibTracker/Records/interface/SiPixelGainCalibrationForHLTGPURcd.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" +#include "FWCore/Utilities/interface/typelookup.h" + +EVENTSETUP_RECORD_REG(SiPixelGainCalibrationForHLTGPURcd); diff --git a/CalibTracker/SiPixelESProducers/BuildFile.xml b/CalibTracker/SiPixelESProducers/BuildFile.xml index 6efeef5ca0d1c..6e64a5b4b94ee 100644 --- a/CalibTracker/SiPixelESProducers/BuildFile.xml +++ b/CalibTracker/SiPixelESProducers/BuildFile.xml @@ -1,10 +1,15 @@ - - - + + + + + + + + diff --git a/CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h b/CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h new file mode 100644 index 0000000000000..6fb487a244e71 --- /dev/null +++ b/CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h @@ -0,0 +1,32 @@ +#ifndef CalibTracker_SiPixelESProducers_interface_SiPixelGainCalibrationForHLTGPU_h +#define CalibTracker_SiPixelESProducers_interface_SiPixelGainCalibrationForHLTGPU_h + +#include "CondFormats/SiPixelObjects/interface/SiPixelGainCalibrationForHLT.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" + +class SiPixelGainCalibrationForHLT; +class SiPixelGainForHLTonGPU; +struct SiPixelGainForHLTonGPU_DecodingStructure; +class TrackerGeometry; + +class SiPixelGainCalibrationForHLTGPU { +public: + explicit SiPixelGainCalibrationForHLTGPU(const SiPixelGainCalibrationForHLT &gains, const TrackerGeometry &geom); + ~SiPixelGainCalibrationForHLTGPU(); + + const SiPixelGainForHLTonGPU *getGPUProductAsync(cudaStream_t cudaStream) const; + const SiPixelGainForHLTonGPU *getCPUProduct() const { return gainForHLTonHost_; } + const SiPixelGainCalibrationForHLT *getOriginalProduct() { return gains_; } + +private: + const SiPixelGainCalibrationForHLT *gains_ = nullptr; + SiPixelGainForHLTonGPU *gainForHLTonHost_ = nullptr; + struct GPUData { + ~GPUData(); + SiPixelGainForHLTonGPU *gainForHLTonGPU = nullptr; + SiPixelGainForHLTonGPU_DecodingStructure *gainDataOnGPU = nullptr; + }; + cms::cuda::ESProduct gpuData_; +}; + +#endif // CalibTracker_SiPixelESProducers_interface_SiPixelGainCalibrationForHLTGPU_h diff --git a/CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h b/CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h new file mode 100644 index 0000000000000..5f875d7dff5a9 --- /dev/null +++ b/CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h @@ -0,0 +1,56 @@ +#ifndef CalibTracker_SiPixelESProducers_interface_SiPixelROCsStatusAndMappingWrapper_h +#define CalibTracker_SiPixelESProducers_interface_SiPixelROCsStatusAndMappingWrapper_h + +#include + +#include + +#include "CondFormats/SiPixelObjects/interface/SiPixelROCsStatusAndMapping.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +class SiPixelFedCablingMap; +class TrackerGeometry; +class SiPixelQuality; + +// TODO: since this has more information than just cabling map, maybe we should invent a better name? +class SiPixelROCsStatusAndMappingWrapper { +public: + SiPixelROCsStatusAndMappingWrapper(SiPixelFedCablingMap const &cablingMap, + TrackerGeometry const &trackerGeom, + SiPixelQuality const *badPixelInfo); + ~SiPixelROCsStatusAndMappingWrapper(); + + bool hasQuality() const { return hasQuality_; } + + // returns pointer to GPU memory + const SiPixelROCsStatusAndMapping *getGPUProductAsync(cudaStream_t cudaStream) const; + + // returns pointer to GPU memory + const unsigned char *getModToUnpAllAsync(cudaStream_t cudaStream) const; + cms::cuda::device::unique_ptr getModToUnpRegionalAsync(std::set const &modules, + cudaStream_t cudaStream) const; + +private: + const SiPixelFedCablingMap *cablingMap_; + std::vector> modToUnpDefault; + unsigned int size; + bool hasQuality_; + + SiPixelROCsStatusAndMapping *cablingMapHost = nullptr; // pointer to struct in CPU + + struct GPUData { + ~GPUData(); + SiPixelROCsStatusAndMapping *cablingMapDevice = nullptr; // pointer to struct in GPU + }; + cms::cuda::ESProduct gpuData_; + + struct ModulesToUnpack { + ~ModulesToUnpack(); + unsigned char *modToUnpDefault = nullptr; // pointer to GPU + }; + cms::cuda::ESProduct modToUnp_; +}; + +#endif // CalibTracker_SiPixelESProducers_interface_SiPixelROCsStatusAndMappingWrapper_h diff --git a/CalibTracker/SiPixelESProducers/plugins/BuildFile.xml b/CalibTracker/SiPixelESProducers/plugins/BuildFile.xml index 5380c9d7d346b..4bef676217b72 100644 --- a/CalibTracker/SiPixelESProducers/plugins/BuildFile.xml +++ b/CalibTracker/SiPixelESProducers/plugins/BuildFile.xml @@ -1,12 +1,14 @@ - - + + + + + - - + diff --git a/CalibTracker/SiPixelESProducers/plugins/SiPixelGainCalibrationForHLTGPUESProducer.cc b/CalibTracker/SiPixelESProducers/plugins/SiPixelGainCalibrationForHLTGPUESProducer.cc new file mode 100644 index 0000000000000..37055ea3e00ca --- /dev/null +++ b/CalibTracker/SiPixelESProducers/plugins/SiPixelGainCalibrationForHLTGPUESProducer.cc @@ -0,0 +1,49 @@ +#include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h" +#include "CalibTracker/Records/interface/SiPixelGainCalibrationForHLTGPURcd.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelGainCalibrationForHLT.h" +#include "CondFormats/DataRecord/interface/SiPixelGainCalibrationForHLTRcd.h" +#include "FWCore/Framework/interface/ESProducer.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/ModuleFactory.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" + +#include + +class SiPixelGainCalibrationForHLTGPUESProducer : public edm::ESProducer { +public: + explicit SiPixelGainCalibrationForHLTGPUESProducer(const edm::ParameterSet& iConfig); + std::unique_ptr produce(const SiPixelGainCalibrationForHLTGPURcd& iRecord); + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + edm::ESGetToken gainsToken_; + edm::ESGetToken geometryToken_; +}; + +SiPixelGainCalibrationForHLTGPUESProducer::SiPixelGainCalibrationForHLTGPUESProducer(const edm::ParameterSet& iConfig) { + auto cc = setWhatProduced(this); + gainsToken_ = cc.consumes(); + geometryToken_ = cc.consumes(); +} + +void SiPixelGainCalibrationForHLTGPUESProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + descriptions.add("siPixelGainCalibrationForHLTGPU", desc); +} + +std::unique_ptr SiPixelGainCalibrationForHLTGPUESProducer::produce( + const SiPixelGainCalibrationForHLTGPURcd& iRecord) { + auto gains = iRecord.getHandle(gainsToken_); + auto geom = iRecord.getHandle(geometryToken_); + return std::make_unique(*gains, *geom); +} + +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" + +DEFINE_FWK_EVENTSETUP_MODULE(SiPixelGainCalibrationForHLTGPUESProducer); diff --git a/CalibTracker/SiPixelESProducers/plugins/SiPixelROCsStatusAndMappingWrapperESProducer.cc b/CalibTracker/SiPixelESProducers/plugins/SiPixelROCsStatusAndMappingWrapperESProducer.cc new file mode 100644 index 0000000000000..9c37860ca9ffe --- /dev/null +++ b/CalibTracker/SiPixelESProducers/plugins/SiPixelROCsStatusAndMappingWrapperESProducer.cc @@ -0,0 +1,68 @@ +#include + +#include "CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h" +#include "CondFormats/DataRecord/interface/SiPixelFedCablingMapRcd.h" +#include "CondFormats/DataRecord/interface/SiPixelQualityRcd.h" +#include "FWCore/Framework/interface/ESProducer.h" +#include "FWCore/Framework/interface/ESTransientHandle.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/ModuleFactory.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "RecoTracker/Record/interface/CkfComponentsRecord.h" // TODO: eventually use something more limited + +class SiPixelROCsStatusAndMappingWrapperESProducer : public edm::ESProducer { +public: + explicit SiPixelROCsStatusAndMappingWrapperESProducer(const edm::ParameterSet& iConfig); + std::unique_ptr produce(const CkfComponentsRecord& iRecord); + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + edm::ESGetToken cablingMapToken_; + edm::ESGetToken qualityToken_; + edm::ESGetToken geometryToken_; + bool useQuality_; +}; + +SiPixelROCsStatusAndMappingWrapperESProducer::SiPixelROCsStatusAndMappingWrapperESProducer( + const edm::ParameterSet& iConfig) + : useQuality_(iConfig.getParameter("UseQualityInfo")) { + auto const& component = iConfig.getParameter("ComponentName"); + auto cc = setWhatProduced(this, component); + cablingMapToken_ = cc.consumes(edm::ESInputTag{"", iConfig.getParameter("CablingMapLabel")}); + if (useQuality_) { + qualityToken_ = cc.consumes(); + } + geometryToken_ = cc.consumes(); +} + +void SiPixelROCsStatusAndMappingWrapperESProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("ComponentName", ""); + desc.add("CablingMapLabel", "")->setComment("CablingMap label"); + desc.add("UseQualityInfo", false); + descriptions.addWithDefaultLabel(desc); +} + +std::unique_ptr SiPixelROCsStatusAndMappingWrapperESProducer::produce( + const CkfComponentsRecord& iRecord) { + auto cablingMap = iRecord.getTransientHandle(cablingMapToken_); + + const SiPixelQuality* quality = nullptr; + if (useQuality_) { + auto qualityInfo = iRecord.getTransientHandle(qualityToken_); + quality = qualityInfo.product(); + } + + auto geom = iRecord.getTransientHandle(geometryToken_); + + return std::make_unique(*cablingMap, *geom, quality); +} + +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" + +DEFINE_FWK_EVENTSETUP_MODULE(SiPixelROCsStatusAndMappingWrapperESProducer); diff --git a/CalibTracker/SiPixelESProducers/src/ES_SiPixelGainCalibrationForHLTGPU.cc b/CalibTracker/SiPixelESProducers/src/ES_SiPixelGainCalibrationForHLTGPU.cc new file mode 100644 index 0000000000000..80932fb468f71 --- /dev/null +++ b/CalibTracker/SiPixelESProducers/src/ES_SiPixelGainCalibrationForHLTGPU.cc @@ -0,0 +1,4 @@ +#include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" + +TYPELOOKUP_DATA_REG(SiPixelGainCalibrationForHLTGPU); diff --git a/CalibTracker/SiPixelESProducers/src/ES_SiPixelROCsStatusAndMappingWrapper.cc b/CalibTracker/SiPixelESProducers/src/ES_SiPixelROCsStatusAndMappingWrapper.cc new file mode 100644 index 0000000000000..45767102b5958 --- /dev/null +++ b/CalibTracker/SiPixelESProducers/src/ES_SiPixelROCsStatusAndMappingWrapper.cc @@ -0,0 +1,4 @@ +#include "CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h" +#include "FWCore/Utilities/interface/typelookup.h" + +TYPELOOKUP_DATA_REG(SiPixelROCsStatusAndMappingWrapper); diff --git a/CalibTracker/SiPixelESProducers/src/SiPixelGainCalibrationForHLTGPU.cc b/CalibTracker/SiPixelESProducers/src/SiPixelGainCalibrationForHLTGPU.cc new file mode 100644 index 0000000000000..66b8d9594353b --- /dev/null +++ b/CalibTracker/SiPixelESProducers/src/SiPixelGainCalibrationForHLTGPU.cc @@ -0,0 +1,101 @@ +#include + +#include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelGainCalibrationForHLT.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelGainForHLTonGPU.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "Geometry/CommonDetUnit/interface/GeomDetType.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +SiPixelGainCalibrationForHLTGPU::SiPixelGainCalibrationForHLTGPU(const SiPixelGainCalibrationForHLT& gains, + const TrackerGeometry& geom) + : gains_(&gains) { + // bizzarre logic (looking for fist strip-det) don't ask + auto const& dus = geom.detUnits(); + unsigned int n_detectors = dus.size(); + for (unsigned int i = 1; i < 7; ++i) { + const auto offset = geom.offsetDU(GeomDetEnumerators::tkDetEnum[i]); + if (offset != dus.size() && dus[offset]->type().isTrackerStrip()) { + if (n_detectors > offset) + n_detectors = offset; + } + } + + LogDebug("SiPixelGainCalibrationForHLTGPU") + << "caching calibs for " << n_detectors << " pixel detectors of size " << gains.data().size() << '\n' + << "sizes " << sizeof(char) << ' ' << sizeof(uint8_t) << ' ' << sizeof(SiPixelGainForHLTonGPU::DecodingStructure); + + cudaCheck(cudaMallocHost((void**)&gainForHLTonHost_, sizeof(SiPixelGainForHLTonGPU))); + gainForHLTonHost_->v_pedestals_ = + (SiPixelGainForHLTonGPU_DecodingStructure*)this->gains_->data().data(); // so it can be used on CPU as well... + + // do not read back from the (possibly write-combined) memory buffer + auto minPed = gains.getPedLow(); + auto maxPed = gains.getPedHigh(); + auto minGain = gains.getGainLow(); + auto maxGain = gains.getGainHigh(); + auto nBinsToUseForEncoding = 253; + + // we will simplify later (not everything is needed....) + gainForHLTonHost_->minPed_ = minPed; + gainForHLTonHost_->maxPed_ = maxPed; + gainForHLTonHost_->minGain_ = minGain; + gainForHLTonHost_->maxGain_ = maxGain; + + gainForHLTonHost_->numberOfRowsAveragedOver_ = 80; + gainForHLTonHost_->nBinsToUseForEncoding_ = nBinsToUseForEncoding; + gainForHLTonHost_->deadFlag_ = 255; + gainForHLTonHost_->noisyFlag_ = 254; + + gainForHLTonHost_->pedPrecision_ = static_cast(maxPed - minPed) / nBinsToUseForEncoding; + gainForHLTonHost_->gainPrecision_ = static_cast(maxGain - minGain) / nBinsToUseForEncoding; + + LogDebug("SiPixelGainCalibrationForHLTGPU") + << "precisions g " << gainForHLTonHost_->pedPrecision_ << ' ' << gainForHLTonHost_->gainPrecision_; + + // fill the index map + auto const& ind = gains.getIndexes(); + LogDebug("SiPixelGainCalibrationForHLTGPU") << ind.size() << " " << n_detectors; + + for (auto i = 0U; i < n_detectors; ++i) { + auto p = std::lower_bound( + ind.begin(), ind.end(), dus[i]->geographicalId().rawId(), SiPixelGainCalibrationForHLT::StrictWeakOrdering()); + assert(p != ind.end() && p->detid == dus[i]->geographicalId()); + assert(p->iend <= gains.data().size()); + assert(p->iend >= p->ibegin); + assert(0 == p->ibegin % 2); + assert(0 == p->iend % 2); + assert(p->ibegin != p->iend); + assert(p->ncols > 0); + gainForHLTonHost_->rangeAndCols_[i] = std::make_pair(SiPixelGainForHLTonGPU::Range(p->ibegin, p->iend), p->ncols); + if (ind[i].detid != dus[i]->geographicalId()) + LogDebug("SiPixelGainCalibrationForHLTGPU") << ind[i].detid << "!=" << dus[i]->geographicalId(); + } +} + +SiPixelGainCalibrationForHLTGPU::~SiPixelGainCalibrationForHLTGPU() { cudaCheck(cudaFreeHost(gainForHLTonHost_)); } + +SiPixelGainCalibrationForHLTGPU::GPUData::~GPUData() { + cudaCheck(cudaFree(gainForHLTonGPU)); + cudaCheck(cudaFree(gainDataOnGPU)); +} + +const SiPixelGainForHLTonGPU* SiPixelGainCalibrationForHLTGPU::getGPUProductAsync(cudaStream_t cudaStream) const { + const auto& data = gpuData_.dataForCurrentDeviceAsync(cudaStream, [this](GPUData& data, cudaStream_t stream) { + cudaCheck(cudaMalloc((void**)&data.gainForHLTonGPU, sizeof(SiPixelGainForHLTonGPU))); + cudaCheck(cudaMalloc((void**)&data.gainDataOnGPU, this->gains_->data().size())); + // gains.data().data() is used also for non-GPU code, we cannot allocate it on aligned and write-combined memory + cudaCheck(cudaMemcpyAsync( + data.gainDataOnGPU, this->gains_->data().data(), this->gains_->data().size(), cudaMemcpyDefault, stream)); + + cudaCheck(cudaMemcpyAsync( + data.gainForHLTonGPU, this->gainForHLTonHost_, sizeof(SiPixelGainForHLTonGPU), cudaMemcpyDefault, stream)); + cudaCheck(cudaMemcpyAsync(&(data.gainForHLTonGPU->v_pedestals_), + &(data.gainDataOnGPU), + sizeof(SiPixelGainForHLTonGPU_DecodingStructure*), + cudaMemcpyDefault, + stream)); + }); + return data.gainForHLTonGPU; +} diff --git a/CalibTracker/SiPixelESProducers/src/SiPixelROCsStatusAndMappingWrapper.cc b/CalibTracker/SiPixelESProducers/src/SiPixelROCsStatusAndMappingWrapper.cc new file mode 100644 index 0000000000000..2437696656d25 --- /dev/null +++ b/CalibTracker/SiPixelESProducers/src/SiPixelROCsStatusAndMappingWrapper.cc @@ -0,0 +1,170 @@ +// C++ includes +#include +#include +#include +#include + +// CUDA includes +#include + +// CMSSW includes +#include "CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelFedCablingMap.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelFedCablingTree.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelQuality.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "Geometry/CommonDetUnit/interface/GeomDetType.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +SiPixelROCsStatusAndMappingWrapper::SiPixelROCsStatusAndMappingWrapper(SiPixelFedCablingMap const& cablingMap, + TrackerGeometry const& trackerGeom, + SiPixelQuality const* badPixelInfo) + : cablingMap_(&cablingMap), modToUnpDefault(pixelgpudetails::MAX_SIZE), hasQuality_(badPixelInfo != nullptr) { + cudaCheck(cudaMallocHost(&cablingMapHost, sizeof(SiPixelROCsStatusAndMapping))); + + std::vector const& fedIds = cablingMap.fedIds(); + std::unique_ptr const& cabling = cablingMap.cablingTree(); + + unsigned int startFed = *(fedIds.begin()); + unsigned int endFed = *(fedIds.end() - 1); + + sipixelobjects::CablingPathToDetUnit path; + int index = 1; + + for (unsigned int fed = startFed; fed <= endFed; fed++) { + for (unsigned int link = 1; link <= pixelgpudetails::MAX_LINK; link++) { + for (unsigned int roc = 1; roc <= pixelgpudetails::MAX_ROC; roc++) { + path = {fed, link, roc}; + const sipixelobjects::PixelROC* pixelRoc = cabling->findItem(path); + cablingMapHost->fed[index] = fed; + cablingMapHost->link[index] = link; + cablingMapHost->roc[index] = roc; + if (pixelRoc != nullptr) { + cablingMapHost->rawId[index] = pixelRoc->rawId(); + cablingMapHost->rocInDet[index] = pixelRoc->idInDetUnit(); + modToUnpDefault[index] = false; + if (badPixelInfo != nullptr) + cablingMapHost->badRocs[index] = badPixelInfo->IsRocBad(pixelRoc->rawId(), pixelRoc->idInDetUnit()); + else + cablingMapHost->badRocs[index] = false; + } else { // store some dummy number + cablingMapHost->rawId[index] = 9999; + cablingMapHost->rocInDet[index] = 9999; + cablingMapHost->badRocs[index] = true; + modToUnpDefault[index] = true; + } + index++; + } + } + } // end of FED loop + + // Given FedId, Link and idinLnk; use the following formula + // to get the rawId and idinDU + // index = (FedID-1200) * MAX_LINK* MAX_ROC + (Link-1)* MAX_ROC + idinLnk; + // where, MAX_LINK = 48, MAX_ROC = 8 for Phase1 as mentioned Danek's email + // FedID varies between 1200 to 1338 (In total 108 FED's) + // Link varies between 1 to 48 + // idinLnk varies between 1 to 8 + + for (int i = 1; i < index; i++) { + if (cablingMapHost->rawId[i] == 9999) { + cablingMapHost->moduleId[i] = 9999; + } else { + /* + std::cout << cablingMapHost->rawId[i] << std::endl; + */ + auto gdet = trackerGeom.idToDetUnit(cablingMapHost->rawId[i]); + if (!gdet) { + LogDebug("SiPixelROCsStatusAndMapping") << " Not found: " << cablingMapHost->rawId[i] << std::endl; + continue; + } + cablingMapHost->moduleId[i] = gdet->index(); + } + LogDebug("SiPixelROCsStatusAndMapping") + << "----------------------------------------------------------------------------" << std::endl; + LogDebug("SiPixelROCsStatusAndMapping") + << i << std::setw(20) << cablingMapHost->fed[i] << std::setw(20) << cablingMapHost->link[i] << std::setw(20) + << cablingMapHost->roc[i] << std::endl; + LogDebug("SiPixelROCsStatusAndMapping") + << i << std::setw(20) << cablingMapHost->rawId[i] << std::setw(20) << cablingMapHost->rocInDet[i] + << std::setw(20) << cablingMapHost->moduleId[i] << std::endl; + LogDebug("SiPixelROCsStatusAndMapping") + << i << std::setw(20) << (bool)cablingMapHost->badRocs[i] << std::setw(20) << std::endl; + LogDebug("SiPixelROCsStatusAndMapping") + << "----------------------------------------------------------------------------" << std::endl; + } + + cablingMapHost->size = index - 1; +} + +SiPixelROCsStatusAndMappingWrapper::~SiPixelROCsStatusAndMappingWrapper() { cudaCheck(cudaFreeHost(cablingMapHost)); } + +const SiPixelROCsStatusAndMapping* SiPixelROCsStatusAndMappingWrapper::getGPUProductAsync( + cudaStream_t cudaStream) const { + const auto& data = gpuData_.dataForCurrentDeviceAsync(cudaStream, [this](GPUData& data, cudaStream_t stream) { + // allocate + cudaCheck(cudaMalloc(&data.cablingMapDevice, sizeof(SiPixelROCsStatusAndMapping))); + + // transfer + cudaCheck(cudaMemcpyAsync( + data.cablingMapDevice, this->cablingMapHost, sizeof(SiPixelROCsStatusAndMapping), cudaMemcpyDefault, stream)); + }); + return data.cablingMapDevice; +} + +const unsigned char* SiPixelROCsStatusAndMappingWrapper::getModToUnpAllAsync(cudaStream_t cudaStream) const { + const auto& data = + modToUnp_.dataForCurrentDeviceAsync(cudaStream, [this](ModulesToUnpack& data, cudaStream_t stream) { + cudaCheck(cudaMalloc((void**)&data.modToUnpDefault, pixelgpudetails::MAX_SIZE_BYTE_BOOL)); + cudaCheck(cudaMemcpyAsync(data.modToUnpDefault, + this->modToUnpDefault.data(), + this->modToUnpDefault.size() * sizeof(unsigned char), + cudaMemcpyDefault, + stream)); + }); + return data.modToUnpDefault; +} + +cms::cuda::device::unique_ptr SiPixelROCsStatusAndMappingWrapper::getModToUnpRegionalAsync( + std::set const& modules, cudaStream_t cudaStream) const { + auto modToUnpDevice = cms::cuda::make_device_unique(pixelgpudetails::MAX_SIZE, cudaStream); + auto modToUnpHost = cms::cuda::make_host_unique(pixelgpudetails::MAX_SIZE, cudaStream); + + std::vector const& fedIds = cablingMap_->fedIds(); + std::unique_ptr const& cabling = cablingMap_->cablingTree(); + + unsigned int startFed = *(fedIds.begin()); + unsigned int endFed = *(fedIds.end() - 1); + + sipixelobjects::CablingPathToDetUnit path; + int index = 1; + + for (unsigned int fed = startFed; fed <= endFed; fed++) { + for (unsigned int link = 1; link <= pixelgpudetails::MAX_LINK; link++) { + for (unsigned int roc = 1; roc <= pixelgpudetails::MAX_ROC; roc++) { + path = {fed, link, roc}; + const sipixelobjects::PixelROC* pixelRoc = cabling->findItem(path); + if (pixelRoc != nullptr) { + modToUnpHost[index] = (not modules.empty()) and (modules.find(pixelRoc->rawId()) == modules.end()); + } else { // store some dummy number + modToUnpHost[index] = true; + } + index++; + } + } + } + + cudaCheck(cudaMemcpyAsync(modToUnpDevice.get(), + modToUnpHost.get(), + pixelgpudetails::MAX_SIZE * sizeof(unsigned char), + cudaMemcpyHostToDevice, + cudaStream)); + return modToUnpDevice; +} + +SiPixelROCsStatusAndMappingWrapper::GPUData::~GPUData() { cudaCheck(cudaFree(cablingMapDevice)); } + +SiPixelROCsStatusAndMappingWrapper::ModulesToUnpack::~ModulesToUnpack() { cudaCheck(cudaFree(modToUnpDefault)); } diff --git a/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCRandom_cff.py b/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCRandom_cff.py index 8185c8cfbb089..149eb1d7f3854 100644 --- a/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCRandom_cff.py +++ b/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCRandom_cff.py @@ -10,12 +10,14 @@ ) from EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi import siPixelDigis -siPixelDigisForLumiR = siPixelDigis.clone() -siPixelDigisForLumiR.InputLabel = cms.InputTag("hltFEDSelectorLumiPixels") +siPixelDigisForLumiR = siPixelDigis.cpu.clone( + InputLabel = "hltFEDSelectorLumiPixels" +) from RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizerPreSplitting_cfi import siPixelClustersPreSplitting -siPixelClustersForLumiR = siPixelClustersPreSplitting.clone() -siPixelClustersForLumiR.src = cms.InputTag("siPixelDigisForLumiR") +siPixelClustersForLumiR = siPixelClustersPreSplitting.cpu.clone( + src = "siPixelDigisForLumiR" +) from Calibration.LumiAlCaRecoProducers.alcaPCCProducer_cfi import alcaPCCProducer alcaPCCProducerRandom = alcaPCCProducer.clone() diff --git a/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCZeroBias_cff.py b/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCZeroBias_cff.py index 0ef9e074cc817..cce52734afeb0 100644 --- a/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCZeroBias_cff.py +++ b/Calibration/LumiAlCaRecoProducers/python/ALCARECOAlCaPCCZeroBias_cff.py @@ -10,12 +10,14 @@ ) from EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi import siPixelDigis -siPixelDigisForLumiZB = siPixelDigis.clone() -siPixelDigisForLumiZB.InputLabel = cms.InputTag("hltFEDSelectorLumiPixels") +siPixelDigisForLumiZB = siPixelDigis.cpu.clone( + InputLabel = "hltFEDSelectorLumiPixels" +) from RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizerPreSplitting_cfi import siPixelClustersPreSplitting -siPixelClustersForLumiZB = siPixelClustersPreSplitting.clone() -siPixelClustersForLumiZB.src = cms.InputTag("siPixelDigisForLumiZB") +siPixelClustersForLumiZB = siPixelClustersPreSplitting.cpu.clone( + src = "siPixelDigisForLumiZB" +) from Calibration.LumiAlCaRecoProducers.alcaPCCProducer_cfi import alcaPCCProducer alcaPCCProducerZeroBias = alcaPCCProducer.clone() diff --git a/Calibration/LumiAlCaRecoProducers/python/ALCARECOLumiPixels_cff.py b/Calibration/LumiAlCaRecoProducers/python/ALCARECOLumiPixels_cff.py index d88e1d4c27506..25c7e5e60cb26 100644 --- a/Calibration/LumiAlCaRecoProducers/python/ALCARECOLumiPixels_cff.py +++ b/Calibration/LumiAlCaRecoProducers/python/ALCARECOLumiPixels_cff.py @@ -9,12 +9,14 @@ ) from EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi import siPixelDigis -siPixelDigisForLumi = siPixelDigis.clone() -siPixelDigisForLumi.InputLabel = cms.InputTag("hltFEDSelectorLumiPixels") +siPixelDigisForLumi = siPixelDigis.cpu.clone( + InputLabel = "hltFEDSelectorLumiPixels" +) from RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizerPreSplitting_cfi import siPixelClustersPreSplitting -siPixelClustersForLumi = siPixelClustersPreSplitting.clone() -siPixelClustersForLumi.src = cms.InputTag("siPixelDigisForLumi") +siPixelClustersForLumi = siPixelClustersPreSplitting.cpu.clone( + src = "siPixelDigisForLumi" +) # Sequence # seqALCARECOLumiPixels = cms.Sequence(ALCARECOLumiPixelsHLT + siPixelDigisForLumi + siPixelClustersForLumi) diff --git a/CondFormats/DataRecord/interface/HcalCombinedRecordsGPU.h b/CondFormats/DataRecord/interface/HcalCombinedRecordsGPU.h new file mode 100644 index 0000000000000..c802c599ed4c5 --- /dev/null +++ b/CondFormats/DataRecord/interface/HcalCombinedRecordsGPU.h @@ -0,0 +1,19 @@ +#ifndef CondFormats_DataRecord_interface_HcalCombinedRecordsGPU_h +#define CondFormats_DataRecord_interface_HcalCombinedRecordsGPU_h + +#include "CondFormats/DataRecord/interface/HcalPedestalWidthsRcd.h" +#include "CondFormats/DataRecord/interface/HcalPedestalsRcd.h" +#include "CondFormats/DataRecord/interface/HcalQIEDataRcd.h" +#include "CondFormats/DataRecord/interface/HcalQIETypesRcd.h" +#include "FWCore/Framework/interface/DependentRecordImplementation.h" + +template +class HcalCombinedRecord : public edm::eventsetup::DependentRecordImplementation, + edm::mpl::Vector> {}; + +using HcalConvertedPedestalsRcd = HcalCombinedRecord; + +using HcalConvertedPedestalWidthsRcd = + HcalCombinedRecord; + +#endif // CondFormats_DataRecord_interface_HcalCombinedRecordsGPU_h diff --git a/CondFormats/DataRecord/src/HcalCombinedRecordsGPU.cc b/CondFormats/DataRecord/src/HcalCombinedRecordsGPU.cc new file mode 100644 index 0000000000000..6da5efbef4f20 --- /dev/null +++ b/CondFormats/DataRecord/src/HcalCombinedRecordsGPU.cc @@ -0,0 +1,5 @@ +#include "CondFormats/DataRecord/interface/HcalCombinedRecordsGPU.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" + +EVENTSETUP_RECORD_REG(HcalConvertedPedestalsRcd); +EVENTSETUP_RECORD_REG(HcalConvertedPedestalWidthsRcd); diff --git a/CondFormats/HcalObjects/BuildFile.xml b/CondFormats/HcalObjects/BuildFile.xml index e89b6fb3f2369..0ed8951b0ee63 100644 --- a/CondFormats/HcalObjects/BuildFile.xml +++ b/CondFormats/HcalObjects/BuildFile.xml @@ -1,13 +1,15 @@ + + - - - - + + + + diff --git a/CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalWidthsGPU.h b/CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalWidthsGPU.h new file mode 100644 index 0000000000000..b2232d5d647a2 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalWidthsGPU.h @@ -0,0 +1,12 @@ +#ifndef CondFormats_HcalObjects_interface_HcalConvertedEffectivePedestalWidthsGPU_h +#define CondFormats_HcalObjects_interface_HcalConvertedEffectivePedestalWidthsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalConvertedPedestalWidthsGPU.h" + +// similar to converted effective pedestals +class HcalConvertedEffectivePedestalWidthsGPU final : public HcalConvertedPedestalWidthsGPU { +public: + using HcalConvertedPedestalWidthsGPU::HcalConvertedPedestalWidthsGPU; +}; + +#endif // RecoLocalCalo_HcalRecAlgos_interface_HcalConvertedEffectivePedestalWidthsGPU_h diff --git a/CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalsGPU.h b/CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalsGPU.h new file mode 100644 index 0000000000000..311485ac7275a --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalsGPU.h @@ -0,0 +1,14 @@ +#ifndef CondFormats_HcalObjects_interface_HcalConvertedEffectivePedestalsGPU_h +#define CondFormats_HcalObjects_interface_HcalConvertedEffectivePedestalsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalConvertedPedestalsGPU.h" + +// Separate access to effective and regular pedestals +// No need to transfer/rearrange effective or vice versa if they are not going +// to be used +class HcalConvertedEffectivePedestalsGPU final : public HcalConvertedPedestalsGPU { +public: + using HcalConvertedPedestalsGPU::HcalConvertedPedestalsGPU; +}; + +#endif // RecoLocalCalo_HcalRecAlgos_interface_HcalConvertedEffectivePedestalsGPU_h diff --git a/CondFormats/HcalObjects/interface/HcalConvertedPedestalWidthsGPU.h b/CondFormats/HcalObjects/interface/HcalConvertedPedestalWidthsGPU.h new file mode 100644 index 0000000000000..bf212265fe5ae --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalConvertedPedestalWidthsGPU.h @@ -0,0 +1,43 @@ +#ifndef CondFormats_HcalObjects_interface_HcalConvertedPedestalWidthsGPU_h +#define CondFormats_HcalObjects_interface_HcalConvertedPedestalWidthsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalPedestalWidths.h" +#include "CondFormats/HcalObjects/interface/HcalPedestals.h" +#include "CondFormats/HcalObjects/interface/HcalQIEData.h" +#include "CondFormats/HcalObjects/interface/HcalQIETypes.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalConvertedPedestalWidthsGPU { +public: + struct Product { + edm::propagate_const_array> values; + }; + +#ifndef __CUDACC__ + // order matters! + HcalConvertedPedestalWidthsGPU(HcalPedestals const&, + HcalPedestalWidths const&, + HcalQIEData const&, + HcalQIETypes const&); + + // will trigger deallocation of Product thru ~Product + ~HcalConvertedPedestalWidthsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; + std::vector> values_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalConvertedPedestalsGPU.h b/CondFormats/HcalObjects/interface/HcalConvertedPedestalsGPU.h new file mode 100644 index 0000000000000..84824fbeb1d2a --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalConvertedPedestalsGPU.h @@ -0,0 +1,42 @@ +#ifndef CondFormats_HcalObjects_interface_HcalConvertedPedestalsGPU_h +#define CondFormats_HcalObjects_interface_HcalConvertedPedestalsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalPedestals.h" +#include "CondFormats/HcalObjects/interface/HcalQIEData.h" +#include "CondFormats/HcalObjects/interface/HcalQIETypes.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalConvertedPedestalsGPU { +public: + struct Product { + edm::propagate_const_array> values; + }; + +#ifndef __CUDACC__ + // order matters! + HcalConvertedPedestalsGPU(HcalPedestals const&, HcalQIEData const&, HcalQIETypes const&); + + // will trigger deallocation of Product thru ~Product + ~HcalConvertedPedestalsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + uint32_t offsetForHashes() const { return offsetForHashes_; } + +protected: + uint64_t totalChannels_; + uint32_t offsetForHashes_; + std::vector> values_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalGainWidthsGPU.h b/CondFormats/HcalObjects/interface/HcalGainWidthsGPU.h new file mode 100644 index 0000000000000..4851239f171df --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalGainWidthsGPU.h @@ -0,0 +1,40 @@ +#ifndef CondFormats_HcalObjects_interface_HcalGainWidthsGPU_h +#define CondFormats_HcalObjects_interface_HcalGainWidthsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalGainWidths.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalGainWidthsGPU { +public: + struct Product { + edm::propagate_const_array> value0; + edm::propagate_const_array> value1; + edm::propagate_const_array> value2; + edm::propagate_const_array> value3; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalGainWidthsGPU(HcalGainWidths const &); + + // will trigger deallocation of Product thru ~Product + ~HcalGainWidthsGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; + std::vector> value0_, value1_, value2_, value3_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalGainsGPU.h b/CondFormats/HcalObjects/interface/HcalGainsGPU.h new file mode 100644 index 0000000000000..030146ffc7cdf --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalGainsGPU.h @@ -0,0 +1,37 @@ +#ifndef CondFormats_HcalObjects_interface_HcalGainsGPU_h +#define CondFormats_HcalObjects_interface_HcalGainsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalGains.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalGainsGPU { +public: + struct Product { + edm::propagate_const_array> values; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalGainsGPU(HcalGains const&); + + // will trigger deallocation of Product thru ~Product + ~HcalGainsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; + std::vector> values_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalLUTCorrsGPU.h b/CondFormats/HcalObjects/interface/HcalLUTCorrsGPU.h new file mode 100644 index 0000000000000..699dfb9706147 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalLUTCorrsGPU.h @@ -0,0 +1,36 @@ +#ifndef CondFormats_HcalObjects_interface_HcalLUTCorrsGPU_h +#define CondFormats_HcalObjects_interface_HcalLUTCorrsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalLUTCorrs.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalLUTCorrsGPU { +public: + struct Product { + edm::propagate_const_array> value; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalLUTCorrsGPU(HcalLUTCorrs const&); + + // will trigger deallocation of Product thru ~Product + ~HcalLUTCorrsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + std::vector> value_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalPedestalWidthsGPU.h b/CondFormats/HcalObjects/interface/HcalPedestalWidthsGPU.h new file mode 100644 index 0000000000000..b39ed2545c5e2 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalPedestalWidthsGPU.h @@ -0,0 +1,71 @@ +#ifndef CondFormats_HcalObjects_interface_HcalPedestalWidthsGPU_h +#define CondFormats_HcalObjects_interface_HcalPedestalWidthsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalPedestalWidths.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalPedestalWidthsGPU { +public: + struct Product { + edm::propagate_const_array> sigma00; + edm::propagate_const_array> sigma01; + edm::propagate_const_array> sigma02; + edm::propagate_const_array> sigma03; + edm::propagate_const_array> sigma10; + edm::propagate_const_array> sigma11; + edm::propagate_const_array> sigma12; + edm::propagate_const_array> sigma13; + edm::propagate_const_array> sigma20; + edm::propagate_const_array> sigma21; + edm::propagate_const_array> sigma22; + edm::propagate_const_array> sigma23; + edm::propagate_const_array> sigma30; + edm::propagate_const_array> sigma31; + edm::propagate_const_array> sigma32; + edm::propagate_const_array> sigma33; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalPedestalWidthsGPU(HcalPedestalWidths const&); + + // will trigger deallocation of Product thru ~Product + ~HcalPedestalWidthsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // as in cpu version + bool unitIsADC() const { return unitIsADC_; } + +private: + bool unitIsADC_; + uint64_t totalChannels_; + std::vector> sigma00_; + std::vector> sigma01_; + std::vector> sigma02_; + std::vector> sigma03_; + std::vector> sigma10_; + std::vector> sigma11_; + std::vector> sigma12_; + std::vector> sigma13_; + std::vector> sigma20_; + std::vector> sigma21_; + std::vector> sigma22_; + std::vector> sigma23_; + std::vector> sigma30_; + std::vector> sigma31_; + std::vector> sigma32_; + std::vector> sigma33_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalPedestalsGPU.h b/CondFormats/HcalObjects/interface/HcalPedestalsGPU.h new file mode 100644 index 0000000000000..957f6f88ef2fa --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalPedestalsGPU.h @@ -0,0 +1,46 @@ +#ifndef CondFormats_HcalObjects_interface_HcalPedestalsGPU_h +#define CondFormats_HcalObjects_interface_HcalPedestalsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalPedestals.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalPedestalsGPU { +public: + struct Product { + edm::propagate_const_array> values; + edm::propagate_const_array> widths; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalPedestalsGPU(HcalPedestals const &); + + // will trigger deallocation of Product thru ~Product + ~HcalPedestalsGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + + // as in cpu version + bool unitIsADC() const { return unitIsADC_; } + + uint32_t offsetForHashes() const { return offsetForHashes_; } + +private: + bool unitIsADC_; + uint64_t totalChannels_; + uint32_t offsetForHashes_; + std::vector> values_; + std::vector> widths_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalQIECodersGPU.h b/CondFormats/HcalObjects/interface/HcalQIECodersGPU.h new file mode 100644 index 0000000000000..0ed389f9c9cca --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalQIECodersGPU.h @@ -0,0 +1,41 @@ +#ifndef CondFormats_HcalObjects_interface_HcalQIECodersGPU_h +#define CondFormats_HcalObjects_interface_HcalQIECodersGPU_h + +#include "CondFormats/HcalObjects/interface/HcalQIEData.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalQIECodersGPU { +public: + static constexpr uint32_t numValuesPerChannel = 16; + + struct Product { + edm::propagate_const_array> offsets; + edm::propagate_const_array> slopes; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalQIECodersGPU(HcalQIEData const &); + + // will trigger deallocation of Product thru ~Product + ~HcalQIECodersGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; + std::vector> offsets_; + std::vector> slopes_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalQIETypesGPU.h b/CondFormats/HcalObjects/interface/HcalQIETypesGPU.h new file mode 100644 index 0000000000000..cdb49764c9145 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalQIETypesGPU.h @@ -0,0 +1,36 @@ +#ifndef CondFormats_HcalObjects_interface_HcalQIETypesGPU_h +#define CondFormats_HcalObjects_interface_HcalQIETypesGPU_h + +#include "CondFormats/HcalObjects/interface/HcalQIETypes.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalQIETypesGPU { +public: + struct Product { + edm::propagate_const_array> values; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalQIETypesGPU(HcalQIETypes const&); + + // will trigger deallocation of Product thru ~Product + ~HcalQIETypesGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + std::vector> values_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalRecoParamsGPU.h b/CondFormats/HcalObjects/interface/HcalRecoParamsGPU.h new file mode 100644 index 0000000000000..f85d26a431ebb --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalRecoParamsGPU.h @@ -0,0 +1,40 @@ +#ifndef CondFormats_HcalObjects_interface_HcalRecoParamsGPU_h +#define CondFormats_HcalObjects_interface_HcalRecoParamsGPU_h + +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalRecoParams; + +class HcalRecoParamsGPU { +public: + struct Product { + edm::propagate_const_array> param1; + edm::propagate_const_array> param2; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalRecoParamsGPU(HcalRecoParams const&); + + // will trigger deallocation of Product thru ~Product + ~HcalRecoParamsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; // hb + he + std::vector> param1_; + std::vector> param2_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalRespCorrsGPU.h b/CondFormats/HcalObjects/interface/HcalRespCorrsGPU.h new file mode 100644 index 0000000000000..6bc570ab52575 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalRespCorrsGPU.h @@ -0,0 +1,36 @@ +#ifndef CondFormats_HcalObjects_interface_HcalRespCorrsGPU_h +#define CondFormats_HcalObjects_interface_HcalRespCorrsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalRespCorrs.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalRespCorrsGPU { +public: + struct Product { + edm::propagate_const_array> values; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalRespCorrsGPU(HcalRespCorrs const&); + + // will trigger deallocation of Product thru ~Product + ~HcalRespCorrsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + std::vector> values_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalSiPMCharacteristicsGPU.h b/CondFormats/HcalObjects/interface/HcalSiPMCharacteristicsGPU.h new file mode 100644 index 0000000000000..9615d8011a256 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalSiPMCharacteristicsGPU.h @@ -0,0 +1,43 @@ +#ifndef CondFormats_HcalObjects_interface_HcalSiPMCharacteristicsGPU_h +#define CondFormats_HcalObjects_interface_HcalSiPMCharacteristicsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalSiPMCharacteristics.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalSiPMCharacteristicsGPU { +public: + struct Product { + edm::propagate_const_array> pixels; + edm::propagate_const_array> parLin1; + edm::propagate_const_array> parLin2; + edm::propagate_const_array> parLin3; + edm::propagate_const_array> crossTalk; + edm::propagate_const_array> auxi1; + edm::propagate_const_array> auxi2; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalSiPMCharacteristicsGPU(HcalSiPMCharacteristics const &); + + // will trigger deallocation of Product thru ~Product + ~HcalSiPMCharacteristicsGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + +private: + std::vector> pixels_, auxi1_; + std::vector> parLin1_, parLin2_, parLin3_, crossTalk_, auxi2_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/interface/HcalSiPMParametersGPU.h b/CondFormats/HcalObjects/interface/HcalSiPMParametersGPU.h new file mode 100644 index 0000000000000..10a69f70defa2 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalSiPMParametersGPU.h @@ -0,0 +1,42 @@ +#ifndef CondFormats_HcalObjects_interface_HcalSiPMParametersGPU_h +#define CondFormats_HcalObjects_interface_HcalSiPMParametersGPU_h + +#include "CondFormats/HcalObjects/interface/HcalSiPMParameters.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalSiPMParametersGPU { +public: + struct Product { + edm::propagate_const_array> type; + edm::propagate_const_array> auxi1; + edm::propagate_const_array> fcByPE; + edm::propagate_const_array> darkCurrent; + edm::propagate_const_array> auxi2; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalSiPMParametersGPU(HcalSiPMParameters const &); + + // will trigger deallocation of Product thru ~Product + ~HcalSiPMParametersGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; + std::vector> type_, auxi1_; + std::vector> fcByPE_, darkCurrent_, auxi2_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_HcalRecAlgos_interface_HcalSiPMParametersGPU_h diff --git a/CondFormats/HcalObjects/interface/HcalTimeCorrsGPU.h b/CondFormats/HcalObjects/interface/HcalTimeCorrsGPU.h new file mode 100644 index 0000000000000..bac5be06198d4 --- /dev/null +++ b/CondFormats/HcalObjects/interface/HcalTimeCorrsGPU.h @@ -0,0 +1,36 @@ +#ifndef CondFormats_HcalObjects_interface_HcalTimeCorrsGPU_h +#define CondFormats_HcalObjects_interface_HcalTimeCorrsGPU_h + +#include "CondFormats/HcalObjects/interface/HcalTimeCorrs.h" +#include "FWCore/Utilities/interface/propagate_const_array.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalTimeCorrsGPU { +public: + struct Product { + edm::propagate_const_array> value; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalTimeCorrsGPU(HcalTimeCorrs const&); + + // will trigger deallocation of Product thru ~Product + ~HcalTimeCorrsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + std::vector> value_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/CondFormats/HcalObjects/src/HcalConvertedEffectivePedestalWidthsGPU.cc b/CondFormats/HcalObjects/src/HcalConvertedEffectivePedestalWidthsGPU.cc new file mode 100644 index 0000000000000..dfc9a9e099ea1 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalConvertedEffectivePedestalWidthsGPU.cc @@ -0,0 +1,4 @@ +#include "CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalWidthsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +TYPELOOKUP_DATA_REG(HcalConvertedEffectivePedestalWidthsGPU); diff --git a/CondFormats/HcalObjects/src/HcalConvertedEffectivePedestalsGPU.cc b/CondFormats/HcalObjects/src/HcalConvertedEffectivePedestalsGPU.cc new file mode 100644 index 0000000000000..92cb76edc6d6f --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalConvertedEffectivePedestalsGPU.cc @@ -0,0 +1,4 @@ +#include "CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +TYPELOOKUP_DATA_REG(HcalConvertedEffectivePedestalsGPU); diff --git a/CondFormats/HcalObjects/src/HcalConvertedPedestalWidthsGPU.cc b/CondFormats/HcalObjects/src/HcalConvertedPedestalWidthsGPU.cc new file mode 100644 index 0000000000000..c03bef2e3439b --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalConvertedPedestalWidthsGPU.cc @@ -0,0 +1,150 @@ +#include + +#include "CondFormats/HcalObjects/interface/HcalConvertedPedestalWidthsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +namespace { + float convert( + float const value, float const width, int const i, HcalQIECoder const& coder, HcalQIEShape const& shape) { + float const y = value; + float const x = width; + unsigned const x1 = static_cast(std::floor(y)); + unsigned const x2 = static_cast(std::floor(y + 1.)); + unsigned iun = static_cast(i); + float const y1 = coder.charge(shape, x1, iun); + float const y2 = coder.charge(shape, x2, iun); + return (y2 - y1) * x; + } +} // namespace + +// FIXME: add proper getters to conditions +HcalConvertedPedestalWidthsGPU::HcalConvertedPedestalWidthsGPU(HcalPedestals const& pedestals, + HcalPedestalWidths const& pedestalWidths, + HcalQIEData const& qieData, + HcalQIETypes const& qieTypes) + : totalChannels_{pedestals.getAllContainers()[0].second.size() + pedestals.getAllContainers()[1].second.size()}, + values_(totalChannels_ * 4) { +#ifdef HCAL_MAHI_CPUDEBUG + std::cout << "hello from converted pedestal widths" << std::endl; + std::cout << "pedestals HB values = " << pedestals.getAllContainers()[0].second.size() + << " HE values = " << pedestals.getAllContainers()[1].second.size() << std::endl; + std::cout << "qiedata HB values = " << qieData.getAllContainers()[0].second.size() + << " HE values = " << qieData.getAllContainers()[1].second.size() << std::endl; +#endif + + // retrieve all collections + auto const pedestalsAll = pedestals.getAllContainers(); + auto const pedestalWidthsAll = pedestalWidths.getAllContainers(); + auto const qieDataAll = qieData.getAllContainers(); + auto const qieTypesAll = qieTypes.getAllContainers(); + + // have to convert to fc if stored in adc + auto const unitIsADC = pedestals.isADC(); + + // fill in barrel + auto const& pedestalBarrelValues = pedestalsAll[0].second; + auto const& pedestalWidthBarrelValues = pedestalWidthsAll[0].second; + auto const& qieDataBarrelValues = qieDataAll[0].second; + auto const& qieTypesBarrelValues = qieTypesAll[0].second; + +#ifdef HCAL_MAHI_CPUDEBUG + assert(pedestalWidthBarrelValues.size() == pedestalBarrelValues.size()); + assert(pedestalBarrelValues.size() == qieDataBarrelValues.size()); + assert(pedestalBarrelValues.size() == qieTypesBarrelValues.size()); +#endif + + for (uint64_t i = 0; i < pedestalBarrelValues.size(); ++i) { + auto const& qieCoder = qieDataBarrelValues[i]; + auto const qieType = qieTypesBarrelValues[i].getValue() > 1 ? 1 : 0; + auto const& qieShape = qieData.getShape(qieType); + + values_[i * 4] = + unitIsADC + ? convert( + pedestalBarrelValues[i].getValue(0), pedestalWidthBarrelValues[i].getWidth(0), 0, qieCoder, qieShape) + : pedestalWidthBarrelValues[i].getWidth(0); + values_[i * 4 + 1] = + unitIsADC + ? convert( + pedestalBarrelValues[i].getValue(1), pedestalWidthBarrelValues[i].getWidth(1), 1, qieCoder, qieShape) + : pedestalWidthBarrelValues[i].getWidth(1); + values_[i * 4 + 2] = + unitIsADC + ? convert( + pedestalBarrelValues[i].getValue(2), pedestalWidthBarrelValues[i].getWidth(2), 2, qieCoder, qieShape) + : pedestalWidthBarrelValues[i].getWidth(2); + values_[i * 4 + 3] = + unitIsADC + ? convert( + pedestalBarrelValues[i].getValue(3), pedestalWidthBarrelValues[i].getWidth(3), 3, qieCoder, qieShape) + : pedestalWidthBarrelValues[i].getWidth(3); + } + + // fill in endcap + auto const& pedestalEndcapValues = pedestalsAll[1].second; + auto const& pedestalWidthEndcapValues = pedestalWidthsAll[1].second; + auto const& qieDataEndcapValues = qieDataAll[1].second; + auto const& qieTypesEndcapValues = qieTypesAll[1].second; + +#ifdef HCAL_MAHI_CPUDEBUG + assert(pedestalWidthEndcapValues.size() == pedestalEndcapValues.size()); + assert(pedestalEndcapValues.size() == qieDataEndcapValues.size()); + assert(pedestalEndcapValues.size() == qieTypesEndcapValues.size()); +#endif + + auto const offset = pedestalWidthBarrelValues.size(); + for (uint64_t i = 0; i < pedestalEndcapValues.size(); ++i) { + auto const& qieCoder = qieDataEndcapValues[i]; + auto const qieType = qieTypesEndcapValues[i].getValue() > 1 ? 1 : 0; + auto const& qieShape = qieData.getShape(qieType); + auto const off = offset + i; + + values_[off * 4] = + unitIsADC + ? convert( + pedestalEndcapValues[i].getValue(0), pedestalWidthEndcapValues[i].getWidth(0), 0, qieCoder, qieShape) + : pedestalWidthEndcapValues[i].getWidth(0); + values_[off * 4 + 1] = + unitIsADC + ? convert( + pedestalEndcapValues[i].getValue(1), pedestalWidthEndcapValues[i].getWidth(1), 1, qieCoder, qieShape) + : pedestalWidthEndcapValues[i].getWidth(1); + values_[off * 4 + 2] = + unitIsADC + ? convert( + pedestalEndcapValues[i].getValue(2), pedestalWidthEndcapValues[i].getWidth(2), 2, qieCoder, qieShape) + : pedestalWidthEndcapValues[i].getWidth(2); + values_[off * 4 + 3] = + unitIsADC + ? convert( + pedestalEndcapValues[i].getValue(3), pedestalWidthEndcapValues[i].getWidth(3), 3, qieCoder, qieShape) + : pedestalWidthEndcapValues[i].getWidth(3); + +#ifdef HCAL_MAHI_CPUDEBUG + if (pedestalEndcapValues[i].rawId() == DETID_TO_DEBUG) { + for (int i = 0; i < 4; i++) + printf("pedestalWidth(%d) = %f original pedestalWidth(%d) = %f\n", + i, + values_[off * 4 + i], + i, + pedestalWidthEndcapValues[i].getWidth(3)); + } +#endif + } +} + +HcalConvertedPedestalWidthsGPU::Product const& HcalConvertedPedestalWidthsGPU::getProduct(cudaStream_t stream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + stream, [this](HcalConvertedPedestalWidthsGPU::Product& product, cudaStream_t stream) { + // allocate + product.values = cms::cuda::make_device_unique(values_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.values, values_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalConvertedPedestalWidthsGPU); diff --git a/CondFormats/HcalObjects/src/HcalConvertedPedestalsGPU.cc b/CondFormats/HcalObjects/src/HcalConvertedPedestalsGPU.cc new file mode 100644 index 0000000000000..5d44f1fd6bd2e --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalConvertedPedestalsGPU.cc @@ -0,0 +1,122 @@ +#include + +#include "CondFormats/HcalObjects/interface/HcalConvertedPedestalsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +namespace { + float convert(float const x, int const i, HcalQIECoder const& coder, HcalQIEShape const& shape) { + int const x1 = static_cast(std::floor(x)); + int const x2 = static_cast(std::floor(x + 1)); + float const y2 = coder.charge(shape, x2, i); + float const y1 = coder.charge(shape, x1, i); + return (y2 - y1) * (x - x1) + y1; + } +} // namespace + +// FIXME: add proper getters to conditions +HcalConvertedPedestalsGPU::HcalConvertedPedestalsGPU(HcalPedestals const& pedestals, + HcalQIEData const& qieData, + HcalQIETypes const& qieTypes) + : totalChannels_{pedestals.getAllContainers()[0].second.size() + pedestals.getAllContainers()[1].second.size()}, + offsetForHashes_{static_cast(pedestals.getAllContainers()[0].second.size())}, + values_(totalChannels_ * 4) { +#ifdef HCAL_MAHI_CPUDEBUG + std::cout << "hello from converted pedestals" << std::endl; + std::cout << "pedestals HB values = " << pedestals.getAllContainers()[0].second.size() + << " HE values = " << pedestals.getAllContainers()[1].second.size() << std::endl; + std::cout << "qiedata HB values = " << qieData.getAllContainers()[0].second.size() + << " HE values = " << qieData.getAllContainers()[1].second.size() << std::endl; +#endif + + // retrieve all collections + auto const pedestalsAll = pedestals.getAllContainers(); + auto const qieDataAll = qieData.getAllContainers(); + auto const qieTypesAll = qieTypes.getAllContainers(); + + // have to convert to fc if stored in adc + auto const unitIsADC = pedestals.isADC(); + + // fill in barrel + auto const& pedestalBarrelValues = pedestalsAll[0].second; + auto const& qieDataBarrelValues = qieDataAll[0].second; + auto const& qieTypesBarrelValues = qieTypesAll[0].second; + +#ifdef HCAL_MAHI_CPUDEBUG + assert(pedestalBarrelValues.size() == qieDataBarrelValues.size()); + assert(pedestalBarrelValues.size() == qieTypesBarrelValues.size()); +#endif + + for (uint64_t i = 0; i < pedestalBarrelValues.size(); ++i) { + auto const& qieCoder = qieDataBarrelValues[i]; + auto const qieType = qieTypesBarrelValues[i].getValue() > 1 ? 1 : 0; + auto const& qieShape = qieData.getShape(qieType); + + values_[i * 4] = unitIsADC ? convert(pedestalBarrelValues[i].getValue(0), 0, qieCoder, qieShape) + : pedestalBarrelValues[i].getValue(0); + values_[i * 4 + 1] = unitIsADC ? convert(pedestalBarrelValues[i].getValue(1), 1, qieCoder, qieShape) + : pedestalBarrelValues[i].getValue(1); + values_[i * 4 + 2] = unitIsADC ? convert(pedestalBarrelValues[i].getValue(2), 2, qieCoder, qieShape) + : pedestalBarrelValues[i].getValue(2); + values_[i * 4 + 3] = unitIsADC ? convert(pedestalBarrelValues[i].getValue(3), 3, qieCoder, qieShape) + : pedestalBarrelValues[i].getValue(3); + } + + // fill in endcap + auto const& pedestalEndcapValues = pedestalsAll[1].second; + auto const& qieDataEndcapValues = qieDataAll[1].second; + auto const& qieTypesEndcapValues = qieTypesAll[1].second; + +#ifdef HCAL_MAHI_CPUDEBUG + assert(pedestalEndcapValues.size() == qieDataEndcapValues.size()); + assert(pedestalEndcapValues.size() == qieTypesEndcapValues.size()); +#endif + + auto const offset = pedestalBarrelValues.size(); + for (uint64_t i = 0; i < pedestalEndcapValues.size(); ++i) { + auto const& qieCoder = qieDataEndcapValues[i]; + auto const qieType = qieTypesEndcapValues[i].getValue() > 1 ? 1 : 0; + auto const& qieShape = qieData.getShape(qieType); + auto const off = offset + i; + + values_[off * 4] = unitIsADC ? convert(pedestalEndcapValues[i].getValue(0), 0, qieCoder, qieShape) + : pedestalEndcapValues[i].getValue(0); + values_[off * 4 + 1] = unitIsADC ? convert(pedestalEndcapValues[i].getValue(1), 1, qieCoder, qieShape) + : pedestalEndcapValues[i].getValue(1); + values_[off * 4 + 2] = unitIsADC ? convert(pedestalEndcapValues[i].getValue(2), 2, qieCoder, qieShape) + : pedestalEndcapValues[i].getValue(2); + values_[off * 4 + 3] = unitIsADC ? convert(pedestalEndcapValues[i].getValue(3), 3, qieCoder, qieShape) + : pedestalEndcapValues[i].getValue(3); + +#ifdef HCAL_MAHI_CPUDEBUG + if (pedestalEndcapValues[i].rawId() == DETID_TO_DEBUG) { + printf("qietype = %d\n", qieType); + printf("ped0 = %f ped1 = %f ped2 = %f ped3 = %f\n", + pedestalEndcapValues[i].getValue(0), + pedestalEndcapValues[i].getValue(1), + pedestalEndcapValues[i].getValue(2), + pedestalEndcapValues[i].getValue(3)); + printf("converted: ped0 = %f ped1 = %f ped2 = %f ped3 = %f\n", + values_[off * 4], + values_[off * 4 + 1], + values_[off * 4 + 2], + values_[off * 4 + 3]); + } +#endif + } +} + +HcalConvertedPedestalsGPU::Product const& HcalConvertedPedestalsGPU::getProduct(cudaStream_t stream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + stream, [this](HcalConvertedPedestalsGPU::Product& product, cudaStream_t stream) { + // allocate + product.values = cms::cuda::make_device_unique(values_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.values, values_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalConvertedPedestalsGPU); diff --git a/CondFormats/HcalObjects/src/HcalGainWidthsGPU.cc b/CondFormats/HcalObjects/src/HcalGainWidthsGPU.cc new file mode 100644 index 0000000000000..fc86ce24b8e2c --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalGainWidthsGPU.cc @@ -0,0 +1,54 @@ +#include "CondFormats/HcalObjects/interface/HcalGainWidths.h" +#include "CondFormats/HcalObjects/interface/HcalGainWidthsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalGainWidthsGPU::HcalGainWidthsGPU(HcalGainWidths const& gains) + : totalChannels_{gains.getAllContainers()[0].second.size() + gains.getAllContainers()[1].second.size()}, + value0_(totalChannels_), + value1_(totalChannels_), + value2_(totalChannels_), + value3_(totalChannels_) { + auto const gainContainers = gains.getAllContainers(); + + // fill in eb + auto const& barrelValues = gainContainers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + value0_[i] = barrelValues[i].getValue(0); + value1_[i] = barrelValues[i].getValue(1); + value2_[i] = barrelValues[i].getValue(2); + value3_[i] = barrelValues[i].getValue(3); + } + + // fill in ee + auto const& endcapValues = gainContainers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + value0_[i + offset] = endcapValues[i].getValue(0); + value1_[i + offset] = endcapValues[i].getValue(1); + value2_[i + offset] = endcapValues[i].getValue(2); + value3_[i + offset] = endcapValues[i].getValue(3); + } +} + +HcalGainWidthsGPU::Product const& HcalGainWidthsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalGainWidthsGPU::Product& product, cudaStream_t stream) { + // allocate + product.value0 = cms::cuda::make_device_unique(value0_.size(), stream); + product.value1 = cms::cuda::make_device_unique(value1_.size(), stream); + product.value2 = cms::cuda::make_device_unique(value2_.size(), stream); + product.value3 = cms::cuda::make_device_unique(value3_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.value0, value0_, stream); + cms::cuda::copyAsync(product.value1, value1_, stream); + cms::cuda::copyAsync(product.value2, value2_, stream); + cms::cuda::copyAsync(product.value3, value3_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalGainWidthsGPU); diff --git a/CondFormats/HcalObjects/src/HcalGainsGPU.cc b/CondFormats/HcalObjects/src/HcalGainsGPU.cc new file mode 100644 index 0000000000000..27f7d548aa7b2 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalGainsGPU.cc @@ -0,0 +1,46 @@ +#include "CondFormats/HcalObjects/interface/HcalGains.h" +#include "CondFormats/HcalObjects/interface/HcalGainsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalGainsGPU::HcalGainsGPU(HcalGains const& gains) + : totalChannels_{gains.getAllContainers()[0].second.size() + gains.getAllContainers()[1].second.size()}, + values_(totalChannels_ * 4) { + auto const gainContainers = gains.getAllContainers(); + + // fill in eb + auto const& barrelValues = gainContainers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + values_[i * 4] = barrelValues[i].getValue(0); + values_[i * 4 + 1] = barrelValues[i].getValue(1); + values_[i * 4 + 2] = barrelValues[i].getValue(2); + values_[i * 4 + 3] = barrelValues[i].getValue(3); + } + + // fill in ee + auto const& endcapValues = gainContainers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + auto const off = offset + i; + values_[off * 4] = endcapValues[i].getValue(0); + values_[off * 4 + 1] = endcapValues[i].getValue(1); + values_[off * 4 + 2] = endcapValues[i].getValue(2); + values_[off * 4 + 3] = endcapValues[i].getValue(3); + } +} + +HcalGainsGPU::Product const& HcalGainsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalGainsGPU::Product& product, cudaStream_t stream) { + // allocate + product.values = cms::cuda::make_device_unique(values_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.values, values_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalGainsGPU); diff --git a/CondFormats/HcalObjects/src/HcalLUTCorrsGPU.cc b/CondFormats/HcalObjects/src/HcalLUTCorrsGPU.cc new file mode 100644 index 0000000000000..889125e92783b --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalLUTCorrsGPU.cc @@ -0,0 +1,38 @@ +#include "CondFormats/HcalObjects/interface/HcalLUTCorrs.h" +#include "CondFormats/HcalObjects/interface/HcalLUTCorrsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalLUTCorrsGPU::HcalLUTCorrsGPU(HcalLUTCorrs const& lutcorrs) + : value_(lutcorrs.getAllContainers()[0].second.size() + lutcorrs.getAllContainers()[1].second.size()) { + auto const containers = lutcorrs.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + value_[i] = barrelValues[i].getValue(); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + value_[i + offset] = endcapValues[i].getValue(); + } +} + +HcalLUTCorrsGPU::Product const& HcalLUTCorrsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalLUTCorrsGPU::Product& product, cudaStream_t stream) { + // allocate + product.value = cms::cuda::make_device_unique(value_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.value, value_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalLUTCorrsGPU); diff --git a/CondFormats/HcalObjects/src/HcalPedestalWidthsGPU.cc b/CondFormats/HcalObjects/src/HcalPedestalWidthsGPU.cc new file mode 100644 index 0000000000000..5e006aba764f8 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalPedestalWidthsGPU.cc @@ -0,0 +1,121 @@ +#include "CondFormats/HcalObjects/interface/HcalPedestalWidths.h" +#include "CondFormats/HcalObjects/interface/HcalPedestalWidthsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalPedestalWidthsGPU::HcalPedestalWidthsGPU(HcalPedestalWidths const& pedestals) + : unitIsADC_{pedestals.isADC()}, + totalChannels_{pedestals.getAllContainers()[0].second.size() + pedestals.getAllContainers()[1].second.size()}, + sigma00_(totalChannels_), + sigma01_(totalChannels_), + sigma02_(totalChannels_), + sigma03_(totalChannels_), + sigma10_(totalChannels_), + sigma11_(totalChannels_), + sigma12_(totalChannels_), + sigma13_(totalChannels_), + sigma20_(totalChannels_), + sigma21_(totalChannels_), + sigma22_(totalChannels_), + sigma23_(totalChannels_), + sigma30_(totalChannels_), + sigma31_(totalChannels_), + sigma32_(totalChannels_), + sigma33_(totalChannels_) { + auto const containers = pedestals.getAllContainers(); + + // fill in hb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + sigma00_[i] = *(barrelValues[i].getValues() /* + 0 */); + sigma01_[i] = *(barrelValues[i].getValues() + 1); + sigma02_[i] = *(barrelValues[i].getValues() + 2); + sigma03_[i] = *(barrelValues[i].getValues() + 3); + sigma10_[i] = *(barrelValues[i].getValues() + 3); + sigma11_[i] = *(barrelValues[i].getValues() + 5); + sigma12_[i] = *(barrelValues[i].getValues() + 6); + sigma13_[i] = *(barrelValues[i].getValues() + 7); + sigma20_[i] = *(barrelValues[i].getValues() + 8); + sigma21_[i] = *(barrelValues[i].getValues() + 9); + sigma22_[i] = *(barrelValues[i].getValues() + 10); + sigma23_[i] = *(barrelValues[i].getValues() + 11); + sigma30_[i] = *(barrelValues[i].getValues() + 12); + sigma31_[i] = *(barrelValues[i].getValues() + 13); + sigma32_[i] = *(barrelValues[i].getValues() + 14); + sigma33_[i] = *(barrelValues[i].getValues() + 15); + } + + // fill in he + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + sigma00_[i + offset] = *(endcapValues[i].getValues() /* + 0 */); + sigma01_[i + offset] = *(endcapValues[i].getValues() + 1); + sigma02_[i + offset] = *(endcapValues[i].getValues() + 2); + sigma03_[i + offset] = *(endcapValues[i].getValues() + 3); + sigma10_[i + offset] = *(endcapValues[i].getValues() + 3); + sigma11_[i + offset] = *(endcapValues[i].getValues() + 5); + sigma12_[i + offset] = *(endcapValues[i].getValues() + 6); + sigma13_[i + offset] = *(endcapValues[i].getValues() + 7); + sigma20_[i + offset] = *(endcapValues[i].getValues() + 8); + sigma21_[i + offset] = *(endcapValues[i].getValues() + 9); + sigma22_[i + offset] = *(endcapValues[i].getValues() + 10); + sigma23_[i + offset] = *(endcapValues[i].getValues() + 11); + sigma30_[i + offset] = *(endcapValues[i].getValues() + 12); + sigma31_[i + offset] = *(endcapValues[i].getValues() + 13); + sigma32_[i + offset] = *(endcapValues[i].getValues() + 14); + sigma33_[i + offset] = *(endcapValues[i].getValues() + 15); + } +} + +HcalPedestalWidthsGPU::Product const& HcalPedestalWidthsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalPedestalWidthsGPU::Product& product, cudaStream_t stream) { + // allocate + product.sigma00 = cms::cuda::make_device_unique(sigma00_.size(), stream); + product.sigma01 = cms::cuda::make_device_unique(sigma01_.size(), stream); + product.sigma02 = cms::cuda::make_device_unique(sigma02_.size(), stream); + product.sigma03 = cms::cuda::make_device_unique(sigma03_.size(), stream); + + product.sigma10 = cms::cuda::make_device_unique(sigma10_.size(), stream); + product.sigma11 = cms::cuda::make_device_unique(sigma11_.size(), stream); + product.sigma12 = cms::cuda::make_device_unique(sigma12_.size(), stream); + product.sigma13 = cms::cuda::make_device_unique(sigma13_.size(), stream); + + product.sigma20 = cms::cuda::make_device_unique(sigma20_.size(), stream); + product.sigma21 = cms::cuda::make_device_unique(sigma21_.size(), stream); + product.sigma22 = cms::cuda::make_device_unique(sigma22_.size(), stream); + product.sigma23 = cms::cuda::make_device_unique(sigma23_.size(), stream); + + product.sigma30 = cms::cuda::make_device_unique(sigma30_.size(), stream); + product.sigma31 = cms::cuda::make_device_unique(sigma31_.size(), stream); + product.sigma32 = cms::cuda::make_device_unique(sigma32_.size(), stream); + product.sigma33 = cms::cuda::make_device_unique(sigma33_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.sigma00, sigma00_, stream); + cms::cuda::copyAsync(product.sigma01, sigma01_, stream); + cms::cuda::copyAsync(product.sigma02, sigma02_, stream); + cms::cuda::copyAsync(product.sigma03, sigma03_, stream); + + cms::cuda::copyAsync(product.sigma10, sigma10_, stream); + cms::cuda::copyAsync(product.sigma11, sigma11_, stream); + cms::cuda::copyAsync(product.sigma12, sigma12_, stream); + cms::cuda::copyAsync(product.sigma13, sigma13_, stream); + + cms::cuda::copyAsync(product.sigma20, sigma20_, stream); + cms::cuda::copyAsync(product.sigma21, sigma21_, stream); + cms::cuda::copyAsync(product.sigma22, sigma22_, stream); + cms::cuda::copyAsync(product.sigma23, sigma23_, stream); + + cms::cuda::copyAsync(product.sigma30, sigma30_, stream); + cms::cuda::copyAsync(product.sigma31, sigma31_, stream); + cms::cuda::copyAsync(product.sigma32, sigma32_, stream); + cms::cuda::copyAsync(product.sigma33, sigma33_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalPedestalWidthsGPU); diff --git a/CondFormats/HcalObjects/src/HcalPedestalsGPU.cc b/CondFormats/HcalObjects/src/HcalPedestalsGPU.cc new file mode 100644 index 0000000000000..57088a4f39621 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalPedestalsGPU.cc @@ -0,0 +1,65 @@ +#include "CondFormats/HcalObjects/interface/HcalPedestals.h" +#include "CondFormats/HcalObjects/interface/HcalPedestalsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalPedestalsGPU::HcalPedestalsGPU(HcalPedestals const& pedestals) + : unitIsADC_{pedestals.isADC()}, + totalChannels_{pedestals.getAllContainers()[0].second.size() + pedestals.getAllContainers()[1].second.size()}, + offsetForHashes_{static_cast(pedestals.getAllContainers()[0].second.size())}, + values_(totalChannels_ * 4), + widths_(totalChannels_ * 4) { +#ifdef HCAL_MAHI_CPUDEBUG + std::cout << "unitIsADC = " << unitIsADC_ << std::endl; +#endif + + auto const containers = pedestals.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + values_[i * 4] = barrelValues[i].getValue(0); + values_[i * 4 + 1] = barrelValues[i].getValue(1); + values_[i * 4 + 2] = barrelValues[i].getValue(2); + values_[i * 4 + 3] = barrelValues[i].getValue(3); + + widths_[i * 4] = barrelValues[i].getWidth(0); + widths_[i * 4 + 1] = barrelValues[i].getWidth(1); + widths_[i * 4 + 2] = barrelValues[i].getWidth(2); + widths_[i * 4 + 3] = barrelValues[i].getWidth(3); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + auto const off = offset + i; + values_[off * 4] = endcapValues[i].getValue(0); + values_[off * 4 + 1] = endcapValues[i].getValue(1); + values_[off * 4 + 2] = endcapValues[i].getValue(2); + values_[off * 4 + 3] = endcapValues[i].getValue(3); + + widths_[off * 4] = endcapValues[i].getWidth(0); + widths_[off * 4 + 1] = endcapValues[i].getWidth(1); + widths_[off * 4 + 2] = endcapValues[i].getWidth(2); + widths_[off * 4 + 3] = endcapValues[i].getWidth(3); + } +} + +HcalPedestalsGPU::Product const& HcalPedestalsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalPedestalsGPU::Product& product, cudaStream_t stream) { + // allocate + product.values = cms::cuda::make_device_unique(values_.size(), stream); + product.widths = cms::cuda::make_device_unique(widths_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.values, values_, stream); + cms::cuda::copyAsync(product.widths, widths_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalPedestalsGPU); diff --git a/CondFormats/HcalObjects/src/HcalQIECodersGPU.cc b/CondFormats/HcalObjects/src/HcalQIECodersGPU.cc new file mode 100644 index 0000000000000..2b9d9d4821e64 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalQIECodersGPU.cc @@ -0,0 +1,51 @@ +#include "CondFormats/HcalObjects/interface/HcalQIECodersGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +HcalQIECodersGPU::HcalQIECodersGPU(HcalQIEData const& qiedata) + : totalChannels_{qiedata.getAllContainers()[0].second.size() + qiedata.getAllContainers()[1].second.size()}, + offsets_(totalChannels_ * numValuesPerChannel), + slopes_(totalChannels_ * numValuesPerChannel) { + auto const containers = qiedata.getAllContainers(); + + // fill in hb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + for (uint32_t k = 0; k < 4; k++) + for (uint32_t l = 0; l < 4; l++) { + auto const linear = k * 4 + l; + offsets_[i * numValuesPerChannel + linear] = barrelValues[i].offset(k, l); + slopes_[i * numValuesPerChannel + linear] = barrelValues[i].slope(k, l); + } + } + + // fill in he + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + auto const off = (i + offset) * numValuesPerChannel; + for (uint32_t k = 0; k < 4; k++) + for (uint32_t l = 0; l < 4; l++) { + auto const linear = k * 4u + l; + offsets_[off + linear] = endcapValues[i].offset(k, l); + slopes_[off + linear] = endcapValues[i].slope(k, l); + } + } +} + +HcalQIECodersGPU::Product const& HcalQIECodersGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalQIECodersGPU::Product& product, cudaStream_t stream) { + // allocate + product.offsets = cms::cuda::make_device_unique(offsets_.size(), stream); + product.slopes = cms::cuda::make_device_unique(slopes_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.offsets, offsets_, stream); + cms::cuda::copyAsync(product.slopes, slopes_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalQIECodersGPU); diff --git a/CondFormats/HcalObjects/src/HcalQIETypesGPU.cc b/CondFormats/HcalObjects/src/HcalQIETypesGPU.cc new file mode 100644 index 0000000000000..77a7bf81c33f0 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalQIETypesGPU.cc @@ -0,0 +1,38 @@ +#include "CondFormats/HcalObjects/interface/HcalQIETypes.h" +#include "CondFormats/HcalObjects/interface/HcalQIETypesGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalQIETypesGPU::HcalQIETypesGPU(HcalQIETypes const& parameters) + : values_(parameters.getAllContainers()[0].second.size() + parameters.getAllContainers()[1].second.size()) { + auto const containers = parameters.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + values_[i] = barrelValues[i].getValue(); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + values_[i + offset] = endcapValues[i].getValue(); + } +} + +HcalQIETypesGPU::Product const& HcalQIETypesGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalQIETypesGPU::Product& product, cudaStream_t stream) { + // allocate + product.values = cms::cuda::make_device_unique(values_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.values, values_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalQIETypesGPU); diff --git a/CondFormats/HcalObjects/src/HcalRecoParamsGPU.cc b/CondFormats/HcalObjects/src/HcalRecoParamsGPU.cc new file mode 100644 index 0000000000000..195028acf5746 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalRecoParamsGPU.cc @@ -0,0 +1,44 @@ +#include "CondFormats/HcalObjects/interface/HcalRecoParams.h" +#include "CondFormats/HcalObjects/interface/HcalRecoParamsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalRecoParamsGPU::HcalRecoParamsGPU(HcalRecoParams const& recoParams) + : totalChannels_{recoParams.getAllContainers()[0].second.size() + recoParams.getAllContainers()[1].second.size()}, + param1_(totalChannels_), + param2_(totalChannels_) { + auto const& containers = recoParams.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + param1_[i] = barrelValues[i].param1(); + param2_[i] = barrelValues[i].param2(); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + param1_[i + offset] = endcapValues[i].param1(); + param2_[i + offset] = endcapValues[i].param2(); + } +} + +HcalRecoParamsGPU::Product const& HcalRecoParamsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalRecoParamsGPU::Product& product, cudaStream_t stream) { + // allocate + product.param1 = cms::cuda::make_device_unique(param1_.size(), stream); + product.param2 = cms::cuda::make_device_unique(param2_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.param1, param1_, stream); + cms::cuda::copyAsync(product.param2, param2_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalRecoParamsGPU); diff --git a/CondFormats/HcalObjects/src/HcalRespCorrsGPU.cc b/CondFormats/HcalObjects/src/HcalRespCorrsGPU.cc new file mode 100644 index 0000000000000..f688191352353 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalRespCorrsGPU.cc @@ -0,0 +1,38 @@ +#include "CondFormats/HcalObjects/interface/HcalRespCorrs.h" +#include "CondFormats/HcalObjects/interface/HcalRespCorrsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalRespCorrsGPU::HcalRespCorrsGPU(HcalRespCorrs const& respcorrs) + : values_(respcorrs.getAllContainers()[0].second.size() + respcorrs.getAllContainers()[1].second.size()) { + auto const containers = respcorrs.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + values_[i] = barrelValues[i].getValue(); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + values_[i + offset] = endcapValues[i].getValue(); + } +} + +HcalRespCorrsGPU::Product const& HcalRespCorrsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalRespCorrsGPU::Product& product, cudaStream_t stream) { + // allocate + product.values = cms::cuda::make_device_unique(values_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.values, values_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalRespCorrsGPU); diff --git a/CondFormats/HcalObjects/src/HcalSiPMCharacteristicsGPU.cc b/CondFormats/HcalObjects/src/HcalSiPMCharacteristicsGPU.cc new file mode 100644 index 0000000000000..059eb8617c37d --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalSiPMCharacteristicsGPU.cc @@ -0,0 +1,63 @@ +#include "CondFormats/HcalObjects/interface/HcalSiPMCharacteristics.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMCharacteristicsGPU.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +HcalSiPMCharacteristicsGPU::HcalSiPMCharacteristicsGPU(HcalSiPMCharacteristics const& parameters) + : pixels_(parameters.getTypes()), + auxi1_(parameters.getTypes()), + parLin1_(parameters.getTypes()), + parLin2_(parameters.getTypes()), + parLin3_(parameters.getTypes()), + crossTalk_(parameters.getTypes()), + auxi2_(parameters.getTypes()) { + for (uint32_t i = 0; i < parameters.getTypes(); i++) { + auto const type = parameters.getType(i); +#ifdef HCAL_MAHI_CPUDEBUG + printf("index = %u type = %d\n", i, type); +#endif + + // for now... + if (static_cast(type) != i + 1) + throw cms::Exception("HcalSiPMCharacteristics") + << "Wrong assumption for HcalSiPMcharacteristics type values, " + << "should be type value <- type index + 1" << std::endl + << "Observed type value = " << type << " and index = " << i << std::endl; + + pixels_[i] = parameters.getPixels(type); + auxi1_[i] = parameters.getAuxi1(type); + parLin1_[i] = parameters.getNonLinearities(type)[0]; + parLin2_[i] = parameters.getNonLinearities(type)[1]; + parLin3_[i] = parameters.getNonLinearities(type)[2]; + crossTalk_[i] = parameters.getCrossTalk(type); + auxi2_[i] = parameters.getAuxi2(type); + } +} + +HcalSiPMCharacteristicsGPU::Product const& HcalSiPMCharacteristicsGPU::getProduct(cudaStream_t stream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + stream, [this](HcalSiPMCharacteristicsGPU::Product& product, cudaStream_t stream) { + // allocate + product.pixels = cms::cuda::make_device_unique(pixels_.size(), stream); + product.auxi1 = cms::cuda::make_device_unique(auxi1_.size(), stream); + product.parLin1 = cms::cuda::make_device_unique(parLin1_.size(), stream); + product.parLin2 = cms::cuda::make_device_unique(parLin2_.size(), stream); + product.parLin3 = cms::cuda::make_device_unique(parLin3_.size(), stream); + product.crossTalk = cms::cuda::make_device_unique(crossTalk_.size(), stream); + product.auxi2 = cms::cuda::make_device_unique(auxi2_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.pixels, pixels_, stream); + cms::cuda::copyAsync(product.auxi1, auxi1_, stream); + cms::cuda::copyAsync(product.parLin1, parLin1_, stream); + cms::cuda::copyAsync(product.parLin2, parLin2_, stream); + cms::cuda::copyAsync(product.parLin3, parLin3_, stream); + cms::cuda::copyAsync(product.crossTalk, crossTalk_, stream); + cms::cuda::copyAsync(product.auxi2, auxi2_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalSiPMCharacteristicsGPU); diff --git a/CondFormats/HcalObjects/src/HcalSiPMParametersGPU.cc b/CondFormats/HcalObjects/src/HcalSiPMParametersGPU.cc new file mode 100644 index 0000000000000..88120e02e54b5 --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalSiPMParametersGPU.cc @@ -0,0 +1,61 @@ +#include "CondFormats/HcalObjects/interface/HcalSiPMParameters.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMParametersGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +HcalSiPMParametersGPU::HcalSiPMParametersGPU(HcalSiPMParameters const& parameters) + : totalChannels_{parameters.getAllContainers()[0].second.size() + parameters.getAllContainers()[1].second.size()}, + type_(totalChannels_), + auxi1_(totalChannels_), + fcByPE_(totalChannels_), + darkCurrent_(totalChannels_), + auxi2_(totalChannels_) { + auto const containers = parameters.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + auto const& item = barrelValues[i]; + type_[i] = item.getType(); + auxi1_[i] = item.getauxi1(); + fcByPE_[i] = item.getFCByPE(); + darkCurrent_[i] = item.getDarkCurrent(); + auxi2_[i] = item.getauxi2(); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + auto const off = offset + i; + auto const& item = endcapValues[i]; + type_[off] = item.getType(); + auxi1_[off] = item.getauxi1(); + fcByPE_[off] = item.getFCByPE(); + darkCurrent_[off] = item.getDarkCurrent(); + auxi2_[off] = item.getauxi2(); + } +} + +HcalSiPMParametersGPU::Product const& HcalSiPMParametersGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalSiPMParametersGPU::Product& product, cudaStream_t stream) { + // allocate + product.type = cms::cuda::make_device_unique(type_.size(), stream); + product.auxi1 = cms::cuda::make_device_unique(auxi1_.size(), stream); + product.fcByPE = cms::cuda::make_device_unique(fcByPE_.size(), stream); + product.darkCurrent = cms::cuda::make_device_unique(darkCurrent_.size(), stream); + product.auxi2 = cms::cuda::make_device_unique(auxi2_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.type, type_, stream); + cms::cuda::copyAsync(product.auxi1, auxi1_, stream); + cms::cuda::copyAsync(product.fcByPE, fcByPE_, stream); + cms::cuda::copyAsync(product.darkCurrent, darkCurrent_, stream); + cms::cuda::copyAsync(product.auxi2, auxi2_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalSiPMParametersGPU); diff --git a/CondFormats/HcalObjects/src/HcalTimeCorrsGPU.cc b/CondFormats/HcalObjects/src/HcalTimeCorrsGPU.cc new file mode 100644 index 0000000000000..02ce05132479c --- /dev/null +++ b/CondFormats/HcalObjects/src/HcalTimeCorrsGPU.cc @@ -0,0 +1,38 @@ +#include "CondFormats/HcalObjects/interface/HcalTimeCorrs.h" +#include "CondFormats/HcalObjects/interface/HcalTimeCorrsGPU.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" + +// FIXME: add proper getters to conditions +HcalTimeCorrsGPU::HcalTimeCorrsGPU(HcalTimeCorrs const& timecorrs) + : value_(timecorrs.getAllContainers()[0].second.size() + timecorrs.getAllContainers()[1].second.size()) { + auto const containers = timecorrs.getAllContainers(); + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + value_[i] = barrelValues[i].getValue(); + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + value_[i + offset] = endcapValues[i].getValue(); + } +} + +HcalTimeCorrsGPU::Product const& HcalTimeCorrsGPU::getProduct(cudaStream_t stream) const { + auto const& product = + product_.dataForCurrentDeviceAsync(stream, [this](HcalTimeCorrsGPU::Product& product, cudaStream_t stream) { + // allocate + product.value = cms::cuda::make_device_unique(value_.size(), stream); + + // transfer + cms::cuda::copyAsync(product.value, value_, stream); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalTimeCorrsGPU); diff --git a/CondFormats/SiPixelObjects/interface/SiPixelGainForHLTonGPU.h b/CondFormats/SiPixelObjects/interface/SiPixelGainForHLTonGPU.h new file mode 100644 index 0000000000000..aa5a127927b90 --- /dev/null +++ b/CondFormats/SiPixelObjects/interface/SiPixelGainForHLTonGPU.h @@ -0,0 +1,74 @@ +#ifndef CondFormats_SiPixelObjects_interface_SiPixelGainForHLTonGPU_h +#define CondFormats_SiPixelObjects_interface_SiPixelGainForHLTonGPU_h + +#include +#include +#include + +// including would pull in the dependency on all of CUDA; +// instead, just define away the CUDA specific attributes to keep GCC happy. +#ifndef __CUDACC__ +#ifndef __host__ +#define __host__ +#endif // __host__ +#ifndef __device__ +#define __device__ +#endif // __device__ +#endif // __CUDACC__ + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +struct SiPixelGainForHLTonGPU_DecodingStructure { + uint8_t gain; + uint8_t ped; +}; + +// copy of SiPixelGainCalibrationForHLT +class SiPixelGainForHLTonGPU { +public: + using DecodingStructure = SiPixelGainForHLTonGPU_DecodingStructure; + + using Range = std::pair; + + inline __host__ __device__ std::pair getPedAndGain( + uint32_t moduleInd, int col, int row, bool& isDeadColumn, bool& isNoisyColumn) const { + auto range = rangeAndCols_[moduleInd].first; + auto nCols = rangeAndCols_[moduleInd].second; + + // determine what averaged data block we are in (there should be 1 or 2 of these depending on if plaquette is 1 by X or 2 by X + unsigned int lengthOfColumnData = (range.second - range.first) / nCols; + unsigned int lengthOfAveragedDataInEachColumn = 2; // we always only have two values per column averaged block + unsigned int numberOfDataBlocksToSkip = row / numberOfRowsAveragedOver_; + + auto offset = range.first + col * lengthOfColumnData + lengthOfAveragedDataInEachColumn * numberOfDataBlocksToSkip; + + assert(offset < range.second); + assert(offset < 3088384); + assert(0 == offset % 2); + + DecodingStructure const* __restrict__ lp = v_pedestals_; + auto s = lp[offset / 2]; + + isDeadColumn = (s.ped & 0xFF) == deadFlag_; + isNoisyColumn = (s.ped & 0xFF) == noisyFlag_; + + return std::make_pair(decodePed(s.ped & 0xFF), decodeGain(s.gain & 0xFF)); + } + + constexpr float decodeGain(unsigned int gain) const { return gain * gainPrecision_ + minGain_; } + constexpr float decodePed(unsigned int ped) const { return ped * pedPrecision_ + minPed_; } + + DecodingStructure* v_pedestals_; + std::pair rangeAndCols_[gpuClustering::maxNumModules]; + + float minPed_, maxPed_, minGain_, maxGain_; + float pedPrecision_, gainPrecision_; + + unsigned int numberOfRowsAveragedOver_; // this is 80!!!! + unsigned int nBinsToUseForEncoding_; + unsigned int deadFlag_; + unsigned int noisyFlag_; +}; + +#endif // CondFormats_SiPixelObjects_interface_SiPixelGainForHLTonGPU_h diff --git a/CondFormats/SiPixelObjects/interface/SiPixelROCsStatusAndMapping.h b/CondFormats/SiPixelObjects/interface/SiPixelROCsStatusAndMapping.h new file mode 100644 index 0000000000000..a0771aaefb366 --- /dev/null +++ b/CondFormats/SiPixelObjects/interface/SiPixelROCsStatusAndMapping.h @@ -0,0 +1,26 @@ +#ifndef CondFormats_SiPixelObjects_interface_SiPixelROCsStatusAndMapping_h +#define CondFormats_SiPixelObjects_interface_SiPixelROCsStatusAndMapping_h + +namespace pixelgpudetails { + // Maximum fed for phase1 is 150 but not all of them are filled + // Update the number FED based on maximum fed found in the cabling map + constexpr unsigned int MAX_FED = 150; + constexpr unsigned int MAX_LINK = 48; // maximum links/channels for Phase 1 + constexpr unsigned int MAX_ROC = 8; + constexpr unsigned int MAX_SIZE = MAX_FED * MAX_LINK * MAX_ROC; + constexpr unsigned int MAX_SIZE_BYTE_BOOL = MAX_SIZE * sizeof(unsigned char); +} // namespace pixelgpudetails + +// TODO: since this has more information than just cabling map, maybe we should invent a better name? +struct SiPixelROCsStatusAndMapping { + alignas(128) unsigned int fed[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned int link[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned int roc[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned int rawId[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned int rocInDet[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned int moduleId[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned char badRocs[pixelgpudetails::MAX_SIZE]; + alignas(128) unsigned int size = 0; +}; + +#endif // CondFormats_SiPixelObjects_interface_SiPixelROCsStatusAndMapping_h diff --git a/Configuration/PyReleaseValidation/python/relval_2017.py b/Configuration/PyReleaseValidation/python/relval_2017.py index d7218ad988efd..bec68824b6477 100644 --- a/Configuration/PyReleaseValidation/python/relval_2017.py +++ b/Configuration/PyReleaseValidation/python/relval_2017.py @@ -48,16 +48,20 @@ 10824.8, 11024.15, 10842.501,10842.502, # 10842.503,10842.504, + 10842.505,10842.506, 10824.501,10824.502, # 10824.503,10824.504, + 10824.505,10824.506, 10824.511,10824.512, # 10824.513,10824.514, 10824.521,10824.522, # 10824.523,10824.524, 11624.911, 11642.911, 11650.0,11634.0,11646.0,11640.0,11834.0,11834.99,11846.0,12024.0, 11634.1,11634.5,11634.7, 11650.501,11650.502, # 11650.503,11650.504, + 11650.505,11650.506, 11634.501,11634.502, # 11634.503,11634.504, + 11634.505,11634.506, 11634.511,11634.512, # 11634.513,11634.514, - 11634.521,11634.522, # 11634.523,11634.524 + 11634.521,11634.522, # 11634.523,11634.524, 11634.24,11834.24, 12434.0,12634.0,12634.99, 12834.0,13034.0,13034.99] diff --git a/Configuration/PyReleaseValidation/python/relval_steps.py b/Configuration/PyReleaseValidation/python/relval_steps.py index 2c1cc0df069c2..aee33100f83f4 100644 --- a/Configuration/PyReleaseValidation/python/relval_steps.py +++ b/Configuration/PyReleaseValidation/python/relval_steps.py @@ -2190,8 +2190,11 @@ def gen2021HiMix(fragment,howMuch): '--era' :'Run2_2016' } -step3_pixelNtupleFit = { - '--procModifiers': 'pixelNtupleFit', +step3_pixel_ntuplet_cpu = { + '--customise': 'RecoPixelVertexing/Configuration/customizePixelTracksSoAonCPU.customizePixelTracksSoAonCPU' +} +step3_pixel_triplets = { + '--customise': 'RecoPixelVertexing/Configuration/customizePixelTracksSoAonCPU.customizePixelTracksForTriplets' } step3_gpu = { '--procModifiers': 'gpu', @@ -2325,8 +2328,11 @@ def gen2021HiMix(fragment,howMuch): steps['RECODR2_2018reHLT_Prompt']=merge([{'--conditions':'auto:run2_data'},steps['RECODR2_2018reHLT']]) steps['RECODR2_2018reHLT_ZBPrompt']=merge([{'--conditions':'auto:run2_data','-s':'RAW2DIGI,L1Reco,RECO,EI,PAT,ALCA:SiStripCalZeroBias+SiStripCalMinBias+TkAlMinBias+EcalESAlign,DQM:@rerecoZeroBias+@ExtraHLT+@miniAODDQM'},steps['RECODR2_2018reHLT']]) steps['RECODR2_2018reHLT_Prompt_pixelTrackingOnly']=merge([{'-s': 'RAW2DIGI:RawToDigi_pixelOnly,RECO:reconstruction_pixelTrackingOnly,DQM:@pixelTrackingOnlyDQM'},steps['RECODR2_2018reHLT_Prompt']]) -steps['RECODR2_2018reHLT_Patatrack_PixelOnlyCPU']=merge([step3_pixelNtupleFit, steps['RECODR2_2018reHLT_Prompt_pixelTrackingOnly']]) +steps['RECODR2_2018reHLT_Patatrack_PixelOnlyCPU']=merge([step3_pixel_ntuplet_cpu, steps['RECODR2_2018reHLT_Prompt_pixelTrackingOnly']]) steps['RECODR2_2018reHLT_Patatrack_PixelOnlyGPU']=merge([step3_gpu, steps['RECODR2_2018reHLT_Prompt_pixelTrackingOnly']]) +steps['RECODR2_2018reHLT_Patatrack_PixelOnlyTripletsCPU']=merge([step3_pixel_ntuplet_cpu, step3_pixel_triplets, steps['RECODR2_2018reHLT_Prompt_pixelTrackingOnly']]) +steps['RECODR2_2018reHLT_Patatrack_PixelOnlyTripletsGPU']=merge([step3_gpu, step3_pixel_triplets, steps['RECODR2_2018reHLT_Prompt_pixelTrackingOnly']]) + steps['RECODR2_2018reHLT_ECALOnlyCPU']=merge([{'-s': 'RAW2DIGI:RawToDigi_ecalOnly,RECO:reconstruction_ecalOnly,DQM:@ecalOnly'},steps['RECODR2_2018reHLT_Prompt']]) steps['RECODR2_2018reHLT_ECALOnlyGPU']=merge([step3_gpu, steps['RECODR2_2018reHLT_ECALOnlyCPU']]) steps['RECODR2_2018reHLT_HCALOnlyCPU']=merge([{'-s': 'RAW2DIGI:RawToDigi_hcalOnly,RECO:reconstruction_hcalOnly,DQM:@hcalOnly+@hcal2Only'},steps['RECODR2_2018reHLT_Prompt']]) diff --git a/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py b/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py index a9876705bf631..f601f5a2335f2 100644 --- a/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py +++ b/Configuration/PyReleaseValidation/python/upgradeWorkflowComponents.py @@ -373,7 +373,26 @@ def condition_(self, fragment, stepList, key, hasHarvest): '-s': 'RAW2DIGI:RawToDigi_pixelOnly,RECO:reconstruction_pixelTrackingOnly,VALIDATION:@pixelTrackingOnlyValidation,DQM:@pixelTrackingOnlyDQM', '--datatier': 'GEN-SIM-RECO,DQMIO', '--eventcontent': 'RECOSIM,DQM', - '--procModifiers': 'pixelNtupleFit' + '--customise' : 'RecoPixelVertexing/Configuration/customizePixelTracksSoAonCPU.customizePixelTracksSoAonCPU' +} + +upgradeWFs['PatatrackPixelOnlyTripletsCPU'] = UpgradeWorkflowPatatrack_PixelOnlyCPU( + steps = [ + 'Reco', + 'HARVEST', + 'RecoGlobal', + 'HARVESTGlobal', + ], + PU = [], + suffix = 'Patatrack_PixelOnlyTripletsCPU', + offset = 0.505, +) + +upgradeWFs['PatatrackPixelOnlyTripletsCPU'].step3 = { + '-s': 'RAW2DIGI:RawToDigi_pixelOnly,RECO:reconstruction_pixelTrackingOnly,VALIDATION:@pixelTrackingOnlyValidation,DQM:@pixelTrackingOnlyDQM', + '--datatier': 'GEN-SIM-RECO,DQMIO', + '--eventcontent': 'RECOSIM,DQM', + '--customise' : 'RecoPixelVertexing/Configuration/customizePixelTracksSoAonCPU.customizePixelTracksSoAonCPU,RecoPixelVertexing/Configuration/customizePixelTracksSoAonCPU.customizePixelTracksForTriplets' } class UpgradeWorkflowPatatrack_PixelOnlyGPU(UpgradeWorkflowPatatrack): @@ -407,6 +426,26 @@ def condition_(self, fragment, stepList, key, hasHarvest): '--procModifiers': 'gpu' } +upgradeWFs['PatatrackPixelOnlyTripletsGPU'] = UpgradeWorkflowPatatrack_PixelOnlyGPU( + steps = [ + 'Reco', + 'HARVEST', + 'RecoGlobal', + 'HARVESTGlobal', + ], + PU = [], + suffix = 'Patatrack_PixelOnlyTripletsGPU', + offset = 0.506, +) + +upgradeWFs['PatatrackPixelOnlyTripletsGPU'].step3 = { + '-s': 'RAW2DIGI:RawToDigi_pixelOnly,RECO:reconstruction_pixelTrackingOnly,VALIDATION:@pixelTrackingOnlyValidation,DQM:@pixelTrackingOnlyDQM', + '--datatier': 'GEN-SIM-RECO,DQMIO', + '--eventcontent': 'RECOSIM,DQM', + '--procModifiers': 'gpu', + '--customise': 'RecoPixelVertexing/Configuration/customizePixelTracksSoAonCPU.customizePixelTracksForTriplets' +} + class UpgradeWorkflowPatatrack_ECALOnlyCPU(UpgradeWorkflowPatatrack): def setup_(self, step, stepName, stepDict, k, properties): if 'Reco' in step: diff --git a/Configuration/StandardSequences/python/RawToDigi_cff.py b/Configuration/StandardSequences/python/RawToDigi_cff.py index d0af52de00a47..102e8b1132f71 100644 --- a/Configuration/StandardSequences/python/RawToDigi_cff.py +++ b/Configuration/StandardSequences/python/RawToDigi_cff.py @@ -1,9 +1,10 @@ import FWCore.ParameterSet.Config as cms +from Configuration.ProcessModifiers.gpu_cff import gpu # This object is used to selectively make changes for different running # scenarios. In this case it makes changes for Run 2. -from EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi import * +from EventFilter.SiPixelRawToDigi.siPixelDigis_cff import * from EventFilter.SiStripRawToDigi.SiStripDigis_cfi import * @@ -45,7 +46,7 @@ from EventFilter.CTPPSRawToDigi.ctppsRawToDigi_cff import * RawToDigiTask = cms.Task(L1TRawToDigiTask, - siPixelDigis, + siPixelDigisTask, siStripDigis, ecalDigisTask, ecalPreshowerDigis, @@ -60,10 +61,10 @@ ) RawToDigi = cms.Sequence(RawToDigiTask) -RawToDigiTask_noTk = RawToDigiTask.copyAndExclude([siPixelDigis, siStripDigis]) +RawToDigiTask_noTk = RawToDigiTask.copyAndExclude([siPixelDigisTask, siStripDigis]) RawToDigi_noTk = cms.Sequence(RawToDigiTask_noTk) -RawToDigiTask_pixelOnly = cms.Task(siPixelDigis) +RawToDigiTask_pixelOnly = cms.Task(siPixelDigisTask, scalersRawToDigi) RawToDigi_pixelOnly = cms.Sequence(RawToDigiTask_pixelOnly) RawToDigiTask_ecalOnly = cms.Task(ecalDigisTask, ecalPreshowerDigis, scalersRawToDigi) @@ -73,8 +74,8 @@ RawToDigi_hcalOnly = cms.Sequence(RawToDigiTask_hcalOnly) scalersRawToDigi.scalersInputTag = 'rawDataCollector' -siPixelDigis.InputLabel = 'rawDataCollector' -ecalDigis.InputLabel = 'rawDataCollector' +siPixelDigis.cpu.InputLabel = 'rawDataCollector' +(~gpu).toModify(ecalDigis, InputLabel='rawDataCollector') ecalPreshowerDigis.sourceTag = 'rawDataCollector' hcalDigis.InputLabel = 'rawDataCollector' muonCSCDigis.InputObjects = 'rawDataCollector' diff --git a/DQM/Integration/python/clients/beam_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/beam_dqm_sourceclient-live_cfg.py index e0d5d05bc193a..4846de0887fde 100644 --- a/DQM/Integration/python/clients/beam_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/beam_dqm_sourceclient-live_cfg.py @@ -294,7 +294,7 @@ process.muonDTDigis.inputLabel = rawDataInputTag process.muonRPCDigis.InputLabel = rawDataInputTag process.scalersRawToDigi.scalersInputTag = rawDataInputTag -process.siPixelDigis.InputLabel = rawDataInputTag +process.siPixelDigis.cpu.InputLabel = rawDataInputTag process.siStripDigis.ProductLabel = rawDataInputTag process.load("RecoVertex.BeamSpotProducer.BeamSpot_cfi") @@ -317,8 +317,7 @@ process.pixelTracksTrackingRegions.RegionPSet.originXPos = 0.08 process.pixelTracksTrackingRegions.RegionPSet.originYPos = -0.03 process.pixelTracksTrackingRegions.RegionPSet.originZPos = 0. - -process.pixelVertices.TkFilterParameters.minPt = process.pixelTracksTrackingRegions.RegionPSet.ptMin +process.pixelVertices.PtMin = process.pixelTracksTrackingRegions.RegionPSet.ptMin process.tracking_FirstStep = cms.Sequence( process.siPixelDigis diff --git a/DQM/Integration/python/clients/beampixel_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/beampixel_dqm_sourceclient-live_cfg.py index 75f0545a5c5ba..a3eac2069e6ed 100644 --- a/DQM/Integration/python/clients/beampixel_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/beampixel_dqm_sourceclient-live_cfg.py @@ -90,12 +90,12 @@ process.siPixelClusterShapeCachePreSplitting = siPixelClusterShapeCache.clone(src = 'siPixelClustersPreSplitting') process.load("RecoLocalTracker.SiPixelRecHits.PixelCPEGeneric_cfi") process.load("RecoPixelVertexing.Configuration.RecoPixelVertexing_cff") -process.pixelVertices.TkFilterParameters.minPt = process.pixelTracksTrackingRegions.RegionPSet.ptMin process.pixelTracksTrackingRegions.RegionPSet.originRadius = cms.double(0.4) process.pixelTracksTrackingRegions.RegionPSet.originHalfLength = cms.double(15.) process.pixelTracksTrackingRegions.RegionPSet.originXPos = cms.double(0.08) process.pixelTracksTrackingRegions.RegionPSet.originYPos = cms.double(-0.03) process.pixelTracksTrackingRegions.RegionPSet.originZPos = cms.double(0.) +process.pixelVertices.PtMin = process.pixelTracksTrackingRegions.RegionPSet.ptMin #---------------------------- @@ -122,7 +122,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") @@ -175,7 +175,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/csc_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/csc_dqm_sourceclient-live_cfg.py index 77b1e6b88b699..747ec4d01b07a 100644 --- a/DQM/Integration/python/clients/csc_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/csc_dqm_sourceclient-live_cfg.py @@ -189,7 +189,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") process.cscMonitor.FEDRawDataCollectionTag = cms.InputTag("rawDataCollector") process.dqmCSCClient.InputObjects = cms.untracked.InputTag("rawDataCollector") @@ -214,7 +214,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.cscMonitor.FEDRawDataCollectionTag = cms.InputTag("rawDataRepacker") process.dqmCSCClient.InputObjects = cms.untracked.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/fed_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/fed_dqm_sourceclient-live_cfg.py index 6feff0263b749..b28f92d0f8d4e 100644 --- a/DQM/Integration/python/clients/fed_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/fed_dqm_sourceclient-live_cfg.py @@ -48,8 +48,8 @@ # Pixel sequence: process.load('Configuration.StandardSequences.MagneticField_cff') process.load('EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi') -process.siPixelDigis.Timing = False -process.siPixelDigis.IncludeErrors = True +process.siPixelDigis.cpu.Timing = False +process.siPixelDigis.cpu.IncludeErrors = True process.load('DQM.SiPixelMonitorRawData.SiPixelMonitorHLT_cfi') process.SiPixelHLTSource.saveFile = False process.SiPixelHLTSource.slowDown = False @@ -92,7 +92,7 @@ # Setting raw data collection label for all subsytem modules, depending on run type: if (process.runType.getRunType() == process.runType.hi_run): process.l1tStage2Fed.rawTag = cms.InputTag('rawDataRepacker') - process.siPixelDigis.InputLabel = cms.InputTag('rawDataRepacker') + process.siPixelDigis.cpu.InputLabel = cms.InputTag('rawDataRepacker') process.SiPixelHLTSource.RawInput = cms.InputTag('rawDataRepacker') process.siStripFEDCheck.RawDataTag = cms.InputTag('rawDataRepacker') process.esRawToDigi.sourceTag = cms.InputTag('rawDataRepacker') @@ -105,7 +105,7 @@ process.cscDQMEvF.InputObjects = cms.untracked.InputTag('rawDataRepacker') else: process.l1tStage2Fed.rawTag = cms.InputTag('rawDataCollector') - process.siPixelDigis.InputLabel = cms.InputTag('rawDataCollector') + process.siPixelDigis.cpu.InputLabel = cms.InputTag('rawDataCollector') process.SiPixelHLTSource.RawInput = cms.InputTag('rawDataCollector') process.siStripFEDCheck.RawDataTag = cms.InputTag('rawDataCollector') process.esRawToDigi.sourceTag = cms.InputTag('rawDataCollector') @@ -126,7 +126,7 @@ # Modules for the FED process.FEDModulesPath = cms.Path( process.l1tStage2Fed - + process.siPixelDigis + + process.siPixelDigis + process.SiPixelHLTSource + process.siStripFEDCheck + process.esRawToDigi diff --git a/DQM/Integration/python/clients/l1t_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/l1t_dqm_sourceclient-live_cfg.py index 301a79f2b2865..b2358e79a1bb6 100644 --- a/DQM/Integration/python/clients/l1t_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/l1t_dqm_sourceclient-live_cfg.py @@ -182,7 +182,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") process.bxTiming.FedSource = cms.untracked.InputTag("rawDataCollector") process.l1s.fedRawData = cms.InputTag("rawDataCollector") @@ -201,7 +201,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.bxTiming.FedSource = cms.untracked.InputTag("rawDataRepacker") process.l1s.fedRawData = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/l1temulator_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/l1temulator_dqm_sourceclient-live_cfg.py index 9701c71d14a3c..e2c9f057f04b1 100644 --- a/DQM/Integration/python/clients/l1temulator_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/l1temulator_dqm_sourceclient-live_cfg.py @@ -197,7 +197,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") #-------------------------------------------------- @@ -219,7 +219,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/l1tstage1_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/l1tstage1_dqm_sourceclient-live_cfg.py index b8ae0bcf233b4..93df769c2dfe4 100644 --- a/DQM/Integration/python/clients/l1tstage1_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/l1tstage1_dqm_sourceclient-live_cfg.py @@ -192,7 +192,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") process.bxTiming.FedSource = cms.untracked.InputTag("rawDataCollector") process.l1s.fedRawData = cms.InputTag("rawDataCollector") @@ -211,7 +211,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.bxTiming.FedSource = cms.untracked.InputTag("rawDataRepacker") process.l1s.fedRawData = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/l1tstage1emulator_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/l1tstage1emulator_dqm_sourceclient-live_cfg.py index 6200618c0fe44..4ed9c5e298890 100644 --- a/DQM/Integration/python/clients/l1tstage1emulator_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/l1tstage1emulator_dqm_sourceclient-live_cfg.py @@ -206,7 +206,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") #-------------------------------------------------- @@ -228,7 +228,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/l1tstage2_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/l1tstage2_dqm_sourceclient-live_cfg.py index c5788228cac1f..0c7f4707b6d8b 100644 --- a/DQM/Integration/python/clients/l1tstage2_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/l1tstage2_dqm_sourceclient-live_cfg.py @@ -138,7 +138,7 @@ process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.muonGEMDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.tcdsDigis.InputLabel = cms.InputTag("rawDataRepacker") process.tcdsRawToDigi.InputLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/l1tstage2emulator_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/l1tstage2emulator_dqm_sourceclient-live_cfg.py index 91f1f564366d0..7b6372229555c 100644 --- a/DQM/Integration/python/clients/l1tstage2emulator_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/l1tstage2emulator_dqm_sourceclient-live_cfg.py @@ -137,7 +137,7 @@ process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.muonGEMDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.tcdsDigis.InputLabel = cms.InputTag("rawDataRepacker") process.tcdsRawToDigi.InputLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/lumi_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/lumi_dqm_sourceclient-live_cfg.py index 47acfcdc471f2..e439454513880 100644 --- a/DQM/Integration/python/clients/lumi_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/lumi_dqm_sourceclient-live_cfg.py @@ -49,7 +49,7 @@ process.load("Configuration.StandardSequences.EndOfProcess_cff") process.load("Configuration.EventContent.EventContent_cff") process.load("Configuration.StandardSequences.Reconstruction_cff") -process.siPixelDigis.InputLabel = cms.InputTag("hltFEDSelectorLumiPixels") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("hltFEDSelectorLumiPixels") process.reconstruction_step = cms.Sequence( process.siPixelDigis + diff --git a/DQM/Integration/python/clients/physics_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/physics_dqm_sourceclient-live_cfg.py index 76b6ae553949c..fef661979c427 100644 --- a/DQM/Integration/python/clients/physics_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/physics_dqm_sourceclient-live_cfg.py @@ -56,7 +56,7 @@ process.dqmSaverPB ) -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") ### process customizations included here from DQM.Integration.config.online_customizations_cfi import * @@ -69,4 +69,4 @@ print("Running with run type = ", process.runType.getRunType()) if (process.runType.getRunType() == process.runType.hi_run): - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/pixel_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/pixel_dqm_sourceclient-live_cfg.py index 5c45b5f46ca9c..b849be77b6e63 100644 --- a/DQM/Integration/python/clients/pixel_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/pixel_dqm_sourceclient-live_cfg.py @@ -104,18 +104,18 @@ # PixelPhase1 Real data raw to digi process.load("EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi") -process.siPixelDigis.IncludeErrors = True +process.siPixelDigis.cpu.IncludeErrors = True if (process.runType.getRunType() == process.runType.hi_run): #-------------------------------- # Heavy Ion Configuration Changes #-------------------------------- - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") else : - process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") - process.siStripDigis.InputLabel = cms.InputTag("rawDataCollector") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") + process.siStripDigis.InputLabel = cms.InputTag("rawDataCollector") ## Collision Reconstruction process.load("Configuration.StandardSequences.RawToDigi_Data_cff") diff --git a/DQM/Integration/python/clients/pixellumi_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/pixellumi_dqm_sourceclient-live_cfg.py index bd15690ede85a..50213e303b8de 100644 --- a/DQM/Integration/python/clients/pixellumi_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/pixellumi_dqm_sourceclient-live_cfg.py @@ -79,7 +79,7 @@ #----------------------- # Real data raw to digi process.load("EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi") -process.siPixelDigis.IncludeErrors = True +process.siPixelDigis.cpu.IncludeErrors = True # Local Reconstruction process.load("RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizer_cfi") @@ -92,13 +92,13 @@ # SelectEvents = cms.vstring('HLT_600Tower*','HLT_L1*','HLT_Jet*','HLT_*Cosmic*','HLT_HT*','HLT_MinBias_*','HLT_Physics*', 'HLT_ZeroBias*','HLT_HcalNZS*')) -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") #-------------------------------- # Heavy Ion Configuration Changes #-------------------------------- if (process.runType.getRunType() == process.runType.hi_run): process.load('Configuration.StandardSequences.RawToDigi_Repacked_cff') - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") if not unitTest: process.source.SelectEvents = cms.untracked.vstring('HLT_HIL1MinimumBiasHF2AND*') diff --git a/DQM/Integration/python/clients/scal_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/scal_dqm_sourceclient-live_cfg.py index 893443f74f1a2..2decc1b774251 100644 --- a/DQM/Integration/python/clients/scal_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/scal_dqm_sourceclient-live_cfg.py @@ -100,7 +100,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") diff --git a/DQM/Integration/python/clients/sistrip_dqm_sourceclient-live_cfg.py b/DQM/Integration/python/clients/sistrip_dqm_sourceclient-live_cfg.py index 37e219bd456c2..889fc8a978d22 100644 --- a/DQM/Integration/python/clients/sistrip_dqm_sourceclient-live_cfg.py +++ b/DQM/Integration/python/clients/sistrip_dqm_sourceclient-live_cfg.py @@ -502,7 +502,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataCollector") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataCollector") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataCollector") -process.siPixelDigis.InputLabel = cms.InputTag("rawDataCollector") +process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataCollector") process.siStripDigis.ProductLabel = cms.InputTag("rawDataCollector") process.siStripFEDMonitor.RawDataTag = cms.untracked.InputTag("rawDataCollector") #-------------------------------------------------- @@ -523,7 +523,7 @@ process.muonDTDigis.inputLabel = cms.InputTag("rawDataRepacker") process.muonRPCDigis.InputLabel = cms.InputTag("rawDataRepacker") process.scalersRawToDigi.scalersInputTag = cms.InputTag("rawDataRepacker") - process.siPixelDigis.InputLabel = cms.InputTag("rawDataRepacker") + process.siPixelDigis.cpu.InputLabel = cms.InputTag("rawDataRepacker") process.siStripDigis.ProductLabel = cms.InputTag("rawDataRepacker") process.siStripFEDMonitor.RawDataTag = cms.untracked.InputTag("rawDataRepacker") diff --git a/DQM/TrackingMonitorClient/python/pixelTrackingEffFromHitPattern_cff.py b/DQM/TrackingMonitorClient/python/pixelTrackingEffFromHitPattern_cff.py index 15ceaf93ed20a..cff85e56d94f7 100644 --- a/DQM/TrackingMonitorClient/python/pixelTrackingEffFromHitPattern_cff.py +++ b/DQM/TrackingMonitorClient/python/pixelTrackingEffFromHitPattern_cff.py @@ -21,7 +21,10 @@ def _layers(suffix, quant, histoPostfix): ] pixelTrackingEffFromHitPattern = DQMEDHarvester("DQMGenericClient", - subDirs = cms.untracked.vstring("Tracking/PixelTrackParameters/HitEffFromHitPattern*"), + subDirs = cms.untracked.vstring("Tracking/PixelTrackParameters/pixelTracks/HitEffFromHitPattern*", + "Tracking/PixelTrackParameters/dzPV0p1/HitEffFromHitPattern*", + "Tracking/PixelTrackParameters/pt_0to1/HitEffFromHitPattern*", + "Tracking/PixelTrackParameters/pt_1/HitEffFromHitPattern*"), efficiency = cms.vstring( _layers("PU", "GoodNumVertices", "") + _layers("BX", "BX", "VsBX") + diff --git a/DQM/TrackingMonitorClient/python/pixelVertexResolutionClient_cfi.py b/DQM/TrackingMonitorClient/python/pixelVertexResolutionClient_cfi.py new file mode 100644 index 0000000000000..2558e88d26012 --- /dev/null +++ b/DQM/TrackingMonitorClient/python/pixelVertexResolutionClient_cfi.py @@ -0,0 +1,7 @@ +import FWCore.ParameterSet.Config as cms + +from DQM.TrackingMonitorClient.primaryVertexResolutionClient_cfi import primaryVertexResolutionClient as _primaryVertexResolutionClient + +pixelVertexResolutionClient = _primaryVertexResolutionClient.clone( + subDirs = ["OfflinePixelPV/Resolution/*"] +) diff --git a/DQM/TrackingMonitorSource/python/pixelTracksMonitoring_cff.py b/DQM/TrackingMonitorSource/python/pixelTracksMonitoring_cff.py index a075f671f05ce..d5deba78b46c8 100644 --- a/DQM/TrackingMonitorSource/python/pixelTracksMonitoring_cff.py +++ b/DQM/TrackingMonitorSource/python/pixelTracksMonitoring_cff.py @@ -1,23 +1,77 @@ import FWCore.ParameterSet.Config as cms import DQM.TrackingMonitor.TrackerCollisionTrackingMonitor_cfi -pixelTracksMonitoring = DQM.TrackingMonitor.TrackerCollisionTrackingMonitor_cfi.TrackerCollisionTrackMon.clone() -pixelTracksMonitoring.FolderName = 'Tracking/PixelTrackParameters' -pixelTracksMonitoring.TrackProducer = 'pixelTracks' -pixelTracksMonitoring.allTrackProducer = 'pixelTracks' -pixelTracksMonitoring.beamSpot = 'offlineBeamSpot' -pixelTracksMonitoring.primaryVertex = 'pixelVertices' -pixelTracksMonitoring.pvNDOF = 1 -pixelTracksMonitoring.doAllPlots = True -pixelTracksMonitoring.doLumiAnalysis = True -pixelTracksMonitoring.doProfilesVsLS = True -pixelTracksMonitoring.doDCAPlots = True -pixelTracksMonitoring.doProfilesVsLS = True -pixelTracksMonitoring.doPlotsVsGoodPVtx = True -pixelTracksMonitoring.doEffFromHitPatternVsPU = False -pixelTracksMonitoring.doEffFromHitPatternVsBX = False -pixelTracksMonitoring.doEffFromHitPatternVsLUMI = False -pixelTracksMonitoring.doPlotsVsGoodPVtx = True -pixelTracksMonitoring.doPlotsVsLUMI = True -pixelTracksMonitoring.doPlotsVsBX = True +pixelTracksMonitor = DQM.TrackingMonitor.TrackerCollisionTrackingMonitor_cfi.TrackerCollisionTrackMon.clone() +pixelTracksMonitor.FolderName = 'Tracking/PixelTrackParameters/pixelTracks' +pixelTracksMonitor.TrackProducer = 'pixelTracks' +pixelTracksMonitor.allTrackProducer = 'pixelTracks' +pixelTracksMonitor.beamSpot = 'offlineBeamSpot' +pixelTracksMonitor.primaryVertex = 'pixelVertices' +pixelTracksMonitor.pvNDOF = 1 +pixelTracksMonitor.doAllPlots = True +pixelTracksMonitor.doLumiAnalysis = True +pixelTracksMonitor.doProfilesVsLS = True +pixelTracksMonitor.doDCAPlots = True +pixelTracksMonitor.doProfilesVsLS = True +pixelTracksMonitor.doPlotsVsGoodPVtx = True +pixelTracksMonitor.doEffFromHitPatternVsPU = False +pixelTracksMonitor.doEffFromHitPatternVsBX = False +pixelTracksMonitor.doEffFromHitPatternVsLUMI = False +pixelTracksMonitor.doPlotsVsGoodPVtx = True +pixelTracksMonitor.doPlotsVsLUMI = True +pixelTracksMonitor.doPlotsVsBX = True +_trackSelector = cms.EDFilter('TrackSelector', + src = cms.InputTag('pixelTracks'), + cut = cms.string("") +) + +pixelTracksPt0to1 = _trackSelector.clone(cut = "pt >= 0 & pt < 1 ") +pixelTracksPt1 = _trackSelector.clone(cut = "pt >= 1 ") +from DQM.TrackingMonitorSource.TrackCollections2monitor_cff import highPurityPV0p1 as _highPurityPV0p1 +pixelTracksPV0p1 = _highPurityPV0p1.clone( + src = "pixelTracks", + quality = "", + vertexTag = "goodPixelVertices" +) + +pixelTracksMonitorPt0to1 = pixelTracksMonitor.clone( + TrackProducer = "pixelTracksPt0to1", + FolderName = "Tracking/PixelTrackParameters/pt_0to1" +) +pixelTracksMonitorPt1 = pixelTracksMonitor.clone( + TrackProducer = "pixelTracksPt1", + FolderName = "Tracking/PixelTrackParameters/pt_1" +) +pixelTracksMonitorPV0p1 = pixelTracksMonitor.clone( + TrackProducer = "pixelTracksPV0p1", + FolderName = "Tracking/PixelTrackParameters/dzPV0p1" +) + + +from CommonTools.ParticleFlow.goodOfflinePrimaryVertices_cfi import goodOfflinePrimaryVertices as _goodOfflinePrimaryVertices +goodPixelVertices = _goodOfflinePrimaryVertices.clone( + src = "pixelVertices", +) + +from DQM.TrackingMonitor.primaryVertexResolution_cfi import primaryVertexResolution as _primaryVertexResolution +pixelVertexResolution = _primaryVertexResolution.clone( + vertexSrc = "goodPixelVertices", + rootFolder = "OfflinePixelPV/Resolution", +) + +pixelTracksMonitoringTask = cms.Task( + goodPixelVertices, + pixelTracksPt0to1, + pixelTracksPt1, + pixelTracksPV0p1, +) + +pixelTracksMonitoring = cms.Sequence( + pixelTracksMonitor + + pixelTracksMonitorPt0to1 + + pixelTracksMonitorPt1 + + pixelTracksMonitorPV0p1 + + pixelVertexResolution, + pixelTracksMonitoringTask +) diff --git a/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py b/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py index 368b328632fd8..29bf311c474d4 100644 --- a/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py +++ b/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py @@ -122,6 +122,7 @@ from DQM.CTPPS.ctppsDQM_cff import * from Validation.RecoTau.DQMSequences_cfi import * from DQM.TrackingMonitorClient.pixelTrackingEffFromHitPattern_cff import * +from DQM.TrackingMonitorClient.pixelVertexResolutionClient_cfi import * DQMHarvestTrackerStrip = cms.Sequence ( SiStripOfflineDQMClient ) @@ -179,7 +180,8 @@ DQMHarvestTrackingZeroBias = cms.Sequence( TrackingOfflineDQMClientZeroBias * dqmFastTimerServiceClient ) -DQMHarvestPixelTracking = cms.Sequence( pixelTrackingEffFromHitPattern ) +DQMHarvestPixelTracking = cms.Sequence( pixelTrackingEffFromHitPattern * + pixelVertexResolutionClient ) DQMHarvestOuterTracker = cms.Sequence( OuterTrackerClient * diff --git a/DQMOffline/Configuration/python/DQMOffline_cff.py b/DQMOffline/Configuration/python/DQMOffline_cff.py index 2001c22352a48..ac28700d4eaf4 100644 --- a/DQMOffline/Configuration/python/DQMOffline_cff.py +++ b/DQMOffline/Configuration/python/DQMOffline_cff.py @@ -157,10 +157,12 @@ #DQMOfflineCommon from DQM.TrackingMonitorSource.pixelTracksMonitoring_cff import * +from DQMOffline.RecoB.PixelVertexMonitor_cff import * from DQM.SiOuterTracker.OuterTrackerSourceConfig_cff import * from Validation.RecoTau.DQMSequences_cfi import * -DQMOfflinePixelTracking = cms.Sequence( pixelTracksMonitoring ) +DQMOfflinePixelTracking = cms.Sequence( pixelTracksMonitoring * + pixelPVMonitor ) DQMOuterTracker = cms.Sequence( DQMOfflineDCS * OuterTrackerSource * diff --git a/DQMOffline/RecoB/python/PixelVertexMonitor_cff.py b/DQMOffline/RecoB/python/PixelVertexMonitor_cff.py new file mode 100644 index 0000000000000..9e293f4478bd6 --- /dev/null +++ b/DQMOffline/RecoB/python/PixelVertexMonitor_cff.py @@ -0,0 +1,8 @@ +import FWCore.ParameterSet.Config as cms + +from DQMOffline.RecoB.PrimaryVertexMonitor_cff import pvMonitor as _pvMonitor +pixelPVMonitor = _pvMonitor.clone( + TopFolderName = "OfflinePixelPV", + vertexLabel = "pixelVertices", + ndof = cms.int32( 1 ) +) diff --git a/DataFormats/CaloRecHit/interface/MultifitComputations.h b/DataFormats/CaloRecHit/interface/MultifitComputations.h index 019f86d11e92b..f2d57d2ddb1e7 100644 --- a/DataFormats/CaloRecHit/interface/MultifitComputations.h +++ b/DataFormats/CaloRecHit/interface/MultifitComputations.h @@ -7,6 +7,8 @@ #include +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" + namespace calo { namespace multifit { @@ -73,7 +75,7 @@ namespace calo { L(0, 0) = sqrtm_0_0; using T = typename MatrixType1::base_type; -#pragma unroll + CMS_UNROLL_LOOP for (int i = 1; i < MatrixType1::stride; i++) { T sumsq{0}; for (int j = 0; j < i; j++) { @@ -199,13 +201,13 @@ namespace calo { constexpr auto NPULSES = MatrixType2::ColsAtCompileTime; constexpr auto NSAMPLES = MatrixType2::RowsAtCompileTime; -#pragma unroll + CMS_UNROLL_LOOP for (int icol = 0; icol < NPULSES; icol++) { float reg_b[NSAMPLES]; float reg_L[NSAMPLES]; -// preload a column and load column 0 of cholesky -#pragma unroll + // preload a column and load column 0 of cholesky + CMS_UNROLL_LOOP for (int i = 0; i < NSAMPLES; i++) { #ifdef __CUDA_ARCH__ // load through the read-only cache @@ -220,16 +222,16 @@ namespace calo { auto x_prev = reg_b[0] / reg_L[0]; A(0, icol) = x_prev; -// iterate -#pragma unroll + // iterate + CMS_UNROLL_LOOP for (int iL = 1; iL < NSAMPLES; iL++) { -// update accum -#pragma unroll + // update accum + CMS_UNROLL_LOOP for (int counter = iL; counter < NSAMPLES; counter++) reg_b[counter] -= x_prev * reg_L[counter]; -// load the next column of cholesky -#pragma unroll + // load the next column of cholesky + CMS_UNROLL_LOOP for (int counter = iL; counter < NSAMPLES; counter++) reg_L[counter] = matrixL(counter, iL); @@ -251,8 +253,8 @@ namespace calo { float reg_b_tmp[NSAMPLES]; float reg_L[NSAMPLES]; -// preload a column and load column 0 of cholesky -#pragma unroll + // preload a column and load column 0 of cholesky + CMS_UNROLL_LOOP for (int i = 0; i < NSAMPLES; i++) { reg_b_tmp[i] = inputAmplitudesView(i); reg_L[i] = matrixL(i, 0); @@ -262,16 +264,16 @@ namespace calo { auto x_prev = reg_b_tmp[0] / reg_L[0]; reg_b[0] = x_prev; -// iterate -#pragma unroll + // iterate + CMS_UNROLL_LOOP for (int iL = 1; iL < NSAMPLES; iL++) { -// update accum -#pragma unroll + // update accum + CMS_UNROLL_LOOP for (int counter = iL; counter < NSAMPLES; counter++) reg_b_tmp[counter] -= x_prev * reg_L[counter]; -// load the next column of cholesky -#pragma unroll + // load the next column of cholesky + CMS_UNROLL_LOOP for (int counter = iL; counter < NSAMPLES; counter++) reg_L[counter] = matrixL(counter, iL); @@ -300,13 +302,13 @@ namespace calo { float results[NPULSES]; // preload results and permute according to the pulse offsets /////////////// ??? this is not done in ECAL -#pragma unroll + CMS_UNROLL_LOOP for (int counter = 0; counter < NPULSES; counter++) { results[counter] = resultAmplitudesVector[counter]; } // load accum -#pragma unroll + CMS_UNROLL_LOOP for (int counter = 0; counter < NSAMPLES; counter++) accum[counter] = -inputAmplitudesView(counter); @@ -315,7 +317,7 @@ namespace calo { float pm_col[NSAMPLES]; // preload a column of pulse matrix -#pragma unroll + CMS_UNROLL_LOOP for (int counter = 0; counter < NSAMPLES; counter++) #ifdef __CUDA_ARCH__ pm_col[counter] = __ldg(&pulseMatrixView.coeffRef(counter, icol)); @@ -323,8 +325,8 @@ namespace calo { pm_col[counter] = pulseMatrixView.coeffRef(counter, icol); #endif - // accum -#pragma unroll + // accum + CMS_UNROLL_LOOP for (int counter = 0; counter < NSAMPLES; counter++) accum[counter] += results[icol] * pm_col[counter]; } @@ -342,7 +344,7 @@ namespace calo { float accumSum = 0; // preload a column and load column 0 of cholesky -#pragma unroll + CMS_UNROLL_LOOP for (int i = 0; i < NSAMPLES; i++) { reg_L[i] = matrixL(i, 0); } @@ -352,15 +354,15 @@ namespace calo { accumSum += x_prev * x_prev; // iterate -#pragma unroll + CMS_UNROLL_LOOP for (int iL = 1; iL < NSAMPLES; iL++) { // update accum -#pragma unroll + CMS_UNROLL_LOOP for (int counter = iL; counter < NSAMPLES; counter++) accum[counter] -= x_prev * reg_L[counter]; - // load the next column of cholesky -#pragma unroll + // load the next column of cholesky + CMS_UNROLL_LOOP for (int counter = iL; counter < NSAMPLES; counter++) reg_L[counter] = matrixL(counter, iL); @@ -417,7 +419,7 @@ namespace calo { auto const icol_real = pulseOffsets(icol); auto const atb = Atb(icol_real); float sum = 0; -#pragma unroll + CMS_UNROLL_LOOP for (int counter = 0; counter < NPULSES; counter++) sum += counter > icol_real ? AtA(counter, icol_real) * solution(counter) : AtA(icol_real, counter) * solution(counter); diff --git a/DataFormats/SiPixelCluster/interface/SiPixelCluster.h b/DataFormats/SiPixelCluster/interface/SiPixelCluster.h index ab4ae1add2132..453d41555a65d 100644 --- a/DataFormats/SiPixelCluster/interface/SiPixelCluster.h +++ b/DataFormats/SiPixelCluster/interface/SiPixelCluster.h @@ -21,6 +21,7 @@ #include #include #include +#include class PixelDigi; @@ -68,19 +69,22 @@ class SiPixelCluster { static constexpr unsigned int MAXSPAN = 255; static constexpr unsigned int MAXPOS = 2047; + static constexpr uint16_t invalidClusterId = std::numeric_limits::max(); + /** Construct from a range of digis that form a cluster and from * a DetID. The range is assumed to be non-empty. */ - SiPixelCluster() {} + SiPixelCluster() = default; SiPixelCluster(unsigned int isize, uint16_t const* adcs, uint16_t const* xpos, uint16_t const* ypos, - uint16_t const xmin, - uint16_t const ymin) - : thePixelOffset(2 * isize), thePixelADC(adcs, adcs + isize) { + uint16_t xmin, + uint16_t ymin, + uint16_t id = invalidClusterId) + : thePixelOffset(2 * isize), thePixelADC(adcs, adcs + isize), theOriginalClusterId(id) { uint16_t maxCol = 0; uint16_t maxRow = 0; for (unsigned int i = 0; i != isize; ++i) { @@ -189,6 +193,10 @@ class SiPixelCluster { float getSplitClusterErrorX() const { return err_x; } float getSplitClusterErrorY() const { return err_y; } + // the original id (they get sorted) + auto originalId() const { return theOriginalClusterId; } + void setOriginalId(uint16_t id) { theOriginalClusterId = id; } + private: std::vector thePixelOffset; std::vector thePixelADC; @@ -198,6 +206,8 @@ class SiPixelCluster { uint8_t thePixelRowSpan = 0; // Span pixel index in the x direction (low edge). uint8_t thePixelColSpan = 0; // Span pixel index in the y direction (left edge). + uint16_t theOriginalClusterId = invalidClusterId; + float err_x = -99999.9f; float err_y = -99999.9f; }; diff --git a/DataFormats/SiPixelCluster/src/classes_def.xml b/DataFormats/SiPixelCluster/src/classes_def.xml index 55c9fd8538417..d43f062877eb0 100644 --- a/DataFormats/SiPixelCluster/src/classes_def.xml +++ b/DataFormats/SiPixelCluster/src/classes_def.xml @@ -4,6 +4,7 @@ + diff --git a/DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h b/DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h new file mode 100644 index 0000000000000..6c016155b1cb0 --- /dev/null +++ b/DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h @@ -0,0 +1,39 @@ +#ifndef DataFormats_SiPixelDigi_interface_SiPixelDigisSoA_h +#define DataFormats_SiPixelDigi_interface_SiPixelDigisSoA_h + +#include +#include + +// The main purpose of this class is to deliver digi and cluster data +// from an EDProducer that transfers the data from GPU to host to an +// EDProducer that converts the SoA to legacy data products. The class +// is independent of any GPU technology, and in prunciple could be +// produced by host code, and be used for other purposes than +// conversion-to-legacy as well. +class SiPixelDigisSoA { +public: + SiPixelDigisSoA() = default; + explicit SiPixelDigisSoA( + size_t nDigis, const uint32_t* pdigi, const uint32_t* rawIdArr, const uint16_t* adc, const int32_t* clus); + ~SiPixelDigisSoA() = default; + + auto size() const { return pdigi_.size(); } + + uint32_t pdigi(size_t i) const { return pdigi_[i]; } + uint32_t rawIdArr(size_t i) const { return rawIdArr_[i]; } + uint16_t adc(size_t i) const { return adc_[i]; } + int32_t clus(size_t i) const { return clus_[i]; } + + const std::vector& pdigiVector() const { return pdigi_; } + const std::vector& rawIdArrVector() const { return rawIdArr_; } + const std::vector& adcVector() const { return adc_; } + const std::vector& clusVector() const { return clus_; } + +private: + std::vector pdigi_; // packed digi (row, col, adc) of each pixel + std::vector rawIdArr_; // DetId of each pixel + std::vector adc_; // ADC of each pixel + std::vector clus_; // cluster id of each pixel +}; + +#endif diff --git a/DataFormats/SiPixelDigi/src/SiPixelDigisSoA.cc b/DataFormats/SiPixelDigi/src/SiPixelDigisSoA.cc new file mode 100644 index 0000000000000..b95c004a50a25 --- /dev/null +++ b/DataFormats/SiPixelDigi/src/SiPixelDigisSoA.cc @@ -0,0 +1,10 @@ +#include "DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h" + +#include + +SiPixelDigisSoA::SiPixelDigisSoA( + size_t nDigis, const uint32_t *pdigi, const uint32_t *rawIdArr, const uint16_t *adc, const int32_t *clus) + : pdigi_(pdigi, pdigi + nDigis), + rawIdArr_(rawIdArr, rawIdArr + nDigis), + adc_(adc, adc + nDigis), + clus_(clus, clus + nDigis) {} diff --git a/DataFormats/SiPixelDigi/src/classes.h b/DataFormats/SiPixelDigi/src/classes.h index 2f36b72ca7df8..1360ee6e469d9 100644 --- a/DataFormats/SiPixelDigi/src/classes.h +++ b/DataFormats/SiPixelDigi/src/classes.h @@ -5,6 +5,7 @@ #include "DataFormats/SiPixelDigi/interface/PixelDigiCollection.h" #include "DataFormats/SiPixelDigi/interface/SiPixelCalibDigi.h" #include "DataFormats/SiPixelDigi/interface/SiPixelCalibDigiError.h" +#include "DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h" #include "DataFormats/Common/interface/Wrapper.h" #include "DataFormats/Common/interface/DetSetVector.h" #include "DataFormats/Common/interface/DetSetVectorNew.h" diff --git a/DataFormats/SiPixelDigi/src/classes_def.xml b/DataFormats/SiPixelDigi/src/classes_def.xml index de7779a5c00ea..e6bc08de161fa 100755 --- a/DataFormats/SiPixelDigi/src/classes_def.xml +++ b/DataFormats/SiPixelDigi/src/classes_def.xml @@ -49,4 +49,7 @@ + + + diff --git a/DataFormats/SiPixelRawData/interface/SiPixelErrorCompact.h b/DataFormats/SiPixelRawData/interface/SiPixelErrorCompact.h new file mode 100644 index 0000000000000..0b1a80868594f --- /dev/null +++ b/DataFormats/SiPixelRawData/interface/SiPixelErrorCompact.h @@ -0,0 +1,13 @@ +#ifndef DataFormats_SiPixelRawData_interface_SiPixelErrorCompact_h +#define DataFormats_SiPixelRawData_interface_SiPixelErrorCompact_h + +#include + +struct SiPixelErrorCompact { + uint32_t rawId; + uint32_t word; + uint8_t errorType; + uint8_t fedId; +}; + +#endif // DataFormats_SiPixelRawData_interface_SiPixelErrorCompact_h diff --git a/DataFormats/SiPixelRawData/interface/SiPixelErrorsSoA.h b/DataFormats/SiPixelRawData/interface/SiPixelErrorsSoA.h new file mode 100644 index 0000000000000..c72c19ce5fda4 --- /dev/null +++ b/DataFormats/SiPixelRawData/interface/SiPixelErrorsSoA.h @@ -0,0 +1,30 @@ +#ifndef DataFormats_SiPixelDigi_interface_SiPixelErrorsSoA_h +#define DataFormats_SiPixelDigi_interface_SiPixelErrorsSoA_h + +#include "DataFormats/SiPixelRawData/interface/SiPixelErrorCompact.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelFormatterErrors.h" + +#include +#include + +class SiPixelErrorsSoA { +public: + SiPixelErrorsSoA() = default; + explicit SiPixelErrorsSoA(size_t nErrors, const SiPixelErrorCompact *error, const SiPixelFormatterErrors *err) + : error_(error, error + nErrors), formatterErrors_(err) {} + ~SiPixelErrorsSoA() = default; + + auto size() const { return error_.size(); } + + const SiPixelFormatterErrors *formatterErrors() const { return formatterErrors_; } + + const SiPixelErrorCompact &error(size_t i) const { return error_[i]; } + + const std::vector &errorVector() const { return error_; } + +private: + std::vector error_; + const SiPixelFormatterErrors *formatterErrors_ = nullptr; +}; + +#endif diff --git a/DataFormats/SiPixelRawData/interface/SiPixelFormatterErrors.h b/DataFormats/SiPixelRawData/interface/SiPixelFormatterErrors.h new file mode 100644 index 0000000000000..9d372737300d4 --- /dev/null +++ b/DataFormats/SiPixelRawData/interface/SiPixelFormatterErrors.h @@ -0,0 +1,12 @@ +#ifndef DataFormats_SiPixelRawData_interface_SiPixelFormatterErrors_h +#define DataFormats_SiPixelRawData_interface_SiPixelFormatterErrors_h + +#include +#include + +#include "DataFormats/SiPixelRawData/interface/SiPixelRawDataError.h" +#include "FWCore/Utilities/interface/typedefs.h" + +using SiPixelFormatterErrors = std::map>; + +#endif // DataFormats_SiPixelRawData_interface_SiPixelFormatterErrors_h diff --git a/DataFormats/SiPixelRawData/src/classes.h b/DataFormats/SiPixelRawData/src/classes.h index 73768cc373013..7a07e9f35f388 100644 --- a/DataFormats/SiPixelRawData/src/classes.h +++ b/DataFormats/SiPixelRawData/src/classes.h @@ -2,6 +2,7 @@ #define SIPIXELRAWDATA_CLASSES_H #include "DataFormats/SiPixelRawData/interface/SiPixelRawDataError.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelErrorsSoA.h" #include "DataFormats/Common/interface/Wrapper.h" #include "DataFormats/Common/interface/DetSetVector.h" #include diff --git a/DataFormats/SiPixelRawData/src/classes_def.xml b/DataFormats/SiPixelRawData/src/classes_def.xml index 827d4b1191cf6..fd2b5dcf27965 100644 --- a/DataFormats/SiPixelRawData/src/classes_def.xml +++ b/DataFormats/SiPixelRawData/src/classes_def.xml @@ -14,4 +14,7 @@ + + + diff --git a/EventFilter/EcalRawToDigi/BuildFile.xml b/EventFilter/EcalRawToDigi/BuildFile.xml index 88f864737813e..2ac1b25233910 100644 --- a/EventFilter/EcalRawToDigi/BuildFile.xml +++ b/EventFilter/EcalRawToDigi/BuildFile.xml @@ -1,14 +1,18 @@ - - + + + + + + diff --git a/EventFilter/EcalRawToDigi/bin/BuildFile.xml b/EventFilter/EcalRawToDigi/bin/BuildFile.xml new file mode 100644 index 0000000000000..792fe438d8799 --- /dev/null +++ b/EventFilter/EcalRawToDigi/bin/BuildFile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/EventFilter/EcalRawToDigi/bin/makeEcalRaw2DigiGpuValidationPlots.cpp b/EventFilter/EcalRawToDigi/bin/makeEcalRaw2DigiGpuValidationPlots.cpp new file mode 100644 index 0000000000000..609c277e19288 --- /dev/null +++ b/EventFilter/EcalRawToDigi/bin/makeEcalRaw2DigiGpuValidationPlots.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "DataFormats/Common/interface/Wrapper.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" + +int main(int argc, char* argv[]) { + if (argc < 3) { + std::cout << "run with: ./ \n"; + exit(0); + } + + // branches to use + edm::Wrapper*wgpuEB = nullptr, *wcpuEB = nullptr; + edm::Wrapper*wgpuEE = nullptr, *wcpuEE = nullptr; + + std::string inFileName{argv[1]}; + std::string outFileName{argv[2]}; + + // prep output + TFile rfout{outFileName.c_str(), "recreate"}; + + int const nbins = 400; + float const last = 4096.; + auto hADCEBGPU = new TH1D("hADCEBGPU", "hADCEBGPU", nbins, 0, last); + auto hADCEBCPU = new TH1D("hADCEBCPU", "hADCEBCPU", nbins, 0, last); + auto hADCEEGPU = new TH1D("hADCEEGPU", "hADCEEGPU", nbins, 0, last); + auto hADCEECPU = new TH1D("hADCEECPU", "hADCEECPU", nbins, 0, last); + + auto hGainEBGPU = new TH1D("hGainEBGPU", "hGainEBGPU", 4, 0, 4); + auto hGainEBCPU = new TH1D("hGainEBCPU", "hGainEBCPU", 4, 0, 4); + auto hGainEEGPU = new TH1D("hGainEEGPU", "hGainEEGPU", 4, 0, 4); + auto hGainEECPU = new TH1D("hGainEECPU", "hGainEECPU", 4, 0, 4); + + auto hADCEBGPUvsCPU = new TH2D("hADCEBGPUvsCPU", "hADCEBGPUvsCPU", nbins, 0, last, nbins, 0, last); + auto hADCEEGPUvsCPU = new TH2D("hADCEEGPUvsCPU", "hADCEEGPUvsCPU", nbins, 0, last, nbins, 0, last); + auto hGainEBGPUvsCPU = new TH2D("hGainEBGPUvsCPU", "hGainEBGPUvsCPU", 4, 0, 4, 4, 0, 4); + auto hGainEEGPUvsCPU = new TH2D("hGainEEGPUvsCPU", "hGainEEGPUvsCPU", 4, 0, 4, 4, 0, 4); + + // prep input + TFile rfin{inFileName.c_str()}; + TTree* rt = (TTree*)rfin.Get("Events"); + rt->SetBranchAddress("EBDigiCollection_ecalCPUDigisProducer_ebDigis_RECO.", &wgpuEB); + rt->SetBranchAddress("EEDigiCollection_ecalCPUDigisProducer_eeDigis_RECO.", &wgpuEE); + rt->SetBranchAddress("EBDigiCollection_ecalDigis_ebDigis_RECO.", &wcpuEB); + rt->SetBranchAddress("EEDigiCollection_ecalDigis_eeDigis_RECO.", &wcpuEE); + + // accumulate + auto const nentries = rt->GetEntries(); + std::cout << ">>> nentries = " << nentries << std::endl; + for (int ie = 0; ie < nentries; ++ie) { + rt->GetEntry(ie); + + auto const ngpuebs = wgpuEB->bareProduct().size(); + auto const ncpuebs = wcpuEB->bareProduct().size(); + auto const ngpuees = wgpuEE->bareProduct().size(); + auto const ncpuees = wcpuEE->bareProduct().size(); + + if (ngpuebs != ncpuebs or ngpuees != ncpuees) { + std::cerr << "*** mismatch in ndigis: " + << "ie = " << ie << " ngpuebs = " << ngpuebs << " ncpuebs = " << ncpuebs << " ngpuees = " << ngpuees + << " ncpuees = " << ncpuees << std::endl; + + // this is a must for now + //assert(ngpuebs==ncpuebs); + //assert(ngpuees==ncpuees); + } + + // assume identical sizes + auto const& idsgpuEB = wgpuEB->bareProduct().ids(); + auto const& datagpuEB = wgpuEB->bareProduct().data(); + auto const& idscpuEB = wcpuEB->bareProduct().ids(); + auto const& datacpuEB = wcpuEB->bareProduct().data(); + for (uint32_t ieb = 0; ieb < ngpuebs; ++ieb) { + auto const& idgpu = idsgpuEB[ieb]; + auto iter2idcpu = std::find(idscpuEB.begin(), idscpuEB.end(), idgpu); + // FIXME + assert(idgpu == *iter2idcpu); + + auto const ptrdiff = iter2idcpu - idscpuEB.begin(); + for (uint32_t s = 0u; s < 10u; s++) { + EcalMGPASample sampleGPU{datagpuEB[ieb * 10 + s]}; + EcalMGPASample sampleCPU{datacpuEB[ptrdiff * 10 + s]}; + + hADCEBGPU->Fill(sampleGPU.adc()); + hGainEBGPU->Fill(sampleGPU.gainId()); + hADCEBCPU->Fill(sampleCPU.adc()); + hGainEBCPU->Fill(sampleCPU.gainId()); + hADCEBGPUvsCPU->Fill(sampleCPU.adc(), sampleGPU.adc()); + hGainEBGPUvsCPU->Fill(sampleCPU.gainId(), sampleGPU.gainId()); + } + } + + auto const& idsgpuEE = wgpuEE->bareProduct().ids(); + auto const& datagpuEE = wgpuEE->bareProduct().data(); + auto const& idscpuEE = wcpuEE->bareProduct().ids(); + auto const& datacpuEE = wcpuEE->bareProduct().data(); + for (uint32_t iee = 0; iee < ngpuees; ++iee) { + auto const& idgpu = idsgpuEE[iee]; + auto iter2idcpu = std::find(idscpuEE.begin(), idscpuEE.end(), idgpu); + // FIXME + assert(idgpu == *iter2idcpu); + + // get the digis + auto const ptrdiff = iter2idcpu - idscpuEE.begin(); + for (uint32_t s = 0u; s < 10u; s++) { + EcalMGPASample sampleGPU{datagpuEE[iee * 10 + s]}; + EcalMGPASample sampleCPU{datacpuEE[ptrdiff * 10 + s]}; + + hADCEEGPU->Fill(sampleGPU.adc()); + hGainEEGPU->Fill(sampleGPU.gainId()); + hADCEECPU->Fill(sampleCPU.adc()); + hGainEECPU->Fill(sampleCPU.gainId()); + hADCEEGPUvsCPU->Fill(sampleCPU.adc(), sampleGPU.adc()); + hGainEEGPUvsCPU->Fill(sampleCPU.gainId(), sampleGPU.gainId()); + } + } + } + + { + TCanvas c{"plots", "plots", 4200, 6200}; + c.Divide(2, 4); + c.cd(1); + { + gPad->SetLogy(); + hADCEBCPU->SetLineColor(kBlack); + hADCEBCPU->SetLineWidth(1.); + hADCEBCPU->Draw(""); + hADCEBGPU->SetLineColor(kBlue); + hADCEBGPU->SetLineWidth(1.); + hADCEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hADCEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(2); + { + gPad->SetLogy(); + hADCEECPU->SetLineColor(kBlack); + hADCEECPU->SetLineWidth(1.); + hADCEECPU->Draw(""); + hADCEEGPU->SetLineColor(kBlue); + hADCEEGPU->SetLineWidth(1.); + hADCEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hADCEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(3); + { + gPad->SetLogy(); + hGainEBCPU->SetLineColor(kBlack); + hGainEBCPU->SetLineWidth(1.); + hGainEBCPU->Draw(""); + hGainEBGPU->SetLineColor(kBlue); + hGainEBGPU->SetLineWidth(1.); + hGainEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hGainEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(4); + { + gPad->SetLogy(); + hGainEECPU->SetLineColor(kBlack); + hGainEECPU->SetLineWidth(1.); + hGainEECPU->Draw(""); + hGainEEGPU->SetLineColor(kBlue); + hGainEEGPU->SetLineWidth(1.); + hGainEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hGainEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(5); + hADCEBGPUvsCPU->Draw("colz"); + c.cd(6); + hADCEEGPUvsCPU->Draw("colz"); + c.cd(7); + hGainEBGPUvsCPU->Draw("colz"); + c.cd(8); + hGainEEGPUvsCPU->Draw("colz"); + c.SaveAs("plots.pdf"); + } + + rfin.Close(); + rfout.Write(); + rfout.Close(); +} diff --git a/EventFilter/EcalRawToDigi/interface/EcalRegionCabling.h b/EventFilter/EcalRawToDigi/interface/EcalRegionCabling.h index fa6e9f5d5a161..38a9ebdf18cb8 100644 --- a/EventFilter/EcalRawToDigi/interface/EcalRegionCabling.h +++ b/EventFilter/EcalRawToDigi/interface/EcalRegionCabling.h @@ -1,14 +1,11 @@ -#ifndef EcalRegionCabling_H -#define EcalRegionCabling_H +#ifndef EventFilter_EcalRawToDigi_interface_EcalRegionCabling_h +#define EventFilter_EcalRawToDigi_interface_EcalRegionCabling_h -#include "Geometry/EcalMapping/interface/EcalElectronicsMapping.h" -#include "Geometry/EcalMapping/interface/ESElectronicsMapper.h" - -#include "DataFormats/EcalRecHit/interface/EcalRecHit.h" -#include "FWCore/ParameterSet/interface/ParameterSet.h" #include "DataFormats/FEDRawData/interface/FEDNumbering.h" - #include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "Geometry/EcalMapping/interface/ESElectronicsMapper.h" +#include "Geometry/EcalMapping/interface/EcalElectronicsMapping.h" class EcalRegionCabling { public: @@ -73,4 +70,4 @@ class EcalRegionCabling { const ESElectronicsMapper* es_mapping_; }; -#endif +#endif // EventFilter_EcalRawToDigi_interface_EcalRegionCabling_h diff --git a/EventFilter/EcalRawToDigi/interface/ElectronicsIdGPU.h b/EventFilter/EcalRawToDigi/interface/ElectronicsIdGPU.h new file mode 100644 index 0000000000000..abedcf5a2d479 --- /dev/null +++ b/EventFilter/EcalRawToDigi/interface/ElectronicsIdGPU.h @@ -0,0 +1,91 @@ +#ifndef EventFilter_EcalRawToDigi_interface_ElectronicsIdGPU_h +#define EventFilter_EcalRawToDigi_interface_ElectronicsIdGPU_h + +#include + +#include "DataFormats/EcalDetId/interface/EcalSubdetector.h" + +namespace ecal { + namespace raw { + + /** \brief Ecal readout channel identification + [32:20] Unused (so far) + [19:13] DCC id + [12:6] tower + [5:3] strip + [2:0] xtal + Index starts from 1 + */ + + class ElectronicsIdGPU { + public: + /** Default constructor -- invalid value */ + constexpr ElectronicsIdGPU() : id_{0xFFFFFFFFu} {} + /** from raw */ + constexpr ElectronicsIdGPU(uint32_t id) : id_{id} {} + /** Constructor from dcc,tower,channel **/ + constexpr ElectronicsIdGPU(uint8_t const dccid, uint8_t const towerid, uint8_t const stripid, uint8_t const xtalid) + : id_{static_cast((xtalid & 0x7) | ((stripid & 0x7) << 3) | ((towerid & 0x7F) << 6) | + ((dccid & 0x7F) << 13))} {} + + constexpr uint32_t operator()() { return id_; } + constexpr uint32_t rawId() const { return id_; } + + /// get the DCC (Ecal Local DCC value not global one) id + constexpr uint8_t dccId() const { return (id_ >> 13) & 0x7F; } + /// get the tower id + constexpr uint8_t towerId() const { return (id_ >> 6) & 0x7F; } + /// get the tower id + constexpr uint8_t stripId() const { return (id_ >> 3) & 0x7; } + /// get the channel id + constexpr uint8_t xtalId() const { return (id_ & 0x7); } + + /// get the subdet + //EcalSubdetector subdet() const; + + /// get a fast, compact, unique index for linear lookups (maximum value = 4194303) + constexpr uint32_t linearIndex() const { return id_ & 0x3FFFFF; } + + /// so far for EndCap only : + //int channelId() const; // xtal id between 1 and 25 + + static constexpr int kTowersInPhi = 4; // see EBDetId + static constexpr int kCrystalsInPhi = 20; // see EBDetId + + static constexpr uint8_t MAX_DCCID = 54; //To be updated with correct and final number + static constexpr uint8_t MIN_DCCID = 1; + static constexpr uint8_t MAX_TOWERID = 70; + static constexpr uint8_t MIN_TOWERID = 1; + static constexpr uint8_t MAX_STRIPID = 5; + static constexpr uint8_t MIN_STRIPID = 1; + static constexpr uint8_t MAX_CHANNELID = 25; + static constexpr uint8_t MIN_CHANNELID = 1; + static constexpr uint8_t MAX_XTALID = 5; + static constexpr uint8_t MIN_XTALID = 1; + + static constexpr int MIN_DCCID_EEM = 1; + static constexpr int MAX_DCCID_EEM = 9; + static constexpr int MIN_DCCID_EBM = 10; + static constexpr int MAX_DCCID_EBM = 27; + static constexpr int MIN_DCCID_EBP = 28; + static constexpr int MAX_DCCID_EBP = 45; + static constexpr int MIN_DCCID_EEP = 46; + static constexpr int MAX_DCCID_EEP = 54; + + static constexpr int DCCID_PHI0_EBM = 10; + static constexpr int DCCID_PHI0_EBP = 28; + + static constexpr int kDCCChannelBoundary = 17; + static constexpr int DCC_EBM = 10; // id of the DCC in EB- which contains phi=0 deg. + static constexpr int DCC_EBP = 28; // id of the DCC in EB+ which contains phi=0 deg. + static constexpr int DCC_EEM = 1; // id of the DCC in EE- which contains phi=0 deg. + static constexpr int DCC_EEP = 46; // id of the DCC in EE+ which contains phi=0 deg. + + private: + uint32_t id_; + }; + + } // namespace raw +} // namespace ecal + +#endif // EventFilter_EcalRawToDigi_interface_ElectronicsIdGPU_h diff --git a/EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h b/EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h new file mode 100644 index 0000000000000..004821afe3ed8 --- /dev/null +++ b/EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h @@ -0,0 +1,47 @@ +#ifndef EventFilter_EcalRawToDigi_interface_ElectronicsMappingGPU_h +#define EventFilter_EcalRawToDigi_interface_ElectronicsMappingGPU_h + +#include "CondFormats/EcalObjects/interface/EcalMappingElectronics.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +namespace ecal { + namespace raw { + + class ElectronicsMappingGPU { + public: + struct Product { + ~Product(); + uint32_t* eid2did; + }; + +#ifndef __CUDACC__ + + // rearrange pedestals + ElectronicsMappingGPU(EcalMappingElectronics const&); + + // will call dealloation for Product thru ~Product + ~ElectronicsMappingGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalElectronicsMappingGPU"}; } + + private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector> eid2did_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ + }; + + } // namespace raw +} // namespace ecal + +#endif // EventFilter_EcalRawToDigi_interface_ElectronicsMappingGPU_h diff --git a/EventFilter/EcalRawToDigi/plugins/BuildFile.xml b/EventFilter/EcalRawToDigi/plugins/BuildFile.xml index e55f1bcaab660..c3c2bd988e2c3 100644 --- a/EventFilter/EcalRawToDigi/plugins/BuildFile.xml +++ b/EventFilter/EcalRawToDigi/plugins/BuildFile.xml @@ -1,10 +1,14 @@ - + + - + - + + + + diff --git a/EventFilter/EcalRawToDigi/plugins/DeclsForKernels.h b/EventFilter/EcalRawToDigi/plugins/DeclsForKernels.h new file mode 100644 index 0000000000000..a6429121adc82 --- /dev/null +++ b/EventFilter/EcalRawToDigi/plugins/DeclsForKernels.h @@ -0,0 +1,70 @@ +#ifndef EventFilter_EcalRawToDigi_plugins_DeclsForKernels_h +#define EventFilter_EcalRawToDigi_plugins_DeclsForKernels_h + +#include + +#include "CUDADataFormats/EcalDigi/interface/DigisCollection.h" +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" +#include "EventFilter/EcalRawToDigi/interface/DCCRawDataDefinitions.h" +#include "EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +namespace ecal { + namespace raw { + + constexpr auto empty_event_size = EMPTYEVENTSIZE; + constexpr uint32_t nfeds_max = 54; + constexpr uint32_t nbytes_per_fed_max = 10 * 1024; + + struct InputDataCPU { + cms::cuda::host::unique_ptr data; + cms::cuda::host::unique_ptr offsets; + cms::cuda::host::unique_ptr feds; + }; + + struct ConfigurationParameters { + uint32_t maxChannelsEE; + uint32_t maxChannelsEB; + }; + + struct OutputDataCPU { + // [0] - eb, [1] - ee + cms::cuda::host::unique_ptr nchannels; + }; + + struct OutputDataGPU { + DigisCollection<::calo::common::DevStoragePolicy> digisEB, digisEE; + + void allocate(ConfigurationParameters const &config, cudaStream_t cudaStream) { + digisEB.data = + cms::cuda::make_device_unique(config.maxChannelsEB * EcalDataFrame::MAXSAMPLES, cudaStream); + digisEE.data = + cms::cuda::make_device_unique(config.maxChannelsEE * EcalDataFrame::MAXSAMPLES, cudaStream); + digisEB.ids = cms::cuda::make_device_unique(config.maxChannelsEB, cudaStream); + digisEE.ids = cms::cuda::make_device_unique(config.maxChannelsEE, cudaStream); + } + }; + + struct ScratchDataGPU { + // [0] = EB + // [1] = EE + cms::cuda::device::unique_ptr pChannelsCounter; + }; + + struct InputDataGPU { + cms::cuda::device::unique_ptr data; + cms::cuda::device::unique_ptr offsets; + cms::cuda::device::unique_ptr feds; + }; + + struct ConditionsProducts { + ElectronicsMappingGPU::Product const &eMappingProduct; + }; + + } // namespace raw +} // namespace ecal + +#endif // EventFilter_EcalRawToDigi_plugins_DeclsForKernels_h diff --git a/EventFilter/EcalRawToDigi/plugins/EcalCPUDigisProducer.cc b/EventFilter/EcalRawToDigi/plugins/EcalCPUDigisProducer.cc new file mode 100644 index 0000000000000..5563dd5b52cc8 --- /dev/null +++ b/EventFilter/EcalRawToDigi/plugins/EcalCPUDigisProducer.cc @@ -0,0 +1,196 @@ +#include + +#include "CUDADataFormats/EcalDigi/interface/DigisCollection.h" +#include "CondFormats/DataRecord/interface/EcalMappingElectronicsRcd.h" +#include "DataFormats/EcalDetId/interface/EcalDetIdCollections.h" +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" +#include "EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "DeclsForKernels.h" +#include "UnpackGPU.h" + +class EcalCPUDigisProducer : public edm::stream::EDProducer { +public: + explicit EcalCPUDigisProducer(edm::ParameterSet const& ps); + ~EcalCPUDigisProducer() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + // input digi collections in GPU-friendly format + using InputProduct = cms::cuda::Product>; + edm::EDGetTokenT digisInEBToken_; + edm::EDGetTokenT digisInEEToken_; + + // output digi collections in legacy format + edm::EDPutTokenT digisOutEBToken_; + edm::EDPutTokenT digisOutEEToken_; + + // whether to produce dummy integrity collections + bool produceDummyIntegrityCollections_; + + // dummy SRP collections + edm::EDPutTokenT ebSrFlagToken_; + edm::EDPutTokenT eeSrFlagToken_; + + // dummy integrity for xtal data + edm::EDPutTokenT ebIntegrityGainErrorsToken_; + edm::EDPutTokenT ebIntegrityGainSwitchErrorsToken_; + edm::EDPutTokenT ebIntegrityChIdErrorsToken_; + + // dummy integrity for xtal data - EE specific (to be rivisited towards EB+EE common collection) + edm::EDPutTokenT eeIntegrityGainErrorsToken_; + edm::EDPutTokenT eeIntegrityGainSwitchErrorsToken_; + edm::EDPutTokenT eeIntegrityChIdErrorsToken_; + + // dummy integrity errors + edm::EDPutTokenT integrityTTIdErrorsToken_; + edm::EDPutTokenT integrityBlockSizeErrorsToken_; + + // FIXME better way to pass pointers from acquire to produce? + std::vector> idsebtmp, idseetmp; + std::vector> dataebtmp, dataeetmp; +}; + +void EcalCPUDigisProducer::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("digisInLabelEB", edm::InputTag{"ecalRawToDigiGPU", "ebDigis"}); + desc.add("digisInLabelEE", edm::InputTag{"ecalRawToDigiGPU", "eeDigis"}); + desc.add("digisOutLabelEB", "ebDigis"); + desc.add("digisOutLabelEE", "eeDigis"); + + desc.add("produceDummyIntegrityCollections", false); + + std::string label = "ecalCPUDigisProducer"; + confDesc.add(label, desc); +} + +EcalCPUDigisProducer::EcalCPUDigisProducer(const edm::ParameterSet& ps) + : // input digi collections in GPU-friendly format + digisInEBToken_{consumes(ps.getParameter("digisInLabelEB"))}, + digisInEEToken_{consumes(ps.getParameter("digisInLabelEE"))}, + // output digi collections in legacy format + digisOutEBToken_{produces(ps.getParameter("digisOutLabelEB"))}, + digisOutEEToken_{produces(ps.getParameter("digisOutLabelEE"))}, + // whether to produce dummy integrity collections + produceDummyIntegrityCollections_{ps.getParameter("produceDummyIntegrityCollections")}, + // dummy SRP collections + ebSrFlagToken_{produceDummyIntegrityCollections_ ? produces() + : edm::EDPutTokenT{}}, + eeSrFlagToken_{produceDummyIntegrityCollections_ ? produces() + : edm::EDPutTokenT{}}, + // dummy integrity for xtal data + ebIntegrityGainErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityGainErrors") + : edm::EDPutTokenT{}}, + ebIntegrityGainSwitchErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityGainSwitchErrors") + : edm::EDPutTokenT{}}, + ebIntegrityChIdErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityChIdErrors") + : edm::EDPutTokenT{}}, + // dummy integrity for xtal data - EE specific (to be rivisited towards EB+EE common collection) + eeIntegrityGainErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityGainErrors") + : edm::EDPutTokenT{}}, + eeIntegrityGainSwitchErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityGainSwitchErrors") + : edm::EDPutTokenT{}}, + eeIntegrityChIdErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityChIdErrors") + : edm::EDPutTokenT{}}, + // dummy integrity errors + integrityTTIdErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityTTIdErrors") + : edm::EDPutTokenT{}}, + integrityBlockSizeErrorsToken_{produceDummyIntegrityCollections_ + ? produces("EcalIntegrityBlockSizeErrors") + : edm::EDPutTokenT{}} {} + +EcalCPUDigisProducer::~EcalCPUDigisProducer() {} + +void EcalCPUDigisProducer::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder taskHolder) { + // retrieve data/ctx + auto const& ebdigisProduct = event.get(digisInEBToken_); + auto const& eedigisProduct = event.get(digisInEEToken_); + cms::cuda::ScopedContextAcquire ctx{ebdigisProduct, std::move(taskHolder)}; + auto const& ebdigis = ctx.get(ebdigisProduct); + auto const& eedigis = ctx.get(eedigisProduct); + + // resize tmp buffers + dataebtmp.resize(ebdigis.size * EcalDataFrame::MAXSAMPLES); + dataeetmp.resize(eedigis.size * EcalDataFrame::MAXSAMPLES); + idsebtmp.resize(ebdigis.size); + idseetmp.resize(eedigis.size); + + // enqeue transfers + cudaCheck(cudaMemcpyAsync( + dataebtmp.data(), ebdigis.data.get(), dataebtmp.size() * sizeof(uint16_t), cudaMemcpyDeviceToHost, ctx.stream())); + cudaCheck(cudaMemcpyAsync( + dataeetmp.data(), eedigis.data.get(), dataeetmp.size() * sizeof(uint16_t), cudaMemcpyDeviceToHost, ctx.stream())); + cudaCheck(cudaMemcpyAsync( + idsebtmp.data(), ebdigis.ids.get(), idsebtmp.size() * sizeof(uint32_t), cudaMemcpyDeviceToHost, ctx.stream())); + cudaCheck(cudaMemcpyAsync( + idseetmp.data(), eedigis.ids.get(), idseetmp.size() * sizeof(uint32_t), cudaMemcpyDeviceToHost, ctx.stream())); +} + +void EcalCPUDigisProducer::produce(edm::Event& event, edm::EventSetup const& setup) { + // output collections + auto digisEB = std::make_unique(); + auto digisEE = std::make_unique(); + digisEB->resize(idsebtmp.size()); + digisEE->resize(idseetmp.size()); + + // cast constness away + // use pointers to buffers instead of move operator= semantics + // cause we have different allocators in there... + auto* dataEB = const_cast(digisEB->data().data()); + auto* dataEE = const_cast(digisEE->data().data()); + auto* idsEB = const_cast(digisEB->ids().data()); + auto* idsEE = const_cast(digisEE->ids().data()); + + // copy data + std::memcpy(dataEB, dataebtmp.data(), dataebtmp.size() * sizeof(uint16_t)); + std::memcpy(dataEE, dataeetmp.data(), dataeetmp.size() * sizeof(uint16_t)); + std::memcpy(idsEB, idsebtmp.data(), idsebtmp.size() * sizeof(uint32_t)); + std::memcpy(idsEE, idseetmp.data(), idseetmp.size() * sizeof(uint32_t)); + + event.put(digisOutEBToken_, std::move(digisEB)); + event.put(digisOutEEToken_, std::move(digisEE)); + + if (produceDummyIntegrityCollections_) { + // dummy SRP collections + event.emplace(ebSrFlagToken_); + event.emplace(eeSrFlagToken_); + // dummy integrity for xtal data + event.emplace(ebIntegrityGainErrorsToken_); + event.emplace(ebIntegrityGainSwitchErrorsToken_); + event.emplace(ebIntegrityChIdErrorsToken_); + // dummy integrity for xtal data - EE specific (to be rivisited towards EB+EE common collection) + event.emplace(eeIntegrityGainErrorsToken_); + event.emplace(eeIntegrityGainSwitchErrorsToken_); + event.emplace(eeIntegrityChIdErrorsToken_); + // dummy integrity errors + event.emplace(integrityTTIdErrorsToken_); + event.emplace(integrityBlockSizeErrorsToken_); + } +} + +DEFINE_FWK_MODULE(EcalCPUDigisProducer); diff --git a/EventFilter/EcalRawToDigi/plugins/EcalRawESProducersGPUDefs.cc b/EventFilter/EcalRawToDigi/plugins/EcalRawESProducersGPUDefs.cc new file mode 100644 index 0000000000000..84fcc7b2b2952 --- /dev/null +++ b/EventFilter/EcalRawToDigi/plugins/EcalRawESProducersGPUDefs.cc @@ -0,0 +1,9 @@ +#include "CondFormats/DataRecord/interface/EcalMappingElectronicsRcd.h" +#include "EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "HeterogeneousCore/CUDACore/interface/ConvertingESProducerT.h" + +using EcalElectronicsMappingGPUESProducer = + ConvertingESProducerT; + +DEFINE_FWK_EVENTSETUP_MODULE(EcalElectronicsMappingGPUESProducer); diff --git a/EventFilter/EcalRawToDigi/plugins/EcalRawToDigiGPU.cc b/EventFilter/EcalRawToDigi/plugins/EcalRawToDigiGPU.cc new file mode 100644 index 0000000000000..4f0743c9b1b51 --- /dev/null +++ b/EventFilter/EcalRawToDigi/plugins/EcalRawToDigiGPU.cc @@ -0,0 +1,155 @@ +#include "CUDADataFormats/EcalDigi/interface/DigisCollection.h" +#include "CondFormats/DataRecord/interface/EcalMappingElectronicsRcd.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" +#include "EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/ESGetToken.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "DeclsForKernels.h" +#include "UnpackGPU.h" + +class EcalRawToDigiGPU : public edm::stream::EDProducer { +public: + explicit EcalRawToDigiGPU(edm::ParameterSet const& ps); + ~EcalRawToDigiGPU() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + edm::EDGetTokenT rawDataToken_; + using OutputProduct = cms::cuda::Product>; + edm::EDPutTokenT digisEBToken_, digisEEToken_; + edm::ESGetToken eMappingToken_; + + cms::cuda::ContextState cudaState_; + + std::vector fedsToUnpack_; + + ecal::raw::ConfigurationParameters config_; + ecal::raw::OutputDataGPU outputGPU_; + ecal::raw::OutputDataCPU outputCPU_; +}; + +void EcalRawToDigiGPU::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("InputLabel", edm::InputTag("rawDataCollector")); + std::vector feds(54); + for (uint32_t i = 0; i < 54; ++i) + feds[i] = i + 601; + desc.add>("FEDs", feds); + desc.add("maxChannelsEB", 61200); + desc.add("maxChannelsEE", 14648); + desc.add("digisLabelEB", "ebDigis"); + desc.add("digisLabelEE", "eeDigis"); + + std::string label = "ecalRawToDigiGPU"; + confDesc.add(label, desc); +} + +EcalRawToDigiGPU::EcalRawToDigiGPU(const edm::ParameterSet& ps) + : rawDataToken_{consumes(ps.getParameter("InputLabel"))}, + digisEBToken_{produces(ps.getParameter("digisLabelEB"))}, + digisEEToken_{produces(ps.getParameter("digisLabelEE"))}, + eMappingToken_{esConsumes()}, + fedsToUnpack_{ps.getParameter>("FEDs")} { + config_.maxChannelsEB = ps.getParameter("maxChannelsEB"); + config_.maxChannelsEE = ps.getParameter("maxChannelsEE"); +} + +EcalRawToDigiGPU::~EcalRawToDigiGPU() {} + +void EcalRawToDigiGPU::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder holder) { + // raii + cms::cuda::ScopedContextAcquire ctx{event.streamID(), std::move(holder), cudaState_}; + + // conditions + edm::ESHandle eMappingHandle = setup.getHandle(eMappingToken_); + auto const& eMappingProduct = eMappingHandle->getProduct(ctx.stream()); + + // bundle up conditions + ecal::raw::ConditionsProducts conditions{eMappingProduct}; + + // event data + edm::Handle rawDataHandle; + event.getByToken(rawDataToken_, rawDataHandle); + + // scratch + ecal::raw::ScratchDataGPU scratchGPU = {cms::cuda::make_device_unique(2, ctx.stream())}; + + // input cpu data + ecal::raw::InputDataCPU inputCPU = { + cms::cuda::make_host_unique(ecal::raw::nfeds_max * ecal::raw::nbytes_per_fed_max, ctx.stream()), + cms::cuda::make_host_unique(ecal::raw::nfeds_max, ctx.stream()), + cms::cuda::make_host_unique(ecal::raw::nfeds_max, ctx.stream())}; + + // input data gpu + ecal::raw::InputDataGPU inputGPU = {cms::cuda::make_device_unique( + ecal::raw::nfeds_max * ecal::raw::nbytes_per_fed_max, ctx.stream()), + cms::cuda::make_device_unique(ecal::raw::nfeds_max, ctx.stream()), + cms::cuda::make_device_unique(ecal::raw::nfeds_max, ctx.stream())}; + + // output cpu + outputCPU_ = {cms::cuda::make_host_unique(2, ctx.stream())}; + + // output gpu + outputGPU_.allocate(config_, ctx.stream()); + + // iterate over feds + // TODO: another idea + // - loop over all feds to unpack and enqueue cuda memcpy + // - accumulate the sizes + // - after the loop launch cuda memcpy for sizes + // - enqueue the kernel + uint32_t currentCummOffset = 0; + uint32_t counter = 0; + for (auto const& fed : fedsToUnpack_) { + auto const& data = rawDataHandle->FEDData(fed); + auto const nbytes = data.size(); + + // skip empty feds + if (nbytes < ecal::raw::empty_event_size) + continue; + + // copy raw data into plain buffer + std::memcpy(inputCPU.data.get() + currentCummOffset, data.data(), nbytes); + // set the offset in bytes from the start + inputCPU.offsets[counter] = currentCummOffset; + inputCPU.feds[counter] = fed; + + // this is the current offset into the vector + currentCummOffset += nbytes; + ++counter; + } + + ecal::raw::entryPoint( + inputCPU, inputGPU, outputGPU_, scratchGPU, outputCPU_, conditions, ctx.stream(), counter, currentCummOffset); +} + +void EcalRawToDigiGPU::produce(edm::Event& event, edm::EventSetup const& setup) { + cms::cuda::ScopedContextProduce ctx{cudaState_}; + + // get the number of channels + outputGPU_.digisEB.size = outputCPU_.nchannels[0]; + outputGPU_.digisEE.size = outputCPU_.nchannels[1]; + + ctx.emplace(event, digisEBToken_, std::move(outputGPU_.digisEB)); + ctx.emplace(event, digisEEToken_, std::move(outputGPU_.digisEE)); + + // reset ptrs that are carried as members + outputCPU_.nchannels.reset(); +} + +DEFINE_FWK_MODULE(EcalRawToDigiGPU); diff --git a/EventFilter/EcalRawToDigi/plugins/UnpackGPU.cu b/EventFilter/EcalRawToDigi/plugins/UnpackGPU.cu new file mode 100644 index 0000000000000..a25bf235d15f6 --- /dev/null +++ b/EventFilter/EcalRawToDigi/plugins/UnpackGPU.cu @@ -0,0 +1,333 @@ +#include "EventFilter/EcalRawToDigi/interface/ElectronicsIdGPU.h" + +#include "UnpackGPU.h" + +namespace ecal { + namespace raw { + + __forceinline__ __device__ void print_raw_buffer(uint8_t const* const buffer, + uint32_t const nbytes, + uint32_t const nbytes_per_row = 20) { + for (uint32_t i = 0; i < nbytes; i++) { + if (i % nbytes_per_row == 0 && i > 0) + printf("\n"); + printf("%02X ", buffer[i]); + } + } + + __forceinline__ __device__ void print_first3bits(uint64_t const* buffer, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + uint8_t const b61 = (buffer[i] >> 61) & 0x1; + uint8_t const b62 = (buffer[i] >> 62) & 0x1; + uint8_t const b63 = (buffer[i] >> 63) & 0x1; + printf("[word: %u] %u%u%u\n", i, b63, b62, b61); + } + } + + __forceinline__ __device__ bool is_barrel(uint8_t dccid) { + return dccid >= ElectronicsIdGPU::MIN_DCCID_EBM && dccid <= ElectronicsIdGPU::MAX_DCCID_EBP; + } + + __forceinline__ __device__ uint8_t fed2dcc(int fed) { return static_cast(fed - 600); } + + __forceinline__ __device__ int zside_for_eb(ElectronicsIdGPU const& eid) { + int dcc = eid.dccId(); + return ((dcc >= ElectronicsIdGPU::MIN_DCCID_EBM && dcc <= ElectronicsIdGPU::MAX_DCCID_EBM)) ? -1 : 1; + } + + __forceinline__ __device__ bool is_synced_towerblock(uint16_t const dccbx, + uint16_t const bx, + uint16_t const dccl1, + uint16_t const l1) { + bool const bxsync = (bx == 0 && dccbx == 3564) || (bx == dccbx && dccbx != 3564); + bool const l1sync = (l1 == ((dccl1 - 1) & 0xfff)); + return bxsync && l1sync; + } + + __forceinline__ __device__ bool right_tower_for_eb(int tower) { + // for EB, two types of tower (LVRB top/bottom) + if ((tower > 12 && tower < 21) || (tower > 28 && tower < 37) || (tower > 44 && tower < 53) || + (tower > 60 && tower < 69)) + return true; + else + return false; + } + + __forceinline__ __device__ uint32_t compute_ebdetid(ElectronicsIdGPU const& eid) { + // as in Geometry/EcalMaping/.../EcalElectronicsMapping + auto const dcc = eid.dccId(); + auto const tower = eid.towerId(); + auto const strip = eid.stripId(); + auto const xtal = eid.xtalId(); + + int smid = 0; + int iphi = 0; + bool EBPlus = (zside_for_eb(eid) > 0); + bool EBMinus = !EBPlus; + + if (zside_for_eb(eid) < 0) { + smid = dcc + 19 - ElectronicsIdGPU::DCCID_PHI0_EBM; + iphi = (smid - 19) * ElectronicsIdGPU::kCrystalsInPhi; + iphi += 5 * ((tower - 1) % ElectronicsIdGPU::kTowersInPhi); + } else { + smid = dcc + 1 - ElectronicsIdGPU::DCCID_PHI0_EBP; + iphi = (smid - 1) * ElectronicsIdGPU::kCrystalsInPhi; + iphi += 5 * (ElectronicsIdGPU::kTowersInPhi - ((tower - 1) % ElectronicsIdGPU::kTowersInPhi) - 1); + } + + bool RightTower = right_tower_for_eb(tower); + int ieta = 5 * ((tower - 1) / ElectronicsIdGPU::kTowersInPhi) + 1; + if (RightTower) { + ieta += (strip - 1); + if (strip % 2 == 1) { + if (EBMinus) + iphi += (xtal - 1) + 1; + else + iphi += (4 - (xtal - 1)) + 1; + } else { + if (EBMinus) + iphi += (4 - (xtal - 1)) + 1; + else + iphi += (xtal - 1) + 1; + } + } else { + ieta += 4 - (strip - 1); + if (strip % 2 == 1) { + if (EBMinus) + iphi += (4 - (xtal - 1)) + 1; + else + iphi += (xtal - 1) + 1; + } else { + if (EBMinus) + iphi += (xtal - 1) + 1; + else + iphi += (4 - (xtal - 1)) + 1; + } + } + + if (zside_for_eb(eid) < 0) + ieta = -ieta; + + DetId did{DetId::Ecal, EcalBarrel}; + return did.rawId() | ((ieta > 0) ? (0x10000 | (ieta << 9)) : ((-ieta) << 9)) | (iphi & 0x1FF); + } + + __forceinline__ __device__ int adc(uint16_t sample) { return sample & 0xfff; } + + __forceinline__ __device__ int gainId(uint16_t sample) { return (sample >> 12) & 0x3; } + + template + __global__ void kernel_unpack_test(unsigned char const* __restrict__ data, + uint32_t const* __restrict__ offsets, + int const* __restrict__ feds, + uint16_t* samplesEB, + uint16_t* samplesEE, + uint32_t* idsEB, + uint32_t* idsEE, + uint32_t* pChannelsCounterEBEE, + uint32_t const* eid2did, + uint32_t const nbytesTotal) { + // indices + auto const ifed = blockIdx.x; + + // offset in bytes + auto const offset = offsets[ifed]; + // fed id + auto const fed = feds[ifed]; + auto const isBarrel = is_barrel(static_cast(fed - 600)); + // size + auto const size = ifed == gridDim.x - 1 ? nbytesTotal - offset : offsets[ifed + 1] - offset; + auto* samples = isBarrel ? samplesEB : samplesEE; + auto* ids = isBarrel ? idsEB : idsEE; + auto* pChannelsCounter = isBarrel ? &pChannelsCounterEBEE[0] : &pChannelsCounterEBEE[1]; + + // offset to the right raw buffer + uint64_t const* buffer = reinterpret_cast(data + offset); + + // dump first 3 bits for each 64-bit word + //print_first3bits(buffer, size / 8); + + // + // fed header + // + auto const fed_header = buffer[0]; + uint32_t bx = (fed_header >> 20) & 0xfff; + uint32_t lv1 = (fed_header >> 32) & 0xffffff; + + // 9 for fed + dcc header + // 36 for 4 EE TCC blocks or 18 for 1 EB TCC block + // 6 for SR block size + + // dcc header w2 + auto const w2 = buffer[2]; + uint8_t const fov = (w2 >> 48) & 0xf; + + // + // print Tower block headers + // + uint8_t ntccblockwords = isBarrel ? 18 : 36; + auto const* tower_blocks_start = buffer + 9 + ntccblockwords + 6; + auto const* trailer = buffer + (size / 8 - 1); + auto const* current_tower_block = tower_blocks_start; + while (current_tower_block != trailer) { + auto const w = *current_tower_block; + uint8_t ttid = w & 0xff; + uint16_t bxlocal = (w >> 16) & 0xfff; + uint16_t lv1local = (w >> 32) & 0xfff; + uint16_t block_length = (w >> 48) & 0x1ff; + + uint16_t const dccbx = bx & 0xfff; + uint16_t const dccl1 = lv1 & 0xfff; + // fov>=1 is required to support simulated data for which bx==bxlocal==0 + if (fov >= 1 && !is_synced_towerblock(dccbx, bxlocal, dccl1, lv1local)) { + current_tower_block += block_length; + continue; + } + + // go through all the channels + // get the next channel coordinates + uint32_t nchannels = (block_length - 1) / 3; + + // 1 threads per channel in this block + for (uint32_t ich = 0; ich < nchannels; ich += NTHREADS) { + auto const i_to_access = ich + threadIdx.x; + // threads outside of the range -> leave the loop + if (i_to_access >= nchannels) + break; + + // inc the channel's counter and get the pos where to store + auto const wdata = current_tower_block[1 + i_to_access * 3]; + uint8_t const stripid = wdata & 0x7; + uint8_t const xtalid = (wdata >> 4) & 0x7; + ElectronicsIdGPU eid{fed2dcc(fed), ttid, stripid, xtalid}; + auto const didraw = isBarrel ? compute_ebdetid(eid) : eid2did[eid.linearIndex()]; + // FIXME: what kind of channels are these guys + if (didraw == 0) + continue; + + // get samples + uint16_t sampleValues[10]; + sampleValues[0] = (wdata >> 16) & 0x3fff; + sampleValues[1] = (wdata >> 32) & 0x3fff; + sampleValues[2] = (wdata >> 48) & 0x3fff; + auto const wdata1 = current_tower_block[2 + i_to_access * 3]; + sampleValues[3] = wdata1 & 0x3fff; + sampleValues[4] = (wdata1 >> 16) & 0x3fff; + sampleValues[5] = (wdata1 >> 32) & 0x3fff; + sampleValues[6] = (wdata1 >> 48) & 0x3fff; + auto const wdata2 = current_tower_block[3 + i_to_access * 3]; + sampleValues[7] = wdata2 & 0x3fff; + sampleValues[8] = (wdata2 >> 16) & 0x3fff; + sampleValues[9] = (wdata2 >> 32) & 0x3fff; + + // check gain + bool isSaturation = true; + short firstGainZeroSampID{-1}, firstGainZeroSampADC{-1}; + for (uint32_t si = 0; si < 10; si++) { + if (gainId(sampleValues[si]) == 0) { + firstGainZeroSampID = si; + firstGainZeroSampADC = adc(sampleValues[si]); + break; + } + } + if (firstGainZeroSampID != -1) { + unsigned int plateauEnd = std::min(10u, (unsigned int)(firstGainZeroSampID + 5)); + for (unsigned int s = firstGainZeroSampID; s < plateauEnd; s++) { + if (gainId(sampleValues[s]) == 0 && adc(sampleValues[s]) == firstGainZeroSampADC) { + ; + } else { + isSaturation = false; + break; + } //it's not saturation + } + // get rid of channels which are stuck in gain0 + if (firstGainZeroSampID < 3) { + isSaturation = false; + } + if (!isSaturation) + continue; + } else { // there is no zero gainId sample + // gain switch check + short numGain = 1; + bool gainSwitchError = false; + for (unsigned int si = 1; si < 10; si++) { + if ((gainId(sampleValues[si - 1]) > gainId(sampleValues[si])) && numGain < 5) + gainSwitchError = true; + if (gainId(sampleValues[si - 1]) == gainId(sampleValues[si])) + numGain++; + else + numGain = 1; + } + if (gainSwitchError) + continue; + } + + auto const pos = atomicAdd(pChannelsCounter, 1); + + // store to global + ids[pos] = didraw; + samples[pos * 10] = sampleValues[0]; + samples[pos * 10 + 1] = sampleValues[1]; + samples[pos * 10 + 2] = sampleValues[2]; + samples[pos * 10 + 3] = sampleValues[3]; + samples[pos * 10 + 4] = sampleValues[4]; + samples[pos * 10 + 5] = sampleValues[5]; + samples[pos * 10 + 6] = sampleValues[6]; + samples[pos * 10 + 7] = sampleValues[7]; + samples[pos * 10 + 8] = sampleValues[8]; + samples[pos * 10 + 9] = sampleValues[9]; + } + + current_tower_block += block_length; + } + } + + void entryPoint(InputDataCPU const& inputCPU, + InputDataGPU& inputGPU, + OutputDataGPU& outputGPU, + ScratchDataGPU& scratchGPU, + OutputDataCPU& outputCPU, + ConditionsProducts const& conditions, + cudaStream_t cudaStream, + uint32_t const nfedsWithData, + uint32_t const nbytesTotal) { + // transfer + cudaCheck(cudaMemcpyAsync(inputGPU.data.get(), + inputCPU.data.get(), + nbytesTotal * sizeof(unsigned char), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(inputGPU.offsets.get(), + inputCPU.offsets.get(), + nfedsWithData * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemsetAsync(scratchGPU.pChannelsCounter.get(), + 0, + sizeof(uint32_t) * 2, // EB + EE + cudaStream)); + cudaCheck(cudaMemcpyAsync( + inputGPU.feds.get(), inputCPU.feds.get(), nfedsWithData * sizeof(int), cudaMemcpyHostToDevice, cudaStream)); + + kernel_unpack_test<32><<>>(inputGPU.data.get(), + inputGPU.offsets.get(), + inputGPU.feds.get(), + outputGPU.digisEB.data.get(), + outputGPU.digisEE.data.get(), + outputGPU.digisEB.ids.get(), + outputGPU.digisEE.ids.get(), + scratchGPU.pChannelsCounter.get(), + conditions.eMappingProduct.eid2did, + nbytesTotal); + cudaCheck(cudaGetLastError()); + + // transfer the counters for how many eb and ee channels we got + cudaCheck(cudaMemcpyAsync(outputCPU.nchannels.get(), + scratchGPU.pChannelsCounter.get(), + sizeof(uint32_t) * 2, + cudaMemcpyDeviceToHost, + cudaStream)); + } + + } // namespace raw +} // namespace ecal diff --git a/EventFilter/EcalRawToDigi/plugins/UnpackGPU.h b/EventFilter/EcalRawToDigi/plugins/UnpackGPU.h new file mode 100644 index 0000000000000..d98906e7e24a7 --- /dev/null +++ b/EventFilter/EcalRawToDigi/plugins/UnpackGPU.h @@ -0,0 +1,23 @@ +#ifndef EventFilter_EcalRawToDigi_plugins_UnpackGPU_h +#define EventFilter_EcalRawToDigi_plugins_UnpackGPU_h + +#include "DeclsForKernels.h" + +namespace ecal { + namespace raw { + + // FIXME: bundle up uint32_t values + void entryPoint(InputDataCPU const&, + InputDataGPU&, + OutputDataGPU&, + ScratchDataGPU&, + OutputDataCPU&, + ConditionsProducts const&, + cudaStream_t, + uint32_t const, + uint32_t const); + + } // namespace raw +} // namespace ecal + +#endif // EventFilter_EcalRawToDigi_plugins_UnpackGPU_h diff --git a/EventFilter/EcalRawToDigi/python/ecalDigis_cff.py b/EventFilter/EcalRawToDigi/python/ecalDigis_cff.py index 849aaeeb414a4..00a54ad56c128 100644 --- a/EventFilter/EcalRawToDigi/python/ecalDigis_cff.py +++ b/EventFilter/EcalRawToDigi/python/ecalDigis_cff.py @@ -5,3 +5,24 @@ ecalDigis = _ecalEBunpacker.clone() ecalDigisTask = cms.Task(ecalDigis) + +# process modifier to run on GPUs +from Configuration.ProcessModifiers.gpu_cff import gpu + +# GPU-friendly EventSetup modules +from EventFilter.EcalRawToDigi.ecalElectronicsMappingGPUESProducer_cfi import ecalElectronicsMappingGPUESProducer + +# raw to digi on GPUs +from EventFilter.EcalRawToDigi.ecalRawToDigiGPU_cfi import ecalRawToDigiGPU as _ecalRawToDigiGPU +ecalDigisGPU = _ecalRawToDigiGPU.clone() + +# copy the digi from the GPU to the CPU and convert to legacy format +from EventFilter.EcalRawToDigi.ecalCPUDigisProducer_cfi import ecalCPUDigisProducer as _ecalCPUDigisProducer +_ecalDigis_gpu = _ecalCPUDigisProducer.clone( + digisInLabelEB = ('ecalDigisGPU', 'ebDigis'), + digisInLabelEE = ('ecalDigisGPU', 'eeDigis'), + produceDummyIntegrityCollections = True +) +gpu.toReplaceWith(ecalDigis, _ecalDigis_gpu) + +gpu.toReplaceWith(ecalDigisTask, cms.Task(ecalElectronicsMappingGPUESProducer, ecalDigisGPU, ecalDigis)) diff --git a/EventFilter/EcalRawToDigi/src/ElectronicsMappingGPU.cc b/EventFilter/EcalRawToDigi/src/ElectronicsMappingGPU.cc new file mode 100644 index 0000000000000..8264c501a896c --- /dev/null +++ b/EventFilter/EcalRawToDigi/src/ElectronicsMappingGPU.cc @@ -0,0 +1,57 @@ +#include "EventFilter/EcalRawToDigi/interface/ElectronicsMappingGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "DataFormats/EcalDetId/interface/EcalElectronicsId.h" + +namespace ecal { + namespace raw { + + // TODO: 0x3FFFFF * 4B ~= 16MB + // tmp solution for linear mapping of eid -> did + ElectronicsMappingGPU::ElectronicsMappingGPU(EcalMappingElectronics const& mapping) : eid2did_(0x3FFFFF) { + // fill in eb + // TODO: EB vector is actually empty + auto const& barrelValues = mapping.barrelItems(); + for (unsigned int i = 0; i < barrelValues.size(); i++) { + EcalElectronicsId eid{barrelValues[i].electronicsid}; + EBDetId did{EBDetId::unhashIndex(i)}; + eid2did_[eid.linearIndex()] = did.rawId(); + } + + // fill in ee + auto const& endcapValues = mapping.endcapItems(); + for (unsigned int i = 0; i < endcapValues.size(); i++) { + EcalElectronicsId eid{endcapValues[i].electronicsid}; + EEDetId did{EEDetId::unhashIndex(i)}; + eid2did_[eid.linearIndex()] = did.rawId(); + } + } + + ElectronicsMappingGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(eid2did)); + } + + ElectronicsMappingGPU::Product const& ElectronicsMappingGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](ElectronicsMappingGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.eid2did, this->eid2did_.size() * sizeof(uint32_t))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.eid2did, + this->eid2did_.data(), + this->eid2did_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; + } + + } // namespace raw +} // namespace ecal + +TYPELOOKUP_DATA_REG(ecal::raw::ElectronicsMappingGPU); diff --git a/EventFilter/HcalRawToDigi/bin/BuildFile.xml b/EventFilter/HcalRawToDigi/bin/BuildFile.xml new file mode 100644 index 0000000000000..7a24968df89c8 --- /dev/null +++ b/EventFilter/HcalRawToDigi/bin/BuildFile.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/EventFilter/HcalRawToDigi/bin/makeHcalRaw2DigiGpuValidationPlots.cpp b/EventFilter/HcalRawToDigi/bin/makeHcalRaw2DigiGpuValidationPlots.cpp new file mode 100644 index 0000000000000..039c38dd9df16 --- /dev/null +++ b/EventFilter/HcalRawToDigi/bin/makeHcalRaw2DigiGpuValidationPlots.cpp @@ -0,0 +1,386 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "CUDADataFormats/HcalDigi/interface/DigiCollection.h" +#include "DataFormats/Common/interface/Wrapper.h" +#include "DataFormats/HcalDigi/interface/HcalDigiCollections.h" + +#define CREATE_HIST_1D(varname, nbins, first, last) auto varname = new TH1D(#varname, #varname, nbins, first, last) + +#define CREATE_HIST_2D(varname, nbins, first, last) \ + auto varname = new TH2D(#varname, #varname, nbins, first, last, nbins, first, last) + +QIE11DigiCollection filterQIE11(QIE11DigiCollection const& coll) { + QIE11DigiCollection out; + out.reserve(coll.size()); + + for (uint32_t i = 0; i < coll.size(); i++) { + auto const df = coll[i]; + auto const id = HcalDetId{df.id()}; + if (id.subdetId() != HcalEndcap) + continue; + + out.push_back(QIE11DataFrame{df}); + } + + return out; +} + +int main(int argc, char* argv[]) { + if (argc < 3) { + std::cout << "run with: ./ \n"; + exit(0); + } + + auto filterf01HE = [](QIE11DigiCollection const& coll) { + QIE11DigiCollection out{coll.samples(), coll.subdetId()}; + out.reserve(coll.size()); + + for (uint32_t i = 0; i < coll.size(); i++) { + auto const df = QIE11DataFrame{coll[i]}; + auto const id = HcalDetId{df.id()}; + if ((df.flavor() == 0 or df.flavor() == 1) and id.subdetId() == HcalEndcap) + out.push_back(df); + } + + return out; + }; + + auto filterf3HB = [](QIE11DigiCollection const& coll) { + QIE11DigiCollection out{coll.samples(), coll.subdetId()}; + out.reserve(coll.size()); + + for (uint32_t i = 0; i < coll.size(); i++) { + auto const df = QIE11DataFrame{coll[i]}; + auto const did = HcalDetId{df.id()}; + if (df.flavor() == 3 and did.subdetId() == HcalBarrel) + out.push_back(df); + } + + return out; + }; + + // branches to use + using Collectionf01 = + hcal::DigiCollection>; + using Collectionf5 = + hcal::DigiCollection>; + using Collectionf3 = + hcal::DigiCollection>; + edm::Wrapper* wgpuf01he = nullptr; + edm::Wrapper* wgpuf5hb = nullptr; + edm::Wrapper* wgpuf3hb = nullptr; + edm::Wrapper* wcpuf01he = nullptr; + edm::Wrapper* wcpuf5hb = nullptr; + + std::string inFileName{argv[1]}; + std::string outFileName{argv[2]}; + + // prep output + TFile rfout{outFileName.c_str(), "recreate"}; + + CREATE_HIST_1D(hADCf01HEGPU, 256, 0, 256); + CREATE_HIST_1D(hADCf01HECPU, 256, 0, 256); + CREATE_HIST_1D(hADCf5HBGPU, 128, 0, 128); + CREATE_HIST_1D(hADCf5HBCPU, 128, 0, 128); + CREATE_HIST_1D(hADCf3HBGPU, 256, 0, 256); + CREATE_HIST_1D(hADCf3HBCPU, 256, 0, 256); + CREATE_HIST_1D(hTDCf01HEGPU, 64, 0, 64); + CREATE_HIST_1D(hTDCf01HECPU, 64, 0, 64); + + CREATE_HIST_2D(hADCf01HEGPUvsCPU, 256, 0, 256); + CREATE_HIST_2D(hADCf3HBGPUvsCPU, 256, 0, 256); + CREATE_HIST_2D(hADCf5HBGPUvsCPU, 128, 0, 128); + CREATE_HIST_2D(hTDCf01HEGPUvsCPU, 64, 0, 64); + CREATE_HIST_2D(hTDCf3HBGPUvsCPU, 4, 0, 4); + + // prep input + TFile rfin{inFileName.c_str()}; + TTree* rt = (TTree*)rfin.Get("Events"); + rt->SetBranchAddress("QIE11DataFrameHcalDataFrameContainer_hcalDigis__RECO.", &wcpuf01he); + rt->SetBranchAddress("HBHEDataFramesSorted_hcalDigis__RECO.", &wcpuf5hb); + rt->SetBranchAddress( + "hcalFlavor5calocommonCUDAHostAllocatorAliascalocommonVecStoragePolicyhcalDigiCollection_hcalCPUDigisProducer_" + "f5HBDigis_RECO.", + &wgpuf5hb); + rt->SetBranchAddress( + "hcalFlavor1calocommonCUDAHostAllocatorAliascalocommonVecStoragePolicyhcalDigiCollection_hcalCPUDigisProducer_" + "f01HEDigis_RECO.", + &wgpuf01he); + rt->SetBranchAddress( + "hcalFlavor3calocommonCUDAHostAllocatorAliascalocommonVecStoragePolicyhcalDigiCollection_hcalCPUDigisProducer_" + "f3HBDigis_RECO.", + &wgpuf3hb); + + // accumulate + auto const nentries = rt->GetEntries(); + std::cout << ">>> nentries = " << nentries << std::endl; + for (int ie = 0; ie < nentries; ++ie) { + rt->GetEntry(ie); + + auto const& f01HEProduct = wgpuf01he->bareProduct(); + auto const& f5HBProduct = wgpuf5hb->bareProduct(); + auto const& f3HBProduct = wgpuf3hb->bareProduct(); + auto const& qie11Product = wcpuf01he->bareProduct(); + auto const qie11Filteredf01 = filterf01HE(qie11Product); + auto const qie11Filteredf3 = filterf3HB(qie11Product); + auto const& qie8Product = wcpuf5hb->bareProduct(); + + auto const ngpuf01he = f01HEProduct.ids.size(); + auto const ngpuf5hb = f5HBProduct.ids.size(); + auto const ngpuf3hb = f3HBProduct.ids.size(); + auto const ncpuf01he = qie11Filteredf01.size(); + auto const ncpuf5hb = qie8Product.size(); + auto const ncpuf3hb = qie11Filteredf3.size(); + + /* + printf("ngpuf01he = %u nqie11 = %u ncpuf01he = %u ngpuf5hb = %u ncpuf5hb = %u\n", + f01HEProduct.size(), qie11Product.size(), qie11Filtered.size(), + f5HBProduct.size(), + static_cast(qie8Product.size())); + */ + + if (ngpuf01he != ncpuf01he) { + std::cerr << "*** mismatch in number of flavor 01 digis for event " << ie << std::endl + << ">>> ngpuf01he = " << ngpuf01he << std::endl + << ">>> ncpuf01he = " << ncpuf01he << std::endl; + } + + { + auto const& idsgpu = f01HEProduct.ids; + auto const& datagpu = f01HEProduct.data; + + for (uint32_t ich = 0; ich < ncpuf01he; ich++) { + auto const cpudf = QIE11DataFrame{qie11Filteredf01[ich]}; + auto const cpuid = cpudf.id(); + auto iter2idgpu = std::find(idsgpu.begin(), idsgpu.end(), cpuid); + + if (iter2idgpu == idsgpu.end()) { + std::cerr << "missing " << HcalDetId{cpuid} << std::endl; + continue; + } + + // FIXME: cna fail... + assert(*iter2idgpu == cpuid); + + auto const ptrdiff = iter2idgpu - idsgpu.begin(); + auto const nsamples_gpu = hcal::compute_nsamples(f01HEProduct.stride); + auto const nsamples_cpu = qie11Filteredf01.samples(); + assert(static_cast(nsamples_cpu) == nsamples_gpu); + + uint32_t ichgpu = ptrdiff; + uint32_t offset = ichgpu * f01HEProduct.stride; + uint16_t const* df_start = datagpu.data() + offset; + for (uint32_t sample = 0u; sample < nsamples_gpu; sample++) { + auto const cpuadc = cpudf[sample].adc(); + auto const gpuadc = hcal::adc_for_sample(df_start, sample); + auto const cputdc = cpudf[sample].tdc(); + auto const gputdc = hcal::tdc_for_sample(df_start, sample); + auto const cpucapid = cpudf[sample].capid(); + auto const gpucapid = hcal::capid_for_sample(df_start, sample); + + hADCf01HEGPU->Fill(gpuadc); + hADCf01HECPU->Fill(cpuadc); + hTDCf01HEGPU->Fill(gputdc); + hTDCf01HECPU->Fill(cputdc); + hADCf01HEGPUvsCPU->Fill(cpuadc, gpuadc); + hTDCf01HEGPUvsCPU->Fill(cputdc, gputdc); + + // At RAW Decoding level there must not be any mistmatches + // in the adc values at all! + assert(static_cast(cpuadc) == gpuadc); + assert(static_cast(cputdc) == gputdc); + assert(static_cast(cpucapid) == gpucapid); + } + } + } + + if (ngpuf3hb != ncpuf3hb) { + std::cerr << "*** mismatch in number of flavor 3 digis for event " << ie << std::endl + << ">>> ngpuf01he = " << ngpuf3hb << std::endl + << ">>> ncpuf01he = " << ncpuf3hb << std::endl; + } + + { + auto const& idsgpu = f3HBProduct.ids; + auto const& datagpu = f3HBProduct.data; + + for (uint32_t ich = 0; ich < ncpuf3hb; ich++) { + auto const cpudf = QIE11DataFrame{qie11Filteredf3[ich]}; + auto const cpuid = cpudf.id(); + auto iter2idgpu = std::find(idsgpu.begin(), idsgpu.end(), cpuid); + + if (iter2idgpu == idsgpu.end()) { + std::cerr << "missing " << HcalDetId{cpuid} << std::endl; + continue; + } + + // FIXME: cna fail... + assert(*iter2idgpu == cpuid); + + auto const ptrdiff = iter2idgpu - idsgpu.begin(); + auto const nsamples_gpu = hcal::compute_nsamples(f3HBProduct.stride); + auto const nsamples_cpu = qie11Filteredf3.samples(); + assert(static_cast(nsamples_cpu) == nsamples_gpu); + + uint32_t ichgpu = ptrdiff; + uint32_t offset = ichgpu * f3HBProduct.stride; + uint16_t const* df_start = datagpu.data() + offset; + for (uint32_t sample = 0u; sample < nsamples_gpu; sample++) { + auto const cpuadc = cpudf[sample].adc(); + auto const gpuadc = hcal::adc_for_sample(df_start, sample); + auto const cputdc = cpudf[sample].tdc(); + auto const gputdc = hcal::tdc_for_sample(df_start, sample); + + hADCf3HBGPU->Fill(gpuadc); + hADCf3HBCPU->Fill(cpuadc); + hADCf3HBGPUvsCPU->Fill(cpuadc, gpuadc); + hTDCf3HBGPUvsCPU->Fill(cputdc, gputdc); + + // At RAW Decoding level there must not be any mistmatches + // in the adc values at all! + assert(static_cast(cpuadc) == gpuadc); + assert(static_cast(cputdc) == gputdc); + } + } + } + + if (ngpuf5hb != ncpuf5hb) { + std::cerr << "*** mismatch in number of flavor 5 digis for event " << ie << std::endl + << ">>> ngpuf5hb = " << ngpuf5hb << std::endl + << ">>> ncpuf5hb = " << ncpuf5hb << std::endl; + } + + { + auto const& idsgpu = f5HBProduct.ids; + auto const& datagpu = f5HBProduct.data; + for (uint32_t i = 0; i < ncpuf5hb; i++) { + auto const cpudf = qie8Product[i]; + auto const cpuid = cpudf.id().rawId(); + auto iter2idgpu = std::find(idsgpu.begin(), idsgpu.end(), cpuid); + if (iter2idgpu == idsgpu.end()) { + std::cerr << "missing " << HcalDetId{cpuid} << std::endl; + continue; + } + + assert(*iter2idgpu == cpuid); + + auto const ptrdiff = iter2idgpu - idsgpu.begin(); + auto const nsamples_gpu = hcal::compute_nsamples(f5HBProduct.stride); + auto const nsamples_cpu = qie8Product[0].size(); + assert(static_cast(nsamples_cpu) == nsamples_gpu); + + uint32_t offset = ptrdiff * f5HBProduct.stride; + uint16_t const* df_start = datagpu.data() + offset; + for (uint32_t sample = 0u; sample < nsamples_gpu; sample++) { + auto const cpuadc = cpudf.sample(sample).adc(); + auto const gpuadc = hcal::adc_for_sample(df_start, sample); + auto const cpucapid = cpudf.sample(sample).capid(); + auto const gpucapid = hcal::capid_for_sample(df_start, sample); + + hADCf5HBGPU->Fill(gpuadc); + hADCf5HBCPU->Fill(cpuadc); + hADCf5HBGPUvsCPU->Fill(cpuadc, gpuadc); + + // the must for us at RAW Decoding stage + assert(static_cast(cpuadc) == gpuadc); + assert(static_cast(cpucapid) == gpucapid); + } + } + } + } + + { + TCanvas c{"plots", "plots", 4200, 6200}; + c.Divide(3, 3); + c.cd(1); + { + gPad->SetLogy(); + hADCf01HECPU->SetLineColor(kBlack); + hADCf01HECPU->SetLineWidth(1.); + hADCf01HECPU->Draw(""); + hADCf01HEGPU->SetLineColor(kBlue); + hADCf01HEGPU->SetLineWidth(1.); + hADCf01HEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hADCf01HEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(2); + { + gPad->SetLogy(); + hADCf5HBCPU->SetLineColor(kBlack); + hADCf5HBCPU->SetLineWidth(1.); + hADCf5HBCPU->Draw(""); + hADCf5HBGPU->SetLineColor(kBlue); + hADCf5HBGPU->SetLineWidth(1.); + hADCf5HBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hADCf5HBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(3); + { + gPad->SetLogy(); + hADCf3HBCPU->SetLineColor(kBlack); + hADCf3HBCPU->SetLineWidth(1.); + hADCf3HBCPU->Draw(""); + hADCf3HBGPU->SetLineColor(kBlue); + hADCf3HBGPU->SetLineWidth(1.); + hADCf3HBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hADCf3HBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(4); + hADCf01HEGPUvsCPU->Draw("colz"); + c.cd(5); + hADCf5HBGPUvsCPU->Draw("colz"); + c.cd(6); + hADCf3HBGPUvsCPU->Draw("colz"); + c.cd(7); + { + gPad->SetLogy(); + hTDCf01HECPU->SetLineColor(kBlack); + hTDCf01HECPU->SetLineWidth(1.); + hTDCf01HECPU->Draw(""); + hTDCf01HEGPU->SetLineColor(kBlue); + hTDCf01HEGPU->SetLineWidth(1.); + hTDCf01HEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hTDCf01HEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(8); + hTDCf01HEGPUvsCPU->Draw("colz"); + c.cd(9); + hTDCf3HBGPUvsCPU->Draw("colz"); + + c.SaveAs("plots.pdf"); + } + + rfin.Close(); + rfout.Write(); + rfout.Close(); +} diff --git a/EventFilter/HcalRawToDigi/plugins/BuildFile.xml b/EventFilter/HcalRawToDigi/plugins/BuildFile.xml index ccf6a061119c2..3077a68a665e4 100644 --- a/EventFilter/HcalRawToDigi/plugins/BuildFile.xml +++ b/EventFilter/HcalRawToDigi/plugins/BuildFile.xml @@ -1,16 +1,27 @@ + + + + + - - - + - - - + + + + + + + + + + + diff --git a/EventFilter/HcalRawToDigi/plugins/DeclsForKernels.h b/EventFilter/HcalRawToDigi/plugins/DeclsForKernels.h new file mode 100644 index 0000000000000..9903b77efb341 --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/DeclsForKernels.h @@ -0,0 +1,86 @@ +#ifndef EventFilter_HcalRawToDigi_interface_DeclsForKernels_h +#define EventFilter_HcalRawToDigi_interface_DeclsForKernels_h + +#include + +#include "CUDADataFormats/HcalDigi/interface/DigiCollection.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +#include "ElectronicsMappingGPU.h" + +namespace hcal { + namespace raw { + + constexpr int32_t empty_event_size = 32; + constexpr uint32_t utca_nfeds_max = 50; + constexpr uint32_t nbytes_per_fed_max = 10 * 1024; + + // each collection corresponds to a particular flavor with a certain number of + // samples per digi + constexpr uint32_t numOutputCollections = 3; + constexpr uint8_t OutputF01HE = 0; + constexpr uint8_t OutputF5HB = 1; + constexpr uint8_t OutputF3HB = 2; + + struct ConfigurationParameters { + uint32_t maxChannelsF01HE; + uint32_t maxChannelsF5HB; + uint32_t maxChannelsF3HB; + uint32_t nsamplesF01HE; + uint32_t nsamplesF5HB; + uint32_t nsamplesF3HB; + }; + + struct InputDataCPU { + cms::cuda::host::unique_ptr data; + cms::cuda::host::unique_ptr offsets; + cms::cuda::host::unique_ptr feds; + }; + + struct OutputDataCPU { + cms::cuda::host::unique_ptr nchannels; + }; + + struct ScratchDataGPU { + // depends on the number of output collections + // that is a statically known predefined number + cms::cuda::device::unique_ptr pChannelsCounters; + }; + + struct OutputDataGPU { + DigiCollection digisF01HE; + DigiCollection digisF5HB; + DigiCollection digisF3HB; + + void allocate(ConfigurationParameters const &config, cudaStream_t cudaStream) { + digisF01HE.data = cms::cuda::make_device_unique( + config.maxChannelsF01HE * compute_stride(config.nsamplesF01HE), cudaStream); + digisF01HE.ids = cms::cuda::make_device_unique(config.maxChannelsF01HE, cudaStream); + + digisF5HB.data = cms::cuda::make_device_unique( + config.maxChannelsF5HB * compute_stride(config.nsamplesF5HB), cudaStream); + digisF5HB.ids = cms::cuda::make_device_unique(config.maxChannelsF5HB, cudaStream); + digisF5HB.npresamples = cms::cuda::make_device_unique(config.maxChannelsF5HB, cudaStream); + + digisF3HB.data = cms::cuda::make_device_unique( + config.maxChannelsF3HB * compute_stride(config.nsamplesF3HB), cudaStream); + digisF3HB.ids = cms::cuda::make_device_unique(config.maxChannelsF3HB, cudaStream); + } + }; + + struct InputDataGPU { + cms::cuda::device::unique_ptr data; + cms::cuda::device::unique_ptr offsets; + cms::cuda::device::unique_ptr feds; + }; + + struct ConditionsProducts { + ElectronicsMappingGPU::Product const &eMappingProduct; + }; + + } // namespace raw +} // namespace hcal + +#endif // EventFilter_HcalRawToDigi_interface_DeclsForKernels_h diff --git a/EventFilter/HcalRawToDigi/plugins/DecodeGPU.cu b/EventFilter/HcalRawToDigi/plugins/DecodeGPU.cu new file mode 100644 index 0000000000000..4f2ca85861b30 --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/DecodeGPU.cu @@ -0,0 +1,593 @@ +#include "DataFormats/HcalDetId/interface/HcalElectronicsId.h" +#include "DataFormats/HcalDetId/interface/HcalSubdetector.h" +#include "DataFormats/HcalDetId/interface/HcalDetId.h" + +#include "EventFilter/HcalRawToDigi/plugins/DecodeGPU.h" + +#include +using namespace cooperative_groups; + +namespace hcal { + namespace raw { + + __forceinline__ __device__ char const* get_subdet_str(DetId const& did) { + switch (did.subdetId()) { + case HcalEmpty: + return "HcalEmpty"; + break; + case HcalBarrel: + return "HcalBarrel"; + break; + case HcalEndcap: + return "HcalEndcap"; + break; + case HcalOuter: + return "HcalOuter"; + break; + case HcalForward: + return "HcalForward"; + break; + case HcalTriggerTower: + return "HcalTriggerTower"; + break; + case HcalOther: + return "HcalOther"; + break; + default: + return "Unknown"; + break; + } + + return "Unknown"; + } + + __forceinline__ __device__ bool is_channel_header_word(uint16_t const* ptr) { + uint8_t bit = (*ptr >> 15) & 0x1; + return bit == 1; + } + + template + constexpr bool is_power_of_two(T x) { + return (x != 0) && ((x & (x - 1)) == 0); + } + + template + __global__ void kernel_rawdecode_test(unsigned char const* data, + uint32_t const* offsets, + int const* feds, + uint32_t const* eid2did, + uint32_t const* eid2tid, + uint16_t* digisF01HE, + uint32_t* idsF01HE, + uint16_t* digisF5HB, + uint32_t* idsF5HB, + uint8_t* npresamplesF5HB, + uint16_t* digisF3HB, + uint32_t* idsF3HB, + uint32_t* pChannelsCounters, + uint32_t const nsamplesF01HE, + uint32_t const nsamplesF5HB, + uint32_t const nsamplesF3HB, + uint32_t const nBytesTotal) { + // in order to properly use cooperative groups + static_assert(is_power_of_two(NTHREADS) == true && NTHREADS <= 32); + + thread_block_tile thread_group = tiled_partition(this_thread_block()); + + auto const iamc = threadIdx.x / NTHREADS; + auto const ifed = blockIdx.x; + auto const offset = offsets[ifed]; + +#ifdef HCAL_RAWDECODE_GPUDEBUG_CG + if (ifed > 0 || iamc > 0) + return; + printf("threadIdx.x = %d rank = %d iamc = %d\n", threadIdx.x, thread_group.thread_rank(), iamc); +#endif + +#ifdef HCAL_RAWDECODE_GPUDEBUG + auto const fed = feds[ifed]; + auto const size = ifed == gridDim.x - 1 ? nBytesTotal - offset : offsets[ifed + 1] - offset; + printf("ifed = %d fed = %d offset = %u size = %u\n", ifed, fed, offset, size); +#endif + + // offset to the right raw buffer + uint64_t const* buffer = reinterpret_cast(data + offset); + +#ifdef HCAL_RAWDECODE_GPUDEBUG + // + // fed header + // + auto const fed_header = buffer[0]; + uint32_t const fed_id = (fed_header >> 8) & 0xfff; + uint32_t const bx = (fed_header >> 20) & 0xfff; + uint32_t const lv1 = (fed_header >> 32) & 0xffffff; + uint8_t const trigger_type = (fed_header >> 56) & 0xf; + uint8_t const bid_fed_header = (fed_header >> 60) & 0xf; + + printf("fed = %d fed_id = %u bx = %u lv1 = %u trigger_type = %u bid = %u\n", + fed, + fed_id, + bx, + lv1, + trigger_type, + bid_fed_header); +#endif + + // amc 13 header + auto const amc13word = buffer[1]; + uint8_t const namc = (amc13word >> 52) & 0xf; + if (iamc >= namc) + return; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + uint8_t const amc13version = (amc13word >> 60) & 0xf; + uint32_t const amc13OrbitNumber = (amc13word >> 4) & 0xffffffffu; + printf("fed = %d namc = %u amc13version = %u amc13OrbitNumber = %u\n", fed, namc, amc13version, amc13OrbitNumber); +#endif + + // compute the offset int to the right buffer + uint32_t amcoffset = 0; + for (uint8_t ii = 0u; ii < iamc; ii++) { + auto const word = buffer[2 + ii]; + int const amcSize = (word >> 32) & 0xffffff; + amcoffset += amcSize; + } + + auto const word = buffer[2 + iamc]; + int const amcSize = (word >> 32) & 0xffffff; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + uint16_t const amcid = word & 0xffff; + int const slot = (word >> 16) & 0xf; + int const amcBlockNumber = (word >> 20) & 0xff; + printf("fed = %d amcid = %u slot = %d amcBlockNumber = %d\n", fed, amcid, slot, amcBlockNumber); + + bool const amcmore = ((word >> 61) & 0x1) != 0; + bool const amcSegmented = ((word >> 60) & 0x1) != 0; + bool const amcLengthOk = ((word >> 62) & 0x1) != 0; + bool const amcCROk = ((word >> 56) & 0x1) != 0; + bool const amcDataPresent = ((word >> 58) & 0x1) != 0; + bool const amcDataValid = ((word >> 56) & 0x1) != 0; + bool const amcEnabled = ((word >> 59) & 0x1) != 0; + printf( + "fed = %d amcmore = %d amcSegmented = %d, amcLengthOk = %d amcCROk = %d\n>> amcDataPresent = %d amcDataValid " + "= %d amcEnabled = %d\n", + fed, + static_cast(amcmore), + static_cast(amcSegmented), + static_cast(amcLengthOk), + static_cast(amcCROk), + static_cast(amcDataPresent), + static_cast(amcDataValid), + static_cast(amcEnabled)); +#endif + + // get to the payload + auto const* payload64 = buffer + 2 + namc + amcoffset; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + // uhtr header v1 1st 64 bits + auto const payload64_w0 = payload64[0]; +#endif + // uhtr n bytes comes from amcSize, according to the cpu version! + uint32_t const data_length64 = amcSize; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + uint16_t bcn = (payload64_w0 >> 20) & 0xfff; + uint32_t evn = (payload64_w0 >> 32) & 0xffffff; + printf("fed = %d data_length64 = %u bcn = %u evn = %u\n", fed, data_length64, bcn, evn); +#endif + + // uhtr header v1 2nd 64 bits + auto const payload64_w1 = payload64[1]; + uint8_t const uhtrcrate = payload64_w1 & 0xff; + uint8_t const uhtrslot = (payload64_w1 >> 8) & 0xf; + uint8_t const presamples = (payload64_w1 >> 12) & 0xf; + uint8_t const payloadFormat = (payload64_w1 >> 44) & 0xf; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + uint16_t const orbitN = (payload64_w1 >> 16) & 0xffff; + uint8_t const firmFlavor = (payload64_w1 >> 32) & 0xff; + uint8_t const eventType = (payload64_w1 >> 40) & 0xf; + printf( + "fed = %d crate = %u slot = %u presamples = %u\n>>> orbitN = %u firmFlavor = %u eventType = %u payloadFormat " + "= %u\n", + fed, + uhtrcrate, + uhtrslot, + presamples, + orbitN, + firmFlavor, + eventType, + payloadFormat); +#endif + + // this should be filtering out uMNio... + if (payloadFormat != 1) + return; + + // skip uhtr header words + auto const channelDataSize = data_length64 - 2; // 2 uhtr header v1 words + auto const* channelDataBuffer64Start = payload64 + 2; // 2 uhtr header v2 wds + auto const* ptr = reinterpret_cast(channelDataBuffer64Start); + auto const* end = ptr + sizeof(uint64_t) / sizeof(uint16_t) * (channelDataSize - 1); + auto const t_rank = thread_group.thread_rank(); + + // iterate through the channel data + while (ptr != end) { + // this is the starting point for this thread group for this iteration + // with respect to this pointer every thread will move forward afterwards + auto const* const start_ptr = ptr; + +#ifdef HCAL_RAWDECODE_GPUDEBUG_CG + thread_group.sync(); +#endif + + // skip to the header word of the right channel for this thread + int counter = 0; + while (counter < thread_group.thread_rank()) { + // just a check for threads that land beyond the end + if (ptr == end) + break; + + // move ptr one forward past header + if (is_channel_header_word(ptr)) + ++ptr; + else { + // go to the next channel and do not consider this guy as a channel + while (ptr != end) + if (!is_channel_header_word(ptr)) + ++ptr; + else + break; + continue; + } + + // skip + while (ptr != end) + if (!is_channel_header_word(ptr)) + ++ptr; + else + break; + counter++; + } + +#ifdef HCAL_RAWDECODE_GPUDEBUG_CG + thread_group.sync(); + printf("ptr - start_ptr = %d counter = %d rank = %d\n", static_cast(ptr - start_ptr), counter, t_rank); +#endif + + // when the end is near, channels will land outside of the [start_ptr, end) region + if (ptr != end) { + // for all of the flavors, these 2 guys have the same bit layout + uint8_t const flavor = (ptr[0] >> 12) & 0x7; + uint8_t const channelid = ptr[0] & 0xff; + auto const* const new_channel_start = ptr; + + // flavor dependent stuff + switch (flavor) { + case 0: + case 1: { + // treat eid and did + uint8_t fiber = (channelid >> 3) & 0x1f; + uint8_t fchannel = channelid & 0x7; + HcalElectronicsId eid{uhtrcrate, uhtrslot, fiber, fchannel, false}; + auto const did = HcalDetId{eid2did[eid.linearIndex()]}; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("erawId = %u linearIndex = %u drawid = %u subdet = %s\n", + eid.rawId(), + eid.linearIndex(), + did.rawId(), + get_subdet_str(did)); + printf("flavor = %u crate = %u slot = %u channelid = %u fiber = %u fchannel = %u\n", + flavor, + uhtrcrate, + uhtrslot, + channelid, + fiber, + fchannel); +#endif + + // remove digis not for HE + if (did.subdetId() != HcalEndcap) + break; + + // count words + auto const* channel_header_word = ptr++; + while (!is_channel_header_word(ptr) && ptr != end) + ++ptr; + auto const* channel_end = ptr; // set ptr + uint32_t const nwords = channel_end - channel_header_word; + + // filter out this digi if nwords does not equal expected + auto const expected_words = compute_stride(nsamplesF01HE); + if (nwords != expected_words) + break; + + // inc the number of digis of this type + auto const pos = atomicAdd(&pChannelsCounters[OutputF01HE], 1); +#ifdef HCAL_RAWDECODE_GPUDEBUG_CG + printf("rank = %d pos = %d\n", thread_group.thread_rank(), pos); +#endif + + // store to global mem words for this digi + idsF01HE[pos] = did.rawId(); + + for (uint32_t iword = 0; iword < expected_words; iword++) + digisF01HE[pos * expected_words + iword] = channel_header_word[iword]; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("nwords = %u\n", nwords); +#endif + + break; + } + case 3: { + // treat eid and did + uint8_t fiber = (channelid >> 3) & 0x1f; + uint8_t fchannel = channelid & 0x7; + HcalElectronicsId eid{uhtrcrate, uhtrslot, fiber, fchannel, false}; + auto const did = HcalDetId{eid2did[eid.linearIndex()]}; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("erawId = %u linearIndex = %u drawid = %u subdet = %s\n", + eid.rawId(), + eid.linearIndex(), + did.rawId(), + get_subdet_str(did)); + printf("flavor = %u crate = %u slot = %u channelid = %u fiber = %u fchannel = %u\n", + flavor, + uhtrcrate, + uhtrslot, + channelid, + fiber, + fchannel); +#endif + + // remove digis not for HE + if (did.subdetId() != HcalBarrel) + break; + + // count words + auto const* channel_header_word = ptr++; + while (!is_channel_header_word(ptr) && ptr != end) + ++ptr; + auto const* channel_end = ptr; // set ptr + uint32_t const nwords = channel_end - channel_header_word; + + // filter out this digi if nwords does not equal expected + auto const expected_words = compute_stride(nsamplesF3HB); + if (nwords != expected_words) + break; + + // inc the number of digis of this type + auto const pos = atomicAdd(&pChannelsCounters[OutputF3HB], 1); + + // store to global mem words for this digi + idsF3HB[pos] = did.rawId(); + for (uint32_t iword = 0; iword < expected_words; iword++) + digisF3HB[pos * expected_words + iword] = channel_header_word[iword]; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("nwords = %u\n", nwords); +#endif + + break; + } + case 2: { + uint8_t fiber = (channelid >> 3) & 0x1f; + uint8_t fchannel = channelid & 0x7; + HcalElectronicsId eid{uhtrcrate, uhtrslot, fiber, fchannel, false}; + auto const did = DetId{eid2did[eid.linearIndex()]}; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("erawId = %u linearIndex = %u drawid = %u subdet = %s\n", + eid.rawId(), + eid.linearIndex(), + did.rawId(), + get_subdet_str(did)); + printf("flavor = %u crate = %u slot = %u channelid = %u fiber = %u fchannel = %u\n", + flavor, + uhtrcrate, + uhtrslot, + channelid, + fiber, + fchannel); +#endif + + break; + } + case 4: { + uint8_t link = (channelid >> 4) & 0xf; + uint8_t tower = channelid & 0xf; + HcalElectronicsId eid{uhtrcrate, uhtrslot, link, tower, true}; + auto const did = DetId{eid2tid[eid.linearIndex()]}; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("erawId = %u linearIndex = %u drawid = %u subdet = %s\n", + eid.rawId(), + eid.linearIndex(), + did.rawId(), + get_subdet_str(did)); + printf("flavor = %u crate = %u slot = %u channelid = %u link = %u tower = %u\n", + flavor, + uhtrcrate, + uhtrslot, + channelid, + link, + tower); +#endif + + break; + } + case 5: { + uint8_t fiber = (channelid >> 2) & 0x3f; + uint8_t fchannel = channelid & 0x3; + HcalElectronicsId eid{uhtrcrate, uhtrslot, fiber, fchannel, false}; + auto const did = HcalDetId{eid2did[eid.linearIndex()]}; + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("erawId = %u linearIndex = %u drawid = %u subdet = %s\n", + eid.rawId(), + eid.linearIndex(), + did.rawId(), + get_subdet_str(did)); + printf("flavor = %u crate = %u slot = %u channelid = %u fiber = %u fchannel = %u\n", + flavor, + uhtrcrate, + uhtrslot, + channelid, + fiber, + fchannel); +#endif + + // remove digis not for HB + if (did.subdetId() != HcalBarrel) + break; + + // count words + auto const* channel_header_word = ptr++; + while (!is_channel_header_word(ptr) && ptr != end) + ++ptr; + auto const* channel_end = ptr; // set ptr + uint32_t const nwords = channel_end - channel_header_word; + + // filter out this digi if nwords does not equal expected + auto const expected_words = compute_stride(nsamplesF5HB); + if (nwords != expected_words) + break; + + // inc the number of digis of this type + auto const pos = atomicAdd(&pChannelsCounters[OutputF5HB], 1); + +#ifdef HCAL_RAWDECODE_GPUDEBUG_CG + printf("rank = %d pos = %d\n", thread_group.thread_rank(), pos); +#endif + + // store to global mem words for this digi + idsF5HB[pos] = did.rawId(); + npresamplesF5HB[pos] = presamples; + for (uint32_t iword = 0; iword < expected_words; iword++) + digisF5HB[pos * expected_words + iword] = channel_header_word[iword]; + + break; + } + case 7: { + uint8_t const fiber = (channelid >> 2) & 0x3f; + uint8_t const fchannel = channelid & 0x3; + HcalElectronicsId eid{uhtrcrate, uhtrslot, fiber, fchannel, false}; + auto const did = DetId{eid2did[eid.linearIndex()]}; + + /* uncomment to check the linear index validity + if (eid.rawId() >= HcalElectronicsId::maxLinearIndex) { +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("*** rawid = %u has no known det id***\n", eid.rawId()); +#endif + break; + } + */ + +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("erawId = %u linearIndex = %u drawid = %u\n", eid.rawId(), eid.linearIndex(), did.rawId()); + printf("flavor = %u crate = %u slot = %u channelid = %u fiber = %u fchannel = %u\n", + flavor, + uhtrcrate, + uhtrslot, + channelid, + fiber, + fchannel); +#endif + + break; + } + default: +#ifdef HCAL_RAWDECODE_GPUDEBUG + printf("flavor = %u crate = %u slot = %u channelid = %u\n", flavor, uhtrcrate, uhtrslot, channelid); +#endif + ; + } + + // skip to the next word in case + // 1) current channel was not treated + // 2) we are in the middle of the digi words and not at the end + while (new_channel_start == ptr || !is_channel_header_word(ptr) && ptr != end) + ++ptr; + } + + // thread with rank 31 of the group will have the ptr pointing to the + // header word of the next channel or the end + int const offset_to_shuffle = ptr - start_ptr; + + // always receive from the last guy in the group + auto const offset_for_rank31 = thread_group.shfl(offset_to_shuffle, NTHREADS - 1); + +#ifdef HCAL_RAWDECODE_GPUDEBUG_CG + printf("rank = %d offset_to_shuffle = %d offset_for_rank32 = %d\n", + thread_group.thread_rank(), + offset_to_shuffle, + offset_for_rank31); +#endif + + // update the ptr for all threads of this group + // NOTE: relative to the start_ptr that is the same for all threads of + // this group + ptr = start_ptr + offset_for_rank31; + } + } + + void entryPoint(InputDataCPU const& inputCPU, + InputDataGPU& inputGPU, + OutputDataGPU& outputGPU, + ScratchDataGPU& scratchGPU, + OutputDataCPU& outputCPU, + ConditionsProducts const& conditions, + ConfigurationParameters const& config, + cudaStream_t cudaStream, + uint32_t const nfedsWithData, + uint32_t const nbytesTotal) { + // transfer + cudaCheck(cudaMemcpyAsync(inputGPU.data.get(), + inputCPU.data.get(), + nbytesTotal * sizeof(unsigned char), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(inputGPU.offsets.get(), + inputCPU.offsets.get(), + nfedsWithData * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck( + cudaMemsetAsync(scratchGPU.pChannelsCounters.get(), 0, sizeof(uint32_t) * numOutputCollections, cudaStream)); + cudaCheck(cudaMemcpyAsync( + inputGPU.feds.get(), inputCPU.feds.get(), nfedsWithData * sizeof(int), cudaMemcpyHostToDevice, cudaStream)); + + // 12 is the max number of modules per crate + kernel_rawdecode_test<32><<>>(inputGPU.data.get(), + inputGPU.offsets.get(), + inputGPU.feds.get(), + conditions.eMappingProduct.eid2did, + conditions.eMappingProduct.eid2tid, + outputGPU.digisF01HE.data.get(), + outputGPU.digisF01HE.ids.get(), + outputGPU.digisF5HB.data.get(), + outputGPU.digisF5HB.ids.get(), + outputGPU.digisF5HB.npresamples.get(), + outputGPU.digisF3HB.data.get(), + outputGPU.digisF3HB.ids.get(), + scratchGPU.pChannelsCounters.get(), + config.nsamplesF01HE, + config.nsamplesF5HB, + config.nsamplesF3HB, + nbytesTotal); + cudaCheck(cudaGetLastError()); + + cudaCheck(cudaMemcpyAsync(outputCPU.nchannels.get(), + scratchGPU.pChannelsCounters.get(), + sizeof(uint32_t) * numOutputCollections, + cudaMemcpyDeviceToHost, + cudaStream)); + } + + } // namespace raw +} // namespace hcal diff --git a/EventFilter/HcalRawToDigi/plugins/DecodeGPU.h b/EventFilter/HcalRawToDigi/plugins/DecodeGPU.h new file mode 100644 index 0000000000000..3d5e4eec32269 --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/DecodeGPU.h @@ -0,0 +1,23 @@ +#ifndef EventFilter_HcalRawToDigi_interface_DecodeGPU_h +#define EventFilter_HcalRawToDigi_interface_DecodeGPU_h + +#include "DeclsForKernels.h" + +namespace hcal { + namespace raw { + + void entryPoint(InputDataCPU const&, + InputDataGPU&, + OutputDataGPU&, + ScratchDataGPU&, + OutputDataCPU&, + ConditionsProducts const&, + ConfigurationParameters const&, + cudaStream_t cudaStream, + uint32_t const, + uint32_t const); + + } +} // namespace hcal + +#endif // EventFilter_HcalRawToDigi_interface_DecodeGPU_h diff --git a/EventFilter/HcalRawToDigi/plugins/ElectronicsMappingGPU.cc b/EventFilter/HcalRawToDigi/plugins/ElectronicsMappingGPU.cc new file mode 100644 index 0000000000000..6b7b89cc6ea77 --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/ElectronicsMappingGPU.cc @@ -0,0 +1,63 @@ +#include "DataFormats/HcalDetId/interface/HcalElectronicsId.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "ElectronicsMappingGPU.h" + +namespace hcal { + namespace raw { + + // TODO: 0x3FFFFF * 4B ~= 16MB + // tmp solution for linear mapping of eid -> did + ElectronicsMappingGPU::ElectronicsMappingGPU(HcalElectronicsMap const& mapping) + : eid2tid_(HcalElectronicsId::maxLinearIndex, 0u), eid2did_(HcalElectronicsId::maxLinearIndex, 0u) { + auto const& eidsPrecision = mapping.allElectronicsIdPrecision(); + for (uint32_t i = 0; i < eidsPrecision.size(); ++i) { + auto const& eid = eidsPrecision[i]; + + // assign + eid2did_[eid.linearIndex()] = eid.isTriggerChainId() ? 0u : mapping.lookup(eid).rawId(); + } + + auto const& eidsTrigger = mapping.allElectronicsIdTrigger(); + for (uint32_t i = 0; i < eidsTrigger.size(); i++) { + auto const& eid = eidsTrigger[i]; + + // assign + eid2tid_[eid.linearIndex()] = eid.isTriggerChainId() ? mapping.lookupTrigger(eid).rawId() : 0u; + } + } + + ElectronicsMappingGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(eid2did)); + cudaCheck(cudaFree(eid2tid)); + } + + ElectronicsMappingGPU::Product const& ElectronicsMappingGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](ElectronicsMappingGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.eid2did, this->eid2did_.size() * sizeof(uint32_t))); + cudaCheck(cudaMalloc((void**)&product.eid2tid, this->eid2tid_.size() * sizeof(uint32_t))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.eid2did, + this->eid2did_.data(), + this->eid2did_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.eid2tid, + this->eid2tid_.data(), + this->eid2tid_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; + } + + } // namespace raw +} // namespace hcal + +TYPELOOKUP_DATA_REG(hcal::raw::ElectronicsMappingGPU); diff --git a/EventFilter/HcalRawToDigi/plugins/ElectronicsMappingGPU.h b/EventFilter/HcalRawToDigi/plugins/ElectronicsMappingGPU.h new file mode 100644 index 0000000000000..0f4c12f02a92d --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/ElectronicsMappingGPU.h @@ -0,0 +1,48 @@ +#ifndef EventFilter_HcalRawToDigi_plugins_ElectronicsMappingGPU_h +#define EventFilter_HcalRawToDigi_plugins_ElectronicsMappingGPU_h + +#include "CondFormats/HcalObjects/interface/HcalElectronicsMap.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#endif + +namespace hcal { + namespace raw { + + class ElectronicsMappingGPU { + public: + struct Product { + ~Product(); + // trigger + uint32_t *eid2tid; + // detector + uint32_t *eid2did; + }; + +#ifndef __CUDACC__ + + // rearrange pedestals + ElectronicsMappingGPU(HcalElectronicsMap const &); + + // will call dealloation for Product thru ~Product + ~ElectronicsMappingGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + + private: + // in the future, we need to arrange so to avoid this copy on the host + // if possible + std::vector> eid2tid_; + std::vector> eid2did_; + + cms::cuda::ESProduct product_; +#endif + }; + + } // namespace raw +} // namespace hcal + +#endif // EventFilter_HcalRawToDigi_plugins_ElectronicsMappingGPU_h diff --git a/EventFilter/HcalRawToDigi/plugins/HcalCPUDigisProducer.cc b/EventFilter/HcalRawToDigi/plugins/HcalCPUDigisProducer.cc new file mode 100644 index 0000000000000..c2b67a10afaff --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/HcalCPUDigisProducer.cc @@ -0,0 +1,117 @@ +#include + +#include "CUDADataFormats/HcalDigi/interface/DigiCollection.h" +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" +#include "DataFormats/HcalDigi/interface/HcalDigiCollections.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +class HcalCPUDigisProducer : public edm::stream::EDProducer { +public: + explicit HcalCPUDigisProducer(edm::ParameterSet const& ps); + ~HcalCPUDigisProducer() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + using IProductTypef01 = cms::cuda::Product>; + edm::EDGetTokenT digisF01HETokenIn_; + using IProductTypef5 = cms::cuda::Product>; + edm::EDGetTokenT digisF5HBTokenIn_; + using IProductTypef3 = cms::cuda::Product>; + edm::EDGetTokenT digisF3HBTokenIn_; + + using OProductTypef01 = + hcal::DigiCollection>; + edm::EDPutTokenT digisF01HETokenOut_; + using OProductTypef5 = + hcal::DigiCollection>; + edm::EDPutTokenT digisF5HBTokenOut_; + using OProductTypef3 = + hcal::DigiCollection>; + edm::EDPutTokenT digisF3HBTokenOut_; + + // needed to pass data from acquire to produce + OProductTypef01 digisf01HE_; + OProductTypef5 digisf5HB_; + OProductTypef3 digisf3HB_; +}; + +void HcalCPUDigisProducer::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("digisLabelF01HEIn", edm::InputTag{"hcalRawToDigiGPU", "f01HEDigisGPU"}); + desc.add("digisLabelF5HBIn", edm::InputTag{"hcalRawToDigiGPU", "f5HBDigisGPU"}); + desc.add("digisLabelF3HBIn", edm::InputTag{"hcalRawToDigiGPU", "f3HBDigisGPU"}); + desc.add("digisLabelF01HEOut", "f01HEDigis"); + desc.add("digisLabelF5HBOut", "f5HBDigis"); + desc.add("digisLabelF3HBOut", "f3HBDigis"); + + confDesc.addWithDefaultLabel(desc); +} + +HcalCPUDigisProducer::HcalCPUDigisProducer(const edm::ParameterSet& ps) + : digisF01HETokenIn_{consumes(ps.getParameter("digisLabelF01HEIn"))}, + digisF5HBTokenIn_{consumes(ps.getParameter("digisLabelF5HBIn"))}, + digisF3HBTokenIn_{consumes(ps.getParameter("digisLabelF3HBIn"))}, + digisF01HETokenOut_{produces(ps.getParameter("digisLabelF01HEOut"))}, + digisF5HBTokenOut_{produces(ps.getParameter("digisLabelF5HBOut"))}, + digisF3HBTokenOut_{produces(ps.getParameter("digisLabelF3HBOut"))} {} + +HcalCPUDigisProducer::~HcalCPUDigisProducer() {} + +void HcalCPUDigisProducer::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder taskHolder) { + // retrieve data/ctx + auto const& f01HEProduct = event.get(digisF01HETokenIn_); + auto const& f5HBProduct = event.get(digisF5HBTokenIn_); + auto const& f3HBProduct = event.get(digisF3HBTokenIn_); + cms::cuda::ScopedContextAcquire ctx{f01HEProduct, std::move(taskHolder)}; + auto const& f01HEDigis = ctx.get(f01HEProduct); + auto const& f5HBDigis = ctx.get(f5HBProduct); + auto const& f3HBDigis = ctx.get(f3HBProduct); + + // resize out tmp buffers + digisf01HE_.stride = f01HEDigis.stride; + digisf5HB_.stride = f5HBDigis.stride; + digisf3HB_.stride = f3HBDigis.stride; + digisf01HE_.resize(f01HEDigis.size); + digisf5HB_.resize(f5HBDigis.size); + digisf3HB_.resize(f3HBDigis.size); + + auto lambdaToTransfer = [&ctx](auto& dest, auto* src) { + using vector_type = typename std::remove_reference::type; + using type = typename vector_type::value_type; + using src_data_type = typename std::remove_pointer::type; + static_assert(std::is_same::value && "Dest and Src data types do not match"); + cudaCheck(cudaMemcpyAsync(dest.data(), src, dest.size() * sizeof(type), cudaMemcpyDeviceToHost, ctx.stream())); + }; + + lambdaToTransfer(digisf01HE_.data, f01HEDigis.data.get()); + lambdaToTransfer(digisf01HE_.ids, f01HEDigis.ids.get()); + + lambdaToTransfer(digisf5HB_.data, f5HBDigis.data.get()); + lambdaToTransfer(digisf5HB_.ids, f5HBDigis.ids.get()); + lambdaToTransfer(digisf5HB_.npresamples, f5HBDigis.npresamples.get()); + + lambdaToTransfer(digisf3HB_.data, f3HBDigis.data.get()); + lambdaToTransfer(digisf3HB_.ids, f3HBDigis.ids.get()); +} + +void HcalCPUDigisProducer::produce(edm::Event& event, edm::EventSetup const& setup) { + event.emplace(digisF01HETokenOut_, std::move(digisf01HE_)); + event.emplace(digisF5HBTokenOut_, std::move(digisf5HB_)); + event.emplace(digisF3HBTokenOut_, std::move(digisf3HB_)); +} + +DEFINE_FWK_MODULE(HcalCPUDigisProducer); diff --git a/EventFilter/HcalRawToDigi/plugins/HcalDigisProducerGPU.cc b/EventFilter/HcalRawToDigi/plugins/HcalDigisProducerGPU.cc new file mode 100644 index 0000000000000..9ca33340f7036 --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/HcalDigisProducerGPU.cc @@ -0,0 +1,235 @@ +#include + +#include "CUDADataFormats/HcalDigi/interface/DigiCollection.h" +#include "DataFormats/HcalDigi/interface/HcalDigiCollections.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +class HcalDigisProducerGPU : public edm::stream::EDProducer { +public: + explicit HcalDigisProducerGPU(edm::ParameterSet const& ps); + ~HcalDigisProducerGPU() override = default; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + // input product tokens + edm::EDGetTokenT hbheDigiToken_; + edm::EDGetTokenT qie11DigiToken_; + + // type aliases + using HostCollectionf01 = + hcal::DigiCollection>; + using DeviceCollectionf01 = hcal::DigiCollection; + using HostCollectionf5 = + hcal::DigiCollection>; + using DeviceCollectionf5 = hcal::DigiCollection; + using HostCollectionf3 = + hcal::DigiCollection>; + using DeviceCollectionf3 = hcal::DigiCollection; + + // output product tokens + using ProductTypef01 = cms::cuda::Product; + edm::EDPutTokenT digisF01HEToken_; + using ProductTypef5 = cms::cuda::Product; + edm::EDPutTokenT digisF5HBToken_; + using ProductTypef3 = cms::cuda::Product; + edm::EDPutTokenT digisF3HBToken_; + + cms::cuda::ContextState cudaState_; + + struct ConfigParameters { + uint32_t maxChannelsF01HE, maxChannelsF5HB, maxChannelsF3HB; + }; + ConfigParameters config_; + + // per event host buffers + HostCollectionf01 hf01_; + HostCollectionf5 hf5_; + HostCollectionf3 hf3_; + + // device products: product owns memory (i.e. not the module) + DeviceCollectionf01 df01_; + DeviceCollectionf5 df5_; + DeviceCollectionf3 df3_; +}; + +void HcalDigisProducerGPU::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + // FIXME + desc.add("hbheDigisLabel", edm::InputTag("hcalDigis")); + desc.add("qie11DigiLabel", edm::InputTag("hcalDigis")); + desc.add("digisLabelF01HE", std::string{"f01HEDigisGPU"}); + desc.add("digisLabelF5HB", std::string{"f5HBDigisGPU"}); + desc.add("digisLabelF3HB", std::string{"f3HBDigisGPU"}); + desc.add("maxChannelsF01HE", 10000u); + desc.add("maxChannelsF5HB", 10000u); + desc.add("maxChannelsF3HB", 10000u); + + confDesc.addWithDefaultLabel(desc); +} + +HcalDigisProducerGPU::HcalDigisProducerGPU(const edm::ParameterSet& ps) + : hbheDigiToken_{consumes(ps.getParameter("hbheDigisLabel"))}, + qie11DigiToken_{consumes(ps.getParameter("qie11DigiLabel"))}, + digisF01HEToken_{produces(ps.getParameter("digisLabelF01HE"))}, + digisF5HBToken_{produces(ps.getParameter("digisLabelF5HB"))}, + digisF3HBToken_{produces(ps.getParameter("digisLabelF3HB"))} { + config_.maxChannelsF01HE = ps.getParameter("maxChannelsF01HE"); + config_.maxChannelsF5HB = ps.getParameter("maxChannelsF5HB"); + config_.maxChannelsF3HB = ps.getParameter("maxChannelsF3HB"); + + // this is a preallocation for the max statically known number of time samples + // actual stride/nsamples will be inferred from data + hf01_.stride = hcal::compute_stride(QIE11DigiCollection::MAXSAMPLES); + hf5_.stride = hcal::compute_stride(HBHEDataFrame::MAXSAMPLES); + hf3_.stride = hcal::compute_stride(QIE11DigiCollection::MAXSAMPLES); + + // preallocate pinned host memory only if CUDA is available + edm::Service cs; + if (cs and cs->enabled()) { + hf01_.reserve(config_.maxChannelsF01HE); + hf5_.reserve(config_.maxChannelsF5HB); + hf3_.reserve(config_.maxChannelsF3HB); + } +} + +void HcalDigisProducerGPU::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder holder) { + // raii + cms::cuda::ScopedContextAcquire ctx{event.streamID(), std::move(holder), cudaState_}; + + // clear host buffers + hf01_.clear(); + hf5_.clear(); + hf3_.clear(); + + // event data + edm::Handle hbheDigis; + edm::Handle qie11Digis; + event.getByToken(hbheDigiToken_, hbheDigis); + event.getByToken(qie11DigiToken_, qie11Digis); + + // init f5 collection + if (not hbheDigis->empty()) { + auto const nsamples = (*hbheDigis)[0].size(); + auto const stride = hcal::compute_stride(nsamples); + hf5_.stride = stride; + + // flavor5 get device blobs + df5_.stride = stride; + df5_.data = cms::cuda::make_device_unique(config_.maxChannelsF5HB * stride, ctx.stream()); + df5_.ids = cms::cuda::make_device_unique(config_.maxChannelsF5HB, ctx.stream()); + df5_.npresamples = cms::cuda::make_device_unique(config_.maxChannelsF5HB, ctx.stream()); + } + + if (not qie11Digis->empty()) { + auto const nsamples = qie11Digis->samples(); + auto const stride01 = hcal::compute_stride(nsamples); + auto const stride3 = hcal::compute_stride(nsamples); + + hf01_.stride = stride01; + hf3_.stride = stride3; + + // flavor 0/1 get devie blobs + df01_.stride = stride01; + df01_.data = cms::cuda::make_device_unique(config_.maxChannelsF01HE * stride01, ctx.stream()); + df01_.ids = cms::cuda::make_device_unique(config_.maxChannelsF01HE, ctx.stream()); + + // flavor3 get device blobs + df3_.stride = stride3; + df3_.data = cms::cuda::make_device_unique(config_.maxChannelsF3HB * stride3, ctx.stream()); + df3_.ids = cms::cuda::make_device_unique(config_.maxChannelsF3HB, ctx.stream()); + } + + for (auto const& hbhe : *hbheDigis) { + auto const id = hbhe.id().rawId(); + auto const presamples = hbhe.presamples(); + hf5_.ids.push_back(id); + hf5_.npresamples.push_back(presamples); + auto const stride = hcal::compute_stride(hbhe.size()); + assert(stride == hf5_.stride && "strides must be the same for every single digi of the collection"); + // simple for now... + static_assert(hcal::Flavor5::HEADER_WORDS == 1); + uint16_t header_word = (1 << 15) | (0x5 << 12) | (0 << 10) | ((hbhe.sample(0).capid() & 0x3) << 8); + hf5_.data.push_back(header_word); + for (unsigned int i = 0; i < stride - hcal::Flavor5::HEADER_WORDS; i++) { + uint16_t s0 = (0 << 7) | (static_cast(hbhe.sample(2 * i).adc()) & 0x7f); + uint16_t s1 = (0 << 7) | (static_cast(hbhe.sample(2 * i + 1).adc()) & 0x7f); + uint16_t sample = (s1 << 8) | s0; + hf5_.data.push_back(sample); + } + } + + for (unsigned int i = 0; i < qie11Digis->size(); i++) { + auto const& digi = QIE11DataFrame{(*qie11Digis)[i]}; + assert(digi.samples() == qie11Digis->samples() && "collection nsamples must equal per digi samples"); + if (digi.flavor() == 0 or digi.flavor() == 1) { + if (digi.detid().subdetId() != HcalEndcap) + continue; + auto const id = digi.detid().rawId(); + hf01_.ids.push_back(id); + for (int hw = 0; hw < hcal::Flavor1::HEADER_WORDS; hw++) + hf01_.data.push_back((*qie11Digis)[i][hw]); + for (int sample = 0; sample < digi.samples(); sample++) { + hf01_.data.push_back((*qie11Digis)[i][hcal::Flavor1::HEADER_WORDS + sample]); + } + } else if (digi.flavor() == 3) { + if (digi.detid().subdetId() != HcalBarrel) + continue; + auto const id = digi.detid().rawId(); + hf3_.ids.push_back(id); + for (int hw = 0; hw < hcal::Flavor3::HEADER_WORDS; hw++) + hf3_.data.push_back((*qie11Digis)[i][hw]); + for (int sample = 0; sample < digi.samples(); sample++) { + hf3_.data.push_back((*qie11Digis)[i][hcal::Flavor3::HEADER_WORDS + sample]); + } + } + } + + auto lambdaToTransfer = [&ctx](auto* dest, auto const& src) { + if (src.empty()) + return; + using vector_type = typename std::remove_reference::type; + using type = typename vector_type::value_type; + using dest_data_type = typename std::remove_pointer::type; + static_assert(std::is_same::value && "Dest and Src data typesdo not match"); + cudaCheck(cudaMemcpyAsync(dest, src.data(), src.size() * sizeof(type), cudaMemcpyHostToDevice, ctx.stream())); + }; + + lambdaToTransfer(df01_.data.get(), hf01_.data); + lambdaToTransfer(df01_.ids.get(), hf01_.ids); + + lambdaToTransfer(df5_.data.get(), hf5_.data); + lambdaToTransfer(df5_.ids.get(), hf5_.ids); + lambdaToTransfer(df5_.npresamples.get(), hf5_.npresamples); + + lambdaToTransfer(df3_.data.get(), hf3_.data); + lambdaToTransfer(df3_.ids.get(), hf3_.ids); + + df01_.size = hf01_.ids.size(); + df5_.size = hf5_.ids.size(); + df3_.size = hf3_.ids.size(); +} + +void HcalDigisProducerGPU::produce(edm::Event& event, edm::EventSetup const& setup) { + cms::cuda::ScopedContextProduce ctx{cudaState_}; + + ctx.emplace(event, digisF01HEToken_, std::move(df01_)); + ctx.emplace(event, digisF5HBToken_, std::move(df5_)); + ctx.emplace(event, digisF3HBToken_, std::move(df3_)); +} + +DEFINE_FWK_MODULE(HcalDigisProducerGPU); diff --git a/EventFilter/HcalRawToDigi/plugins/HcalESProducerGPUDefs.cc b/EventFilter/HcalRawToDigi/plugins/HcalESProducerGPUDefs.cc new file mode 100644 index 0000000000000..749a98e990755 --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/HcalESProducerGPUDefs.cc @@ -0,0 +1,10 @@ +#include "CondFormats/DataRecord/interface/HcalElectronicsMapRcd.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "HeterogeneousCore/CUDACore/interface/ConvertingESProducerT.h" + +#include "ElectronicsMappingGPU.h" + +using HcalElectronicsMappingGPUESProducer = + ConvertingESProducerT; + +DEFINE_FWK_EVENTSETUP_MODULE(HcalElectronicsMappingGPUESProducer); diff --git a/EventFilter/HcalRawToDigi/plugins/HcalRawToDigiGPU.cc b/EventFilter/HcalRawToDigi/plugins/HcalRawToDigiGPU.cc new file mode 100644 index 0000000000000..7e8388a5f4d2f --- /dev/null +++ b/EventFilter/HcalRawToDigi/plugins/HcalRawToDigiGPU.cc @@ -0,0 +1,200 @@ +#include + +#include "CondFormats/DataRecord/interface/HcalElectronicsMapRcd.h" +#include "DataFormats/FEDRawData/interface/FEDNumbering.h" +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +#include "DeclsForKernels.h" +#include "DecodeGPU.h" +#include "ElectronicsMappingGPU.h" + +class HcalRawToDigiGPU : public edm::stream::EDProducer { +public: + explicit HcalRawToDigiGPU(edm::ParameterSet const& ps); + ~HcalRawToDigiGPU() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + edm::EDGetTokenT rawDataToken_; + using ProductTypef01 = cms::cuda::Product>; + edm::EDPutTokenT digisF01HEToken_; + using ProductTypef5 = cms::cuda::Product>; + edm::EDPutTokenT digisF5HBToken_; + using ProductTypef3 = cms::cuda::Product>; + edm::EDPutTokenT digisF3HBToken_; + + cms::cuda::ContextState cudaState_; + + std::vector fedsToUnpack_; + + hcal::raw::ConfigurationParameters config_; + hcal::raw::OutputDataGPU outputGPU_; + hcal::raw::OutputDataCPU outputCPU_; +}; + +void HcalRawToDigiGPU::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("InputLabel", edm::InputTag("rawDataCollector")); + auto nFeds = FEDNumbering::MAXHCALuTCAFEDID - FEDNumbering::MINHCALuTCAFEDID + 1; + std::vector feds(nFeds); + for (int i = 0; i < nFeds; ++i) + feds[i] = i + FEDNumbering::MINHCALuTCAFEDID; + desc.add>("FEDs", feds); + desc.add("maxChannelsF01HE", 10000u); + desc.add("maxChannelsF5HB", 10000u); + desc.add("maxChannelsF3HB", 10000u); + desc.add("nsamplesF01HE", 8); + desc.add("nsamplesF5HB", 8); + desc.add("nsamplesF3HB", 8); + desc.add("digisLabelF5HB", "f5HBDigisGPU"); + desc.add("digisLabelF01HE", "f01HEDigisGPU"); + desc.add("digisLabelF3HB", "f3HBDigisGPU"); + + std::string label = "hcalRawToDigiGPU"; + confDesc.add(label, desc); +} + +HcalRawToDigiGPU::HcalRawToDigiGPU(const edm::ParameterSet& ps) + : rawDataToken_{consumes(ps.getParameter("InputLabel"))}, + digisF01HEToken_{produces(ps.getParameter("digisLabelF01HE"))}, + digisF5HBToken_{produces(ps.getParameter("digisLabelF5HB"))}, + digisF3HBToken_{produces(ps.getParameter("digisLabelF3HB"))}, + fedsToUnpack_{ps.getParameter>("FEDs")} { + config_.maxChannelsF01HE = ps.getParameter("maxChannelsF01HE"); + config_.maxChannelsF5HB = ps.getParameter("maxChannelsF5HB"); + config_.maxChannelsF3HB = ps.getParameter("maxChannelsF3HB"); + config_.nsamplesF01HE = ps.getParameter("nsamplesF01HE"); + config_.nsamplesF5HB = ps.getParameter("nsamplesF5HB"); + config_.nsamplesF3HB = ps.getParameter("nsamplesF3HB"); +} + +HcalRawToDigiGPU::~HcalRawToDigiGPU() {} + +void HcalRawToDigiGPU::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder holder) { + // raii + cms::cuda::ScopedContextAcquire ctx{event.streamID(), std::move(holder), cudaState_}; + + // conditions + edm::ESHandle eMappingHandle; + setup.get().get(eMappingHandle); + auto const& eMappingProduct = eMappingHandle->getProduct(ctx.stream()); + + // bundle up conditions + hcal::raw::ConditionsProducts conditions{eMappingProduct}; + + // event data + edm::Handle rawDataHandle; + event.getByToken(rawDataToken_, rawDataHandle); + + // scratch + hcal::raw::ScratchDataGPU scratchGPU = { + cms::cuda::make_device_unique(hcal::raw::numOutputCollections, ctx.stream())}; + + // input cpu data + hcal::raw::InputDataCPU inputCPU = {cms::cuda::make_host_unique( + hcal::raw::utca_nfeds_max * hcal::raw::nbytes_per_fed_max, ctx.stream()), + cms::cuda::make_host_unique(hcal::raw::utca_nfeds_max, ctx.stream()), + cms::cuda::make_host_unique(hcal::raw::utca_nfeds_max, ctx.stream())}; + + // input data gpu + hcal::raw::InputDataGPU inputGPU = { + cms::cuda::make_device_unique(hcal::raw::utca_nfeds_max * hcal::raw::nbytes_per_fed_max, + ctx.stream()), + cms::cuda::make_device_unique(hcal::raw::utca_nfeds_max, ctx.stream()), + cms::cuda::make_device_unique(hcal::raw::utca_nfeds_max, ctx.stream())}; + + // output cpu + outputCPU_ = {cms::cuda::make_host_unique(hcal::raw::numOutputCollections, ctx.stream())}; + + // output gpu + outputGPU_.allocate(config_, ctx.stream()); + + // iterate over feds + // TODO: another idea + // - loop over all feds to unpack and enqueue cuda memcpy + // - accumulate the sizes + // - after the loop launch cuda memcpy for sizes + // - enqueue the kernel + uint32_t currentCummOffset = 0; + uint32_t counter = 0; + for (auto const& fed : fedsToUnpack_) { + auto const& data = rawDataHandle->FEDData(fed); + auto const nbytes = data.size(); + + // skip empty feds + if (nbytes < hcal::raw::empty_event_size) + continue; + +#ifdef HCAL_RAWDECODE_CPUDEBUG + printf("fed = %d nbytes = %lu\n", fed, nbytes); +#endif + + // copy raw data into plain buffer + std::memcpy(inputCPU.data.get() + currentCummOffset, data.data(), nbytes); + // set the offset in bytes from the start + inputCPU.offsets[counter] = currentCummOffset; + inputCPU.feds[counter] = fed; + + // this is the current offset into the vector + currentCummOffset += nbytes; + ++counter; + } + + hcal::raw::entryPoint(inputCPU, + inputGPU, + outputGPU_, + scratchGPU, + outputCPU_, + conditions, + config_, + ctx.stream(), + counter, + currentCummOffset); +} + +void HcalRawToDigiGPU::produce(edm::Event& event, edm::EventSetup const& setup) { + cms::cuda::ScopedContextProduce ctx{cudaState_}; + +#ifdef HCAL_RAWDECODE_CPUDEBUG + printf("f01he channels = %u f5hb channesl = %u\n", + outputCPU_.nchannels[hcal::raw::OutputF01HE], + outputCPU_.nchannels[hcal::raw::OutputF5HB]); +#endif + + // FIXME: use sizes of views directly for cuda mem cpy? + auto const nchannelsF01HE = outputCPU_.nchannels[hcal::raw::OutputF01HE]; + auto const nchannelsF5HB = outputCPU_.nchannels[hcal::raw::OutputF5HB]; + auto const nchannelsF3HB = outputCPU_.nchannels[hcal::raw::OutputF3HB]; + outputGPU_.digisF01HE.size = nchannelsF01HE; + outputGPU_.digisF5HB.size = nchannelsF5HB; + outputGPU_.digisF3HB.size = nchannelsF3HB; + outputGPU_.digisF01HE.stride = hcal::compute_stride(config_.nsamplesF01HE); + outputGPU_.digisF5HB.stride = hcal::compute_stride(config_.nsamplesF5HB); + outputGPU_.digisF3HB.stride = hcal::compute_stride(config_.nsamplesF3HB); + + ctx.emplace(event, digisF01HEToken_, std::move(outputGPU_.digisF01HE)); + ctx.emplace(event, digisF5HBToken_, std::move(outputGPU_.digisF5HB)); + ctx.emplace(event, digisF3HBToken_, std::move(outputGPU_.digisF3HB)); + + // reset ptrs that are carried as members + outputCPU_.nchannels.reset(); +} + +DEFINE_FWK_MODULE(HcalRawToDigiGPU); diff --git a/EventFilter/SiPixelRawToDigi/plugins/BuildFile.xml b/EventFilter/SiPixelRawToDigi/plugins/BuildFile.xml index f92aa68373927..212738e941533 100644 --- a/EventFilter/SiPixelRawToDigi/plugins/BuildFile.xml +++ b/EventFilter/SiPixelRawToDigi/plugins/BuildFile.xml @@ -1,4 +1,7 @@ + + + diff --git a/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigiErrorsFromSoA.cc b/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigiErrorsFromSoA.cc new file mode 100644 index 0000000000000..b487942a1419b --- /dev/null +++ b/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigiErrorsFromSoA.cc @@ -0,0 +1,183 @@ +#include + +#include "CondFormats/DataRecord/interface/SiPixelFedCablingMapRcd.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelFedCablingMap.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelFedCablingTree.h" +#include "DataFormats/Common/interface/DetSetVector.h" +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/DetId/interface/DetIdCollection.h" +#include "DataFormats/FEDRawData/interface/FEDNumbering.h" +#include "DataFormats/SiPixelDetId/interface/PixelFEDChannel.h" +#include "DataFormats/SiPixelDigi/interface/PixelDigi.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelErrorsSoA.h" +#include "EventFilter/SiPixelRawToDigi/interface/PixelDataFormatter.h" +#include "FWCore/Framework/interface/ESTransientHandle.h" +#include "FWCore/Framework/interface/ESWatcher.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" + +class SiPixelDigiErrorsFromSoA : public edm::stream::EDProducer<> { +public: + explicit SiPixelDigiErrorsFromSoA(const edm::ParameterSet& iConfig); + ~SiPixelDigiErrorsFromSoA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::Event& iEvent, const edm::EventSetup& iSetup) override; + + const edm::ESGetToken cablingToken_; + const edm::EDGetTokenT digiErrorSoAGetToken_; + const edm::EDPutTokenT> errorPutToken_; + const edm::EDPutTokenT tkErrorPutToken_; + const edm::EDPutTokenT userErrorPutToken_; + const edm::EDPutTokenT> disabledChannelPutToken_; + + edm::ESWatcher cablingWatcher_; + std::unique_ptr cabling_; + + const std::vector tkerrorlist_; + const std::vector usererrorlist_; + + const bool usePhase1_; +}; + +SiPixelDigiErrorsFromSoA::SiPixelDigiErrorsFromSoA(const edm::ParameterSet& iConfig) + : cablingToken_(esConsumes(edm::ESInputTag("", iConfig.getParameter("CablingMapLabel")))), + digiErrorSoAGetToken_{consumes(iConfig.getParameter("digiErrorSoASrc"))}, + errorPutToken_{produces>()}, + tkErrorPutToken_{produces()}, + userErrorPutToken_{produces("UserErrorModules")}, + disabledChannelPutToken_{produces>()}, + tkerrorlist_(iConfig.getParameter>("ErrorList")), + usererrorlist_(iConfig.getParameter>("UserErrorList")), + usePhase1_(iConfig.getParameter("UsePhase1")) {} + +void SiPixelDigiErrorsFromSoA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("digiErrorSoASrc", edm::InputTag("siPixelDigiErrorsSoA")); + // the configuration parameters here are named following those in SiPixelRawToDigi + desc.add("CablingMapLabel", "")->setComment("CablingMap label"); + desc.add("UsePhase1", false)->setComment("## Use phase1"); + desc.add>("ErrorList", std::vector{29}) + ->setComment("## ErrorList: list of error codes used by tracking to invalidate modules"); + desc.add>("UserErrorList", std::vector{40}) + ->setComment("## UserErrorList: list of error codes used by Pixel experts for investigation"); + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelDigiErrorsFromSoA::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + // pack errors into collection + + // initialize cabling map or update if necessary + if (cablingWatcher_.check(iSetup)) { + // cabling map, which maps online address (fed->link->ROC->local pixel) to offline (DetId->global pixel) + const SiPixelFedCablingMap* cablingMap = &iSetup.getData(cablingToken_); + cabling_ = cablingMap->cablingTree(); + LogDebug("map version:") << cabling_->version(); + } + + const auto& digiErrors = iEvent.get(digiErrorSoAGetToken_); + + edm::DetSetVector errorcollection{}; + DetIdCollection tkerror_detidcollection{}; + DetIdCollection usererror_detidcollection{}; + edmNew::DetSetVector disabled_channelcollection{}; + + PixelDataFormatter formatter(cabling_.get(), usePhase1_); // for phase 1 & 0 + const PixelDataFormatter::Errors* formatterErrors = digiErrors.formatterErrors(); + assert(formatterErrors != nullptr); + auto errors = *formatterErrors; // make a copy + PixelDataFormatter::DetErrors nodeterrors; + + auto size = digiErrors.size(); + for (auto i = 0U; i < size; i++) { + SiPixelErrorCompact err = digiErrors.error(i); + if (err.errorType != 0) { + SiPixelRawDataError error(err.word, err.errorType, err.fedId + FEDNumbering::MINSiPixeluTCAFEDID); + errors[err.rawId].push_back(error); + } + } + + constexpr uint32_t dummydetid = 0xffffffff; + typedef PixelDataFormatter::Errors::iterator IE; + for (auto& error : errors) { + uint32_t errordetid = error.first; + if (errordetid == dummydetid) { // errors given dummy detId must be sorted by Fed + nodeterrors.insert(nodeterrors.end(), errors[errordetid].begin(), errors[errordetid].end()); + } else { + edm::DetSet& errorDetSet = errorcollection.find_or_insert(errordetid); + errorDetSet.data.insert(errorDetSet.data.end(), error.second.begin(), error.second.end()); + // Fill detid of the detectors where there is error AND the error number is listed + // in the configurable error list in the job option cfi. + // Code needs to be here, because there can be a set of errors for each + // entry in the for loop over PixelDataFormatter::Errors + + std::vector disabledChannelsDetSet; + + for (auto const& aPixelError : errorDetSet) { + // For the time being, we extend the error handling functionality with ErrorType 25 + // In the future, we should sort out how the usage of tkerrorlist can be generalized + if (aPixelError.getType() == 25) { + int fedId = aPixelError.getFedId(); + const sipixelobjects::PixelFEDCabling* fed = cabling_->fed(fedId); + if (fed) { + cms_uint32_t linkId = formatter.linkId(aPixelError.getWord32()); + const sipixelobjects::PixelFEDLink* link = fed->link(linkId); + if (link) { + // The "offline" 0..15 numbering is fixed by definition, also, the FrameConversion depends on it + // in contrast, the ROC-in-channel numbering is determined by hardware --> better to use the "offline" scheme + PixelFEDChannel ch = {fed->id(), linkId, 25, 0}; + for (unsigned int iRoc = 1; iRoc <= link->numberOfROCs(); iRoc++) { + const sipixelobjects::PixelROC* roc = link->roc(iRoc); + if (roc->idInDetUnit() < ch.roc_first) + ch.roc_first = roc->idInDetUnit(); + if (roc->idInDetUnit() > ch.roc_last) + ch.roc_last = roc->idInDetUnit(); + } + if (ch.roc_first < ch.roc_last) + disabledChannelsDetSet.push_back(ch); + } + } + } else { + // fill list of detIds to be turned off by tracking + if (!tkerrorlist_.empty()) { + auto it_find = std::find(tkerrorlist_.begin(), tkerrorlist_.end(), aPixelError.getType()); + if (it_find != tkerrorlist_.end()) { + tkerror_detidcollection.push_back(errordetid); + } + } + } + + // fill list of detIds with errors to be studied + if (!usererrorlist_.empty()) { + auto it_find = std::find(usererrorlist_.begin(), usererrorlist_.end(), aPixelError.getType()); + if (it_find != usererrorlist_.end()) { + usererror_detidcollection.push_back(errordetid); + } + } + + } // loop on DetSet of errors + + if (!disabledChannelsDetSet.empty()) { + disabled_channelcollection.insert(errordetid, disabledChannelsDetSet.data(), disabledChannelsDetSet.size()); + } + + } // if error assigned to a real DetId + } // loop on errors in event for this FED + + edm::DetSet& errorDetSet = errorcollection.find_or_insert(dummydetid); + errorDetSet.data = nodeterrors; + + iEvent.emplace(errorPutToken_, std::move(errorcollection)); + iEvent.emplace(tkErrorPutToken_, std::move(tkerror_detidcollection)); + iEvent.emplace(userErrorPutToken_, std::move(usererror_detidcollection)); + iEvent.emplace(disabledChannelPutToken_, std::move(disabled_channelcollection)); +} + +DEFINE_FWK_MODULE(SiPixelDigiErrorsFromSoA); diff --git a/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigiErrorsSoAFromCUDA.cc b/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigiErrorsSoAFromCUDA.cc new file mode 100644 index 0000000000000..f2c7d0de5fe24 --- /dev/null +++ b/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigiErrorsSoAFromCUDA.cc @@ -0,0 +1,79 @@ +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelErrorsSoA.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +class SiPixelDigiErrorsSoAFromCUDA : public edm::stream::EDProducer { +public: + explicit SiPixelDigiErrorsSoAFromCUDA(const edm::ParameterSet& iConfig); + ~SiPixelDigiErrorsSoAFromCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void acquire(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) override; + void produce(edm::Event& iEvent, const edm::EventSetup& iSetup) override; + + edm::EDGetTokenT> digiErrorGetToken_; + edm::EDPutTokenT digiErrorPutToken_; + + cms::cuda::host::unique_ptr data_; + cms::cuda::SimpleVector error_; + const SiPixelFormatterErrors* formatterErrors_ = nullptr; +}; + +SiPixelDigiErrorsSoAFromCUDA::SiPixelDigiErrorsSoAFromCUDA(const edm::ParameterSet& iConfig) + : digiErrorGetToken_( + consumes>(iConfig.getParameter("src"))), + digiErrorPutToken_(produces()) {} + +void SiPixelDigiErrorsSoAFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("src", edm::InputTag("siPixelClustersCUDA")); + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelDigiErrorsSoAFromCUDA::acquire(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) { + // Do the transfer in a CUDA stream parallel to the computation CUDA stream + cms::cuda::ScopedContextAcquire ctx{iEvent.streamID(), std::move(waitingTaskHolder)}; + + const auto& gpuDigiErrors = ctx.get(iEvent, digiErrorGetToken_); + + auto tmp = gpuDigiErrors.dataErrorToHostAsync(ctx.stream()); + error_ = tmp.first; + data_ = std::move(tmp.second); + formatterErrors_ = &(gpuDigiErrors.formatterErrors()); +} + +void SiPixelDigiErrorsSoAFromCUDA::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + // The following line copies the data from the pinned host memory to + // regular host memory. In principle that feels unnecessary (why not + // just use the pinned host memory?). There are a few arguments for + // doing it though + // - Now can release the pinned host memory back to the (caching) allocator + // * if we'd like to keep the pinned memory, we'd need to also + // keep the CUDA stream around as long as that, or allow pinned + // host memory to be allocated without a CUDA stream + // - What if a CPU algorithm would produce the same SoA? We can't + // use cudaMallocHost without a GPU... + iEvent.emplace(digiErrorPutToken_, error_.size(), error_.data(), formatterErrors_); + + error_ = cms::cuda::make_SimpleVector(0, nullptr); + data_.reset(); + formatterErrors_ = nullptr; +} + +// define as framework plugin +DEFINE_FWK_MODULE(SiPixelDigiErrorsSoAFromCUDA); diff --git a/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigisSoAFromCUDA.cc b/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigisSoAFromCUDA.cc new file mode 100644 index 0000000000000..dbec74585998f --- /dev/null +++ b/EventFilter/SiPixelRawToDigi/plugins/SiPixelDigisSoAFromCUDA.cc @@ -0,0 +1,83 @@ +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" + +class SiPixelDigisSoAFromCUDA : public edm::stream::EDProducer { +public: + explicit SiPixelDigisSoAFromCUDA(const edm::ParameterSet& iConfig); + ~SiPixelDigisSoAFromCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void acquire(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) override; + void produce(edm::Event& iEvent, const edm::EventSetup& iSetup) override; + + edm::EDGetTokenT> digiGetToken_; + edm::EDPutTokenT digiPutToken_; + + cms::cuda::host::unique_ptr pdigi_; + cms::cuda::host::unique_ptr rawIdArr_; + cms::cuda::host::unique_ptr adc_; + cms::cuda::host::unique_ptr clus_; + + int nDigis_; +}; + +SiPixelDigisSoAFromCUDA::SiPixelDigisSoAFromCUDA(const edm::ParameterSet& iConfig) + : digiGetToken_(consumes>(iConfig.getParameter("src"))), + digiPutToken_(produces()) {} + +void SiPixelDigisSoAFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("src", edm::InputTag("siPixelClustersCUDA")); + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelDigisSoAFromCUDA::acquire(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) { + // Do the transfer in a CUDA stream parallel to the computation CUDA stream + cms::cuda::ScopedContextAcquire ctx{iEvent.streamID(), std::move(waitingTaskHolder)}; + + const auto& gpuDigis = ctx.get(iEvent, digiGetToken_); + + nDigis_ = gpuDigis.nDigis(); + pdigi_ = gpuDigis.pdigiToHostAsync(ctx.stream()); + rawIdArr_ = gpuDigis.rawIdArrToHostAsync(ctx.stream()); + adc_ = gpuDigis.adcToHostAsync(ctx.stream()); + clus_ = gpuDigis.clusToHostAsync(ctx.stream()); +} + +void SiPixelDigisSoAFromCUDA::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + // The following line copies the data from the pinned host memory to + // regular host memory. In principle that feels unnecessary (why not + // just use the pinned host memory?). There are a few arguments for + // doing it though + // - Now can release the pinned host memory back to the (caching) allocator + // * if we'd like to keep the pinned memory, we'd need to also + // keep the CUDA stream around as long as that, or allow pinned + // host memory to be allocated without a CUDA stream + // - What if a CPU algorithm would produce the same SoA? We can't + // use cudaMallocHost without a GPU... + iEvent.emplace(digiPutToken_, nDigis_, pdigi_.get(), rawIdArr_.get(), adc_.get(), clus_.get()); + + pdigi_.reset(); + rawIdArr_.reset(); + adc_.reset(); + clus_.reset(); +} + +// define as framework plugin +DEFINE_FWK_MODULE(SiPixelDigisSoAFromCUDA); diff --git a/EventFilter/SiPixelRawToDigi/python/SiPixelRawToDigi_cfi.py b/EventFilter/SiPixelRawToDigi/python/SiPixelRawToDigi_cfi.py index 12ff657cefd8e..50c8f0fcabd3c 100644 --- a/EventFilter/SiPixelRawToDigi/python/SiPixelRawToDigi_cfi.py +++ b/EventFilter/SiPixelRawToDigi/python/SiPixelRawToDigi_cfi.py @@ -1,7 +1,24 @@ import FWCore.ParameterSet.Config as cms -import EventFilter.SiPixelRawToDigi.siPixelRawToDigi_cfi +from EventFilter.SiPixelRawToDigi.siPixelRawToDigi_cfi import siPixelRawToDigi as _siPixelRawToDigi -siPixelDigis = EventFilter.SiPixelRawToDigi.siPixelRawToDigi_cfi.siPixelRawToDigi.clone() +from HeterogeneousCore.CUDACore.SwitchProducerCUDA import SwitchProducerCUDA +siPixelDigis = SwitchProducerCUDA( + cpu = _siPixelRawToDigi.clone() +) from Configuration.Eras.Modifier_phase1Pixel_cff import phase1Pixel -phase1Pixel.toModify(siPixelDigis, UsePhase1=True) +phase1Pixel.toModify(siPixelDigis.cpu, UsePhase1=True) + +from Configuration.ProcessModifiers.gpu_cff import gpu +gpu.toModify(siPixelDigis, + cuda = cms.EDAlias( + siPixelDigiErrors = cms.VPSet( + cms.PSet(type = cms.string("DetIdedmEDCollection")), + cms.PSet(type = cms.string("SiPixelRawDataErroredmDetSetVector")), + cms.PSet(type = cms.string("PixelFEDChanneledmNewDetSetVector")) + ), + siPixelDigisClustersPreSplitting = cms.VPSet( + cms.PSet(type = cms.string("PixelDigiedmDetSetVector")) + ) + ) +) diff --git a/EventFilter/SiPixelRawToDigi/python/siPixelDigis_cff.py b/EventFilter/SiPixelRawToDigi/python/siPixelDigis_cff.py new file mode 100644 index 0000000000000..5c1ff74be9c69 --- /dev/null +++ b/EventFilter/SiPixelRawToDigi/python/siPixelDigis_cff.py @@ -0,0 +1,30 @@ +import FWCore.ParameterSet.Config as cms + +from EventFilter.SiPixelRawToDigi.SiPixelRawToDigi_cfi import siPixelDigis +from EventFilter.SiPixelRawToDigi.siPixelDigisSoAFromCUDA_cfi import siPixelDigisSoAFromCUDA as _siPixelDigisSoAFromCUDA +from EventFilter.SiPixelRawToDigi.siPixelDigiErrorsSoAFromCUDA_cfi import siPixelDigiErrorsSoAFromCUDA as _siPixelDigiErrorsSoAFromCUDA +from EventFilter.SiPixelRawToDigi.siPixelDigiErrorsFromSoA_cfi import siPixelDigiErrorsFromSoA as _siPixelDigiErrorsFromSoA + +siPixelDigisTask = cms.Task(siPixelDigis) + +siPixelDigisSoA = _siPixelDigisSoAFromCUDA.clone( + src = "siPixelClustersPreSplittingCUDA" +) +siPixelDigiErrorsSoA = _siPixelDigiErrorsSoAFromCUDA.clone( + src = "siPixelClustersPreSplittingCUDA" +) +siPixelDigiErrors = _siPixelDigiErrorsFromSoA.clone() + +from Configuration.Eras.Modifier_phase1Pixel_cff import phase1Pixel +phase1Pixel.toModify(siPixelDigiErrors, UsePhase1=True) + +siPixelDigisTaskCUDA = cms.Task( + siPixelDigisSoA, + siPixelDigiErrorsSoA, + siPixelDigiErrors +) + +from Configuration.ProcessModifiers.gpu_cff import gpu +_siPixelDigisTask_gpu = siPixelDigisTask.copy() +_siPixelDigisTask_gpu.add(siPixelDigisTaskCUDA) +gpu.toReplaceWith(siPixelDigisTask, _siPixelDigisTask_gpu) diff --git a/FWCore/Utilities/interface/CMSUnrollLoop.h b/FWCore/Utilities/interface/CMSUnrollLoop.h new file mode 100644 index 0000000000000..a46df28a224e7 --- /dev/null +++ b/FWCore/Utilities/interface/CMSUnrollLoop.h @@ -0,0 +1,51 @@ +#ifndef FWCore_Utilities_interface_CMSUnrollLoop_h +#define FWCore_Utilities_interface_CMSUnrollLoop_h + +// convert the macro argument to a null-terminated quoted string +#define STRINGIFY_(ARG) #ARG +#define STRINGIFY(ARG) STRINGIFY_(ARG) + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +// CUDA or HIP device compiler + +#define CMS_UNROLL_LOOP _Pragma(STRINGIFY(unroll)) +#define CMS_UNROLL_LOOP_COUNT(N) _Pragma(STRINGIFY(unroll N)) +#define CMS_UNROLL_LOOP_DISABLE _Pragma(STRINGIFY(unroll 1)) + +#define CMS_DEVICE_UNROLL_LOOP _Pragma(STRINGIFY(unroll)) +#define CMS_DEVICE_UNROLL_LOOP_COUNT(N) _Pragma(STRINGIFY(unroll N)) +#define CMS_DEVICE_UNROLL_LOOP_DISABLE _Pragma(STRINGIFY(unroll 1)) + +#else // defined (__CUDA_ARCH__) || defined (__HIP_DEVICE_COMPILE__) + +// any host compiler +#define CMS_DEVICE_UNROLL_LOOP +#define CMS_DEVICE_UNROLL_LOOP_COUNT(N) +#define CMS_DEVICE_UNROLL_LOOP_DISABLE + +#if defined(__clang__) +// clang host compiler + +#define CMS_UNROLL_LOOP _Pragma(STRINGIFY(clang loop unroll(enable))) +#define CMS_UNROLL_LOOP_COUNT(N) _Pragma(STRINGIFY(clang loop unroll_count(N))) +#define CMS_UNROLL_LOOP_DISABLE _Pragma(STRINGIFY(clang loop unroll(disable))) + +#elif defined(__GNUC__) +// GCC host compiler + +#define CMS_UNROLL_LOOP _Pragma(STRINGIFY(GCC ivdep)) +#define CMS_UNROLL_LOOP_COUNT(N) _Pragma(STRINGIFY(GCC unroll N)) _Pragma(STRINGIFY(GCC ivdep)) +#define CMS_UNROLL_LOOP_DISABLE _Pragma(STRINGIFY(GCC unroll 1)) + +#else +// unsupported or unknown compiler + +#define CMS_UNROLL_LOOP +#define CMS_UNROLL_LOOP_COUNT(N) +#define CMS_UNROLL_LOOP_DISABLE + +#endif // defined(__clang__) || defined(__GNUC__) || ... + +#endif // defined (__CUDA_ARCH__) || defined (__HIP_DEVICE_COMPILE__) + +#endif // FWCore_Utilities_interface_CMSUnrollLoop_h diff --git a/FastSimulation/Tracking/python/SeedingMigration.py b/FastSimulation/Tracking/python/SeedingMigration.py index 751670daa50c8..3a982eba55e36 100644 --- a/FastSimulation/Tracking/python/SeedingMigration.py +++ b/FastSimulation/Tracking/python/SeedingMigration.py @@ -13,8 +13,9 @@ def _hitSetProducerToFactoryPSet(producer): "PixelTripletLargeTipEDProducer": "PixelTripletLargeTipGenerator", "MultiHitFromChi2EDProducer": "MultiHitGeneratorFromChi2", "CAHitTripletEDProducer": "CAHitTripletGenerator", - "CAHitQuadrupletEDProducer": "CAHitQuadrupletGenerator", - } + "CAHitQuadrupletEDProducer": "CAHitQuadrupletGenerator", + "CAHitNtupletHeterogeneousEDProducer": "CAHitQuadrupletGenerator", + } ret = cms.PSet() _copy(producer, ret) ret.ComponentName = cms.string(_map[producer._TypedParameterizable__type]); diff --git a/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h b/Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h index cefdbe4b3296a..c2b5bc9d95f83 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 { @@ -20,6 +21,96 @@ namespace phase1PixelTopology { constexpr uint32_t numPixsInModule = uint32_t(numRowsInModule) * uint32_t(numColsInModule); + constexpr uint32_t numberOfModules = 1856; + constexpr uint32_t numberOfLayers = 10; + constexpr uint32_t layerStart[numberOfLayers + 1] = {0, + 96, + 320, + 672, // barrel + 1184, + 1296, + 1408, // positive endcap + 1520, + 1632, + 1744, // negative endcap + numberOfModules}; + constexpr char const* layerName[numberOfLayers] = { + "BL1", + "BL2", + "BL3", + "BL4", // barrel + "E+1", + "E+2", + "E+3", // positive endcap + "E-1", + "E-2", + "E-3" // negative endcap + }; + + constexpr uint32_t numberOfModulesInBarrel = 1184; + constexpr uint32_t numberOfLaddersInBarrel = numberOfModulesInBarrel / 8; + + template + constexpr auto map_to_array_helper(Function f, std::index_sequence) + -> std::array::type, sizeof...(Indices)> { + return {{f(Indices)...}}; + } + + template + constexpr auto map_to_array(Function f) -> std::array::type, N> { + return map_to_array_helper(f, std::make_index_sequence{}); + } + + constexpr uint32_t findMaxModuleStride() { + bool go = true; + int n = 2; + while (go) { + for (uint8_t i = 1; i < std::size(layerStart); ++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 < std::size(layerStart); ++i) + if (detId < layerStart[i + 1]) + return i; + return std::size(layerStart); + } + + constexpr uint8_t findLayerFromCompact(uint32_t detId) { + detId *= maxModuleStride; + for (uint8_t i = 0; i < std::size(layerStart); ++i) + if (detId < layerStart[i + 1]) + return i; + return std::size(layerStart); + } + + constexpr uint32_t layerIndexSize = numberOfModules / maxModuleStride; + constexpr std::array layer = map_to_array(findLayerFromCompact); + + constexpr bool validateLayerIndex() { + bool res = true; + for (auto i = 0U; i < numberOfModules; ++i) { + auto j = i / maxModuleStride; + res &= (layer[j] < 10); + res &= (i >= layerStart[layer[j]]); + res &= (i < layerStart[layer[j] + 1]); + } + return res; + } + + static_assert(validateLayerIndex(), "layer from detIndex algo is buggy"); + // this is for the ROC n<512 (upgrade 1024) constexpr inline uint16_t divu52(uint16_t n) { n = n >> 2; @@ -31,9 +122,11 @@ namespace phase1PixelTopology { } constexpr inline bool isEdgeX(uint16_t px) { return (px == 0) | (px == lastRowInModule); } + constexpr inline bool isEdgeY(uint16_t py) { return (py == 0) | (py == lastColInModule); } constexpr inline uint16_t toRocX(uint16_t px) { return (px < numRowsInRoc) ? px : px - numRowsInRoc; } + constexpr inline uint16_t toRocY(uint16_t py) { auto roc = divu52(py); return py - 52 * roc; @@ -64,6 +157,18 @@ namespace phase1PixelTopology { return py + shift; } + //FIXME move it elsewhere? + struct AverageGeometry { + static constexpr auto numberOfLaddersInBarrel = phase1PixelTopology::numberOfLaddersInBarrel; + float ladderZ[numberOfLaddersInBarrel]; + float ladderX[numberOfLaddersInBarrel]; + float ladderY[numberOfLaddersInBarrel]; + float ladderR[numberOfLaddersInBarrel]; + float ladderMinZ[numberOfLaddersInBarrel]; + float ladderMaxZ[numberOfLaddersInBarrel]; + float endCapZ[2]; // just for pos and neg Layer1 + }; + } // namespace phase1PixelTopology #endif // Geometry_TrackerGeometryBuilder_phase1PixelTopology_h diff --git a/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp b/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp index 9a00efbff9a9a..8dfae57b685b4 100644 --- a/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp +++ b/Geometry/TrackerGeometryBuilder/test/phase1PixelTopology_t.cpp @@ -142,5 +142,19 @@ int main() { assert(std::get<1>(ori) == bp); } + for (auto i = 0U; i < phase1PixelTopology::numberOfLayers; ++i) { + std::cout << "layer " << i << ", \"" << phase1PixelTopology::layerName[i] << "\", [" + << phase1PixelTopology::layerStart[i] << ", " << phase1PixelTopology::layerStart[i + 1] << ")" + << std::endl; + } + + for (auto i = 0U; i < phase1PixelTopology::numberOfModules; ++i) { + int layer = phase1PixelTopology::layer[i / phase1PixelTopology::maxModuleStride]; + //std::cout << "module " << i << ": " << "layer " << layer << ", \"" << phase1PixelTopology::layerName[layer] << "\", [" << phase1PixelTopology::layerStart[layer] << ", " << phase1PixelTopology::layerStart[layer+1] << ")" << std::endl; + assert(layer < 10); + assert(i >= phase1PixelTopology::layerStart[layer]); + assert(i < phase1PixelTopology::layerStart[layer + 1]); + } + return 0; } diff --git a/HLTrigger/Configuration/python/customizeHLTforPatatrack.py b/HLTrigger/Configuration/python/customizeHLTforPatatrack.py new file mode 100644 index 0000000000000..6b1807b8389b5 --- /dev/null +++ b/HLTrigger/Configuration/python/customizeHLTforPatatrack.py @@ -0,0 +1,783 @@ +import copy +import FWCore.ParameterSet.Config as cms +from HeterogeneousCore.CUDACore.SwitchProducerCUDA import SwitchProducerCUDA +from HLTrigger.Configuration.common import * +from Configuration.Eras.Modifier_run3_common_cff import run3_common + + +# force the SwitchProducerCUDA choice to pick a specific backend: True for offloading to a gpu, False for running on cpu +def forceGpuOffload(status = True): + import HeterogeneousCore.CUDACore.SwitchProducerCUDA + HeterogeneousCore.CUDACore.SwitchProducerCUDA._cuda_enabled_cached = bool(status) + + +# reset the SwitchProducerCUDA choice to pick a backend depending on the availability of a supported gpu +def resetGpuOffload(): + import HeterogeneousCore.CUDACore.SwitchProducerCUDA + HeterogeneousCore.CUDACore.SwitchProducerCUDA._cuda_enabled_cached = None + HeterogeneousCore.CUDACore.SwitchProducerCUDA._switch_cuda() + + +# check if CUDA is enabled, using the same mechanism as the SwitchProducerCUDA +def cudaIsEnabled(): + import HeterogeneousCore.CUDACore.SwitchProducerCUDA + return HeterogeneousCore.CUDACore.SwitchProducerCUDA._switch_cuda()[0] + + +# customisation for running the Patatrack reconstruction, common parts +def customiseCommon(process): + + # Services + + process.load("HeterogeneousCore.CUDAServices.CUDAService_cfi") + process.CUDAService.enabled = cudaIsEnabled() + + process.load("HeterogeneousCore.CUDAServices.NVProfilerService_cfi") + + + # Paths and EndPaths + + # the hltGetConditions module would force gpu-specific ESProducers to run even if no supported gpu is present + if 'hltGetConditions' in process.__dict__: + del process.hltGetConditions + + # produce a boolean to track if the events ar being processed on gpu (true) or cpu (false) + process.statusOnGPU = SwitchProducerCUDA( + cpu = cms.EDProducer("BooleanProducer", value = cms.bool(False)), + cuda = cms.EDProducer("BooleanProducer", value = cms.bool(True)) + ) + + process.statusOnGPUFilter = cms.EDFilter("BooleanFilter", + src = cms.InputTag("statusOnGPU") + ) + + if 'Status_OnGPU' in process.__dict__: + replace_with(process.Status_OnGPU, cms.Path(process.statusOnGPU + process.statusOnGPUFilter)) + else: + process.Status_OnGPU = cms.Path(process.statusOnGPU + process.statusOnGPUFilter) + if 'HLTSchedule' in process.__dict__: + process.HLTSchedule.append(process.Status_OnGPU) + if process.schedule is not None: + process.schedule.append(process.Status_OnGPU) + + + # make the ScoutingCaloMuonOutput endpath compatible with using Tasks in the Scouting paths + if 'hltOutputScoutingCaloMuon' in process.__dict__ and not 'hltPreScoutingCaloMuonOutputSmart' in process.__dict__: + process.hltPreScoutingCaloMuonOutputSmart = cms.EDFilter( "TriggerResultsFilter", + l1tIgnoreMaskAndPrescale = cms.bool( False ), + l1tResults = cms.InputTag( "" ), + hltResults = cms.InputTag( 'TriggerResults','','@currentProcess' ), + triggerConditions = process.hltOutputScoutingCaloMuon.SelectEvents.SelectEvents, + throw = cms.bool( True ) + ) + insert_modules_after(process, process.hltPreScoutingCaloMuonOutput, process.hltPreScoutingCaloMuonOutputSmart) + + # make the ScoutingPFOutput endpath compatible with using Tasks in the Scouting paths + if 'hltOutputScoutingPF' in process.__dict__ and not 'hltPreScoutingPFOutputSmart' in process.__dict__: + process.hltPreScoutingPFOutputSmart = cms.EDFilter( "TriggerResultsFilter", + l1tIgnoreMaskAndPrescale = cms.bool( False ), + l1tResults = cms.InputTag( "" ), + hltResults = cms.InputTag( 'TriggerResults','','@currentProcess' ), + triggerConditions = process.hltOutputScoutingPF.SelectEvents.SelectEvents, + throw = cms.bool( True ) + ) + insert_modules_after(process, process.hltPreScoutingPFOutput, process.hltPreScoutingPFOutputSmart) + + + # done + return process + + +# customisation for running the "Patatrack" pixel local reconstruction +def customisePixelLocalReconstruction(process): + + if not 'HLTDoLocalPixelSequence' in process.__dict__: + return process + + + # FIXME replace the Sequences with empty ones to avoid exanding them during the (re)definition of Modules and EDAliases + + process.HLTDoLocalPixelSequence = cms.Sequence() + + + # Event Setup + + process.load("CalibTracker.SiPixelESProducers.siPixelGainCalibrationForHLTGPU_cfi") # this should be used only on GPUs, will crash otherwise + process.load("CalibTracker.SiPixelESProducers.siPixelROCsStatusAndMappingWrapperESProducer_cfi") # this should be used only on GPUs, will crash otherwise + process.load("RecoLocalTracker.SiPixelRecHits.PixelCPEFastESProducer_cfi") + + + # Modules and EDAliases + + # referenced in HLTDoLocalPixelTask + + # transfer the beamspot to the gpu + from RecoVertex.BeamSpotProducer.offlineBeamSpotToCUDA_cfi import offlineBeamSpotToCUDA as _offlineBeamSpotToCUDA + process.hltOnlineBeamSpotToCUDA = _offlineBeamSpotToCUDA.clone( + src = "hltOnlineBeamSpot" + ) + + # reconstruct the pixel digis and clusters on the gpu + from RecoLocalTracker.SiPixelClusterizer.siPixelRawToClusterCUDA_cfi import siPixelRawToClusterCUDA as _siPixelRawToClusterCUDA + process.hltSiPixelClustersCUDA = _siPixelRawToClusterCUDA.clone() + # use the pixel channel calibrations scheme for Run 3 + run3_common.toModify(process.hltSiPixelClustersCUDA, isRun2 = False) + + # copy the pixel digis errors to the host + from EventFilter.SiPixelRawToDigi.siPixelDigiErrorsSoAFromCUDA_cfi import siPixelDigiErrorsSoAFromCUDA as _siPixelDigiErrorsSoAFromCUDA + process.hltSiPixelDigiErrorsSoA = _siPixelDigiErrorsSoAFromCUDA.clone( + src = "hltSiPixelClustersCUDA" + ) + + # convert the pixel digis errors to the legacy format + from EventFilter.SiPixelRawToDigi.siPixelDigiErrorsFromSoA_cfi import siPixelDigiErrorsFromSoA as _siPixelDigiErrorsFromSoA + process.hltSiPixelDigiErrors = _siPixelDigiErrorsFromSoA.clone( + digiErrorSoASrc = "hltSiPixelDigiErrorsSoA", + UsePhase1 = True + ) + + # copy the pixel digis (except errors) and clusters to the host + from EventFilter.SiPixelRawToDigi.siPixelDigisSoAFromCUDA_cfi import siPixelDigisSoAFromCUDA as _siPixelDigisSoAFromCUDA + process.hltSiPixelDigisSoA = _siPixelDigisSoAFromCUDA.clone( + src = "hltSiPixelClustersCUDA" + ) + + # convert the pixel digis (except errors) and clusters to the legacy format + from RecoLocalTracker.SiPixelClusterizer.siPixelDigisClustersFromSoA_cfi import siPixelDigisClustersFromSoA as _siPixelDigisClustersFromSoA + process.hltSiPixelDigisClusters = _siPixelDigisClustersFromSoA.clone( + src = "hltSiPixelDigisSoA" + ) + + # SwitchProducer wrapping the legacy pixel digis producer or an alias combining the pixel digis information converted from SoA + process.hltSiPixelDigis = SwitchProducerCUDA( + # legacy producer + cpu = process.hltSiPixelDigis, + # alias used to access products from multiple conversion modules + cuda = cms.EDAlias( + hltSiPixelDigisClusters = cms.VPSet( + cms.PSet(type = cms.string("PixelDigiedmDetSetVector")) + ), + hltSiPixelDigiErrors = cms.VPSet( + cms.PSet(type = cms.string("DetIdedmEDCollection")), + cms.PSet(type = cms.string("SiPixelRawDataErroredmDetSetVector")), + cms.PSet(type = cms.string("PixelFEDChanneledmNewDetSetVector")) + ) + ) + ) + + # SwitchProducer wrapping the legacy pixel cluster producer or an alias for the pixel clusters information converted from SoA + process.hltSiPixelClusters = SwitchProducerCUDA( + # legacy producer + cpu = process.hltSiPixelClusters, + # alias used to access products from multiple conversion modules + cuda = cms.EDAlias( + hltSiPixelDigisClusters = cms.VPSet( + cms.PSet(type = cms.string("SiPixelClusteredmNewDetSetVector")) + ) + ) + ) + + # reconstruct the pixel rechits on the gpu + from RecoLocalTracker.SiPixelRecHits.siPixelRecHitCUDA_cfi import siPixelRecHitCUDA as _siPixelRecHitCUDA + process.hltSiPixelRecHitsCUDA = _siPixelRecHitCUDA.clone( + src = "hltSiPixelClustersCUDA", + beamSpot = "hltOnlineBeamSpotToCUDA" + ) + + # SwitchProducer wrapping the legacy pixel rechit producer or the transfer of the pixel rechits to the host and the conversion from SoA + from RecoLocalTracker.SiPixelRecHits.siPixelRecHitFromCUDA_cfi import siPixelRecHitFromCUDA as _siPixelRecHitFromCUDA + process.hltSiPixelRecHits = SwitchProducerCUDA( + # legacy producer + cpu = process.hltSiPixelRecHits, + # converter to legacy format + cuda = _siPixelRecHitFromCUDA.clone( + pixelRecHitSrc = "hltSiPixelRecHitsCUDA", + src = "hltSiPixelClusters" + ) + ) + + + # Tasks and Sequences + + process.HLTDoLocalPixelTask = cms.Task( + process.hltOnlineBeamSpotToCUDA, # transfer the beamspot to the gpu + process.hltSiPixelClustersCUDA, # reconstruct the pixel digis and clusters on the gpu + process.hltSiPixelRecHitsCUDA, # reconstruct the pixel rechits on the gpu + process.hltSiPixelDigisSoA, # copy the pixel digis (except errors) and clusters to the host + process.hltSiPixelDigisClusters, # convert the pixel digis (except errors) and clusters to the legacy format + process.hltSiPixelDigiErrorsSoA, # copy the pixel digis errors to the host + process.hltSiPixelDigiErrors, # convert the pixel digis errors to the legacy format + process.hltSiPixelDigis, # SwitchProducer wrapping the legacy pixel digis producer or an alias combining the pixel digis information converted from SoA + process.hltSiPixelClusters, # SwitchProducer wrapping the legacy pixel cluster producer or an alias for the pixel clusters information converted from SoA + process.hltSiPixelClustersCache, # legacy module, used by the legacy pixel quadruplet producer + process.hltSiPixelRecHits) # SwitchProducer wrapping the legacy pixel rechit producer or the transfer of the pixel rechits to the host and the conversion from SoA + + process.HLTDoLocalPixelSequence = cms.Sequence(process.HLTDoLocalPixelTask) + + + # done + return process + + +# customisation for running the "Patatrack" pixel track reconstruction +def customisePixelTrackReconstruction(process): + + if not 'HLTRecoPixelTracksSequence' in process.__dict__: + return process + + + # FIXME replace the Sequences with empty ones to avoid exanding them during the (re)definition of Modules and EDAliases + + process.HLTRecoPixelTracksSequence = cms.Sequence() + process.HLTRecopixelvertexingSequence = cms.Sequence() + + + # Modules and EDAliases + + # referenced in process.HLTRecoPixelTracksTask + + # cpu only: convert the pixel rechits from legacy to SoA format + from RecoLocalTracker.SiPixelRecHits.siPixelRecHitSoAFromLegacy_cfi import siPixelRecHitSoAFromLegacy as _siPixelRecHitSoAFromLegacy + process.hltSiPixelRecHitSoA = _siPixelRecHitSoAFromLegacy.clone( + src = "hltSiPixelClusters", + beamSpot = "hltOnlineBeamSpot", + convertToLegacy = True + ) + + # build pixel ntuplets and pixel tracks in SoA format on gpu + from RecoPixelVertexing.PixelTriplets.caHitNtupletCUDA_cfi import caHitNtupletCUDA as _caHitNtupletCUDA + process.hltPixelTracksCUDA = _caHitNtupletCUDA.clone( + idealConditions = False, + pixelRecHitSrc = "hltSiPixelRecHitsCUDA", + onGPU = True + ) + # use quality cuts tuned for Run 2 ideal conditions for all Run 3 workflows + run3_common.toModify(process.hltPixelTracksCUDA, idealConditions = True) + + # SwitchProducer providing the pixel tracks in SoA format on cpu + process.hltPixelTracksSoA = SwitchProducerCUDA( + # build pixel ntuplets and pixel tracks in SoA format on cpu + cpu = _caHitNtupletCUDA.clone( + idealConditions = False, + pixelRecHitSrc = "hltSiPixelRecHitSoA", + onGPU = False + ), + # transfer the pixel tracks in SoA format to the host + cuda = cms.EDProducer("PixelTrackSoAFromCUDA", + src = cms.InputTag("hltPixelTracksCUDA") + ) + ) + # use quality cuts tuned for Run 2 ideal conditions for all Run 3 workflows + run3_common.toModify(process.hltPixelTracksSoA.cpu, idealConditions = True) + + # convert the pixel tracks from SoA to legacy format + from RecoPixelVertexing.PixelTrackFitting.pixelTrackProducerFromSoA_cfi import pixelTrackProducerFromSoA as _pixelTrackProducerFromSoA + process.hltPixelTracks = _pixelTrackProducerFromSoA.clone( + beamSpot = "hltOnlineBeamSpot", + pixelRecHitLegacySrc = "hltSiPixelRecHits", + trackSrc = "hltPixelTracksSoA" + ) + + + # referenced in process.HLTRecopixelvertexingTask + + # build pixel vertices in SoA format on gpu + from RecoPixelVertexing.PixelVertexFinding.pixelVertexCUDA_cfi import pixelVertexCUDA as _pixelVertexCUDA + process.hltPixelVerticesCUDA = _pixelVertexCUDA.clone( + pixelTrackSrc = "hltPixelTracksCUDA", + onGPU = True + ) + + # build or transfer pixel vertices in SoA format on cpu + process.hltPixelVerticesSoA = SwitchProducerCUDA( + # build pixel vertices in SoA format on cpu + cpu = _pixelVertexCUDA.clone( + pixelTrackSrc = "hltPixelTracksSoA", + onGPU = False + ), + # transfer the pixel vertices in SoA format to cpu + cuda = cms.EDProducer("PixelVertexSoAFromCUDA", + src = cms.InputTag("hltPixelVerticesCUDA") + ) + ) + + # convert the pixel vertices from SoA to legacy format + from RecoPixelVertexing.PixelVertexFinding.pixelVertexFromSoA_cfi import pixelVertexFromSoA as _pixelVertexFromSoA + process.hltPixelVertices = _pixelVertexFromSoA.clone( + src = "hltPixelVerticesSoA", + TrackCollection = "hltPixelTracks", + beamSpot = "hltOnlineBeamSpot" + ) + + + # Tasks and Sequences + + process.HLTRecoPixelTracksTask = cms.Task( + process.hltPixelTracksTrackingRegions, # from the original sequence + process.hltSiPixelRecHitSoA, # pixel rechits on cpu, converted to SoA + process.hltPixelTracksCUDA, # pixel ntuplets on gpu, in SoA format + process.hltPixelTracksSoA, # pixel ntuplets on cpu, in SoA format + process.hltPixelTracks) # pixel tracks on cpu, in legacy format + + + process.HLTRecoPixelTracksSequence = cms.Sequence(process.HLTRecoPixelTracksTask) + + process.HLTRecopixelvertexingTask = cms.Task( + process.HLTRecoPixelTracksTask, + process.hltPixelVerticesCUDA, # pixel vertices on gpu, in SoA format + process.hltPixelVerticesSoA, # pixel vertices on cpu, in SoA format + process.hltPixelVertices, # pixel vertices on cpu, in legacy format + process.hltTrimmedPixelVertices) # from the original sequence + + process.HLTRecopixelvertexingSequence = cms.Sequence( + process.hltPixelTracksFitter + # not used here, kept for compatibility with legacy sequences + process.hltPixelTracksFilter, # not used here, kept for compatibility with legacy sequences + process.HLTRecopixelvertexingTask) + + + # done + return process + + +# customisation for offloading the ECAL local reconstruction via CUDA if a supported gpu is present +def customiseEcalLocalReconstruction(process): + + if not 'HLTDoFullUnpackingEgammaEcalSequence' in process.__dict__: + return process + + + # FIXME replace the Sequences with empty ones to avoid exanding them during the (re)definition of Modules and EDAliases + + process.HLTDoFullUnpackingEgammaEcalMFSequence = cms.Sequence() + process.HLTDoFullUnpackingEgammaEcalWithoutPreshowerSequence = cms.Sequence() + process.HLTDoFullUnpackingEgammaEcalSequence = cms.Sequence() + + + # Event Setup + + process.load("EventFilter.EcalRawToDigi.ecalElectronicsMappingGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalGainRatiosGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalPedestalsGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalPulseCovariancesGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalPulseShapesGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalSamplesCorrelationGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalTimeBiasCorrectionsGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalTimeCalibConstantsGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalMultifitParametersGPUESProducer_cfi") + + process.load("RecoLocalCalo.EcalRecProducers.ecalRechitADCToGeVConstantGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalRechitChannelStatusGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalIntercalibConstantsGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosRefGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAlphasGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalLinearCorrectionsGPUESProducer_cfi") + process.load("RecoLocalCalo.EcalRecProducers.ecalRecHitParametersGPUESProducer_cfi") + + + # Modules and EDAliases + + # ECAL unpacker running on gpu + process.hltEcalDigisGPU = cms.EDProducer("EcalRawToDigiGPU", + InputLabel = cms.InputTag("rawDataCollector"), + FEDs = cms.vint32( + 601, 602, 603, 604, 605, + 606, 607, 608, 609, 610, + 611, 612, 613, 614, 615, + 616, 617, 618, 619, 620, + 621, 622, 623, 624, 625, + 626, 627, 628, 629, 630, + 631, 632, 633, 634, 635, + 636, 637, 638, 639, 640, + 641, 642, 643, 644, 645, + 646, 647, 648, 649, 650, + 651, 652, 653, 654 + ), + digisLabelEB = cms.string("ebDigis"), + digisLabelEE = cms.string("eeDigis"), + maxChannelsEB = cms.uint32(61200), + maxChannelsEE = cms.uint32(14648), + ) + + # SwitchProducer wrapping the legacy ECAL unpacker or the ECAL digi converter from SoA format on gpu to legacy format on cpu + process.hltEcalDigisLegacy = process.hltEcalDigis.clone() + + process.hltEcalDigis = SwitchProducerCUDA( + # legacy producer + cpu = cms.EDAlias( + hltEcalDigisLegacy = cms.VPSet( + cms.PSet(type = cms.string("EBDigiCollection")), + cms.PSet(type = cms.string("EEDigiCollection")), + cms.PSet(type = cms.string("EBDetIdedmEDCollection")), + cms.PSet(type = cms.string("EEDetIdedmEDCollection")), + cms.PSet(type = cms.string("EBSrFlagsSorted")), + cms.PSet(type = cms.string("EESrFlagsSorted")), + cms.PSet(type = cms.string("EcalElectronicsIdedmEDCollection"), fromProductInstance = cms.string("EcalIntegrityBlockSizeErrors")), + cms.PSet(type = cms.string("EcalElectronicsIdedmEDCollection"), fromProductInstance = cms.string("EcalIntegrityTTIdErrors")) + ) + ), + # convert ECAL digis from SoA format on gpu to legacy format on cpu + cuda = cms.EDProducer("EcalCPUDigisProducer", + digisInLabelEB = cms.InputTag("hltEcalDigisGPU", "ebDigis"), + digisInLabelEE = cms.InputTag("hltEcalDigisGPU", "eeDigis"), + digisOutLabelEB = cms.string("ebDigis"), + digisOutLabelEE = cms.string("eeDigis"), + produceDummyIntegrityCollections = cms.bool(True) + ) + ) + + # ECAL multifit running on gpu + from RecoLocalCalo.EcalRecProducers.ecalUncalibRecHitProducerGPU_cfi import ecalUncalibRecHitProducerGPU as _ecalUncalibRecHitProducerGPU + process.hltEcalUncalibRecHitGPU = _ecalUncalibRecHitProducerGPU.clone( + digisLabelEB = ("hltEcalDigisGPU", "ebDigis"), + digisLabelEE = ("hltEcalDigisGPU", "eeDigis"), + shouldRunTimingComputation = False + ) + + # copy the ECAL uncalibrated rechits from gpu to cpu in SoA format + process.hltEcalUncalibRecHitSoA = cms.EDProducer("EcalCPUUncalibRecHitProducer", + containsTimingInformation = cms.bool(False), + recHitsInLabelEB = cms.InputTag("hltEcalUncalibRecHitGPU", "EcalUncalibRecHitsEB"), + recHitsInLabelEE = cms.InputTag("hltEcalUncalibRecHitGPU", "EcalUncalibRecHitsEE"), + recHitsOutLabelEB = cms.string("EcalUncalibRecHitsEB"), + recHitsOutLabelEE = cms.string("EcalUncalibRecHitsEE") + ) + + # SwitchProducer wrapping the legacy ECAL uncalibrated rechits producer or a converter from SoA to legacy format + process.hltEcalUncalibRecHit = SwitchProducerCUDA( + # legacy producer + cpu = process.hltEcalUncalibRecHit, + # convert the ECAL uncalibrated rechits from SoA to legacy format + cuda = cms.EDProducer("EcalUncalibRecHitConvertGPU2CPUFormat", + recHitsLabelGPUEB = cms.InputTag("hltEcalUncalibRecHitSoA", "EcalUncalibRecHitsEB"), + recHitsLabelGPUEE = cms.InputTag("hltEcalUncalibRecHitSoA", "EcalUncalibRecHitsEE"), + recHitsLabelCPUEB = cms.string("EcalUncalibRecHitsEB"), + recHitsLabelCPUEE = cms.string("EcalUncalibRecHitsEE") + ) + ) + + # Reconstructing the ECAL calibrated rechits on gpu works, but is extremely slow. + # Disable it for the time being, until the performance has been addressed. + """ + process.hltEcalRecHitGPU = cms.EDProducer("EcalRecHitProducerGPU", + uncalibrecHitsInLabelEB = cms.InputTag("hltEcalUncalibRecHitGPU","EcalUncalibRecHitsEB"), + uncalibrecHitsInLabelEE = cms.InputTag("hltEcalUncalibRecHitGPU","EcalUncalibRecHitsEE"), + recHitsLabelEB = cms.string("EcalRecHitsEB"), + recHitsLabelEE = cms.string("EcalRecHitsEE"), + maxNumberHitsEB = cms.uint32(61200), + maxNumberHitsEE = cms.uint32(14648), + ChannelStatusToBeExcluded = cms.vstring( + "kDAC", + "kNoisy", + "kNNoisy", + "kFixedG6", + "kFixedG1", + "kFixedG0", + "kNonRespondingIsolated", + "kDeadVFE", + "kDeadFE", + "kNoDataNoTP"), + killDeadChannels = cms.bool(True), + EBLaserMIN = cms.double(0.01), + EELaserMIN = cms.double(0.01), + EBLaserMAX = cms.double(30.0), + EELaserMAX = cms.double(30.0), + flagsMapDBReco = cms.PSet( + kGood = cms.vstring("kOk","kDAC","kNoLaser","kNoisy"), + kNoisy = cms.vstring("kNNoisy","kFixedG6","kFixedG1"), + kNeighboursRecovered = cms.vstring("kFixedG0", "kNonRespondingIsolated", "kDeadVFE"), + kTowerRecovered = cms.vstring("kDeadFE"), + kDead = cms.vstring("kNoDataNoTP") + ), + recoverEBIsolatedChannels = cms.bool(False), + recoverEEIsolatedChannels = cms.bool(False), + recoverEBVFE = cms.bool(False), + recoverEEVFE = cms.bool(False), + recoverEBFE = cms.bool(True), + recoverEEFE = cms.bool(True), + ) + + process.hltEcalRecHitSoA = cms.EDProducer("EcalCPURecHitProducer", + recHitsInLabelEB = cms.InputTag("hltEcalRecHitGPU", "EcalRecHitsEB"), + recHitsInLabelEE = cms.InputTag("hltEcalRecHitGPU", "EcalRecHitsEE"), + recHitsOutLabelEB = cms.string("EcalRecHitsEB"), + recHitsOutLabelEE = cms.string("EcalRecHitsEE"), + containsTimingInformation = cms.bool(False), + ) + + # SwitchProducer wrapping the legacy ECAL calibrated rechits producer or a converter from SoA to legacy format + process.hltEcalRecHit = SwitchProducerCUDA( + # legacy producer + cpu = process.hltEcalRecHit, + # convert the ECAL calibrated rechits from SoA to legacy format + cuda = cms.EDProducer("EcalRecHitConvertGPU2CPUFormat", + recHitsLabelGPUEB = cms.InputTag("hltEcalRecHitSoA", "EcalRecHitsEB"), + recHitsLabelGPUEE = cms.InputTag("hltEcalRecHitSoA", "EcalRecHitsEE"), + recHitsLabelCPUEB = cms.string("EcalRecHitsEB"), + recHitsLabelCPUEE = cms.string("EcalRecHitsEE"), + ) + """ + + + # SwitchProducer wrapping the legacy ECAL rechits producer + # the gpu unpacker does not produce the TPs used for the recovery, so the SwitchProducer alias does not provide them: + # - the cpu uncalibrated rechit producer may mark them for recovery, read the TPs explicitly from the legacy unpacker + # - the gpu uncalibrated rechit producer does not flag them for recovery, so the TPs are not necessary + process.hltEcalRecHit = SwitchProducerCUDA( + cpu = process.hltEcalRecHit.clone( + triggerPrimitiveDigiCollection = cms.InputTag('hltEcalDigisLegacy', 'EcalTriggerPrimitives') + ), + cuda = process.hltEcalRecHit.clone( + triggerPrimitiveDigiCollection = cms.InputTag('unused') + ) + ) + + # Tasks and Sequences + + process.HLTDoFullUnpackingEgammaEcalWithoutPreshowerTask = cms.Task( + process.hltEcalDigisGPU, # unpack ECAL digis on gpu + process.hltEcalDigisLegacy, # legacy producer, referenced in the SwitchProducer + process.hltEcalDigis, # SwitchProducer + process.hltEcalUncalibRecHitGPU, # run ECAL local reconstruction and multifit on gpu + process.hltEcalUncalibRecHitSoA, # needed by hltEcalPhiSymFilter - copy to host + process.hltEcalUncalibRecHit, # needed by hltEcalPhiSymFilter - convert to legacy format + # process.hltEcalRecHitGPU, # make ECAL calibrated rechits on gpu + # process.hltEcalRecHitSoA, # copy to host + process.hltEcalDetIdToBeRecovered, # legacy producer + process.hltEcalRecHit) # legacy producer + + process.HLTDoFullUnpackingEgammaEcalWithoutPreshowerSequence = cms.Sequence( + process.HLTDoFullUnpackingEgammaEcalWithoutPreshowerTask) + + process.HLTPreshowerTask = cms.Task( + process.hltEcalPreshowerDigis, # unpack ECAL preshower digis on the host + process.hltEcalPreshowerRecHit) # build ECAL preshower rechits on the host + + process.HLTPreshowerSequence = cms.Sequence(process.HLTPreshowerTask) + + process.HLTDoFullUnpackingEgammaEcalTask = cms.Task( + process.HLTDoFullUnpackingEgammaEcalWithoutPreshowerTask, + process.HLTPreshowerTask) + + process.HLTDoFullUnpackingEgammaEcalSequence = cms.Sequence( + process.HLTDoFullUnpackingEgammaEcalTask) + + process.HLTDoFullUnpackingEgammaEcalMFSequence = cms.Sequence( + process.HLTDoFullUnpackingEgammaEcalTask) + + + # done + return process + +# customisation for offloading the HCAL local reconstruction via CUDA if a supported gpu is present +def customiseHcalLocalReconstruction(process): + + if not 'HLTDoLocalHcalSequence' in process.__dict__: + return process + + + # FIXME replace the Sequences with empty ones to avoid exanding them during the (re)definition of Modules and EDAliases + + process.HLTDoLocalHcalSequence = cms.Sequence() + process.HLTStoppedHSCPLocalHcalReco = cms.Sequence() + + + # Event Setup + + process.load("EventFilter.HcalRawToDigi.hcalElectronicsMappingGPUESProducer_cfi") + + process.load("RecoLocalCalo.HcalRecProducers.hcalGainsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalGainWidthsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalLUTCorrsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalConvertedPedestalsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalConvertedEffectivePedestalsGPUESProducer_cfi") + process.hcalConvertedEffectivePedestalsGPUESProducer.label0 = "withTopoEff" + process.load("RecoLocalCalo.HcalRecProducers.hcalConvertedPedestalWidthsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalConvertedEffectivePedestalWidthsGPUESProducer_cfi") + process.hcalConvertedEffectivePedestalWidthsGPUESProducer.label0 = "withTopoEff" + process.hcalConvertedEffectivePedestalWidthsGPUESProducer.label1 = "withTopoEff" + process.load("RecoLocalCalo.HcalRecProducers.hcalQIECodersGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalRecoParamsWithPulseShapesGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalRespCorrsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalTimeCorrsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalQIETypesGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalSiPMParametersGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalSiPMCharacteristicsGPUESProducer_cfi") + process.load("RecoLocalCalo.HcalRecProducers.hcalMahiPulseOffsetsGPUESProducer_cfi") + + + # Modules and EDAliases + + # The HCAL unpacker running on the gpu supports only the HB and HE digis. + # So, run the legacy unacker on the cpu, then convert the HB and HE digis + # to SoA format and copy them to the gpu. + process.hltHcalDigisGPU = cms.EDProducer("HcalDigisProducerGPU", + hbheDigisLabel = cms.InputTag("hltHcalDigis"), + qie11DigiLabel = cms.InputTag("hltHcalDigis"), + digisLabelF01HE = cms.string(""), + digisLabelF5HB = cms.string(""), + digisLabelF3HB = cms.string(""), + maxChannelsF01HE = cms.uint32(10000), + maxChannelsF5HB = cms.uint32(10000), + maxChannelsF3HB = cms.uint32(10000) + ) + + # run the HCAL local reconstruction (including Method 0 and MAHI) on gpu + from RecoLocalCalo.HcalRecProducers.hbheRecHitProducerGPU_cfi import hbheRecHitProducerGPU as _hbheRecHitProducerGPU + process.hltHbherecoGPU = _hbheRecHitProducerGPU.clone( + digisLabelF01HE = "hltHcalDigisGPU", + digisLabelF5HB = "hltHcalDigisGPU", + digisLabelF3HB = "hltHcalDigisGPU", + recHitsLabelM0HBHE = "" + ) + + # transfer the HCAL rechits to the cpu, and convert them to the legacy format + from RecoLocalCalo.HcalRecProducers.hcalCPURecHitsProducer_cfi import hcalCPURecHitsProducer as _hcalCPURecHitsProducer + process.hltHbherecoFromGPU = _hcalCPURecHitsProducer.clone( + recHitsM0LabelIn = "hltHbherecoGPU", + recHitsM0LabelOut = "", + recHitsLegacyLabelOut = "" + ) + + # SwitchProducer between the legacy producer and the copy from gpu with conversion + process.hltHbhereco = SwitchProducerCUDA( + # legacy producer + cpu = process.hltHbhereco.clone(), + # alias to the rechits converted to legacy format + cuda = cms.EDAlias( + hltHbherecoFromGPU = cms.VPSet( + cms.PSet(type = cms.string("HBHERecHitsSorted")) + ) + ) + ) + + + # Tasks and Sequences + + process.HLTDoLocalHcalTask = cms.Task( + process.hltHcalDigis, # legacy producer, unpack HCAL digis on cpu + process.hltHcalDigisGPU, # copy to gpu and convert to SoA format + process.hltHbherecoGPU, # run the HCAL local reconstruction (including Method 0 and MAHI) on gpu + process.hltHbherecoFromGPU, # transfer the HCAL rechits to the cpu, and convert them to the legacy format + process.hltHbhereco, # SwitchProducer between the legacy producer and the copy from gpu with conversion + process.hltHfprereco, # legacy producer + process.hltHfreco, # legacy producer + process.hltHoreco) # legacy producer + + process.HLTDoLocalHcalSequence = cms.Sequence( + process.HLTDoLocalHcalTask) + + process.HLTStoppedHSCPLocalHcalRecoTask = cms.Task( + process.hltHcalDigis, # legacy producer, unpack HCAL digis on cpu + process.hltHcalDigisGPU, # copy to gpu and convert to SoA format + process.hltHbherecoGPU, # run the HCAL local reconstruction (including Method 0 and MAHI) on gpu + process.hltHbherecoFromGPU, # transfer the HCAL rechits to the cpu, and convert them to the legacy format + process.hltHbhereco) # SwitchProducer between the legacy producer and the copy from gpu with conversion + + process.HLTStoppedHSCPLocalHcalReco = cms.Sequence( + process.HLTStoppedHSCPLocalHcalRecoTask) + + + # done + return process + + +# customisation for running the Patatrack reconstruction, with automatic offload via CUDA when a supported gpu is available +def customizeHLTforPatatrack(process): + process = customiseCommon(process) + process = customisePixelLocalReconstruction(process) + process = customisePixelTrackReconstruction(process) + process = customiseEcalLocalReconstruction(process) + process = customiseHcalLocalReconstruction(process) + return process + + +def _addConsumerPath(process): + # add to a path all consumers and the tasks that define the producers + process.Consumer = cms.Path( + process.HLTBeginSequence + + process.hltPixelConsumer + + process.hltEcalConsumer + + process.hltHbheConsumer, + process.HLTDoLocalPixelTask, + process.HLTRecoPixelTracksTask, + process.HLTRecopixelvertexingTask, + process.HLTDoFullUnpackingEgammaEcalTask, + process.HLTDoLocalHcalTask, + ) + + if 'HLTSchedule' in process.__dict__: + process.HLTSchedule.append(process.Consumer) + if process.schedule is not None: + process.schedule.append(process.Consumer) + + # done + return process + + +def consumeGPUSoAProducts(process): + # consume the Pixel tracks and vertices on the GPU in SoA format + process.hltPixelConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltPixelTracksCUDA', 'hltPixelVerticesCUDA' ) + ) + + # consume the ECAL uncalibrated rechits on the GPU in SoA format + process.hltEcalConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltEcalUncalibRecHitGPU' ) + ) + + # consume the HCAL rechits on the GPU in SoA format + process.hltHbheConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltHbherecoGPU' ) + ) + + # add to a path all consumers and the tasks that define the producers + process = _addConsumerPath(process) + + # done + return process + + +def consumeCPUSoAProducts(process): + # consume the Pixel tracks and vertices on the CPU in SoA format + process.hltPixelConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltPixelTracksSoA', 'hltPixelVerticesSoA' ) + ) + + # consume the ECAL uncalibrated rechits on the CPU in SoA format + process.hltEcalConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltEcalUncalibRecHitSoA' ) + ) + + # consume the HCAL rechits on the CPU in legacy format + process.hltHbheConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltHbhereco' ) + ) + + # add to a path all consumers and the tasks that define the producers + process = _addConsumerPath(process) + + # done + return process + +def consumeCPULegacyProducts(process): + # consume the Pixel tracks and vertices on the CPU in legacy format + process.hltPixelConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltPixelTracks', 'hltPixelVertices' ) + ) + + # consume the ECAL runcalibrated echits on the CPU in legacy format + process.hltEcalConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltEcalUncalibRecHit' ) + ) + + # consume the HCAL rechits on the CPU in legacy format + process.hltHbheConsumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring( 'hltHbhereco' ) + ) + + # add to a path all consumers and the tasks that define the producers + process = _addConsumerPath(process) + + # done + return process diff --git a/HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h b/HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h index f9b4b2f8a4c16..8bd51d3fa7959 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h +++ b/HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h @@ -11,21 +11,26 @@ #include #include +// include the CUDA runtime header to define some of the attributes, types and sybols also on the CPU #include +// make sure function are inlined to avoid multiple definition +#undef __global__ +#define __global__ inline __attribute__((always_inline)) + +#undef __forceinline__ +#define __forceinline__ inline __attribute__((always_inline)) + namespace cms { namespace cudacompat { -#ifndef __CUDA_RUNTIME_H__ - struct dim3 { - uint32_t x, y, z; - }; -#endif + // run serially with a single thread + // 1-dimensional block const dim3 threadIdx = {0, 0, 0}; const dim3 blockDim = {1, 1, 1}; - - extern thread_local dim3 blockIdx; - extern thread_local dim3 gridDim; + // 1-dimensional grid + const dim3 blockIdx = {0, 0, 0}; + const dim3 gridDim = {1, 1, 1}; template T1 atomicCAS(T1* address, T1 compare, T2 val) { @@ -78,35 +83,12 @@ namespace cms { return *x; } - inline void resetGrid() { - blockIdx = {0, 0, 0}; - gridDim = {1, 1, 1}; - } - } // namespace cudacompat } // namespace cms -// some not needed as done by cuda runtime... -#ifndef __CUDA_RUNTIME_H__ -#define __host__ -#define __device__ -#define __global__ -#define __shared__ -#define __forceinline__ -#endif - -// make sure function are inlined to avoid multiple definition -#ifndef __CUDA_ARCH__ -#undef __global__ -#define __global__ inline __attribute__((always_inline)) -#undef __forceinline__ -#define __forceinline__ inline __attribute__((always_inline)) -#endif - -#ifndef __CUDA_ARCH__ +// make the cudacompat implementation available in the global namespace using namespace cms::cudacompat; -#endif -#endif +#endif // __CUDACC__ #endif // HeterogeneousCore_CUDAUtilities_interface_cudaCompat_h diff --git a/HeterogeneousCore/CUDAUtilities/interface/prefixScan.h b/HeterogeneousCore/CUDAUtilities/interface/prefixScan.h index 33dc6a18ffa2a..1a779fc677ff7 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/prefixScan.h +++ b/HeterogeneousCore/CUDAUtilities/interface/prefixScan.h @@ -3,6 +3,7 @@ #include +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" #include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" #include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" @@ -13,7 +14,7 @@ __device__ void __forceinline__ warpPrefixScan(T const* __restrict__ ci, T* __re // ci and co may be the same auto x = ci[i]; auto laneId = threadIdx.x & 0x1f; -#pragma unroll + CMS_UNROLL_LOOP for (int offset = 1; offset < 32; offset <<= 1) { auto y = __shfl_up_sync(mask, x, offset); if (laneId >= offset) @@ -26,7 +27,7 @@ template __device__ void __forceinline__ warpPrefixScan(T* c, uint32_t i, uint32_t mask) { auto x = c[i]; auto laneId = threadIdx.x & 0x1f; -#pragma unroll + CMS_UNROLL_LOOP for (int offset = 1; offset < 32; offset <<= 1) { auto y = __shfl_up_sync(mask, x, offset); if (laneId >= offset) diff --git a/HeterogeneousCore/CUDAUtilities/interface/radixSort.h b/HeterogeneousCore/CUDAUtilities/interface/radixSort.h index e4b2905782825..0958a81bc5dbb 100644 --- a/HeterogeneousCore/CUDAUtilities/interface/radixSort.h +++ b/HeterogeneousCore/CUDAUtilities/interface/radixSort.h @@ -6,6 +6,7 @@ #include #include +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" #include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" template @@ -124,7 +125,7 @@ __device__ __forceinline__ void radixSortImpl( if (threadIdx.x < sb) { auto x = c[threadIdx.x]; auto laneId = threadIdx.x & 0x1f; -#pragma unroll + CMS_UNROLL_LOOP for (int offset = 1; offset < 32; offset <<= 1) { auto y = __shfl_up_sync(0xffffffff, x, offset); if (laneId >= offset) diff --git a/HeterogeneousCore/CUDAUtilities/src/cudaCompat.cc b/HeterogeneousCore/CUDAUtilities/src/cudaCompat.cc deleted file mode 100644 index 7b8efda8e3811..0000000000000 --- a/HeterogeneousCore/CUDAUtilities/src/cudaCompat.cc +++ /dev/null @@ -1,17 +0,0 @@ -#include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" - -namespace cms { - namespace cudacompat { - thread_local dim3 blockIdx; - thread_local dim3 gridDim; - } // namespace cudacompat -} // namespace cms - -namespace { - struct InitGrid { - InitGrid() { cms::cudacompat::resetGrid(); } - }; - - const InitGrid initGrid; - -} // namespace diff --git a/HeterogeneousCore/MPICore/BuildFile.xml b/HeterogeneousCore/MPICore/BuildFile.xml new file mode 100644 index 0000000000000..91b083944c269 --- /dev/null +++ b/HeterogeneousCore/MPICore/BuildFile.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/HeterogeneousCore/MPICore/interface/WrapperHandle.h b/HeterogeneousCore/MPICore/interface/WrapperHandle.h new file mode 100644 index 0000000000000..4e8ae1bfc4b0c --- /dev/null +++ b/HeterogeneousCore/MPICore/interface/WrapperHandle.h @@ -0,0 +1,232 @@ +#ifndef WrapperHandle_h +#define WrapperHandle_h + +#include +#include +#include +#include + +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "DataFormats/Common/interface/WrapperBase.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Utilities/interface/TypeID.h" + +namespace edm { + + // specialise Handle for a WrapperBase + + template <> + class Handle : public HandleBase { + public: + // default constructor + Handle() : HandleBase(), type_(nullptr) {} + + // throws exception if `type` is not a known C++ class type + Handle(std::string const& type) : HandleBase(), type_(&TypeWithDict::byName(type).typeInfo()) {} + + Handle(WrapperBase const* wrapper, Provenance const* prov, std::string const& type) + : HandleBase(wrapper, prov), type_(&TypeWithDict::byName(type).typeInfo()) {} + + // throws exception if `type` is invalid + Handle(std::type_info const& type) : HandleBase(), type_(&type) {} + + Handle(WrapperBase const* wrapper, Provenance const* prov, std::type_info const& type) + : HandleBase(wrapper, prov), type_(&type) {} + + // used when the attempt to get the data failed + Handle(std::shared_ptr&& whyFailed) : HandleBase(std::move(whyFailed)), type_(nullptr) {} + + // copiable and moveable + Handle(Handle const& h) = default; + Handle(Handle&& h) = default; + + Handle& operator=(Handle const& h) = default; + Handle& operator=(Handle&& h) = default; + + // (non-trivial) default destructor + ~Handle() = default; + + // reimplement swap over HandleBase + // DO NOT swap with a HandleBase or with a different Handle + void swap(Handle& other) { + HandleBase::swap(static_cast(other)); + std::swap(type_, other.type_); + } + + WrapperBase const* product() const { return static_cast(productStorage()); } + + WrapperBase const* operator->() const { return product(); } + + WrapperBase const& operator*() const { return *product(); } + + std::type_info const& typeInfo() const { return *type_; } + + private: + std::type_info const* type_ = nullptr; + }; + + // swap free function + + inline void swap(Handle& a, Handle& b) { a.swap(b); } + + // specialise the conversion from a BasicHandle into a Handle + + template <> + inline void convert_handle(BasicHandle&& bh, Handle& result) { + if (bh.failedToGet()) { + Handle h(std::move(bh.whyFailedFactory())); + result = std::move(h); + return; + } + WrapperBase const* wrapper = bh.wrapper(); + if (wrapper == nullptr) { + handleimpl::throwInvalidReference(); + } + if (not(wrapper->dynamicTypeInfo() == result.typeInfo())) { + handleimpl::throwConvertTypeError(result.typeInfo(), bh.wrapper()->dynamicTypeInfo()); + } + Handle h(wrapper, bh.provenance(), result.typeInfo()); + result = std::move(h); + } + + // specialise OrphanHandle for a WrapperBase + + template <> + class OrphanHandle : public OrphanHandleBase { + public: + // default constructed handles are invalid + OrphanHandle() : OrphanHandleBase(), type_(nullptr) {} + + // does not take ownership of the WrapperBase + // throws an exception if `type` is an invalid or unknown C++ class type + OrphanHandle(WrapperBase const* wrapper, std::string const& type, ProductID const& id) + : OrphanHandleBase(wrapper, id), type_(&TypeWithDict::byName(type).typeInfo()) {} + + // does not take ownership of the WrapperBase + // assumes `type` to be a valid and known C++ type + OrphanHandle(WrapperBase const* wrapper, std::type_info const& type, ProductID const& id) + : OrphanHandleBase(wrapper, id), type_(&type) {} + + // copiable and moveable + OrphanHandle(OrphanHandle const& h) = default; + OrphanHandle(OrphanHandle&& h) = default; + + OrphanHandle& operator=(OrphanHandle const& h) = default; + OrphanHandle& operator=(OrphanHandle&& h) = default; + + // default destructor + ~OrphanHandle() = default; + + // reimplement swap over OrphanHandleBase + // DO NOT swap with a OrphanHandleBase or with a different OrphanHandle + void swap(OrphanHandle& other) { + OrphanHandleBase::swap(static_cast(other)); + std::swap(type_, other.type_); + } + + WrapperBase const* product() const { return static_cast(productStorage()); } + + WrapperBase const* operator->() const { return product(); } + + WrapperBase const& operator*() const { return *product(); } + + std::type_info const& typeInfo() const { return *type_; } + + private: + std::type_info const* type_ = nullptr; + }; + + // swap free function + + inline void swap(OrphanHandle& a, OrphanHandle& b) { a.swap(b); } + + // specialise the Event methods for getting a WrapperBase + + template <> + inline bool Event::getByLabel(InputTag const& tag, Handle& result) const { + result.clear(); + BasicHandle bh = provRecorder_.getByLabel_(TypeID(result.typeInfo()), tag, moduleCallingContext_); + convert_handle(std::move(bh), result); // throws on conversion error + if (result.failedToGet()) { + return false; + } + addToGotBranchIDs(*result.provenance()); + return true; + } + + template <> + inline bool Event::getByLabel(std::string const& label, + std::string const& productInstanceName, + Handle& result) const { + result.clear(); + BasicHandle bh = provRecorder_.getByLabel_( + TypeID(result.typeInfo()), label, productInstanceName, emptyString_, moduleCallingContext_); + convert_handle(std::move(bh), result); // throws on conversion error + if (result.failedToGet()) { + return false; + } + addToGotBranchIDs(*result.provenance()); + return true; + } + + template <> + inline bool Event::getByToken(EDGetToken token, Handle& result) const { + result.clear(); + BasicHandle bh = provRecorder_.getByToken_(TypeID(result.typeInfo()), PRODUCT_TYPE, token, moduleCallingContext_); + convert_handle(std::move(bh), result); // throws on conversion error + if (result.failedToGet()) { + return false; + } + addToGotBranchIDs(*result.provenance()); + return true; + } + + template <> + inline bool Event::getByToken(EDGetTokenT token, Handle& result) const { + result.clear(); + BasicHandle bh = provRecorder_.getByToken_(TypeID(result.typeInfo()), PRODUCT_TYPE, token, moduleCallingContext_); + convert_handle(std::move(bh), result); // throws on conversion error + if (result.failedToGet()) { + return false; + } + addToGotBranchIDs(*result.provenance()); + return true; + } + + // specialise the Event methods for putting a WrapperBase + + template <> + inline OrphanHandle Event::putImpl(EDPutToken::value_type index, std::unique_ptr product) { + // the underlying collection `post_insert` is not called, as it is assumed + // it was done before creating the Wrapper + assert(index < putProducts().size()); + //putProducts()[index] = std::move(product->wrapper()); + putProducts()[index] = std::move(product); + WrapperBase const* prod = putProducts()[index].get(); + auto const& prodType = prod->dynamicTypeInfo(); + auto const& prodID = provRecorder_.getProductID(index); + return OrphanHandle(prod, prodType, prodID); + } + + template <> + inline OrphanHandle Event::put(EDPutToken token, std::unique_ptr product) { + auto const& typeInfo = product->dynamicTypeInfo(); + if (UNLIKELY(product.get() == nullptr)) { + // null pointer is illegal + principal_get_adapter_detail::throwOnPutOfNullProduct( + "Event", TypeID(typeInfo), provRecorder_.productInstanceLabel(token)); + } + if (UNLIKELY(token.isUninitialized())) { + principal_get_adapter_detail::throwOnPutOfUninitializedToken("Event", typeInfo); + } + if (UNLIKELY(provRecorder_.getTypeIDForPutTokenIndex(token.index()) != TypeID(typeInfo))) { + principal_get_adapter_detail::throwOnPutOfWrongType(typeInfo, + provRecorder_.getTypeIDForPutTokenIndex(token.index())); + } + return putImpl(token.index(), std::move(product)); + } + +} // namespace edm + +#endif // WrapperHandle_h diff --git a/HeterogeneousCore/MPICore/interface/serialization.h b/HeterogeneousCore/MPICore/interface/serialization.h new file mode 100644 index 0000000000000..698d9df14723c --- /dev/null +++ b/HeterogeneousCore/MPICore/interface/serialization.h @@ -0,0 +1,97 @@ +#ifndef HeterogeneousCore_MPICore_interface_serialization_h +#define HeterogeneousCore_MPICore_interface_serialization_h + +#include +#include +#include +#include +#include + +#include "DataFormats/Common/interface/WrapperBase.h" + +namespace io { + + // io::unique_buffer manages a memory buffer of run-time fixed size; + // the underlying memory is release when the io::unique_buffer is destroyed. + class unique_buffer { + public: + // default constructor + unique_buffer(); + + // allocate a new buffer of the specified size + unique_buffer(size_t size); + + // adopt an existing memory buffer of the specified size + unique_buffer(std::byte* data, size_t size); + unique_buffer(std::unique_ptr&& data, size_t size); + + // default copy and move constructors and assignment operators + unique_buffer(unique_buffer const&) = delete; + unique_buffer(unique_buffer&&) = default; + unique_buffer& operator=(unique_buffer const&) = delete; + unique_buffer& operator=(unique_buffer&&) = default; + + // default destructor, releasing the owned memory + ~unique_buffer() = default; + + // access to the underlying memory + std::byte const* data() const; + std::byte* data(); + + // release ownership of the underlying memory + std::byte* release(); + + // return the size of of the buffer + size_t size() const; + + private: + std::unique_ptr data_; + size_t size_; + }; + + // dump the content of a buffer, using the same format as `hd` + template + T& operator<<(T& out, unique_buffer const& buffer) { + auto data = reinterpret_cast(buffer.data()); + auto size = buffer.size(); + unsigned int l = 0; + for (; l < size / 16; ++l) { + out << std::setw(8) << std::setfill('0') << std::hex << l * 16 << std::dec; + for (unsigned int i = l * 16; i < (l + 1) * 16; ++i) { + out << ((i % 8 == 0) ? " " : " "); + out << std::setw(2) << std::setfill('0') << std::hex << (((int)data[i]) & 0xff) << std::dec; + } + out << " |"; + for (unsigned int i = l * 16; i < (l + 1) * 16; ++i) { + out << (char)(std::isprint(data[i]) ? data[i] : '.'); + } + out << "|\n"; + } + if (size % 16 != 0) { + out << std::setw(8) << std::setfill('0') << std::hex << l * 16 << std::dec; + for (unsigned int i = l * 16; i < size; ++i) { + out << ((i % 8 == 0) ? " " : " "); + out << std::setw(2) << std::setfill('0') << std::hex << (((int)data[i]) & 0xff) << std::dec; + } + for (unsigned int i = size; i < (l + 1) * 16; ++i) { + out << ((i % 8 == 0) ? " " : " "); + } + out << " |"; + for (unsigned int i = l * 16; i < size; ++i) { + out << (char)(std::isprint(data[i]) ? data[i] : '.'); + } + out << "|\n"; + } + out << std::setw(8) << std::setfill('0') << std::hex << size << std::dec << '\n'; + return out; + } + + // serialise a Wrapper into an io::unique_buffer + io::unique_buffer serialize(edm::WrapperBase const& wrapper); + + // deserialise a Wrapper from an io::unique_buffer and store it in a unique_ptr + std::unique_ptr deserialize(io::unique_buffer const& buffer); + +} // namespace io + +#endif // HeterogeneousCore_MPICore_interface_serialization_h diff --git a/HeterogeneousCore/MPICore/src/serialization.cc b/HeterogeneousCore/MPICore/src/serialization.cc new file mode 100644 index 0000000000000..661fec9cb7959 --- /dev/null +++ b/HeterogeneousCore/MPICore/src/serialization.cc @@ -0,0 +1,70 @@ +#include +#include + +#include +#include + +#include "HeterogeneousCore/MPICore/interface/serialization.h" + +namespace io { + + // default constructor + unique_buffer::unique_buffer() : data_(nullptr), size_(0) {} + + // allocate a new buffer of the specified size + unique_buffer::unique_buffer(size_t size) : data_(new std::byte[size]), size_(size) {} + + // adopt an existing memory buffer of the specified size + unique_buffer::unique_buffer(std::byte* data, size_t size) : data_(data), size_(size) {} + + // adopt an existing memory buffer of the specified size + unique_buffer::unique_buffer(std::unique_ptr&& data, size_t size) : data_(std::move(data)), size_(size) {} + + // access to the underlying memory + std::byte const* unique_buffer::data() const { return data_.get(); } + + // access to the underlying memory + std::byte* unique_buffer::data() { return data_.get(); } + + // release ownership of the underlying memory + std::byte* unique_buffer::release() { return data_.release(); } + + // return the size of of the buffer + size_t unique_buffer::size() const { return size_; } + + // serialise a Wrapper into a char[] via a TBufferFile + unique_buffer serialize(edm::WrapperBase const& wrapper) { + /* TODO + * construct the buffer with an initial size based on the wrapped class size ? + * take into account any offset to/from the edm::WrapperBase base class ? + */ + TBufferFile serializer(TBuffer::kWrite); + serializer.WriteObjectAny(&wrapper, TClass::GetClass(wrapper.wrappedTypeInfo())); + + size_t size = serializer.Length(); + std::byte* data = reinterpret_cast(serializer.Buffer()); + serializer.DetachBuffer(); + + return unique_buffer(data, size); + } + + std::unique_ptr deserialize(unique_buffer const& buffer) { + TBufferFile deserializer(TBuffer::kRead, buffer.size(), (void*)buffer.data(), false); + + /* TODO try different versions: + * does not work ? + return std::unique_ptr(reinterpret_cast(deserializer.ReadObjectAny(edmWrapperBaseClass))); + * works but maybe not always ? + return std::unique_ptr(reinterpret_cast(deserializer.ReadObjectAny(nullptr))); + * not useful ? + return std::unique_ptr(reinterpret_cast(deserializer.ReadObjectAny(rootType))); + * not useful ? + static TClass const* edmWrapperBaseClass = TClass::GetClass(typeid(edm::WrapperBase)); + TClass * rootType = wrappedType.getClass(); + int offset = rootType->GetBaseClassOffset(edmWrapperBaseClass); + return std::unique_ptr(reinterpret_cast(reinterpret_cast(deserializer.ReadObjectAny(rootType)) + offset)); + */ + return std::unique_ptr(reinterpret_cast(deserializer.ReadObjectAny(nullptr))); + } + +} // namespace io diff --git a/RecoLocalCalo/Configuration/python/customizeEcalOnlyForProfiling.py b/RecoLocalCalo/Configuration/python/customizeEcalOnlyForProfiling.py new file mode 100644 index 0000000000000..4fa955bd33836 --- /dev/null +++ b/RecoLocalCalo/Configuration/python/customizeEcalOnlyForProfiling.py @@ -0,0 +1,51 @@ +import FWCore.ParameterSet.Config as cms + +# Customise the ECAL-only reconstruction to run on GPU +# +# Currently, this means running only the unpacker and multifit, up to the uncalbrated rechits +def customizeEcalOnlyForProfilingGPUOnly(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('ecalMultiFitUncalibRecHitGPU') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process + + +# Customise the ECAL-only reconstruction to run on GPU, and copy the data to the host +# +# Currently, this means running only the unpacker and multifit, up to the uncalbrated rechits +def customizeEcalOnlyForProfilingGPUWithHostCopy(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('ecalMultiFitUncalibRecHitSoA') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process + + +# Customise the ECAL-only reconstruction to run on GPU, copy the data to the host, and convert to legacy format +# +# Currently, this means running only the unpacker and multifit, up to the uncalbrated rechits, on the GPU +# and the rechits producer on the CPU +# +# The same customisation can be also used on the CPU workflow, running up to the rechits on CPU. +def customizeEcalOnlyForProfiling(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('ecalRecHit') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process diff --git a/RecoLocalCalo/Configuration/python/customizeHcalOnlyForProfiling.py b/RecoLocalCalo/Configuration/python/customizeHcalOnlyForProfiling.py new file mode 100644 index 0000000000000..b3a2548791ae5 --- /dev/null +++ b/RecoLocalCalo/Configuration/python/customizeHcalOnlyForProfiling.py @@ -0,0 +1,60 @@ +import FWCore.ParameterSet.Config as cms + +# Customise the HCAL-only reconstruction to run on GPU +# +# Currently, this means: +# - running the unpacker on CPU, converting the digis into SoA format and copying them to GPU; +# - running the HBHE local reconstruction, including MAHI, on GPU. +def customizeHcalOnlyForProfilingGPUOnly(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('hbheRecHitProducerGPU') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process + + +# Customise the HCAL-only reconstruction to run on GPU, and copy the data to the host +# +# Currently, this means: +# - running the unpacker on CPU, converting the digis into SoA format and copying them to GPU; +# - running the HBHE local reconstruction, including MAHI, on GPU; +# - copying the rechits to CPU and converting them to legacy format. +# +# (this is equivalent to customizeHcalOnlyForProfiling, as the copy and conversion is done by the same module) +def customizeHcalOnlyForProfilingGPUWithHostCopy(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('hbheprereco') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process + + +# Customise the HCAL-only reconstruction to run on GPU, copy the data to the host, and convert to legacy format +# +# Currently, this means: +# - running the unpacker on CPU, converting the digis into SoA format and copying them to GPU; +# - running the HBHE local reconstruction, including MAHI, on GPU; +# - copying the rechits to CPU and converting them to legacy format. +# +# The same customisation can be also used on the CPU workflow, running up to the rechits on CPU. +def customizeHcalOnlyForProfiling(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('hbheprereco') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process diff --git a/RecoLocalCalo/Configuration/python/ecalLocalRecoSequence_cff.py b/RecoLocalCalo/Configuration/python/ecalLocalRecoSequence_cff.py index 06fecf4787baf..75ae5fc0c202f 100644 --- a/RecoLocalCalo/Configuration/python/ecalLocalRecoSequence_cff.py +++ b/RecoLocalCalo/Configuration/python/ecalLocalRecoSequence_cff.py @@ -1,4 +1,5 @@ import FWCore.ParameterSet.Config as cms +from Configuration.ProcessModifiers.gpu_cff import gpu # TPG condition needed by ecalRecHit producer if TT recovery is ON from RecoLocalCalo.EcalRecProducers.ecalRecHitTPGConditions_cff import * @@ -43,6 +44,61 @@ ecalOnlyLocalRecoSequence = cms.Sequence(ecalOnlyLocalRecoTask) +# ECAL rechit calibrations on GPU +from RecoLocalCalo.EcalRecProducers.ecalRechitADCToGeVConstantGPUESProducer_cfi import ecalRechitADCToGeVConstantGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalRechitChannelStatusGPUESProducer_cfi import ecalRechitChannelStatusGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalIntercalibConstantsGPUESProducer_cfi import ecalIntercalibConstantsGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosGPUESProducer_cfi import ecalLaserAPDPNRatiosGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosRefGPUESProducer_cfi import ecalLaserAPDPNRatiosRefGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalLaserAlphasGPUESProducer_cfi import ecalLaserAlphasGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalLinearCorrectionsGPUESProducer_cfi import ecalLinearCorrectionsGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalRecHitParametersGPUESProducer_cfi import ecalRecHitParametersGPUESProducer + +# ECAL rechits running on GPU +from RecoLocalCalo.EcalRecProducers.ecalRecHitGPU_cfi import ecalRecHitGPU as _ecalRecHitGPU +ecalRecHitGPU = _ecalRecHitGPU.clone( + uncalibrecHitsInLabelEB = cms.InputTag('ecalMultiFitUncalibRecHitGPU', 'EcalUncalibRecHitsEB'), + uncalibrecHitsInLabelEE = cms.InputTag('ecalMultiFitUncalibRecHitGPU', 'EcalUncalibRecHitsEE') +) + +# copy the rechits from GPU to CPU +from RecoLocalCalo.EcalRecProducers.ecalCPURecHitProducer_cfi import ecalCPURecHitProducer as _ecalCPURecHitProducer +ecalRecHitSoA = _ecalCPURecHitProducer.clone( + recHitsInLabelEB = cms.InputTag('ecalRecHitGPU', 'EcalRecHitsEB'), + recHitsInLabelEE = cms.InputTag('ecalRecHitGPU', 'EcalRecHitsEE') +) + +# convert the rechits from SoA to legacy format +from RecoLocalCalo.EcalRecProducers.ecalRecHitConvertGPU2CPUFormat_cfi import ecalRecHitConvertGPU2CPUFormat as _ecalRecHitConvertGPU2CPUFormat +_ecalRecHit_gpu = _ecalRecHitConvertGPU2CPUFormat.clone( + recHitsLabelGPUEB = cms.InputTag('ecalRecHitSoA', 'EcalRecHitsEB'), + recHitsLabelGPUEE = cms.InputTag('ecalRecHitSoA', 'EcalRecHitsEE') +) +# TODO: the ECAL calibrated rechits produced on the GPU are not correct, yet. +# When they are working and validated, remove this comment and uncomment the next line: +#gpu.toReplaceWith(ecalRecHit, _ecalRecHit_gpu) + +# ECAL reconstruction on GPU +gpu.toReplaceWith(ecalRecHitNoTPTask, cms.Task( + # ECAL rechit calibrations on GPU + ecalRechitADCToGeVConstantGPUESProducer, + ecalRechitChannelStatusGPUESProducer, + ecalIntercalibConstantsGPUESProducer, + ecalLaserAPDPNRatiosGPUESProducer, + ecalLaserAPDPNRatiosRefGPUESProducer, + ecalLaserAlphasGPUESProducer, + ecalLinearCorrectionsGPUESProducer, + ecalRecHitParametersGPUESProducer, + # ECAL rechits running on GPU + ecalRecHitGPU, + # copy the rechits from GPU to CPU + ecalRecHitSoA, + # convert the rechits from SoA to legacy format + ecalRecHit, + # ECAL preshower rechit legacy module + ecalPreshowerRecHit +)) + # Phase 2 modifications from RecoLocalCalo.EcalRecProducers.ecalDetailedTimeRecHit_cfi import * _phase2_timing_ecalRecHitTask = cms.Task( ecalRecHitTask.copy() , ecalDetailedTimeRecHit ) diff --git a/RecoLocalCalo/Configuration/python/hcalGlobalReco_cff.py b/RecoLocalCalo/Configuration/python/hcalGlobalReco_cff.py index 70207e36ba654..fbb4c53f9f28b 100644 --- a/RecoLocalCalo/Configuration/python/hcalGlobalReco_cff.py +++ b/RecoLocalCalo/Configuration/python/hcalGlobalReco_cff.py @@ -4,7 +4,18 @@ hcalGlobalRecoTask = cms.Task(hbhereco) hcalGlobalRecoSequence = cms.Sequence(hcalGlobalRecoTask) +#--- for Run 3 and later +from Configuration.Eras.Modifier_run3_HB_cff import run3_HB + from RecoLocalCalo.HcalRecProducers.HBHEPhase1Reconstructor_cfi import hbheprereco as _phase1_hbheprereco +run3_HB.toReplaceWith(hbhereco, _phase1_hbheprereco) -from Configuration.Eras.Modifier_run3_HB_cff import run3_HB -run3_HB.toReplaceWith( hbhereco, _phase1_hbheprereco ) # >=Run3 +#--- for Run 3 on GPU +from Configuration.ProcessModifiers.gpu_cff import gpu + +from RecoLocalCalo.HcalRecProducers.hcalCPURecHitsProducer_cfi import hcalCPURecHitsProducer as _hcalCPURecHitsProducer +gpu.toReplaceWith(hbhereco, _hcalCPURecHitsProducer.clone( + recHitsM0LabelIn = "hbheRecHitProducerGPU", + recHitsM0LabelOut = "", + recHitsLegacyLabelOut = "" +)) diff --git a/RecoLocalCalo/Configuration/python/hcalLocalReco_cff.py b/RecoLocalCalo/Configuration/python/hcalLocalReco_cff.py index 84e7498b7eb2d..a7bdce3b916af 100644 --- a/RecoLocalCalo/Configuration/python/hcalLocalReco_cff.py +++ b/RecoLocalCalo/Configuration/python/hcalLocalReco_cff.py @@ -16,20 +16,17 @@ from RecoLocalCalo.HcalRecProducers.HcalHitReconstructor_ho_cfi import * from RecoLocalCalo.HcalRecProducers.HcalHitReconstructor_hf_cfi import * from RecoLocalCalo.HcalRecProducers.HcalHitReconstructor_zdc_cfi import * -hcalLocalRecoTask = cms.Task(hbheprereco,hfreco,horeco,zdcreco) +hcalLocalRecoTask = cms.Task(hbheprereco, hfreco, horeco, zdcreco) hcalLocalRecoSequence = cms.Sequence(hcalLocalRecoTask) from RecoLocalCalo.HcalRecProducers.hfprereco_cfi import hfprereco from RecoLocalCalo.HcalRecProducers.HFPhase1Reconstructor_cfi import hfreco as _phase1_hfreco from RecoLocalCalo.HcalRecProducers.hbheplan1_cfi import hbheplan1 -#--- for HCALonly wf -hcalOnlyLocalRecoTask = cms.Task(hbheprereco,hfprereco,hfreco,horeco) - -# copy for cosmics +#--- for cosmics _default_hfreco = hfreco.clone() -#--- Phase1 +#--- for Phase 1 _phase1_hcalLocalRecoTask = hcalLocalRecoTask.copy() _phase1_hcalLocalRecoTask.add(hfprereco) @@ -37,7 +34,7 @@ run2_HF_2017.toReplaceWith( hcalLocalRecoTask, _phase1_hcalLocalRecoTask ) run2_HF_2017.toReplaceWith( hfreco, _phase1_hfreco ) from Configuration.Eras.Modifier_run2_HCAL_2017_cff import run2_HCAL_2017 -run2_HCAL_2017.toReplaceWith( hbheprereco, _phase1_hbheprereco ) +run2_HCAL_2017.toReplaceWith(hbheprereco, _phase1_hbheprereco) _plan1_hcalLocalRecoTask = _phase1_hcalLocalRecoTask.copy() _plan1_hcalLocalRecoTask.add(hbheplan1) @@ -50,12 +47,39 @@ from Configuration.ProcessModifiers.run2_HECollapse_2018_cff import run2_HECollapse_2018 run2_HECollapse_2018.toReplaceWith(hcalLocalRecoTask, _collapse_hcalLocalRecoTask) -#--- from >=Run3 +#--- for Run 3 and later _run3_hcalLocalRecoTask = _phase1_hcalLocalRecoTask.copy() _run3_hcalLocalRecoTask.remove(hbheprereco) from Configuration.Eras.Modifier_run3_HB_cff import run3_HB -run3_HB.toReplaceWith( hcalLocalRecoTask, _run3_hcalLocalRecoTask ) +run3_HB.toReplaceWith(hcalLocalRecoTask, _run3_hcalLocalRecoTask) + +#--- for Run 3 on GPU +from Configuration.ProcessModifiers.gpu_cff import gpu + +from RecoLocalCalo.HcalRecProducers.hbheRecHitProducerGPUTask_cff import * +_run3_hcalLocalRecoGPUTask = _run3_hcalLocalRecoTask.copy() +_run3_hcalLocalRecoGPUTask.add(hbheRecHitProducerGPUTask) +gpu.toReplaceWith(hcalLocalRecoTask, _run3_hcalLocalRecoGPUTask) + +#--- HCAL-only workflow for Run 3 +# FIXME rename `hbheprereco` to `hbhereco` and use it from hcalGlobalRecoTask +hcalOnlyLocalRecoTask = cms.Task(hbheprereco, hfprereco, hfreco, horeco) + +#--- HCAL-only workflow for Run 3 on GPU +from Configuration.ProcessModifiers.gpu_cff import gpu + +_hcalOnlyLocalRecoGPUTask = hcalOnlyLocalRecoTask.copy() +_hcalOnlyLocalRecoGPUTask.add(hbheRecHitProducerGPUTask) +gpu.toReplaceWith(hcalOnlyLocalRecoTask, _hcalOnlyLocalRecoGPUTask) + +from RecoLocalCalo.HcalRecProducers.hcalCPURecHitsProducer_cfi import hcalCPURecHitsProducer as _hcalCPURecHitsProducer +gpu.toReplaceWith(hbheprereco, _hcalCPURecHitsProducer.clone( + recHitsM0LabelIn = "hbheRecHitProducerGPU", + recHitsM0LabelOut = "", + recHitsLegacyLabelOut = "" +)) +#--- for FastSim _fastSim_hcalLocalRecoTask = hcalLocalRecoTask.copyAndExclude([zdcreco]) from Configuration.Eras.Modifier_fastSim_cff import fastSim fastSim.toReplaceWith( hcalLocalRecoTask, _fastSim_hcalLocalRecoTask ) diff --git a/RecoLocalCalo/EcalRecAlgos/BuildFile.xml b/RecoLocalCalo/EcalRecAlgos/BuildFile.xml index 2eaf053c342dd..c2858ae76d7bc 100644 --- a/RecoLocalCalo/EcalRecAlgos/BuildFile.xml +++ b/RecoLocalCalo/EcalRecAlgos/BuildFile.xml @@ -1,9 +1,13 @@ + + + + @@ -11,6 +15,7 @@ + diff --git a/RecoLocalCalo/EcalRecAlgos/bin/BuildFile.xml b/RecoLocalCalo/EcalRecAlgos/bin/BuildFile.xml new file mode 100644 index 0000000000000..4c98171091b84 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/bin/BuildFile.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/RecoLocalCalo/EcalRecAlgos/bin/makeEcalMultifitResultsGpuValidationPlots.cpp b/RecoLocalCalo/EcalRecAlgos/bin/makeEcalMultifitResultsGpuValidationPlots.cpp new file mode 100644 index 0000000000000..f010e3afdbb18 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/bin/makeEcalMultifitResultsGpuValidationPlots.cpp @@ -0,0 +1,564 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "DataFormats/Common/interface/Wrapper.h" +#include "DataFormats/EcalRecHit/interface/EcalUncalibratedRecHit.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHitCollections.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" + +#include "TStyle.h" + +void setAxis(TH2D *histo) { + histo->GetXaxis()->SetTitle("cpu"); + histo->GetYaxis()->SetTitle("gpu"); +} + +void setAxisDelta(TH2D *histo) { + histo->GetXaxis()->SetTitle("cpu"); + histo->GetYaxis()->SetTitle("#Delta gpu-cpu"); +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + std::cout << "run with: ./validateGPU \n"; + exit(0); + } + + gStyle->SetOptStat("ourme"); + + edm::Wrapper>> *wgpuEB = + nullptr; + edm::Wrapper>> *wgpuEE = + nullptr; + edm::Wrapper *wcpuEB = nullptr; + edm::Wrapper *wcpuEE = nullptr; + + std::string fileName = argv[1]; + std::string outFileName = argv[2]; + + // output + TFile rfout{outFileName.c_str(), "recreate"}; + + int nbins_count = 200; + float last_count = 5000.; + int nbins_count_delta = 201; + + int nbins = 300; + float last = 3000.; + + // int nbins_chi2 = 1000; + // float last_chi2 = 1000.; + int nbins_chi2 = 1000; + float last_chi2 = 200.; + + int nbins_flags = 100; + float last_flags = 100.; + float delta_flags = 20; + + int nbins_delta = 201; // use an odd number to center around 0 + float delta = 0.2; + + // RecHits plots for EB and EE on both GPU and CPU + auto hRechitsEBGPU = new TH1D("RechitsEBGPU", "RechitsEBGPU; No. of Rechits", nbins_count, 0, last_count); + auto hRechitsEBCPU = new TH1D("RechitsEBCPU", "RechitsEBCPU; No. of Rechits", nbins_count, 0, last_count); + auto hRechitsEEGPU = new TH1D("RechitsEEGPU", "RechitsEEGPU; No. of Rechits", nbins_count, 0, last_count); + auto hRechitsEECPU = new TH1D("RechitsEECPU", "RechitsEECPU; No. of Rechits", nbins_count, 0, last_count); + auto hRechitsEBGPUCPUratio = new TH1D("RechitsEBGPU/CPUratio", "RechitsEBGPU/CPUratio; GPU/CPU", 50, 0.9, 1.1); + auto hRechitsEEGPUCPUratio = new TH1D("RechitsEEGPU/CPUratio", "RechitsEEGPU/CPUratio; GPU/CPU", 50, 0.9, 1.1); + + auto hSOIAmplitudesEBGPU = new TH1D("hSOIAmplitudesEBGPU", "hSOIAmplitudesEBGPU", nbins, 0, last); + auto hSOIAmplitudesEEGPU = new TH1D("hSOIAmplitudesEEGPU", "hSOIAmplitudesEEGPU", nbins, 0, last); + auto hSOIAmplitudesEBCPU = new TH1D("hSOIAmplitudesEBCPU", "hSOIAmplitudesEBCPU", nbins, 0, last); + auto hSOIAmplitudesEECPU = new TH1D("hSOIAmplitudesEECPU", "hSOIAmplitudesEECPU", nbins, 0, last); + auto hSOIAmplitudesEBGPUCPUratio = + new TH1D("SOIAmplitudesEBGPU/CPUratio", "SOIAmplitudesEBGPU/CPUratio; GPU/CPU", 200, 0.9, 1.1); + auto hSOIAmplitudesEEGPUCPUratio = + new TH1D("SOIAmplitudesEEGPU/CPUratio", "SOIAmplitudesEEGPU/CPUratio; GPU/CPU", 200, 0.9, 1.1); + + auto hChi2EBGPU = new TH1D("hChi2EBGPU", "hChi2EBGPU", nbins_chi2, 0, last_chi2); + auto hChi2EEGPU = new TH1D("hChi2EEGPU", "hChi2EEGPU", nbins_chi2, 0, last_chi2); + auto hChi2EBCPU = new TH1D("hChi2EBCPU", "hChi2EBCPU", nbins_chi2, 0, last_chi2); + auto hChi2EECPU = new TH1D("hChi2EECPU", "hChi2EECPU", nbins_chi2, 0, last_chi2); + auto hChi2EBGPUCPUratio = new TH1D("Chi2EBGPU/CPUratio", "Chi2EBGPU/CPUratio; GPU/CPU", 200, 0.9, 1.1); + auto hChi2EEGPUCPUratio = new TH1D("Chi2EEGPU/CPUratio", "Chi2EEGPU/CPUratio; GPU/CPU", 200, 0.9, 1.1); + + auto hFlagsEBGPU = new TH1D("hFlagsEBGPU", "hFlagsEBGPU", nbins_flags, 0, last_flags); + auto hFlagsEEGPU = new TH1D("hFlagsEEGPU", "hFlagsEEGPU", nbins_flags, 0, last_flags); + auto hFlagsEBCPU = new TH1D("hFlagsEBCPU", "hFlagsEBCPU", nbins_flags, 0, last_flags); + auto hFlagsEECPU = new TH1D("hFlagsEECPU", "hFlagsEECPU", nbins_flags, 0, last_flags); + auto hFlagsEBGPUCPUratio = new TH1D("FlagsEBGPU/CPUratio", "FlagsEBGPU/CPUratio; GPU/CPU", 200, 0.9, 1.1); + auto hFlagsEEGPUCPUratio = new TH1D("FlagsEEGPU/CPUratio", "FlagsEEGPU/CPUratio; GPU/CPU", 200, 0.9, 1.1); + + auto hSOIAmplitudesEBGPUvsCPU = + new TH2D("hSOIAmplitudesEBGPUvsCPU", "hSOIAmplitudesEBGPUvsCPU", nbins, 0, last, nbins, 0, last); + setAxis(hSOIAmplitudesEBGPUvsCPU); + auto hSOIAmplitudesEEGPUvsCPU = + new TH2D("hSOIAmplitudesEEGPUvsCPU", "hSOIAmplitudesEEGPUvsCPU", nbins, 0, last, nbins, 0, last); + setAxis(hSOIAmplitudesEEGPUvsCPU); + auto hSOIAmplitudesEBdeltavsCPU = + new TH2D("hSOIAmplitudesEBdeltavsCPU", "hSOIAmplitudesEBdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + setAxisDelta(hSOIAmplitudesEBdeltavsCPU); + auto hSOIAmplitudesEEdeltavsCPU = + new TH2D("hSOIAmplitudesEEdeltavsCPU", "hSOIAmplitudesEEdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + setAxisDelta(hSOIAmplitudesEEdeltavsCPU); + + auto hChi2EBGPUvsCPU = + new TH2D("hChi2EBGPUvsCPU", "hChi2EBGPUvsCPU", nbins_chi2, 0, last_chi2, nbins_chi2, 0, last_chi2); + setAxis(hChi2EBGPUvsCPU); + auto hChi2EEGPUvsCPU = + new TH2D("hChi2EEGPUvsCPU", "hChi2EEGPUvsCPU", nbins_chi2, 0, last_chi2, nbins_chi2, 0, last_chi2); + setAxis(hChi2EEGPUvsCPU); + auto hChi2EBdeltavsCPU = + new TH2D("hChi2EBdeltavsCPU", "hChi2EBdeltavsCPU", nbins_chi2, 0, last_chi2, nbins_delta, -delta, delta); + setAxisDelta(hChi2EBdeltavsCPU); + auto hChi2EEdeltavsCPU = + new TH2D("hChi2EEdeltavsCPU", "hChi2EEdeltavsCPU", nbins_chi2, 0, last_chi2, nbins_delta, -delta, delta); + setAxisDelta(hChi2EEdeltavsCPU); + + auto hFlagsEBGPUvsCPU = + new TH2D("hFlagsEBGPUvsCPU", "hFlagsEBGPUvsCPU", nbins_flags, 0, last_flags, nbins_flags, 0, last_flags); + setAxis(hFlagsEBGPUvsCPU); + auto hFlagsEEGPUvsCPU = + new TH2D("hFlagsEEGPUvsCPU", "hFlagsEEGPUvsCPU", nbins_flags, 0, last_flags, nbins_flags, 0, last_flags); + setAxis(hFlagsEEGPUvsCPU); + auto hFlagsEBdeltavsCPU = new TH2D( + "hFlagsEBdeltavsCPU", "hFlagsEBdeltavsCPU", nbins_flags, 0, last_flags, nbins_delta, -delta_flags, delta_flags); + setAxisDelta(hFlagsEBdeltavsCPU); + auto hFlagsEEdeltavsCPU = new TH2D( + "hFlagsEEdeltavsCPU", "hFlagsEEdeltavsCPU", nbins_flags, 0, last_flags, nbins_delta, -delta_flags, delta_flags); + setAxisDelta(hFlagsEEdeltavsCPU); + + auto hRechitsEBGPUvsCPU = new TH2D( + "RechitsEBGPUvsCPU", "RechitsEBGPUvsCPU; CPU; GPU", last_count, 0, last_count, last_count, 0, last_count); + setAxis(hRechitsEBGPUvsCPU); + auto hRechitsEEGPUvsCPU = new TH2D( + "RechitsEEGPUvsCPU", "RechitsEEGPUvsCPU; CPU; GPU", last_count, 0, last_count, last_count, 0, last_count); + setAxis(hRechitsEEGPUvsCPU); + auto hRechitsEBdeltavsCPU = new TH2D( + "RechitsEBdeltavsCPU", "RechitsEBdeltavsCPU", nbins_count, 0, last_count, nbins_count_delta, -delta, delta); + setAxisDelta(hRechitsEBdeltavsCPU); + auto hRechitsEEdeltavsCPU = new TH2D( + "RechitsEEdeltavsCPU", "RechitsEEdeltavsCPU", nbins_count, 0, last_count, nbins_count_delta, -delta, delta); + setAxisDelta(hRechitsEEdeltavsCPU); + + // input + std::cout << "validating file " << fileName << std::endl; + TFile rf{fileName.c_str()}; + TTree *rt = (TTree *)rf.Get("Events"); + rt->SetBranchAddress( + "calocommonCUDAHostAllocatorAliascalocommonVecStoragePolicyecalUncalibratedRecHit_ecalCPUUncalibRecHitProducer_" + "EcalUncalibRecHitsEB_RECO.", + &wgpuEB); + rt->SetBranchAddress( + "calocommonCUDAHostAllocatorAliascalocommonVecStoragePolicyecalUncalibratedRecHit_ecalCPUUncalibRecHitProducer_" + "EcalUncalibRecHitsEE_RECO.", + &wgpuEE); + rt->SetBranchAddress("EcalUncalibratedRecHitsSorted_ecalMultiFitUncalibRecHit_EcalUncalibRecHitsEB_RECO.", &wcpuEB); + rt->SetBranchAddress("EcalUncalibratedRecHitsSorted_ecalMultiFitUncalibRecHit_EcalUncalibRecHitsEE_RECO.", &wcpuEE); + + constexpr float eps_diff = 1e-3; + + // accumulate + auto const nentries = rt->GetEntries(); + std::cout << "#events to validate over: " << nentries << std::endl; + for (int ie = 0; ie < nentries; ++ie) { + rt->GetEntry(ie); + + const char *ordinal[] = {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"}; + auto cpu_eb_size = wcpuEB->bareProduct().size(); + auto cpu_ee_size = wcpuEE->bareProduct().size(); + auto gpu_eb_size = wgpuEB->bareProduct().amplitude.size(); + auto gpu_ee_size = wgpuEE->bareProduct().amplitude.size(); + + float eb_ratio = (float)gpu_eb_size / cpu_eb_size; + float ee_ratio = (float)gpu_ee_size / cpu_ee_size; + + // Filling up the histograms on events sizes for EB and EE on both GPU and CPU + hRechitsEBGPU->Fill(gpu_eb_size); + hRechitsEBCPU->Fill(cpu_eb_size); + hRechitsEEGPU->Fill(gpu_ee_size); + hRechitsEECPU->Fill(cpu_ee_size); + hRechitsEBGPUvsCPU->Fill(cpu_eb_size, gpu_eb_size); + hRechitsEEGPUvsCPU->Fill(cpu_ee_size, gpu_ee_size); + hRechitsEBGPUCPUratio->Fill(eb_ratio); + hRechitsEEGPUCPUratio->Fill(ee_ratio); + hRechitsEBdeltavsCPU->Fill(cpu_eb_size, gpu_eb_size - cpu_eb_size); + hRechitsEEdeltavsCPU->Fill(cpu_ee_size, gpu_ee_size - cpu_ee_size); + + if (cpu_eb_size != gpu_eb_size or cpu_ee_size != gpu_ee_size) { + std::cerr << ie << ordinal[ie % 10] << " entry:\n" + << " EB size: " << std::setw(4) << cpu_eb_size << " (cpu) vs " << std::setw(4) << gpu_eb_size + << " (gpu)\n" + << " EE size: " << std::setw(4) << cpu_ee_size << " (cpu) vs " << std::setw(4) << gpu_ee_size + << " (gpu)" << std::endl; + continue; + } + + assert(wgpuEB->bareProduct().amplitude.size() == wcpuEB->bareProduct().size()); + assert(wgpuEE->bareProduct().amplitude.size() == wcpuEE->bareProduct().size()); + auto const neb = wcpuEB->bareProduct().size(); + auto const nee = wcpuEE->bareProduct().size(); + + for (uint32_t i = 0; i < neb; ++i) { + auto const did_gpu = wgpuEB->bareProduct().did[i]; + auto const soi_amp_gpu = wgpuEB->bareProduct().amplitude[i]; + auto const cpu_iter = wcpuEB->bareProduct().find(DetId{did_gpu}); + if (cpu_iter == wcpuEB->bareProduct().end()) { + std::cerr << ie << ordinal[ie % 10] << " entry\n" + << " Did not find a DetId " << did_gpu << " in a CPU collection\n"; + continue; + } + auto const soi_amp_cpu = cpu_iter->amplitude(); + auto const chi2_gpu = wgpuEB->bareProduct().chi2[i]; + auto const chi2_cpu = cpu_iter->chi2(); + + auto const flags_gpu = wgpuEB->bareProduct().flags[i]; + auto const flags_cpu = cpu_iter->flags(); + + hSOIAmplitudesEBGPU->Fill(soi_amp_gpu); + hSOIAmplitudesEBCPU->Fill(soi_amp_cpu); + hSOIAmplitudesEBGPUvsCPU->Fill(soi_amp_cpu, soi_amp_gpu); + hSOIAmplitudesEBdeltavsCPU->Fill(soi_amp_cpu, soi_amp_gpu - soi_amp_cpu); + if (soi_amp_cpu > 0) + hSOIAmplitudesEBGPUCPUratio->Fill((float)soi_amp_gpu / soi_amp_cpu); + + hChi2EBGPU->Fill(chi2_gpu); + hChi2EBCPU->Fill(chi2_cpu); + hChi2EBGPUvsCPU->Fill(chi2_cpu, chi2_gpu); + hChi2EBdeltavsCPU->Fill(chi2_cpu, chi2_gpu - chi2_cpu); + if (chi2_cpu > 0) + hChi2EBGPUCPUratio->Fill((float)chi2_gpu / chi2_cpu); + + if (std::abs(chi2_gpu / chi2_cpu - 1) > 0.05 || std::abs(soi_amp_gpu / soi_amp_cpu - 1) > 0.05) { + std::cout << " ---- EB " << std::endl; + std::cout << " eventid = " << ie << " xtal = " << i << std::endl; + std::cout << " chi2_gpu = " << chi2_gpu << " chi2_cpu = " << chi2_cpu << std::endl; + std::cout << " soi_amp_gpu = " << soi_amp_gpu << " soi_amp_cpu = " << soi_amp_cpu << std::endl; + std::cout << " flags_gpu = " << flags_gpu << " flags_cpu = " << flags_cpu << std::endl; + } + + hFlagsEBGPU->Fill(flags_gpu); + hFlagsEBCPU->Fill(flags_cpu); + hFlagsEBGPUvsCPU->Fill(flags_cpu, flags_gpu); + hFlagsEBdeltavsCPU->Fill(flags_cpu, flags_gpu - flags_cpu); + if (flags_cpu > 0) + hFlagsEBGPUCPUratio->Fill((float)flags_gpu / flags_cpu); + + if (flags_cpu != flags_gpu) { + std::cout << " >> No! Different flag cpu:gpu = " << flags_cpu << " : " << flags_gpu; + std::cout << std::endl; + } + + if ((std::abs(soi_amp_gpu - soi_amp_cpu) >= eps_diff) or (std::abs(chi2_gpu - chi2_cpu) >= eps_diff) or + std::isnan(chi2_gpu) or (flags_cpu != flags_gpu)) { + printf("EB eventid = %d chid = %d amp_gpu = %f amp_cpu %f chi2_gpu = %f chi2_cpu = %f\n", + ie, + i, + soi_amp_gpu, + soi_amp_cpu, + chi2_gpu, + chi2_cpu); + if (std::isnan(chi2_gpu)) + printf("*** nan ***\n"); + } + } + + for (uint32_t i = 0; i < nee; ++i) { + auto const did_gpu = wgpuEE->bareProduct().did[i]; + auto const soi_amp_gpu = wgpuEE->bareProduct().amplitude[i]; + auto const cpu_iter = wcpuEE->bareProduct().find(DetId{did_gpu}); + if (cpu_iter == wcpuEE->bareProduct().end()) { + std::cerr << ie << ordinal[ie % 10] << " entry\n" + << " did not find a DetId " << did_gpu << " in a CPU collection\n"; + continue; + } + auto const soi_amp_cpu = cpu_iter->amplitude(); + auto const chi2_gpu = wgpuEE->bareProduct().chi2[i]; + auto const chi2_cpu = cpu_iter->chi2(); + + auto const flags_gpu = wgpuEE->bareProduct().flags[i]; + auto const flags_cpu = cpu_iter->flags(); + + hSOIAmplitudesEEGPU->Fill(soi_amp_gpu); + hSOIAmplitudesEECPU->Fill(soi_amp_cpu); + hSOIAmplitudesEEGPUvsCPU->Fill(soi_amp_cpu, soi_amp_gpu); + hSOIAmplitudesEEdeltavsCPU->Fill(soi_amp_cpu, soi_amp_gpu - soi_amp_cpu); + if (soi_amp_cpu > 0) + hSOIAmplitudesEEGPUCPUratio->Fill((float)soi_amp_gpu / soi_amp_cpu); + + hChi2EEGPU->Fill(chi2_gpu); + hChi2EECPU->Fill(chi2_cpu); + hChi2EEGPUvsCPU->Fill(chi2_cpu, chi2_gpu); + hChi2EEdeltavsCPU->Fill(chi2_cpu, chi2_gpu - chi2_cpu); + if (chi2_cpu > 0) + hChi2EEGPUCPUratio->Fill((float)chi2_gpu / chi2_cpu); + + if (std::abs(chi2_gpu / chi2_cpu - 1) > 0.05 || std::abs(soi_amp_gpu / soi_amp_cpu - 1) > 0.05) { + std::cout << " ---- EE " << std::endl; + std::cout << " eventid = " << ie << " xtal = " << i << std::endl; + std::cout << " chi2_gpu = " << chi2_gpu << " chi2_cpu = " << chi2_cpu << std::endl; + std::cout << " soi_amp_gpu = " << soi_amp_gpu << " soi_amp_cpu = " << soi_amp_cpu << std::endl; + std::cout << " flags_gpu = " << flags_gpu << " flags_cpu = " << flags_cpu << std::endl; + } + + hFlagsEEGPU->Fill(flags_gpu); + hFlagsEECPU->Fill(flags_cpu); + hFlagsEEGPUvsCPU->Fill(flags_cpu, flags_gpu); + hFlagsEEdeltavsCPU->Fill(flags_cpu, flags_gpu - flags_cpu); + if (flags_cpu > 0) + hFlagsEEGPUCPUratio->Fill((float)flags_gpu / flags_cpu); + + if (flags_cpu != flags_gpu) { + std::cout << " >> No! Different flag cpu:gpu = " << flags_cpu << " : " << flags_gpu; + std::cout << std::endl; + } + + if ((std::abs(soi_amp_gpu - soi_amp_cpu) >= eps_diff) or (std::abs(chi2_gpu - chi2_cpu) >= eps_diff) or + std::isnan(chi2_gpu) or (flags_cpu != flags_gpu)) { + printf("EE eventid = %d chid = %d amp_gpu = %f amp_cpu %f chi2_gpu = %f chi2_cpu = %f\n", + ie, + static_cast(neb + i), + soi_amp_gpu, + soi_amp_cpu, + chi2_gpu, + chi2_cpu); + if (std::isnan(chi2_gpu)) + printf("*** nan ***\n"); + } + } + } + + { + TCanvas c("plots", "plots", 1750, 860); + c.Divide(3, 2); + + c.cd(1); + { + gPad->SetLogy(); + hSOIAmplitudesEBCPU->SetLineColor(kBlack); + hSOIAmplitudesEBCPU->SetLineWidth(1.); + hSOIAmplitudesEBCPU->Draw(""); + hSOIAmplitudesEBGPU->SetLineColor(kBlue); + hSOIAmplitudesEBGPU->SetLineWidth(1.); + hSOIAmplitudesEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hSOIAmplitudesEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + + c.cd(4); + { + gPad->SetLogy(); + hSOIAmplitudesEECPU->SetLineColor(kBlack); + hSOIAmplitudesEECPU->SetLineWidth(1.); + hSOIAmplitudesEECPU->Draw(""); + hSOIAmplitudesEEGPU->SetLineColor(kBlue); + hSOIAmplitudesEEGPU->SetLineWidth(1.); + hSOIAmplitudesEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hSOIAmplitudesEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + + c.cd(2); + gPad->SetGrid(); + hSOIAmplitudesEBGPUvsCPU->Draw("COLZ"); + + c.cd(5); + gPad->SetGrid(); + hSOIAmplitudesEEGPUvsCPU->Draw("COLZ"); + + c.cd(3); + + hSOIAmplitudesEBGPUCPUratio->Draw(""); + + c.cd(6); + + hSOIAmplitudesEEGPUCPUratio->Draw(""); + + c.SaveAs("ecal-amplitudes.root"); + c.SaveAs("ecal-amplitudes.png"); + + // chi2 + + c.cd(1); + { + gPad->SetLogy(); + hChi2EBCPU->SetLineColor(kBlack); + hChi2EBCPU->SetLineWidth(1.); + hChi2EBCPU->Draw(""); + hChi2EBGPU->SetLineColor(kBlue); + hChi2EBGPU->SetLineWidth(1.); + hChi2EBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hChi2EBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + + c.cd(4); + { + gPad->SetLogy(); + hChi2EECPU->SetLineColor(kBlack); + hChi2EECPU->SetLineWidth(1.); + hChi2EECPU->Draw(""); + hChi2EEGPU->SetLineColor(kBlue); + hChi2EEGPU->SetLineWidth(1.); + hChi2EEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hChi2EEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + + c.cd(2); + gPad->SetGrid(); + hChi2EBGPUvsCPU->Draw("COLZ"); + + c.cd(5); + gPad->SetGrid(); + hChi2EEGPUvsCPU->Draw("COLZ"); + + c.cd(3); + + hChi2EBGPUCPUratio->Draw(""); + + c.cd(6); + + hChi2EEGPUCPUratio->Draw(""); + + c.SaveAs("ecal-chi2.root"); + c.SaveAs("ecal-chi2.png"); + + // flags + + c.cd(1); + { + gPad->SetLogy(); + hFlagsEBCPU->SetLineColor(kBlack); + hFlagsEBCPU->SetLineWidth(1.); + hFlagsEBCPU->Draw(""); + hFlagsEBGPU->SetLineColor(kBlue); + hFlagsEBGPU->SetLineWidth(1.); + hFlagsEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hFlagsEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + + c.cd(4); + { + gPad->SetLogy(); + hFlagsEECPU->SetLineColor(kBlack); + hFlagsEECPU->SetLineWidth(1.); + hFlagsEECPU->Draw(""); + hFlagsEEGPU->SetLineColor(kBlue); + hFlagsEEGPU->SetLineWidth(1.); + hFlagsEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hFlagsEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + + c.cd(2); + gPad->SetGrid(); + hFlagsEBGPUvsCPU->Draw("COLZ"); + + c.cd(5); + gPad->SetGrid(); + hFlagsEEGPUvsCPU->Draw("COLZ"); + + c.cd(3); + hFlagsEBGPUCPUratio->Draw(""); + + c.cd(6); + hFlagsEEGPUCPUratio->Draw(""); + + c.SaveAs("ecal-flags.root"); + c.SaveAs("ecal-flags.png"); + + TCanvas cRechits("Rechits", "Rechits", 1750, 860); + cRechits.Divide(3, 2); + + // Plotting the sizes of GPU vs CPU for each event of EB + cRechits.cd(1); + { + gPad->SetLogy(); + hRechitsEBCPU->SetLineColor(kRed); + hRechitsEBCPU->SetLineWidth(2); + hRechitsEBCPU->Draw(""); + hRechitsEBGPU->SetLineColor(kBlue); + hRechitsEBGPU->SetLineWidth(2); + hRechitsEBGPU->Draw("sames"); + cRechits.Update(); + auto stats = (TPaveStats *)hRechitsEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cRechits.cd(4); + { + gPad->SetLogy(); + hRechitsEECPU->SetLineColor(kRed); + hRechitsEECPU->SetLineWidth(2); + hRechitsEECPU->Draw(""); + hRechitsEEGPU->SetLineColor(kBlue); + hRechitsEEGPU->SetLineWidth(2); + hRechitsEEGPU->Draw("sames"); + cRechits.Update(); + auto stats = (TPaveStats *)hRechitsEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cRechits.cd(2); + { hRechitsEBGPUvsCPU->Draw("COLZ"); } + cRechits.cd(5); + { hRechitsEEGPUvsCPU->Draw("COLZ"); } + cRechits.cd(3); + { + gPad->SetLogy(); + hRechitsEBGPUCPUratio->Draw(""); + } + cRechits.cd(6); + { + gPad->SetLogy(); + hRechitsEEGPUCPUratio->Draw(""); + } + cRechits.SaveAs("ecal-rechits.root"); + cRechits.SaveAs("ecal-rechits.png"); + } + + rf.Close(); + rfout.Write(); + rfout.Close(); + + return 0; +} diff --git a/RecoLocalCalo/EcalRecAlgos/bin/makeEcalRechitValidationPlots.cpp b/RecoLocalCalo/EcalRecAlgos/bin/makeEcalRechitValidationPlots.cpp new file mode 100644 index 0000000000000..42d1fceaf8b76 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/bin/makeEcalRechitValidationPlots.cpp @@ -0,0 +1,864 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormats/Common/interface/Wrapper.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHit.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHitCollections.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" + +int main(int argc, char *argv[]) { + if (argc < 3) { + std::cout << "run with: ./makeEcalRechitValidationPlots \n"; + exit(0); + } + // Set the GPU and CPU pointers for both EB and EE + edm::Wrapper>> *wgpuEB = nullptr; + edm::Wrapper>> *wgpuEE = nullptr; + edm::Wrapper *wcpuEB = nullptr; + edm::Wrapper *wcpuEE = nullptr; + + std::string fileName = argv[1]; // The input file containing the data to be validated (i.e. result.root) + std::string outFileName = argv[2]; //The output file in which the validation results will be saved (i.e. output.root) + + //output + TFile rfout{outFileName.c_str(), "recreate"}; + + int nbins = 200; + int last = 5000.; + + int nbins_energy = 300; + float last_energy = 2.; + + int nbins_chi2 = 200; + float last_chi2 = 100.; + + int nbins_flag = 40; + // int nbins_flag = 1000; + int last_flag = 1500; + // int nbins_flag = 40; + // int last_flag = 10000; + + int nbins_extra = 200; + int last_extra = 200; + + int nbins_delta = 201; // use an odd number to center around 0 + float delta = 0.2; + + // RecHits plots for EB and EE on both GPU and CPU + auto hRechitsEBGPU = new TH1D("RechitsEBGPU", "RechitsEBGPU; No. of Rechits. No Filter GPU", nbins, 0, last); + auto hRechitsEBCPU = new TH1D("RechitsEBCPU", "RechitsEBCPU; No. of Rechits. No Filter GPU", nbins, 0, last); + auto hRechitsEEGPU = new TH1D("RechitsEEGPU", "RechitsEEGPU; No. of Rechits. No Filter GPU", nbins, 0, last); + auto hRechitsEECPU = new TH1D("RechitsEECPU", "RechitsEECPU; No. of Rechits. No Filter GPU", nbins, 0, last); + auto hRechitsEBGPUvsCPU = + new TH2D("RechitsEBGPUvsCPU", "RechitsEBGPUvsCPU; CPU; GPU. No Filter GPU", last, 0, last, last, 0, last); + auto hRechitsEEGPUvsCPU = + new TH2D("RechitsEEGPUvsCPU", "RechitsEEGPUvsCPU; CPU; GPU. No Filter GPU", last, 0, last, last, 0, last); + auto hRechitsEBGPUCPUratio = + new TH1D("RechitsEBGPU/CPUratio", "RechitsEBGPU/CPUratio; GPU/CPU. No Filter GPU", 200, 0.95, 1.05); + auto hRechitsEEGPUCPUratio = + new TH1D("RechitsEEGPU/CPUratio", "RechitsEEGPU/CPUratio; GPU/CPU. No Filter GPU", 200, 0.95, 1.05); + auto hRechitsEBdeltavsCPU = + new TH2D("RechitsEBdeltavsCPU", "RechitsEBdeltavsCPU. No Filter GPU", nbins, 0, last, nbins_delta, -delta, delta); + auto hRechitsEEdeltavsCPU = + new TH2D("RechitsEEdeltavsCPU", "RechitsEEdeltavsCPU. No Filter GPU", nbins, 0, last, nbins_delta, -delta, delta); + + // RecHits plots for EB and EE on both GPU and CPU + auto hSelectedRechitsEBGPU = new TH1D("RechitsEBGPU", "RechitsEBGPU; No. of Rechits", nbins, 0, last); + auto hSelectedRechitsEBCPU = new TH1D("RechitsEBCPU", "RechitsEBCPU; No. of Rechits", nbins, 0, last); + auto hSelectedRechitsEEGPU = new TH1D("RechitsEEGPU", "RechitsEEGPU; No. of Rechits", nbins, 0, last); + auto hSelectedRechitsEECPU = new TH1D("RechitsEECPU", "RechitsEECPU; No. of Rechits", nbins, 0, last); + auto hSelectedRechitsEBGPUvsCPU = + new TH2D("RechitsEBGPUvsCPU", "RechitsEBGPUvsCPU; CPU; GPU", last, 0, last, last, 0, last); + auto hSelectedRechitsEEGPUvsCPU = + new TH2D("RechitsEEGPUvsCPU", "RechitsEEGPUvsCPU; CPU; GPU", last, 0, last, last, 0, last); + auto hSelectedRechitsEBGPUCPUratio = + new TH1D("RechitsEBGPU/CPUratio", "RechitsEBGPU/CPUratio; GPU/CPU", 200, 0.95, 1.05); + auto hSelectedRechitsEEGPUCPUratio = + new TH1D("RechitsEEGPU/CPUratio", "RechitsEEGPU/CPUratio; GPU/CPU", 200, 0.95, 1.05); + auto hSelectedRechitsEBdeltavsCPU = + new TH2D("RechitsEBdeltavsCPU", "RechitsEBdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + auto hSelectedRechitsEEdeltavsCPU = + new TH2D("RechitsEEdeltavsCPU", "RechitsEEdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + + // RecHits plots for EB and EE on both GPU and CPU + auto hPositiveRechitsEBGPU = new TH1D("RechitsEBGPU", "RechitsEBGPU; No. of Rechits", nbins, 0, last); + auto hPositiveRechitsEBCPU = new TH1D("RechitsEBCPU", "RechitsEBCPU; No. of Rechits", nbins, 0, last); + auto hPositiveRechitsEEGPU = new TH1D("RechitsEEGPU", "RechitsEEGPU; No. of Rechits", nbins, 0, last); + auto hPositiveRechitsEECPU = new TH1D("RechitsEECPU", "RechitsEECPU; No. of Rechits", nbins, 0, last); + auto hPositiveRechitsEBGPUvsCPU = + new TH2D("RechitsEBGPUvsCPU", "RechitsEBGPUvsCPU; CPU; GPU", last, 0, last, last, 0, last); + auto hPositiveRechitsEEGPUvsCPU = + new TH2D("RechitsEEGPUvsCPU", "RechitsEEGPUvsCPU; CPU; GPU", last, 0, last, last, 0, last); + auto hPositiveRechitsEBGPUCPUratio = + new TH1D("RechitsEBGPU/CPUratio", "RechitsEBGPU/CPUratio; GPU/CPU", 200, 0.95, 1.05); + auto hPositiveRechitsEEGPUCPUratio = + new TH1D("RechitsEEGPU/CPUratio", "RechitsEEGPU/CPUratio; GPU/CPU", 200, 0.95, 1.05); + auto hPositiveRechitsEBdeltavsCPU = + new TH2D("RechitsEBdeltavsCPU", "RechitsEBdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + auto hPositiveRechitsEEdeltavsCPU = + new TH2D("RechitsEEdeltavsCPU", "RechitsEEdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + + // Energies plots for EB and EE on both GPU and CPU + auto hEnergiesEBGPU = new TH1D("EnergiesEBGPU", "EnergiesEBGPU; Energy [GeV]", nbins_energy, 0, last_energy); + auto hEnergiesEEGPU = new TH1D("EnergiesEEGPU", "EnergiesEEGPU; Energy [GeV]", nbins_energy, 0, last_energy); + auto hEnergiesEBCPU = new TH1D("EnergiesEBCPU", "EnergiesEBCPU; Energy [GeV]", nbins_energy, 0, last_energy); + auto hEnergiesEECPU = new TH1D("EnergiesEECPU", "EnergiesEECPU; Energy [GeV]", nbins_energy, 0, last_energy); + auto hEnergiesEBGPUvsCPU = new TH2D( + "EnergiesEBGPUvsCPU", "EnergiesEBGPUvsCPU; CPU; GPU", nbins_energy, 0, last_energy, nbins_energy, 0, last_energy); + auto hEnergiesEEGPUvsCPU = new TH2D( + "EnergiesEEGPUvsCPU", "EnergiesEEGPUvsCPU; CPU; GPU", nbins_energy, 0, last_energy, nbins_energy, 0, last_energy); + auto hEnergiesEBGPUCPUratio = new TH1D("EnergiesEBGPU/CPUratio", "EnergiesEBGPU/CPUratio; GPU/CPU", 100, 0.8, 1.2); + auto hEnergiesEEGPUCPUratio = new TH1D("EnergiesEEGPU/CPUratio", "EnergiesEEGPU/CPUratio; GPU/CPU", 100, 0.8, 1.2); + auto hEnergiesEBdeltavsCPU = + new TH2D("EnergiesEBdeltavsCPU", "EnergiesEBdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + auto hEnergiesEEdeltavsCPU = + new TH2D("EnergiesEEdeltavsCPU", "EnergiesEEdeltavsCPU", nbins, 0, last, nbins_delta, -delta, delta); + + // Chi2 plots for EB and EE on both GPU and CPU + auto hChi2EBGPU = new TH1D("Chi2EBGPU", "Chi2EBGPU; Ch^{2}", nbins_chi2, 0, last_chi2); + auto hChi2EEGPU = new TH1D("Chi2EEGPU", "Chi2EEGPU; Ch^{2}", nbins_chi2, 0, last_chi2); + auto hChi2EBCPU = new TH1D("Chi2EBCPU", "Chi2EBCPU; Ch^{2}", nbins_chi2, 0, last_chi2); + auto hChi2EECPU = new TH1D("Chi2EECPU", "Chi2EECPU; Ch^{2}", nbins_chi2, 0, last_chi2); + auto hChi2EBGPUvsCPU = new TH2D("Chi2EBGPUvsCPU", "Chi2EBGPUvsCPU; CPU; GPU", nbins_chi2, 0, 100, nbins_chi2, 0, 100); + auto hChi2EEGPUvsCPU = new TH2D("Chi2EEGPUvsCPU", "Chi2EEGPUvsCPU; CPU; GPU", nbins_chi2, 0, 100, nbins_chi2, 0, 100); + auto hChi2EBGPUCPUratio = new TH1D("Chi2EBGPU/CPUratio", "Chi2EBGPU/CPUratio; GPU/CPU", 100, 0.8, 1.2); + auto hChi2EEGPUCPUratio = new TH1D("Chi2EEGPU/CPUratio", "Chi2EEGPU/CPUratio; GPU/CPU", 100, 0.8, 1.2); + auto hChi2EBdeltavsCPU = + new TH2D("Chi2EBdeltavsCPU", "Chi2EBdeltavsCPU", nbins_chi2, 0, last_chi2, nbins_delta, -delta, delta); + auto hChi2EEdeltavsCPU = + new TH2D("Chi2EEdeltavsCPU", "Chi2EEdeltavsCPU", nbins_chi2, 0, last_chi2, nbins_delta, -delta, delta); + + // Flags plots for EB and EE on both GPU and CPU + auto hFlagsEBGPU = new TH1D("FlagsEBGPU", "FlagsEBGPU; Flags", nbins_flag, -10, last_flag); + auto hFlagsEBCPU = new TH1D("FlagsEBCPU", "FlagsEBCPU; Flags", nbins_flag, -10, last_flag); + auto hFlagsEEGPU = new TH1D("FlagsEEGPU", "FlagsEEGPU; Flags", nbins_flag, -10, last_flag); + auto hFlagsEECPU = new TH1D("FlagsEECPU", "FlagsEECPU; Flags", nbins_flag, -10, last_flag); + auto hFlagsEBGPUvsCPU = + new TH2D("FlagsEBGPUvsCPU", "FlagsEBGPUvsCPU; CPU; GPU", nbins_flag, -10, last_flag, nbins_flag, -10, last_flag); + auto hFlagsEEGPUvsCPU = + new TH2D("FlagsEEGPUvsCPU", "FlagsEEGPUvsCPU; CPU; GPU", nbins_flag, -10, last_flag, nbins_flag, -10, last_flag); + auto hFlagsEBGPUCPUratio = new TH1D("FlagsEBGPU/CPUratio", "FlagsEBGPU/CPUratio; GPU/CPU", 50, -5, 10); + auto hFlagsEEGPUCPUratio = new TH1D("FlagsEEGPU/CPUratio", "FlagsEEGPU/CPUratio; GPU/CPU", 50, -5, 10); + auto hFlagsEBdeltavsCPU = + new TH2D("FlagsEBdeltavsCPU", "FlagsEBdeltavsCPU", nbins_flag, -10, last_flag, nbins_delta, -delta, delta); + auto hFlagsEEdeltavsCPU = + new TH2D("FlagsEEdeltavsCPU", "FlagsEEdeltavsCPU", nbins_flag, -10, last_flag, nbins_delta, -delta, delta); + + // Extras plots for EB and EE on both GPU and CPU + auto hExtrasEBGPU = new TH1D("ExtrasEBGPU", "ExtrasEBGPU; No. of Extras", nbins_extra, 0, last_extra); + auto hExtrasEBCPU = new TH1D("ExtrasEBCPU", "ExtrasEBCPU; No. of Extras", nbins_extra, 0, last_extra); + auto hExtrasEEGPU = new TH1D("ExtrasEEGPU", "ExtrasEEGPU; No. of Extras", nbins_extra, 0, last_extra); + auto hExtrasEECPU = new TH1D("ExtrasEECPU", "ExtrasEECPU; No. of Extras", nbins_extra, 0, last_extra); + auto hExtrasEBGPUvsCPU = new TH2D( + "ExtrasEBGPUvsCPU", "ExtrasEBGPUvsCPU; CPU; GPU", nbins_extra, 0, last_extra, nbins_extra, 0, last_extra); + auto hExtrasEEGPUvsCPU = new TH2D( + "ExtrasEEGPUvsCPU", "ExtrasEEGPUvsCPU; CPU; GPU", nbins_extra, 0, last_extra, nbins_extra, 0, last_extra); + auto hExtrasEBGPUCPUratio = new TH1D("ExtrasEBGPU/CPUratio", "ExtrasEBGPU/CPUratio; GPU/CPU", 50, 0.0, 2.0); + auto hExtrasEEGPUCPUratio = new TH1D("ExtrasEEGPU/CPUratio", "ExtrasEEGPU/CPUratio; GPU/CPU", 50, 0.0, 2.0); + auto hExtrasEBdeltavsCPU = + new TH2D("ExtrasEBdeltavsCPU", "ExtrasEBdeltavsCPU", nbins_extra, 0, last_extra, nbins_delta, -delta, delta); + auto hExtrasEEdeltavsCPU = + new TH2D("ExtrasEEdeltavsCPU", "ExtrasEEdeltavsCPU", nbins_extra, 0, last_extra, nbins_delta, -delta, delta); + + // input file setup for tree + std::cout << "validating file " << fileName << std::endl; + TFile rf{fileName.c_str()}; + TTree *rt = (TTree *)rf.Get("Events"); + + // Allocating the appropriate data to their respective pointers + rt->SetBranchAddress("ecalTagsoaecalRecHit_ecalCPURecHitProducer_EcalRecHitsEB_RECO.", &wgpuEB); + rt->SetBranchAddress("ecalTagsoaecalRecHit_ecalCPURecHitProducer_EcalRecHitsEE_RECO.", &wgpuEE); + rt->SetBranchAddress("EcalRecHitsSorted_ecalRecHit_EcalRecHitsEB_RECO.", &wcpuEB); + rt->SetBranchAddress("EcalRecHitsSorted_ecalRecHit_EcalRecHitsEE_RECO.", &wcpuEE); + + // constexpr float eps_diff = 1e-3; + + // accumulate sizes for events and sizes of each event on both GPU and CPU + // auto const nentries = rt->GetEntries(); + int nentries = rt->GetEntries(); + + //---- AM: tests + if (nentries > 1000) { + nentries = 1000; + } + // nentries = 1; + + std::cout << "#events to validate over: " << nentries << std::endl; + for (int ie = 0; ie < nentries; ++ie) { + rt->GetEntry(ie); + + // const char* ordinal[] = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; + auto cpu_eb_size = wcpuEB->bareProduct().size(); + auto cpu_ee_size = wcpuEE->bareProduct().size(); + auto gpu_eb_size = wgpuEB->bareProduct().energy.size(); + auto gpu_ee_size = wgpuEE->bareProduct().energy.size(); + float eb_ratio = (float)gpu_eb_size / cpu_eb_size; + float ee_ratio = (float)gpu_ee_size / cpu_ee_size; + + // Filling up the histograms on events sizes for EB and EE on both GPU and CPU + hRechitsEBGPU->Fill(gpu_eb_size); + hRechitsEBCPU->Fill(cpu_eb_size); + hRechitsEEGPU->Fill(gpu_ee_size); + hRechitsEECPU->Fill(cpu_ee_size); + hRechitsEBGPUvsCPU->Fill(cpu_eb_size, gpu_eb_size); + hRechitsEEGPUvsCPU->Fill(cpu_ee_size, gpu_ee_size); + hRechitsEBGPUCPUratio->Fill(eb_ratio); + hRechitsEEGPUCPUratio->Fill(ee_ratio); + hRechitsEBdeltavsCPU->Fill(cpu_eb_size, gpu_eb_size - cpu_eb_size); + hRechitsEEdeltavsCPU->Fill(cpu_ee_size, gpu_ee_size - cpu_ee_size); + + /* + * // condition that sizes on GPU and CPU should be the same for EB or EE + * if (cpu_eb_size != gpu_eb_size or cpu_ee_size != gpu_ee_size) { + * std::cerr << ie << ordinal[ie % 10] << " entry:\n" + * << " EB size: " << std::setw(4) << cpu_eb_size << " (cpu) vs " << std::setw(4) << gpu_eb_size << " (gpu)\n" + * << " EE size: " << std::setw(4) << cpu_ee_size << " (cpu) vs " << std::setw(4) << gpu_ee_size << " (gpu)" << std::endl; + * + * continue; + } + assert(wgpuEB->bareProduct().energy.size() == wcpuEB->bareProduct().size()); + assert(wgpuEE->bareProduct().energy.size() == wcpuEE->bareProduct().size()); + auto const neb = wcpuEB->bareProduct().size(); //like cpu_eb_size but set to constant + auto const nee = wcpuEE->bareProduct().size(); //like cpu_ee_size but set to constant + */ + + uint selected_gpu_eb_size = 0; + uint selected_gpu_ee_size = 0; + + uint positive_gpu_eb_size = 0; + uint positive_gpu_ee_size = 0; + + // EB: + for (uint32_t i = 0; i < gpu_eb_size; ++i) { + auto const did_gpu = wgpuEB->bareProduct().did[i]; // set the did for the current RecHit + // Set the variables for GPU + auto const enr_gpu = wgpuEB->bareProduct().energy[i]; + auto const chi2_gpu = wgpuEB->bareProduct().chi2[i]; + auto const flag_gpu = wgpuEB->bareProduct().flagBits[i]; + auto const extra_gpu = wgpuEB->bareProduct().extra[i]; + + // you have "-1" if the crystal is not selected + if (enr_gpu >= 0) { + selected_gpu_eb_size++; + + if (enr_gpu > 0) { + positive_gpu_eb_size++; + } + + // find the Rechit on CPU reflecting the same did + auto const cpu_iter = wcpuEB->bareProduct().find(DetId{did_gpu}); + if (cpu_iter == wcpuEB->bareProduct().end()) { + // std::cerr << ie << ordinal[ie % 10] << " entry\n" + // << " Did not find a DetId " << did_gpu_eb + // << " in a CPU collection\n"; + std::cerr << " Did not find a DetId " << did_gpu << " in a CPU collection\n"; + continue; + } + // Set the variables for CPU + auto const enr_cpu = cpu_iter->energy(); + auto const chi2_cpu = cpu_iter->chi2(); + // auto const flag_cpu = cpu_iter->flagBits(); + auto const flag_cpu = 1; + // auto const extra_cpu = cpu_iter->extra(); + auto const extra_cpu = 1; + // auto const flag_cpu = cpu_iter->flagBits() ? cpu_iter->flagBits():-1; + // auto const extra_cpu = cpu_iter->extra() ? cpu_iter->extra():-1; + + // AM: TEST + // if (extra_cpu != 10) continue; + + // Fill the energy and Chi2 histograms for GPU and CPU and their comparisons with delta + hEnergiesEBGPU->Fill(enr_gpu); + hEnergiesEBCPU->Fill(enr_cpu); + // std::cout<<"EB CPU Energy:\t"<Fill(enr_cpu, enr_gpu); + hEnergiesEBGPUCPUratio->Fill(enr_gpu / enr_cpu); + hEnergiesEBdeltavsCPU->Fill(enr_cpu, enr_gpu - enr_cpu); + + hChi2EBGPU->Fill(chi2_gpu); + hChi2EBCPU->Fill(chi2_cpu); + hChi2EBGPUvsCPU->Fill(chi2_cpu, chi2_gpu); + hChi2EBGPUCPUratio->Fill(chi2_gpu / chi2_cpu); + hChi2EBdeltavsCPU->Fill(chi2_cpu, chi2_gpu - chi2_cpu); + + hFlagsEBGPU->Fill(flag_gpu); + hFlagsEBCPU->Fill(flag_cpu); + hFlagsEBGPUvsCPU->Fill(flag_cpu, flag_gpu); + hFlagsEBGPUCPUratio->Fill(flag_cpu ? flag_gpu / flag_cpu : -1); + hFlagsEBdeltavsCPU->Fill(flag_cpu, flag_gpu - flag_cpu); + + hExtrasEBGPU->Fill(extra_gpu); + hExtrasEBCPU->Fill(extra_cpu); + hExtrasEBGPUvsCPU->Fill(extra_cpu, extra_gpu); + hExtrasEBGPUCPUratio->Fill(extra_cpu ? extra_gpu / extra_cpu : -1); + hExtrasEBdeltavsCPU->Fill(extra_cpu, extra_gpu - extra_cpu); + + // Check if abs difference between GPU and CPU values for energy and Chi2 are smaller than eps, if not print message + // if ((std::abs(enr_gpu - enr_cpu) >= eps_diff) or + // (std::abs(chi2_gpu - chi2_cpu) >= eps_diff) or std::isnan(chi2_gpu)) + // { + // printf("EB eventid = %d chid = %d energy_gpu = %f energy_cpu %f chi2_gpu = %f chi2_cpu = %f\n", + // ie, i, enr_gpu, enr_cpu, chi2_gpu, chi2_cpu); + // if (std::isnan(chi2_gpu)) + // printf("*** nan ***\n"); + // } + } + } + + // EE: + for (uint32_t i = 0; i < gpu_ee_size; ++i) { + auto const did_gpu = wgpuEE->bareProduct().did[i]; // set the did for the current RecHit + // Set the variables for GPU + auto const enr_gpu = wgpuEE->bareProduct().energy[i]; + auto const chi2_gpu = wgpuEE->bareProduct().chi2[i]; + auto const flag_gpu = wgpuEE->bareProduct().flagBits[i]; + auto const extra_gpu = wgpuEE->bareProduct().extra[i]; + + // you have "-1" if the crystal is not selected + if (enr_gpu >= 0) { + selected_gpu_ee_size++; + + if (enr_gpu > 0) { + positive_gpu_ee_size++; + } + + // find the Rechit on CPU reflecting the same did + auto const cpu_iter = wcpuEE->bareProduct().find(DetId{did_gpu}); + if (cpu_iter == wcpuEE->bareProduct().end()) { + // std::cerr << ie << ordinal[ie % 10] << " entry\n" + // << " Did not find a DetId " << did_gpu + // << " in a CPU collection\n"; + std::cerr << " Did not find a DetId " << did_gpu << " in a CPU collection\n"; + continue; + } + // Set the variables for CPU + auto const enr_cpu = cpu_iter->energy(); + auto const chi2_cpu = cpu_iter->chi2(); + // auto const flag_cpu = cpu_iter->flagBits(); + auto const flag_cpu = 1; + // auto const extra_cpu = cpu_iter->extra(); + auto const extra_cpu = 1; + // auto const flag_cpu = cpu_iter->flagBits()?cpu_iter->flagBits():-1; + // auto const extra_cpu = cpu_iter->extra()?cpu_iter->extra():-1; + + // AM: TEST + // if (extra_cpu != 10) continue; + + // Fill the energy and Chi2 histograms for GPU and CPU and their comparisons with delta + hEnergiesEEGPU->Fill(enr_gpu); + hEnergiesEECPU->Fill(enr_cpu); + hEnergiesEEGPUvsCPU->Fill(enr_cpu, enr_gpu); + hEnergiesEEGPUCPUratio->Fill(enr_gpu / enr_cpu); + hEnergiesEEdeltavsCPU->Fill(enr_cpu, enr_gpu - enr_cpu); + + hChi2EEGPU->Fill(chi2_gpu); + hChi2EECPU->Fill(chi2_cpu); + hChi2EEGPUvsCPU->Fill(chi2_cpu, chi2_gpu); + hChi2EEGPUCPUratio->Fill(chi2_gpu / chi2_cpu); + hChi2EEdeltavsCPU->Fill(chi2_cpu, chi2_gpu - chi2_cpu); + + hFlagsEEGPU->Fill(flag_gpu); + hFlagsEECPU->Fill(flag_cpu); + hFlagsEEGPUvsCPU->Fill(flag_cpu, flag_gpu); + hFlagsEEGPUCPUratio->Fill(flag_cpu ? flag_gpu / flag_cpu : -1); + hFlagsEEdeltavsCPU->Fill(flag_cpu, flag_gpu - flag_cpu); + + hExtrasEEGPU->Fill(extra_gpu); + hExtrasEECPU->Fill(extra_cpu); + hExtrasEEGPUvsCPU->Fill(extra_cpu, extra_gpu); + hExtrasEEGPUCPUratio->Fill(extra_cpu ? extra_gpu / extra_cpu : -1); + hExtrasEEdeltavsCPU->Fill(extra_cpu, extra_gpu - extra_cpu); + + // Check if abs difference between GPU and CPU values for energy and Chi2 are smaller than eps, if not print message + // if ((std::abs(enr_gpu - enr_cpu) >= eps_diff) or + // (std::abs(chi2_gpu - chi2_cpu) >= eps_diff) or std::isnan(chi2_gpu)) + // { + // printf("EE eventid = %d chid = %d energy_gpu = %f energy_cpu %f chi2_gpu = %f chi2_cpu = %f\n", + // ie, i, enr_gpu, enr_cpu, chi2_gpu, chi2_cpu); + // if (std::isnan(chi2_gpu)) + // printf("*** nan ***\n"); + // } + } + } + + // + // now the rechit counting + // + float selected_eb_ratio = (float)selected_gpu_eb_size / cpu_eb_size; + float selected_ee_ratio = (float)selected_gpu_ee_size / cpu_ee_size; + + // Filling up the histograms on events sizes for EB and EE on both GPU and CPU + hSelectedRechitsEBGPU->Fill(selected_gpu_eb_size); + hSelectedRechitsEBCPU->Fill(cpu_eb_size); + hSelectedRechitsEEGPU->Fill(selected_gpu_ee_size); + hSelectedRechitsEECPU->Fill(cpu_ee_size); + hSelectedRechitsEBGPUvsCPU->Fill(cpu_eb_size, selected_gpu_eb_size); + hSelectedRechitsEEGPUvsCPU->Fill(cpu_ee_size, selected_gpu_ee_size); + hSelectedRechitsEBGPUCPUratio->Fill(selected_eb_ratio); + hSelectedRechitsEEGPUCPUratio->Fill(selected_ee_ratio); + hSelectedRechitsEBdeltavsCPU->Fill(cpu_eb_size, selected_gpu_eb_size - cpu_eb_size); + hSelectedRechitsEEdeltavsCPU->Fill(cpu_ee_size, selected_gpu_ee_size - cpu_ee_size); + + // + // now the rechit counting + // + + uint positive_cpu_eb_size = 0; + uint positive_cpu_ee_size = 0; + + // EB: + for (uint32_t i = 0; i < cpu_eb_size; ++i) { + auto const enr_cpu = wcpuEB->bareProduct()[i].energy(); + if (enr_cpu > 0) { + positive_cpu_eb_size++; + } + } + // EE: + for (uint32_t i = 0; i < cpu_ee_size; ++i) { + auto const enr_cpu = wcpuEE->bareProduct()[i].energy(); + if (enr_cpu > 0) { + positive_cpu_ee_size++; + } + } + + float positive_eb_ratio = (float)positive_gpu_eb_size / positive_cpu_eb_size; + float positive_ee_ratio = (float)positive_gpu_ee_size / positive_cpu_ee_size; + + // Filling up the histograms on events sizes for EB and EE on both GPU and CPU + hPositiveRechitsEBGPU->Fill(positive_gpu_eb_size); + hPositiveRechitsEBCPU->Fill(positive_cpu_eb_size); + hPositiveRechitsEEGPU->Fill(positive_gpu_ee_size); + hPositiveRechitsEECPU->Fill(positive_cpu_ee_size); + hPositiveRechitsEBGPUvsCPU->Fill(positive_cpu_eb_size, positive_gpu_eb_size); + hPositiveRechitsEEGPUvsCPU->Fill(positive_cpu_ee_size, positive_gpu_ee_size); + hPositiveRechitsEBGPUCPUratio->Fill(positive_eb_ratio); + hPositiveRechitsEEGPUCPUratio->Fill(positive_ee_ratio); + hPositiveRechitsEBdeltavsCPU->Fill(positive_cpu_eb_size, positive_gpu_eb_size - positive_cpu_eb_size); + hPositiveRechitsEEdeltavsCPU->Fill(positive_cpu_ee_size, positive_gpu_ee_size - positive_cpu_ee_size); + + if (cpu_eb_size != selected_gpu_eb_size or cpu_ee_size != selected_gpu_ee_size) { + // std::cerr << ie << ordinal[ie % 10] << " entry:\n" + std::cerr << ie << " entry:\n" + << " EB size: " << std::setw(4) << cpu_eb_size << " (cpu) vs " << std::setw(4) << selected_gpu_eb_size + << " (gpu)\n" + << " EE size: " << std::setw(4) << cpu_ee_size << " (cpu) vs " << std::setw(4) << selected_gpu_ee_size + << " (gpu)" << std::endl; + } + } + + // Plotting the results: + { + // Canvases Setup: + TCanvas cAllRechits("AllRechits", "AllRechits", 1750, 860); + cAllRechits.Divide(3, 2); + TCanvas cRechits("Rechits", "Rechits", 1750, 860); + cRechits.Divide(3, 2); + TCanvas cRechitsPositive("RechitsPositive", "RechitsPositive", 1750, 860); + cRechitsPositive.Divide(3, 2); + TCanvas cEnergies("Energies", "Energies", 1750, 860); + cEnergies.Divide(3, 2); + TCanvas cChi2("Chi2", "Chi2", 1750, 860); + cChi2.Divide(3, 2); + TCanvas cFlags("Flags", "Flags", 1750, 860); + cFlags.Divide(3, 2); + TCanvas cExtras("Extras", "Extras", 1750, 860); + cExtras.Divide(3, 2); + + // Plotting the sizes of GPU vs CPU for each event of EB + cAllRechits.cd(1); + { + gPad->SetLogy(); + hRechitsEBCPU->SetLineColor(kRed); + hRechitsEBCPU->SetLineWidth(2); + hRechitsEBCPU->Draw(""); + hRechitsEBGPU->SetLineColor(kBlue); + hRechitsEBGPU->SetLineWidth(2); + hRechitsEBGPU->Draw("sames"); + cAllRechits.Update(); + auto stats = (TPaveStats *)hRechitsEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cAllRechits.cd(4); + { + gPad->SetLogy(); + hRechitsEECPU->SetLineColor(kRed); + hRechitsEECPU->SetLineWidth(2); + hRechitsEECPU->Draw(""); + hRechitsEEGPU->SetLineColor(kBlue); + hRechitsEEGPU->SetLineWidth(2); + hRechitsEEGPU->Draw("sames"); + cAllRechits.Update(); + auto stats = (TPaveStats *)hRechitsEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cAllRechits.cd(2); + { + gStyle->SetPalette(55); + hRechitsEBGPUvsCPU->Draw("COLZ"); + } + cAllRechits.cd(5); + { + gStyle->SetPalette(55); + hRechitsEEGPUvsCPU->Draw("COLZ"); + } + cAllRechits.cd(3); + { + gPad->SetLogy(); + //hRechitsEBdeltavsCPU->Draw("COLZ"); + hRechitsEBGPUCPUratio->Draw(""); + } + cAllRechits.cd(6); + { + gPad->SetLogy(); + //hRechitsEEdeltavsCPU->Draw("COLZ"); + hRechitsEEGPUCPUratio->Draw(""); + } + cAllRechits.SaveAs("ecal-allrechits.root"); + cAllRechits.SaveAs("ecal-allrechits.png"); + + // Plotting the sizes of GPU vs CPU for each event of EB + cRechits.cd(1); + { + gPad->SetLogy(); + hSelectedRechitsEBCPU->SetLineColor(kRed); + hSelectedRechitsEBCPU->SetLineWidth(2); + hSelectedRechitsEBCPU->Draw(""); + hSelectedRechitsEBGPU->SetLineColor(kBlue); + hSelectedRechitsEBGPU->SetLineWidth(2); + hSelectedRechitsEBGPU->Draw("sames"); + cRechits.Update(); + auto stats = (TPaveStats *)hSelectedRechitsEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cRechits.cd(4); + { + gPad->SetLogy(); + hSelectedRechitsEECPU->SetLineColor(kRed); + hSelectedRechitsEECPU->SetLineWidth(2); + hSelectedRechitsEECPU->Draw(""); + hSelectedRechitsEEGPU->SetLineColor(kBlue); + hSelectedRechitsEEGPU->SetLineWidth(2); + hSelectedRechitsEEGPU->Draw("sames"); + cRechits.Update(); + auto stats = (TPaveStats *)hSelectedRechitsEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cRechits.cd(2); + { + gStyle->SetPalette(55); + hSelectedRechitsEBGPUvsCPU->Draw("COLZ"); + } + cRechits.cd(5); + { + gStyle->SetPalette(55); + hSelectedRechitsEEGPUvsCPU->Draw("COLZ"); + } + cRechits.cd(3); + { + gPad->SetLogy(); + //hSelectedRechitsEBdeltavsCPU->Draw("COLZ"); + hSelectedRechitsEBGPUCPUratio->Draw(""); + } + cRechits.cd(6); + { + gPad->SetLogy(); + //hSelectedRechitsEEdeltavsCPU->Draw("COLZ"); + hSelectedRechitsEEGPUCPUratio->Draw(""); + } + cRechits.SaveAs("ecal-rechits.root"); + cRechits.SaveAs("ecal-rechits.png"); + + // Plotting the sizes of GPU vs CPU for each event of EB + cRechitsPositive.cd(1); + { + gPad->SetLogy(); + hPositiveRechitsEBCPU->SetLineColor(kRed); + hPositiveRechitsEBCPU->SetLineWidth(2); + hPositiveRechitsEBCPU->Draw(""); + hPositiveRechitsEBGPU->SetLineColor(kBlue); + hPositiveRechitsEBGPU->SetLineWidth(2); + hPositiveRechitsEBGPU->Draw("sames"); + cRechitsPositive.Update(); + auto stats = (TPaveStats *)hPositiveRechitsEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cRechitsPositive.cd(4); + { + gPad->SetLogy(); + hPositiveRechitsEECPU->SetLineColor(kRed); + hPositiveRechitsEECPU->SetLineWidth(2); + hPositiveRechitsEECPU->Draw(""); + hPositiveRechitsEEGPU->SetLineColor(kBlue); + hPositiveRechitsEEGPU->SetLineWidth(2); + hPositiveRechitsEEGPU->Draw("sames"); + cRechitsPositive.Update(); + auto stats = (TPaveStats *)hPositiveRechitsEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cRechitsPositive.cd(2); + { + gStyle->SetPalette(55); + hPositiveRechitsEBGPUvsCPU->Draw("COLZ"); + } + cRechitsPositive.cd(5); + { + gStyle->SetPalette(55); + hPositiveRechitsEEGPUvsCPU->Draw("COLZ"); + } + cRechitsPositive.cd(3); + { + gPad->SetLogy(); + //hPositiveRechitsEBdeltavsCPU->Draw("COLZ"); + hPositiveRechitsEBGPUCPUratio->Draw(""); + } + cRechitsPositive.cd(6); + { + gPad->SetLogy(); + //hPositiveRechitsEEdeltavsCPU->Draw("COLZ"); + hPositiveRechitsEEGPUCPUratio->Draw(""); + } + cRechitsPositive.SaveAs("ecal-rechits-positive.root"); + cRechitsPositive.SaveAs("ecal-rechits-positive.png"); + + cEnergies.cd(1); + { + gPad->SetLogy(); + hEnergiesEBCPU->SetLineColor(kBlack); + hEnergiesEBCPU->SetLineWidth(2); + hEnergiesEBCPU->Draw(""); + hEnergiesEBGPU->SetLineColor(kBlue); + hEnergiesEBGPU->SetLineWidth(2); + hEnergiesEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hEnergiesEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cEnergies.cd(4); + { + gPad->SetLogy(); + hEnergiesEECPU->SetLineColor(kBlack); + hEnergiesEECPU->SetLineWidth(2); + hEnergiesEECPU->Draw(""); + hEnergiesEEGPU->SetLineColor(kBlue); + hEnergiesEEGPU->SetLineWidth(2); + hEnergiesEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hEnergiesEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cEnergies.cd(2); + { hEnergiesEBGPUvsCPU->Draw("COLZ"); } + cEnergies.cd(5); + { hEnergiesEEGPUvsCPU->Draw("COLZ"); } + cEnergies.cd(3); + { + gPad->SetLogy(); + //hEnergiesEBdeltavsCPU->Draw("COLZ"); + hEnergiesEBGPUCPUratio->Draw(""); + } + cEnergies.cd(6); + { + gPad->SetLogy(); + //hEnergiesEEdeltavsCPU->Draw("COLZ"); + hEnergiesEEGPUCPUratio->Draw(""); + } + cEnergies.SaveAs("ecal-energies.root"); + cEnergies.SaveAs("ecal-energies.png"); + + cChi2.cd(1); + { + gPad->SetLogy(); + hChi2EBCPU->SetLineColor(kBlack); + hChi2EBCPU->SetLineWidth(2); + hChi2EBCPU->Draw(""); + hChi2EBGPU->SetLineColor(kBlue); + hChi2EBGPU->SetLineWidth(2); + hChi2EBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hChi2EBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cChi2.cd(4); + { + gPad->SetLogy(); + hChi2EECPU->SetLineColor(kBlack); + hChi2EECPU->SetLineWidth(2); + hChi2EECPU->Draw(""); + hChi2EEGPU->SetLineColor(kBlue); + hChi2EEGPU->SetLineWidth(2); + hChi2EEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hChi2EEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cChi2.cd(2); + { hChi2EBGPUvsCPU->Draw("COLZ"); } + cChi2.cd(5); + { hChi2EEGPUvsCPU->Draw("COLZ"); } + cChi2.cd(3); + { + gPad->SetLogy(); + //hChi2EBdeltavsCPU->Draw("COLZ"); + hChi2EBGPUCPUratio->Draw(""); + } + cChi2.cd(6); + { + gPad->SetLogy(); + //hChi2EEdeltavsCPU->Draw("COLZ"); + hChi2EEGPUCPUratio->Draw(""); + } + cChi2.SaveAs("ecal-chi2.root"); + cChi2.SaveAs("ecal-chi2.png"); + + cFlags.cd(1); + { + gPad->SetLogy(); + hFlagsEBCPU->SetLineColor(kBlack); + hFlagsEBCPU->SetLineWidth(2); + hFlagsEBCPU->Draw(""); + hFlagsEBGPU->SetLineColor(kBlue); + hFlagsEBGPU->SetLineWidth(2); + hFlagsEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hFlagsEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cFlags.cd(4); + { + gPad->SetLogy(); + hFlagsEECPU->SetLineColor(kBlack); + hFlagsEECPU->SetLineWidth(2); + hFlagsEECPU->Draw(""); + hFlagsEEGPU->SetLineColor(kBlue); + hFlagsEEGPU->SetLineWidth(2); + hFlagsEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hFlagsEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cFlags.cd(2); + { hFlagsEBGPUvsCPU->Draw("COLZ"); } + cFlags.cd(5); + { hFlagsEEGPUvsCPU->Draw("COLZ"); } + cFlags.cd(3); + { + gPad->SetLogy(); + //hFlagsEBdeltavsCPU->Draw("COLZ"); + hFlagsEBGPUCPUratio->Draw(""); + } + cFlags.cd(6); + { + gPad->SetLogy(); + //hFlagsEEdeltavsCPU->Draw("COLZ"); + hFlagsEEGPUCPUratio->Draw(""); + } + cFlags.SaveAs("ecal-flags.root"); + cFlags.SaveAs("ecal-flags.png"); + + cExtras.cd(1); + { + gPad->SetLogy(); + hExtrasEBCPU->SetLineColor(kBlack); + hExtrasEBCPU->SetLineWidth(2); + hExtrasEBCPU->Draw(""); + hExtrasEBGPU->SetLineColor(kBlue); + hExtrasEBGPU->SetLineWidth(2); + hExtrasEBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hExtrasEBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cExtras.cd(4); + { + gPad->SetLogy(); + hExtrasEECPU->SetLineColor(kBlack); + hExtrasEECPU->SetLineWidth(2); + hExtrasEECPU->Draw(""); + hExtrasEEGPU->SetLineColor(kBlue); + hExtrasEEGPU->SetLineWidth(2); + hExtrasEEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats *)hExtrasEEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + cExtras.cd(2); + { hExtrasEBGPUvsCPU->Draw("COLZ"); } + cExtras.cd(5); + { hExtrasEEGPUvsCPU->Draw("COLZ"); } + cExtras.cd(3); + { + gPad->SetLogy(); + //hExtrasEBdeltavsCPU->Draw("COLZ"); + hExtrasEBGPUCPUratio->Draw(""); + } + cExtras.cd(6); + { + gPad->SetLogy(); + //hExtrasEEdeltavsCPU->Draw("COLZ"); + hExtrasEEGPUCPUratio->Draw(""); + } + cExtras.SaveAs("ecal-extras.root"); + cExtras.SaveAs("ecal-extras.png"); + } + + // Close all open files + rf.Close(); + rfout.Write(); + rfout.Close(); + + return 0; +} diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h new file mode 100644 index 0000000000000..a3f65d0b509fc --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h @@ -0,0 +1,43 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalGainRatiosGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalGainRatiosGPU_h + +#include "CondFormats/EcalObjects/interface/EcalGainRatios.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalGainRatiosGPU { +public: + struct Product { + ~Product(); + float *gain12Over6 = nullptr, *gain6Over1 = nullptr; + }; + +#ifndef __CUDACC__ + + // rearrange pedestals + EcalGainRatiosGPU(EcalGainRatios const&); + + // will call dealloation for Product thru ~Product + ~EcalGainRatiosGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalGainRatiosGPU"}; } + +private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector> gain12Over6_; + std::vector> gain6Over1_; + + cms::cuda::ESProduct product_; + +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalGainRatiosGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h new file mode 100644 index 0000000000000..4b5401ff0316f --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h @@ -0,0 +1,43 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalIntercalibConstantsGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalIntercalibConstantsGPU_h + +#include "CondFormats/EcalObjects/interface/EcalIntercalibConstants.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalIntercalibConstantsGPU { +public: + struct Product { + ~Product(); + float* values = nullptr; + }; + +#ifndef __CUDACC__ + // + EcalIntercalibConstantsGPU(EcalIntercalibConstants const&); + + // will call dealloation for Product thru ~Product + ~EcalIntercalibConstantsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // TODO: do this centrally + // get offset for hashes. equals number of barrel items + uint32_t getOffset() const { return valuesEB_.size(); } + + // + static std::string name() { return std::string{"ecalIntercalibConstantsGPU"}; } + +private: + std::vector const& valuesEB_; + std::vector const& valuesEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalIntercalibConstantsGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h new file mode 100644 index 0000000000000..4a6cd34fcd171 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h @@ -0,0 +1,53 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAPDPNRatiosGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAPDPNRatiosGPU_h + +#include "CondFormats/EcalObjects/interface/EcalLaserAPDPNRatios.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalLaserAPDPNRatiosGPU { +public: + struct Product { + ~Product(); + float *p1 = nullptr; + float *p2 = nullptr; + float *p3 = nullptr; + edm::TimeValue_t *t1 = nullptr; + edm::TimeValue_t *t2 = nullptr; + edm::TimeValue_t *t3 = nullptr; + }; + +#ifndef __CUDACC__ + + // + EcalLaserAPDPNRatiosGPU(EcalLaserAPDPNRatios const &); + + // will call dealloation for Product thru ~Product + ~EcalLaserAPDPNRatiosGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalLaserAPDPNRatiosGPU"}; } + +private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector > p1_; + std::vector > p2_; + std::vector > p3_; + + std::vector > t1_; + std::vector > t2_; + std::vector > t3_; + + cms::cuda::ESProduct product_; + +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAPDPNRatiosGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h new file mode 100644 index 0000000000000..985bfd9579f7c --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h @@ -0,0 +1,43 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAPDPNRatiosRefGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAPDPNRatiosRefGPU_h + +#include "CondFormats/EcalObjects/interface/EcalLaserAPDPNRatiosRef.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalLaserAPDPNRatiosRefGPU { +public: + struct Product { + ~Product(); + float* values = nullptr; + }; + +#ifndef __CUDACC__ + // + EcalLaserAPDPNRatiosRefGPU(EcalLaserAPDPNRatiosRef const&); + + // will call dealloation for Product thru ~Product + ~EcalLaserAPDPNRatiosRefGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // TODO: do this centrally + // get offset for hashes. equals number of barrel items + uint32_t getOffset() const { return valuesEB_.size(); } + + // + static std::string name() { return std::string{"ecalLaserAPDPNRatiosRefGPU"}; } + +private: + std::vector const& valuesEB_; + std::vector const& valuesEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAPDPNRatiosRefGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h new file mode 100644 index 0000000000000..9dd05e9ee3c4d --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h @@ -0,0 +1,43 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAlphasGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAlphasGPU_h + +#include "CondFormats/EcalObjects/interface/EcalLaserAlphas.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalLaserAlphasGPU { +public: + struct Product { + ~Product(); + float* values = nullptr; + }; + +#ifndef __CUDACC__ + // + EcalLaserAlphasGPU(EcalLaserAlphas const&); + + // will call dealloation for Product thru ~Product + ~EcalLaserAlphasGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // TODO: do this centrally + // get offset for hashes. equals number of barrel items + uint32_t getOffset() const { return valuesEB_.size(); } + + // + static std::string name() { return std::string{"ecalLaserAlphasGPU"}; } + +private: + std::vector const& valuesEB_; + std::vector const& valuesEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalLaserAlphasGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h new file mode 100644 index 0000000000000..343bdf1dd1afc --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h @@ -0,0 +1,53 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalLinearCorrectionsGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalLinearCorrectionsGPU_h + +#include "CondFormats/EcalObjects/interface/EcalLinearCorrections.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalLinearCorrectionsGPU { +public: + struct Product { + ~Product(); + float *p1 = nullptr; + float *p2 = nullptr; + float *p3 = nullptr; + edm::TimeValue_t *t1 = nullptr; + edm::TimeValue_t *t2 = nullptr; + edm::TimeValue_t *t3 = nullptr; + }; + +#ifndef __CUDACC__ + + // + EcalLinearCorrectionsGPU(EcalLinearCorrections const &); + + // will call dealloation for Product thru ~Product + ~EcalLinearCorrectionsGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalLinearCorrectionsGPU"}; } + +private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector> p1_; + std::vector> p2_; + std::vector> p3_; + + std::vector> t1_; + std::vector> t2_; + std::vector> t3_; + + cms::cuda::ESProduct product_; + +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalLinearCorrectionsGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h new file mode 100644 index 0000000000000..56aa0579ff77f --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h @@ -0,0 +1,39 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalMultifitParametersGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalMultifitParametersGPU_h + +#include + +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalMultifitParametersGPU { +public: + struct Product { + ~Product(); + double *amplitudeFitParametersEB, *amplitudeFitParametersEE, *timeFitParametersEB, *timeFitParametersEE; + }; + +#ifndef __CUDACC__ + EcalMultifitParametersGPU(edm::ParameterSet const&); + + ~EcalMultifitParametersGPU() = default; + + Product const& getProduct(cudaStream_t) const; + + std::array> const>, 4> getValues() const { + return {{amplitudeFitParametersEB_, amplitudeFitParametersEE_, timeFitParametersEB_, timeFitParametersEE_}}; + } + +private: + std::vector> amplitudeFitParametersEB_, amplitudeFitParametersEE_, + timeFitParametersEB_, timeFitParametersEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalMultifitParametersGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h new file mode 100644 index 0000000000000..5387c422ddd9e --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h @@ -0,0 +1,47 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalPedestalsGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalPedestalsGPU_h + +#include "CondFormats/EcalObjects/interface/EcalPedestals.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalPedestalsGPU { +public: + struct Product { + ~Product(); + float *mean_x12 = nullptr, *mean_x6 = nullptr, *mean_x1 = nullptr; + float *rms_x12 = nullptr, *rms_x6 = nullptr, *rms_x1 = nullptr; + }; + +#ifndef __CUDACC__ + + // rearrange pedestals + EcalPedestalsGPU(EcalPedestals const &); + + // will call dealloation for Product thru ~Product + ~EcalPedestalsGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalPedestalsGPU"}; } + +private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector> mean_x12_; + std::vector> rms_x12_; + std::vector> mean_x6_; + std::vector> rms_x6_; + std::vector> mean_x1_; + std::vector> rms_x1_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalPedestalsGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h new file mode 100644 index 0000000000000..6c5a3d9b95e2e --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h @@ -0,0 +1,40 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalPulseCovariancesGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalPulseCovariancesGPU_h + +#include "CondFormats/EcalObjects/interface/EcalPulseCovariances.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalPulseCovariancesGPU { +public: + struct Product { + ~Product(); + EcalPulseCovariance* values = nullptr; + }; + +#ifndef __CUDACC__ + // rearrange pedestals + EcalPulseCovariancesGPU(EcalPulseCovariances const&); + + // will call dealloation for Product thru ~Product + ~EcalPulseCovariancesGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalPulseCovariancesGPU"}; } + +private: + // reuse original vectors (although with default allocator) + std::vector const& valuesEB_; + std::vector const& valuesEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalPulseCovariancesGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h new file mode 100644 index 0000000000000..3edb2c9bcdfd3 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h @@ -0,0 +1,40 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalPulseShapesGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalPulseShapesGPU_h + +#include "CondFormats/EcalObjects/interface/EcalPulseShapes.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalPulseShapesGPU { +public: + struct Product { + ~Product(); + EcalPulseShape* values = nullptr; + }; + +#ifndef __CUDACC__ + // rearrange pedestals + EcalPulseShapesGPU(EcalPulseShapes const&); + + // will call dealloation for Product thru ~Product + ~EcalPulseShapesGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalPulseShapesGPU"}; } + +private: + // reuse original vectors (although with default allocator) + std::vector const& valuesEB_; + std::vector const& valuesEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalPulseShapesGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalRecHitParametersGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalRecHitParametersGPU.h new file mode 100644 index 0000000000000..c5d3dd0388d15 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalRecHitParametersGPU.h @@ -0,0 +1,47 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalRecHitParametersGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalRecHitParametersGPU_h + +#include + +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalRecHitParametersGPU { +public: + struct Product { + ~Product(); + int *ChannelStatusToBeExcluded, *expanded_v_DB_reco_flags; + uint32_t *expanded_Sizes_v_DB_reco_flags, *expanded_flagbit_v_DB_reco_flags; + }; + +#ifndef __CUDACC__ + EcalRecHitParametersGPU(edm::ParameterSet const &); + + ~EcalRecHitParametersGPU() = default; + + Product const &getProduct(cudaStream_t) const; + + using intvec = std::reference_wrapper> const>; + using uint32vec = std::reference_wrapper> const>; + std::tuple getValues() const { + return {ChannelStatusToBeExcluded_, + expanded_v_DB_reco_flags_, + expanded_Sizes_v_DB_reco_flags_, + expanded_flagbit_v_DB_reco_flags_}; + } + +private: + std::vector> ChannelStatusToBeExcluded_; + std::vector> expanded_v_DB_reco_flags_; + std::vector> expanded_Sizes_v_DB_reco_flags_, + expanded_flagbit_v_DB_reco_flags_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalRecHitParametersGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h new file mode 100644 index 0000000000000..7d4d3cc60fd5c --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h @@ -0,0 +1,42 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalRechitADCToGeVConstantGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalRechitADCToGeVConstantGPU_h + +#include "CondFormats/EcalObjects/interface/EcalADCToGeVConstant.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalRechitADCToGeVConstantGPU { +public: + struct Product { + ~Product(); + float* adc2gev = nullptr; + }; + +#ifndef __CUDACC__ + + // + EcalRechitADCToGeVConstantGPU(EcalADCToGeVConstant const&); + + // will call dealloation for Product thru ~Product + ~EcalRechitADCToGeVConstantGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalRechitADCToGeVConstantGPU"}; } + +private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector> adc2gev_; + + cms::cuda::ESProduct product_; + +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalRechitADCToGeVConstantGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h new file mode 100644 index 0000000000000..bab99ab656c2d --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h @@ -0,0 +1,42 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalRechitChannelStatusGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalRechitChannelStatusGPU_h + +#include "CondFormats/EcalObjects/interface/EcalChannelStatus.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalRechitChannelStatusGPU { +public: + struct Product { + ~Product(); + uint16_t* status = nullptr; + }; + +#ifndef __CUDACC__ + + // + EcalRechitChannelStatusGPU(EcalChannelStatus const&); + + // will call dealloation for Product thru ~Product + ~EcalRechitChannelStatusGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalRechitChannelStatusGPU"}; } + +private: + // in the future, we need to arrange so to avoid this copy on the host + // store eb first then ee + std::vector> status_; + + cms::cuda::ESProduct product_; + +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalRechitChannelStatusGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h new file mode 100644 index 0000000000000..e1dee2d505e6c --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h @@ -0,0 +1,44 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalSamplesCorrelationGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalSamplesCorrelationGPU_h + +#include "CondFormats/EcalObjects/interface/EcalSamplesCorrelation.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalSamplesCorrelationGPU { +public: + struct Product { + ~Product(); + double *EBG12SamplesCorrelation = nullptr, *EBG6SamplesCorrelation = nullptr, *EBG1SamplesCorrelation = nullptr; + double *EEG12SamplesCorrelation = nullptr, *EEG6SamplesCorrelation = nullptr, *EEG1SamplesCorrelation = nullptr; + }; + +#ifndef __CUDACC__ + // rearrange pedestals + EcalSamplesCorrelationGPU(EcalSamplesCorrelation const&); + + // will call dealloation for Product thru ~Product + ~EcalSamplesCorrelationGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalSamplesCorrelationGPU"}; } + +private: + std::vector const& EBG12SamplesCorrelation_; + std::vector const& EBG6SamplesCorrelation_; + std::vector const& EBG1SamplesCorrelation_; + std::vector const& EEG12SamplesCorrelation_; + std::vector const& EEG6SamplesCorrelation_; + std::vector const& EEG1SamplesCorrelation_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalSamplesCorrelationGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h new file mode 100644 index 0000000000000..9e2bf0aa18909 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h @@ -0,0 +1,49 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalTimeBiasCorrectionsGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalTimeBiasCorrectionsGPU_h + +#include "CondFormats/EcalObjects/interface/EcalTimeBiasCorrections.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalTimeBiasCorrectionsGPU { +public: + struct Product { + ~Product(); + float *EBTimeCorrAmplitudeBins, *EBTimeCorrShiftBins; + float *EETimeCorrAmplitudeBins, *EETimeCorrShiftBins; + int EBTimeCorrAmplitudeBinsSize, EETimeCorrAmplitudeBinsSize; + }; + + // rearrange pedestals + EcalTimeBiasCorrectionsGPU(EcalTimeBiasCorrections const&); + +#ifndef __CUDACC__ + + // will call dealloation for Product thru ~Product + ~EcalTimeBiasCorrectionsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // + static std::string name() { return std::string{"ecalTimeBiasCorrectionsGPU"}; } +#endif // __CUDACC__ + + std::vector const& EBTimeCorrAmplitudeBins() const { return EBTimeCorrAmplitudeBins_; } + std::vector const& EETimeCorrAmplitudeBins() const { return EETimeCorrAmplitudeBins_; } + +private: + std::vector const& EBTimeCorrAmplitudeBins_; + std::vector const& EBTimeCorrShiftBins_; + std::vector const& EETimeCorrAmplitudeBins_; + std::vector const& EETimeCorrShiftBins_; + +#ifndef __CUDACC__ + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalTimeBiasCorrectionsGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h b/RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h new file mode 100644 index 0000000000000..823334d433cc2 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h @@ -0,0 +1,43 @@ +#ifndef RecoLocalCalo_EcalRecAlgos_interface_EcalTimeCalibConstantsGPU_h +#define RecoLocalCalo_EcalRecAlgos_interface_EcalTimeCalibConstantsGPU_h + +#include "CondFormats/EcalObjects/interface/EcalTimeCalibConstants.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif // __CUDACC__ + +class EcalTimeCalibConstantsGPU { +public: + struct Product { + ~Product(); + float* values = nullptr; + }; + +#ifndef __CUDACC__ + // rearrange pedestals + EcalTimeCalibConstantsGPU(EcalTimeCalibConstants const&); + + // will call dealloation for Product thru ~Product + ~EcalTimeCalibConstantsGPU() = default; + + // get device pointers + Product const& getProduct(cudaStream_t) const; + + // TODO: do this centrally + // get offset for hashes. equals number of barrel items + uint32_t getOffset() const { return valuesEB_.size(); } + + // + static std::string name() { return std::string{"ecalTimeCalibConstantsGPU"}; } + +private: + std::vector const& valuesEB_; + std::vector const& valuesEE_; + + cms::cuda::ESProduct product_; +#endif // __CUDACC__ +}; + +#endif // RecoLocalCalo_EcalRecAlgos_interface_EcalTimeCalibConstantsGPU_h diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalGainRatiosGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalGainRatiosGPU.cc new file mode 100644 index 0000000000000..d5980d8a757aa --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalGainRatiosGPU.cc @@ -0,0 +1,52 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalGainRatiosGPU::EcalGainRatiosGPU(EcalGainRatios const& values) + : gain12Over6_(values.size()), gain6Over1_(values.size()) { + // fill in eb + auto const& barrelValues = values.barrelItems(); + for (unsigned int i = 0; i < barrelValues.size(); i++) { + gain12Over6_[i] = barrelValues[i].gain12Over6(); + gain6Over1_[i] = barrelValues[i].gain6Over1(); + } + + // fill in ee + auto const& endcapValues = values.endcapItems(); + auto const offset = barrelValues.size(); + for (unsigned int i = 0; i < endcapValues.size(); i++) { + gain12Over6_[offset + i] = endcapValues[i].gain12Over6(); + gain6Over1_[offset + i] = endcapValues[i].gain6Over1(); + } +} + +EcalGainRatiosGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(gain12Over6)); + cudaCheck(cudaFree(gain6Over1)); +} + +EcalGainRatiosGPU::Product const& EcalGainRatiosGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalGainRatiosGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.gain12Over6, this->gain12Over6_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.gain6Over1, this->gain6Over1_.size() * sizeof(float))); + // transfer + cudaCheck(cudaMemcpyAsync(product.gain12Over6, + this->gain12Over6_.data(), + this->gain12Over6_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.gain6Over1, + this->gain6Over1_.data(), + this->gain6Over1_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalGainRatiosGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalIntercalibConstantsGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalIntercalibConstantsGPU.cc new file mode 100644 index 0000000000000..dec10cff57dd0 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalIntercalibConstantsGPU.cc @@ -0,0 +1,40 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalIntercalibConstantsGPU::EcalIntercalibConstantsGPU(EcalIntercalibConstants const& values) + : valuesEB_{values.barrelItems()}, valuesEE_{values.endcapItems()} {} + +EcalIntercalibConstantsGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +EcalIntercalibConstantsGPU::Product const& EcalIntercalibConstantsGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalIntercalibConstantsGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck( + cudaMalloc((void**)&product.values, (this->valuesEB_.size() + this->valuesEE_.size()) * sizeof(float))); + + // offset in floats, not bytes + auto const offset = this->valuesEB_.size(); + + // transfer + cudaCheck(cudaMemcpyAsync(product.values, + this->valuesEB_.data(), + this->valuesEB_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.values + offset, + this->valuesEE_.data(), + this->valuesEE_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalIntercalibConstantsGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAPDPNRatiosGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAPDPNRatiosGPU.cc new file mode 100644 index 0000000000000..4aa92ea6750fe --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAPDPNRatiosGPU.cc @@ -0,0 +1,86 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalLaserAPDPNRatiosGPU::EcalLaserAPDPNRatiosGPU(EcalLaserAPDPNRatios const& values) + : p1_(values.getLaserMap().size()), + p2_(values.getLaserMap().size()), + p3_(values.getLaserMap().size()), + t1_(values.getTimeMap().size()), + t2_(values.getTimeMap().size()), + t3_(values.getTimeMap().size()) { + // fill in eb + // auto const& barrelValues = values.barrelItems(); + for (unsigned int i = 0; i < values.getLaserMap().barrelItems().size(); i++) { + p1_[i] = values.getLaserMap().barrelItems()[i].p1; + p2_[i] = values.getLaserMap().barrelItems()[i].p2; + p3_[i] = values.getLaserMap().barrelItems()[i].p3; + } + + // fill in ee + // auto const& endcapValues = values.endcapItems(); + auto const offset_laser = values.getLaserMap().barrelItems().size(); + for (unsigned int i = 0; i < values.getLaserMap().endcapItems().size(); i++) { + p1_[offset_laser + i] = values.getLaserMap().endcapItems()[i].p1; + p2_[offset_laser + i] = values.getLaserMap().endcapItems()[i].p2; + p3_[offset_laser + i] = values.getLaserMap().endcapItems()[i].p3; + } + + // Time is a simple std::vector + // typedef std::vector EcalLaserTimeStampMap; + for (unsigned int i = 0; i < values.getTimeMap().size(); i++) { + t1_[i] = values.getTimeMap()[i].t1.value(); + t2_[i] = values.getTimeMap()[i].t2.value(); + t3_[i] = values.getTimeMap()[i].t3.value(); + } +} + +EcalLaserAPDPNRatiosGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(p1)); + cudaCheck(cudaFree(p2)); + cudaCheck(cudaFree(p3)); + cudaCheck(cudaFree(t1)); + cudaCheck(cudaFree(t2)); + cudaCheck(cudaFree(t3)); +} + +EcalLaserAPDPNRatiosGPU::Product const& EcalLaserAPDPNRatiosGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalLaserAPDPNRatiosGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.p1, this->p1_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.p2, this->p2_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.p3, this->p3_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.t1, this->t1_.size() * sizeof(edm::TimeValue_t))); + cudaCheck(cudaMalloc((void**)&product.t2, this->t2_.size() * sizeof(edm::TimeValue_t))); + cudaCheck(cudaMalloc((void**)&product.t3, this->t3_.size() * sizeof(edm::TimeValue_t))); + // transfer + cudaCheck(cudaMemcpyAsync( + product.p1, this->p1_.data(), this->p1_.size() * sizeof(float), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync( + product.p2, this->p2_.data(), this->p2_.size() * sizeof(float), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync( + product.p3, this->p3_.data(), this->p3_.size() * sizeof(float), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync(product.t1, + this->t1_.data(), + this->t1_.size() * sizeof(edm::TimeValue_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.t2, + this->t2_.data(), + this->t2_.size() * sizeof(edm::TimeValue_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.t3, + this->t3_.data(), + this->t3_.size() * sizeof(edm::TimeValue_t), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalLaserAPDPNRatiosGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAPDPNRatiosRefGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAPDPNRatiosRefGPU.cc new file mode 100644 index 0000000000000..8f77cf48fe1d1 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAPDPNRatiosRefGPU.cc @@ -0,0 +1,40 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalLaserAPDPNRatiosRefGPU::EcalLaserAPDPNRatiosRefGPU(EcalLaserAPDPNRatiosRef const& values) + : valuesEB_{values.barrelItems()}, valuesEE_{values.endcapItems()} {} + +EcalLaserAPDPNRatiosRefGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +EcalLaserAPDPNRatiosRefGPU::Product const& EcalLaserAPDPNRatiosRefGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalLaserAPDPNRatiosRefGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck( + cudaMalloc((void**)&product.values, (this->valuesEB_.size() + this->valuesEE_.size()) * sizeof(float))); + + // offset in floats, not bytes + auto const offset = this->valuesEB_.size(); + + // transfer + cudaCheck(cudaMemcpyAsync(product.values, + this->valuesEB_.data(), + this->valuesEB_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.values + offset, + this->valuesEE_.data(), + this->valuesEE_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalLaserAPDPNRatiosRefGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAlphasGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAlphasGPU.cc new file mode 100644 index 0000000000000..91de441bff683 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalLaserAlphasGPU.cc @@ -0,0 +1,40 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalLaserAlphasGPU::EcalLaserAlphasGPU(EcalLaserAlphas const& values) + : valuesEB_{values.barrelItems()}, valuesEE_{values.endcapItems()} {} + +EcalLaserAlphasGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +EcalLaserAlphasGPU::Product const& EcalLaserAlphasGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalLaserAlphasGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck( + cudaMalloc((void**)&product.values, (this->valuesEB_.size() + this->valuesEE_.size()) * sizeof(float))); + + // offset in floats, not bytes + auto const offset = this->valuesEB_.size(); + + // transfer + cudaCheck(cudaMemcpyAsync(product.values, + this->valuesEB_.data(), + this->valuesEB_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.values + offset, + this->valuesEE_.data(), + this->valuesEE_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalLaserAlphasGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalLinearCorrectionsGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalLinearCorrectionsGPU.cc new file mode 100644 index 0000000000000..0af2a9044ab65 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalLinearCorrectionsGPU.cc @@ -0,0 +1,84 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalLinearCorrectionsGPU::EcalLinearCorrectionsGPU(EcalLinearCorrections const& values) + : p1_(values.getValueMap().size()), + p2_(values.getValueMap().size()), + p3_(values.getValueMap().size()), + t1_(values.getTimeMap().size()), + t2_(values.getTimeMap().size()), + t3_(values.getTimeMap().size()) { + // fill in eb + for (unsigned int i = 0; i < values.getValueMap().barrelItems().size(); i++) { + p1_[i] = values.getValueMap().barrelItems()[i].p1; + p2_[i] = values.getValueMap().barrelItems()[i].p2; + p3_[i] = values.getValueMap().barrelItems()[i].p3; + } + + // fill in ee + auto const offset_laser = values.getValueMap().barrelItems().size(); + for (unsigned int i = 0; i < values.getValueMap().endcapItems().size(); i++) { + p1_[offset_laser + i] = values.getValueMap().endcapItems()[i].p1; + p2_[offset_laser + i] = values.getValueMap().endcapItems()[i].p2; + p3_[offset_laser + i] = values.getValueMap().endcapItems()[i].p3; + } + + // Time is a simple std::vector + // typedef std::vector EcalLaserTimeStampMap; + for (unsigned int i = 0; i < values.getTimeMap().size(); i++) { + t1_[i] = values.getTimeMap()[i].t1.value(); + t2_[i] = values.getTimeMap()[i].t2.value(); + t3_[i] = values.getTimeMap()[i].t3.value(); + } +} + +EcalLinearCorrectionsGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(p1)); + cudaCheck(cudaFree(p2)); + cudaCheck(cudaFree(p3)); + cudaCheck(cudaFree(t1)); + cudaCheck(cudaFree(t2)); + cudaCheck(cudaFree(t3)); +} + +EcalLinearCorrectionsGPU::Product const& EcalLinearCorrectionsGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalLinearCorrectionsGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.p1, this->p1_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.p2, this->p2_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.p3, this->p3_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.t1, this->t1_.size() * sizeof(edm::TimeValue_t))); + cudaCheck(cudaMalloc((void**)&product.t2, this->t2_.size() * sizeof(edm::TimeValue_t))); + cudaCheck(cudaMalloc((void**)&product.t3, this->t3_.size() * sizeof(edm::TimeValue_t))); + // transfer + cudaCheck(cudaMemcpyAsync( + product.p1, this->p1_.data(), this->p1_.size() * sizeof(float), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync( + product.p2, this->p2_.data(), this->p2_.size() * sizeof(float), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync( + product.p3, this->p3_.data(), this->p3_.size() * sizeof(float), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync(product.t1, + this->t1_.data(), + this->t1_.size() * sizeof(edm::TimeValue_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.t2, + this->t2_.data(), + this->t2_.size() * sizeof(edm::TimeValue_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.t3, + this->t3_.data(), + this->t3_.size() * sizeof(edm::TimeValue_t), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalLinearCorrectionsGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalMultifitParametersGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalMultifitParametersGPU.cc new file mode 100644 index 0000000000000..010da6444b614 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalMultifitParametersGPU.cc @@ -0,0 +1,66 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalMultifitParametersGPU::EcalMultifitParametersGPU(edm::ParameterSet const& ps) { + auto const& amplitudeFitParametersEB = ps.getParameter>("EBamplitudeFitParameters"); + auto const& amplitudeFitParametersEE = ps.getParameter>("EEamplitudeFitParameters"); + auto const& timeFitParametersEB = ps.getParameter>("EBtimeFitParameters"); + auto const& timeFitParametersEE = ps.getParameter>("EEtimeFitParameters"); + + amplitudeFitParametersEB_.resize(amplitudeFitParametersEB.size()); + amplitudeFitParametersEE_.resize(amplitudeFitParametersEE.size()); + timeFitParametersEB_.resize(timeFitParametersEB.size()); + timeFitParametersEE_.resize(timeFitParametersEE.size()); + + std::copy(amplitudeFitParametersEB.begin(), amplitudeFitParametersEB.end(), amplitudeFitParametersEB_.begin()); + std::copy(amplitudeFitParametersEE.begin(), amplitudeFitParametersEE.end(), amplitudeFitParametersEE_.begin()); + std::copy(timeFitParametersEB.begin(), timeFitParametersEB.end(), timeFitParametersEB_.begin()); + std::copy(timeFitParametersEE.begin(), timeFitParametersEE.end(), timeFitParametersEE_.begin()); +} + +EcalMultifitParametersGPU::Product::~Product() { + cudaCheck(cudaFree(amplitudeFitParametersEB)); + cudaCheck(cudaFree(amplitudeFitParametersEE)); + cudaCheck(cudaFree(timeFitParametersEB)); + cudaCheck(cudaFree(timeFitParametersEE)); +} + +EcalMultifitParametersGPU::Product const& EcalMultifitParametersGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalMultifitParametersGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.amplitudeFitParametersEB, + this->amplitudeFitParametersEB_.size() * sizeof(double))); + cudaCheck(cudaMalloc((void**)&product.amplitudeFitParametersEE, + this->amplitudeFitParametersEE_.size() * sizeof(double))); + cudaCheck(cudaMalloc((void**)&product.timeFitParametersEB, this->timeFitParametersEB_.size() * sizeof(double))); + cudaCheck(cudaMalloc((void**)&product.timeFitParametersEE, this->timeFitParametersEE_.size() * sizeof(double))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.amplitudeFitParametersEB, + this->amplitudeFitParametersEB_.data(), + this->amplitudeFitParametersEB_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.amplitudeFitParametersEE, + this->amplitudeFitParametersEE_.data(), + this->amplitudeFitParametersEE_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.timeFitParametersEB, + this->timeFitParametersEB_.data(), + this->timeFitParametersEB_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.timeFitParametersEE, + this->timeFitParametersEE_.data(), + this->timeFitParametersEE_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + }); + return product; +} + +TYPELOOKUP_DATA_REG(EcalMultifitParametersGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalPedestalsGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalPedestalsGPU.cc new file mode 100644 index 0000000000000..9e3284cd9c7c8 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalPedestalsGPU.cc @@ -0,0 +1,94 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalPedestalsGPU::EcalPedestalsGPU(EcalPedestals const& pedestals) + : mean_x12_(pedestals.size()), + rms_x12_(pedestals.size()), + mean_x6_(pedestals.size()), + rms_x6_(pedestals.size()), + mean_x1_(pedestals.size()), + rms_x1_(pedestals.size()) { + // fill in eb + auto const& barrelValues = pedestals.barrelItems(); + for (unsigned int i = 0; i < barrelValues.size(); i++) { + mean_x12_[i] = barrelValues[i].mean_x12; + rms_x12_[i] = barrelValues[i].rms_x12; + mean_x6_[i] = barrelValues[i].mean_x6; + rms_x6_[i] = barrelValues[i].rms_x6; + mean_x1_[i] = barrelValues[i].mean_x1; + rms_x1_[i] = barrelValues[i].rms_x1; + } + + // fill in ee + auto const& endcapValues = pedestals.endcapItems(); + auto const offset = barrelValues.size(); + for (unsigned int i = 0; i < endcapValues.size(); i++) { + mean_x12_[offset + i] = endcapValues[i].mean_x12; + rms_x12_[offset + i] = endcapValues[i].rms_x12; + mean_x6_[offset + i] = endcapValues[i].mean_x6; + rms_x6_[offset + i] = endcapValues[i].rms_x6; + mean_x1_[offset + i] = endcapValues[i].mean_x1; + rms_x1_[offset + i] = endcapValues[i].rms_x1; + } +} + +EcalPedestalsGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(mean_x12)); + cudaCheck(cudaFree(rms_x12)); + cudaCheck(cudaFree(mean_x6)); + cudaCheck(cudaFree(rms_x6)); + cudaCheck(cudaFree(mean_x1)); + cudaCheck(cudaFree(rms_x1)); +} + +EcalPedestalsGPU::Product const& EcalPedestalsGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalPedestalsGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.mean_x12, this->mean_x12_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.rms_x12, this->mean_x12_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.mean_x6, this->mean_x12_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.rms_x6, this->mean_x12_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.mean_x1, this->mean_x12_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.rms_x1, this->mean_x12_.size() * sizeof(float))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.mean_x12, + this->mean_x12_.data(), + this->mean_x12_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.rms_x12, + this->rms_x12_.data(), + this->rms_x12_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.mean_x6, + this->mean_x6_.data(), + this->mean_x6_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.rms_x6, + this->rms_x6_.data(), + this->rms_x6_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.mean_x1, + this->mean_x1_.data(), + this->mean_x1_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.rms_x1, + this->rms_x1_.data(), + this->rms_x1_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalPedestalsGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalPulseCovariancesGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalPulseCovariancesGPU.cc new file mode 100644 index 0000000000000..bbeda99652e22 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalPulseCovariancesGPU.cc @@ -0,0 +1,42 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalPulseCovariancesGPU::EcalPulseCovariancesGPU(EcalPulseCovariances const& values) + : valuesEB_{values.barrelItems()}, valuesEE_{values.endcapItems()} {} + +EcalPulseCovariancesGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +EcalPulseCovariancesGPU::Product const& EcalPulseCovariancesGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalPulseCovariancesGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.values, + (this->valuesEE_.size() + this->valuesEB_.size()) * sizeof(EcalPulseCovariance))); + + // offset in terms of sizeof(EcalPulseCovariance) + uint32_t offset = this->valuesEB_.size(); + + // transfer eb + cudaCheck(cudaMemcpyAsync(product.values, + this->valuesEB_.data(), + this->valuesEB_.size() * sizeof(EcalPulseCovariance), + cudaMemcpyHostToDevice, + cudaStream)); + + // transfer ee starting at values + offset + cudaCheck(cudaMemcpyAsync(product.values + offset, + this->valuesEE_.data(), + this->valuesEE_.size() * sizeof(EcalPulseCovariance), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalPulseCovariancesGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalPulseShapesGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalPulseShapesGPU.cc new file mode 100644 index 0000000000000..aee122a01627d --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalPulseShapesGPU.cc @@ -0,0 +1,42 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalPulseShapesGPU::EcalPulseShapesGPU(EcalPulseShapes const& values) + : valuesEB_{values.barrelItems()}, valuesEE_{values.endcapItems()} {} + +EcalPulseShapesGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +EcalPulseShapesGPU::Product const& EcalPulseShapesGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalPulseShapesGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.values, + (this->valuesEE_.size() + this->valuesEB_.size()) * sizeof(EcalPulseShape))); + + // offset in terms of sizeof(EcalPulseShape) - plain c array + uint32_t offset = this->valuesEB_.size(); + + // transfer eb + cudaCheck(cudaMemcpyAsync(product.values, + this->valuesEB_.data(), + this->valuesEB_.size() * sizeof(EcalPulseShape), + cudaMemcpyHostToDevice, + cudaStream)); + + // transfer ee starting at values + offset + cudaCheck(cudaMemcpyAsync(product.values + offset, + this->valuesEE_.data(), + this->valuesEE_.size() * sizeof(EcalPulseShape), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalPulseShapesGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalRecHitParametersGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalRecHitParametersGPU.cc new file mode 100644 index 0000000000000..0f6812d6d6ffe --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalRecHitParametersGPU.cc @@ -0,0 +1,82 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRecHitParametersGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "CommonTools/Utils/interface/StringToEnumValue.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHit.h" + +EcalRecHitParametersGPU::EcalRecHitParametersGPU(edm::ParameterSet const& ps) { + auto const& ChannelStatusToBeExcluded = StringToEnumValue( + ps.getParameter>("ChannelStatusToBeExcluded")); + + ChannelStatusToBeExcluded_.resize(ChannelStatusToBeExcluded.size()); + std::copy(ChannelStatusToBeExcluded.begin(), ChannelStatusToBeExcluded.end(), ChannelStatusToBeExcluded_.begin()); + + // https://github.com/cms-sw/cmssw/blob/266e21cfc9eb409b093e4cf064f4c0a24c6ac293/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitWorkerSimple.cc + + // Traslate string representation of flagsMapDBReco into enum values + const edm::ParameterSet& p = ps.getParameter("flagsMapDBReco"); + std::vector recoflagbitsStrings = p.getParameterNames(); + + for (unsigned int i = 0; i != recoflagbitsStrings.size(); ++i) { + EcalRecHit::Flags recoflagbit = (EcalRecHit::Flags)StringToEnumValue(recoflagbitsStrings[i]); + std::vector dbstatus_s = p.getParameter>(recoflagbitsStrings[i]); + // std::vector dbstatuses; + for (unsigned int j = 0; j != dbstatus_s.size(); ++j) { + EcalChannelStatusCode::Code dbstatus = + (EcalChannelStatusCode::Code)StringToEnumValue(dbstatus_s[j]); + expanded_v_DB_reco_flags_.push_back(dbstatus); + } + + expanded_Sizes_v_DB_reco_flags_.push_back(dbstatus_s.size()); + expanded_flagbit_v_DB_reco_flags_.push_back(recoflagbit); + } +} + +EcalRecHitParametersGPU::Product::~Product() { + cudaCheck(cudaFree(ChannelStatusToBeExcluded)); + cudaCheck(cudaFree(expanded_v_DB_reco_flags)); + cudaCheck(cudaFree(expanded_Sizes_v_DB_reco_flags)); + cudaCheck(cudaFree(expanded_flagbit_v_DB_reco_flags)); +} + +EcalRecHitParametersGPU::Product const& EcalRecHitParametersGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalRecHitParametersGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.ChannelStatusToBeExcluded, + this->ChannelStatusToBeExcluded_.size() * sizeof(int))); + cudaCheck(cudaMalloc((void**)&product.expanded_v_DB_reco_flags, + this->expanded_v_DB_reco_flags_.size() * sizeof(int))); + cudaCheck(cudaMalloc((void**)&product.expanded_Sizes_v_DB_reco_flags, + this->expanded_Sizes_v_DB_reco_flags_.size() * sizeof(uint32_t))); + cudaCheck(cudaMalloc((void**)&product.expanded_flagbit_v_DB_reco_flags, + this->expanded_flagbit_v_DB_reco_flags_.size() * sizeof(uint32_t))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.ChannelStatusToBeExcluded, + this->ChannelStatusToBeExcluded_.data(), + this->ChannelStatusToBeExcluded_.size() * sizeof(int), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.expanded_v_DB_reco_flags, + this->expanded_v_DB_reco_flags_.data(), + this->expanded_v_DB_reco_flags_.size() * sizeof(int), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.expanded_Sizes_v_DB_reco_flags, + this->expanded_Sizes_v_DB_reco_flags_.data(), + this->expanded_Sizes_v_DB_reco_flags_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.expanded_flagbit_v_DB_reco_flags, + this->expanded_flagbit_v_DB_reco_flags_.data(), + this->expanded_flagbit_v_DB_reco_flags_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + }); + return product; +} + +TYPELOOKUP_DATA_REG(EcalRecHitParametersGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalRechitADCToGeVConstantGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalRechitADCToGeVConstantGPU.cc new file mode 100644 index 0000000000000..5f01068f95186 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalRechitADCToGeVConstantGPU.cc @@ -0,0 +1,34 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalRechitADCToGeVConstantGPU::EcalRechitADCToGeVConstantGPU(EcalADCToGeVConstant const& values) + : adc2gev_(2) // size is 2, one form EB and one for EE +{ + adc2gev_[0] = values.getEBValue(); + adc2gev_[1] = values.getEEValue(); +} + +EcalRechitADCToGeVConstantGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(adc2gev)); +} + +EcalRechitADCToGeVConstantGPU::Product const& EcalRechitADCToGeVConstantGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalRechitADCToGeVConstantGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.adc2gev, this->adc2gev_.size() * sizeof(float))); + // transfer + cudaCheck(cudaMemcpyAsync(product.adc2gev, + this->adc2gev_.data(), + this->adc2gev_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalRechitADCToGeVConstantGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalRechitChannelStatusGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalRechitChannelStatusGPU.cc new file mode 100644 index 0000000000000..1e6801fbd326a --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalRechitChannelStatusGPU.cc @@ -0,0 +1,42 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalRechitChannelStatusGPU::EcalRechitChannelStatusGPU(EcalChannelStatus const& values) : status_(values.size()) { + // fill in eb + auto const& barrelValues = values.barrelItems(); + for (unsigned int i = 0; i < barrelValues.size(); i++) { + status_[i] = barrelValues[i].getEncodedStatusCode(); + } + + // fill in ee + auto const& endcapValues = values.endcapItems(); + auto const offset = barrelValues.size(); + for (unsigned int i = 0; i < endcapValues.size(); i++) { + status_[offset + i] = endcapValues[i].getEncodedStatusCode(); + } +} + +EcalRechitChannelStatusGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(status)); +} + +EcalRechitChannelStatusGPU::Product const& EcalRechitChannelStatusGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalRechitChannelStatusGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.status, this->status_.size() * sizeof(uint16_t))); + // transfer + cudaCheck(cudaMemcpyAsync(product.status, + this->status_.data(), + this->status_.size() * sizeof(uint16_t), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalRechitChannelStatusGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalSamplesCorrelationGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalSamplesCorrelationGPU.cc new file mode 100644 index 0000000000000..2a98067f51d9e --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalSamplesCorrelationGPU.cc @@ -0,0 +1,76 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalSamplesCorrelationGPU::EcalSamplesCorrelationGPU(EcalSamplesCorrelation const& values) + : EBG12SamplesCorrelation_{values.EBG12SamplesCorrelation}, + EBG6SamplesCorrelation_{values.EBG6SamplesCorrelation}, + EBG1SamplesCorrelation_{values.EBG1SamplesCorrelation}, + EEG12SamplesCorrelation_{values.EEG12SamplesCorrelation}, + EEG6SamplesCorrelation_{values.EEG6SamplesCorrelation}, + EEG1SamplesCorrelation_{values.EEG1SamplesCorrelation} {} + +EcalSamplesCorrelationGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(EBG12SamplesCorrelation)); + cudaCheck(cudaFree(EBG6SamplesCorrelation)); + cudaCheck(cudaFree(EBG1SamplesCorrelation)); + cudaCheck(cudaFree(EEG12SamplesCorrelation)); + cudaCheck(cudaFree(EEG6SamplesCorrelation)); + cudaCheck(cudaFree(EEG1SamplesCorrelation)); +} + +EcalSamplesCorrelationGPU::Product const& EcalSamplesCorrelationGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalSamplesCorrelationGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.EBG12SamplesCorrelation, + this->EBG12SamplesCorrelation_.size() * sizeof(double))); + cudaCheck( + cudaMalloc((void**)&product.EBG6SamplesCorrelation, this->EBG6SamplesCorrelation_.size() * sizeof(double))); + cudaCheck( + cudaMalloc((void**)&product.EBG1SamplesCorrelation, this->EBG1SamplesCorrelation_.size() * sizeof(double))); + cudaCheck(cudaMalloc((void**)&product.EEG12SamplesCorrelation, + this->EEG12SamplesCorrelation_.size() * sizeof(double))); + cudaCheck( + cudaMalloc((void**)&product.EEG6SamplesCorrelation, this->EEG6SamplesCorrelation_.size() * sizeof(double))); + cudaCheck( + cudaMalloc((void**)&product.EEG1SamplesCorrelation, this->EEG1SamplesCorrelation_.size() * sizeof(double))); + // transfer + cudaCheck(cudaMemcpyAsync(product.EBG12SamplesCorrelation, + this->EBG12SamplesCorrelation_.data(), + this->EBG12SamplesCorrelation_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EBG6SamplesCorrelation, + this->EBG6SamplesCorrelation_.data(), + this->EBG6SamplesCorrelation_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EBG1SamplesCorrelation, + this->EBG1SamplesCorrelation_.data(), + this->EBG1SamplesCorrelation_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EEG12SamplesCorrelation, + this->EEG12SamplesCorrelation_.data(), + this->EEG12SamplesCorrelation_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EEG6SamplesCorrelation, + this->EEG6SamplesCorrelation_.data(), + this->EEG6SamplesCorrelation_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EEG1SamplesCorrelation, + this->EEG1SamplesCorrelation_.data(), + this->EEG1SamplesCorrelation_.size() * sizeof(double), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalSamplesCorrelationGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalTimeBiasCorrectionsGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalTimeBiasCorrectionsGPU.cc new file mode 100644 index 0000000000000..9ab0a6302a9c4 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalTimeBiasCorrectionsGPU.cc @@ -0,0 +1,61 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalTimeBiasCorrectionsGPU::EcalTimeBiasCorrectionsGPU(EcalTimeBiasCorrections const& values) + : EBTimeCorrAmplitudeBins_{values.EBTimeCorrAmplitudeBins}, + EBTimeCorrShiftBins_{values.EBTimeCorrShiftBins}, + EETimeCorrAmplitudeBins_{values.EETimeCorrAmplitudeBins}, + EETimeCorrShiftBins_{values.EETimeCorrShiftBins} {} + +EcalTimeBiasCorrectionsGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(EBTimeCorrAmplitudeBins)); + cudaCheck(cudaFree(EBTimeCorrShiftBins)); + cudaCheck(cudaFree(EETimeCorrAmplitudeBins)); + cudaCheck(cudaFree(EETimeCorrShiftBins)); +} + +EcalTimeBiasCorrectionsGPU::Product const& EcalTimeBiasCorrectionsGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalTimeBiasCorrectionsGPU::Product& product, cudaStream_t cudaStream) { + // to get the size of vectors later on + // should be removed and host conditions' objects used directly + product.EBTimeCorrAmplitudeBinsSize = this->EBTimeCorrAmplitudeBins_.size(); + product.EETimeCorrAmplitudeBinsSize = this->EETimeCorrAmplitudeBins_.size(); + + // malloc + cudaCheck(cudaMalloc((void**)&product.EBTimeCorrAmplitudeBins, + this->EBTimeCorrAmplitudeBins_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.EBTimeCorrShiftBins, this->EBTimeCorrShiftBins_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.EETimeCorrAmplitudeBins, + this->EETimeCorrAmplitudeBins_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.EETimeCorrShiftBins, this->EETimeCorrShiftBins_.size() * sizeof(float))); + // transfer + cudaCheck(cudaMemcpyAsync(product.EBTimeCorrAmplitudeBins, + this->EBTimeCorrAmplitudeBins_.data(), + this->EBTimeCorrAmplitudeBins_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EBTimeCorrShiftBins, + this->EBTimeCorrShiftBins_.data(), + this->EBTimeCorrShiftBins_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EETimeCorrAmplitudeBins, + this->EETimeCorrAmplitudeBins_.data(), + this->EETimeCorrAmplitudeBins_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.EETimeCorrShiftBins, + this->EETimeCorrShiftBins_.data(), + this->EETimeCorrShiftBins_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalTimeBiasCorrectionsGPU); diff --git a/RecoLocalCalo/EcalRecAlgos/src/EcalTimeCalibConstantsGPU.cc b/RecoLocalCalo/EcalRecAlgos/src/EcalTimeCalibConstantsGPU.cc new file mode 100644 index 0000000000000..d724a33f1d4e1 --- /dev/null +++ b/RecoLocalCalo/EcalRecAlgos/src/EcalTimeCalibConstantsGPU.cc @@ -0,0 +1,40 @@ +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +EcalTimeCalibConstantsGPU::EcalTimeCalibConstantsGPU(EcalTimeCalibConstants const& values) + : valuesEB_{values.barrelItems()}, valuesEE_{values.endcapItems()} {} + +EcalTimeCalibConstantsGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +EcalTimeCalibConstantsGPU::Product const& EcalTimeCalibConstantsGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](EcalTimeCalibConstantsGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck( + cudaMalloc((void**)&product.values, (this->valuesEB_.size() + this->valuesEE_.size()) * sizeof(float))); + + // offset in floats, not bytes + auto const offset = this->valuesEB_.size(); + + // transfer + cudaCheck(cudaMemcpyAsync(product.values, + this->valuesEB_.data(), + this->valuesEB_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.values + offset, + this->valuesEE_.data(), + this->valuesEE_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(EcalTimeCalibConstantsGPU); diff --git a/RecoLocalCalo/EcalRecProducers/BuildFile.xml b/RecoLocalCalo/EcalRecProducers/BuildFile.xml index 25939a2a69b8e..aa19516964fd9 100644 --- a/RecoLocalCalo/EcalRecProducers/BuildFile.xml +++ b/RecoLocalCalo/EcalRecProducers/BuildFile.xml @@ -1,5 +1,11 @@ - + + + + + + + diff --git a/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationCommonKernels.cu b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationCommonKernels.cu new file mode 100644 index 0000000000000..f1b1a53a78a30 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationCommonKernels.cu @@ -0,0 +1,486 @@ +#include +#include + +#include + +#include "CondFormats/EcalObjects/interface/EcalPulseCovariances.h" +#include "CondFormats/EcalObjects/interface/EcalPulseShapes.h" +#include "CondFormats/EcalObjects/interface/EcalSamplesCorrelation.h" +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" +#include "DataFormats/EcalRecHit/interface/EcalUncalibratedRecHit.h" +#include "DataFormats/Math/interface/approx_exp.h" +#include "DataFormats/Math/interface/approx_log.h" +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" + +#include "AmplitudeComputationCommonKernels.h" +#include "KernelHelpers.h" + +namespace ecal { + namespace multifit { + + /// + /// assume kernel launch configuration is + /// (MAXSAMPLES * nchannels, blocks) + /// + __global__ void kernel_prep_1d_and_initialize(EcalPulseShape const* shapes_in, + uint16_t const* digis_in_eb, + uint32_t const* dids_eb, + uint16_t const* digis_in_ee, + uint32_t const* dids_ee, + SampleVector* amplitudes, + SampleVector* amplitudesForMinimizationEB, + SampleVector* amplitudesForMinimizationEE, + SampleGainVector* gainsNoise, + float const* mean_x1, + float const* mean_x12, + float const* rms_x12, + float const* mean_x6, + float const* gain6Over1, + float const* gain12Over6, + bool* hasSwitchToGain6, + bool* hasSwitchToGain1, + bool* isSaturated, + ::ecal::reco::StorageScalarType* energiesEB, + ::ecal::reco::StorageScalarType* energiesEE, + ::ecal::reco::StorageScalarType* chi2EB, + ::ecal::reco::StorageScalarType* chi2EE, + ::ecal::reco::StorageScalarType* g_pedestalEB, + ::ecal::reco::StorageScalarType* g_pedestalEE, + uint32_t* dids_outEB, + uint32_t* dids_outEE, + uint32_t* flagsEB, + uint32_t* flagsEE, + char* acState, + BXVectorType* bxs, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs, + bool const gainSwitchUseMaxSampleEB, + bool const gainSwitchUseMaxSampleEE, + int const nchannels) { + constexpr bool dynamicPedestal = false; //---- default to false, ok + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + constexpr int sample_max = 5; + constexpr int full_pulse_max = 9; + int const tx = threadIdx.x + blockIdx.x * blockDim.x; + int const nchannels_per_block = blockDim.x / nsamples; + int const ch = tx / nsamples; + // for accessing input arrays + int const inputCh = ch >= offsetForInputs ? ch - offsetForInputs : ch; + int const inputTx = ch >= offsetForInputs ? tx - offsetForInputs * 10 : tx; + // eb is first and then ee + auto const* digis_in = ch >= offsetForInputs ? digis_in_ee : digis_in_eb; + auto const* dids = ch >= offsetForInputs ? dids_ee : dids_eb; + int const sample = threadIdx.x % nsamples; + + // need to ref the right ptr + // macro is for clarity and safety +#define ARRANGE(var) auto* var = ch >= offsetForInputs ? var##EE : var##EB + ARRANGE(amplitudesForMinimization); + ARRANGE(energies); + ARRANGE(chi2); + ARRANGE(g_pedestal); + ARRANGE(dids_out); + ARRANGE(flags); +#undef ARRANGE + + if (ch < nchannels) { + // array of 10 x channels per block + // TODO: any other way of doing simple reduction + // assume bool is 1 byte, should be quite safe + extern __shared__ char shared_mem[]; + bool* shr_hasSwitchToGain6 = reinterpret_cast(shared_mem); + bool* shr_hasSwitchToGain1 = shr_hasSwitchToGain6 + nchannels_per_block * nsamples; + bool* shr_hasSwitchToGain0 = shr_hasSwitchToGain1 + nchannels_per_block * nsamples; + bool* shr_isSaturated = shr_hasSwitchToGain0 + nchannels_per_block * nsamples; + bool* shr_hasSwitchToGain0_tmp = shr_isSaturated + nchannels_per_block * nsamples; + char* shr_counts = reinterpret_cast(shr_hasSwitchToGain0_tmp) + nchannels_per_block * nsamples; + + // + // indices + // + auto const did = DetId{dids[inputCh]}; + auto const isBarrel = did.subdetId() == EcalBarrel; + // TODO offset for ee, 0 for eb + auto const hashedId = isBarrel ? ecal::reconstruction::hashedIndexEB(did.rawId()) + : offsetForHashes + ecal::reconstruction::hashedIndexEE(did.rawId()); + + // + // pulse shape template + + // will be used in the future for setting state + auto const rmsForChecking = rms_x12[hashedId]; + + // + // amplitudes + // + int const adc = ecal::mgpa::adc(digis_in[inputTx]); + int const gainId = ecal::mgpa::gainId(digis_in[inputTx]); + SampleVector::Scalar amplitude = 0.; + SampleVector::Scalar pedestal = 0.; + SampleVector::Scalar gainratio = 0.; + + // store into shared mem for initialization + shr_hasSwitchToGain6[threadIdx.x] = gainId == EcalMgpaBitwiseGain6; + shr_hasSwitchToGain1[threadIdx.x] = gainId == EcalMgpaBitwiseGain1; + shr_hasSwitchToGain0_tmp[threadIdx.x] = gainId == EcalMgpaBitwiseGain0; + shr_hasSwitchToGain0[threadIdx.x] = shr_hasSwitchToGain0_tmp[threadIdx.x]; + shr_counts[threadIdx.x] = 0; + __syncthreads(); + + // non-divergent branch (except for the last 4 threads) + if (threadIdx.x <= blockDim.x - 5) { + CMS_UNROLL_LOOP + for (int i = 0; i < 5; i++) + shr_counts[threadIdx.x] += shr_hasSwitchToGain0[threadIdx.x + i]; + } + shr_isSaturated[threadIdx.x] = shr_counts[threadIdx.x] == 5; + + // + // unrolled reductions + // + if (sample < 5) { + shr_hasSwitchToGain6[threadIdx.x] = + shr_hasSwitchToGain6[threadIdx.x] || shr_hasSwitchToGain6[threadIdx.x + 5]; + shr_hasSwitchToGain1[threadIdx.x] = + shr_hasSwitchToGain1[threadIdx.x] || shr_hasSwitchToGain1[threadIdx.x + 5]; + + // duplication of hasSwitchToGain0 in order not to + // introduce another syncthreads + shr_hasSwitchToGain0_tmp[threadIdx.x] = + shr_hasSwitchToGain0_tmp[threadIdx.x] || shr_hasSwitchToGain0_tmp[threadIdx.x + 5]; + } + __syncthreads(); + + if (sample < 2) { + // note, both threads per channel take value [3] twice to avoid another if + shr_hasSwitchToGain6[threadIdx.x] = shr_hasSwitchToGain6[threadIdx.x] || + shr_hasSwitchToGain6[threadIdx.x + 2] || + shr_hasSwitchToGain6[threadIdx.x + 3]; + shr_hasSwitchToGain1[threadIdx.x] = shr_hasSwitchToGain1[threadIdx.x] || + shr_hasSwitchToGain1[threadIdx.x + 2] || + shr_hasSwitchToGain1[threadIdx.x + 3]; + + shr_hasSwitchToGain0_tmp[threadIdx.x] = shr_hasSwitchToGain0_tmp[threadIdx.x] || + shr_hasSwitchToGain0_tmp[threadIdx.x + 2] || + shr_hasSwitchToGain0_tmp[threadIdx.x + 3]; + + // sample < 2 -> first 2 threads of each channel will be used here + // => 0 -> will compare 3 and 4 and put into 0 + // => 1 -> will compare 4 and 5 and put into 1 + shr_isSaturated[threadIdx.x] = shr_isSaturated[threadIdx.x + 3] || shr_isSaturated[threadIdx.x + 4]; + } + __syncthreads(); + + bool check_hasSwitchToGain0 = false; + + if (sample == 0) { + shr_hasSwitchToGain6[threadIdx.x] = + shr_hasSwitchToGain6[threadIdx.x] || shr_hasSwitchToGain6[threadIdx.x + 1]; + shr_hasSwitchToGain1[threadIdx.x] = + shr_hasSwitchToGain1[threadIdx.x] || shr_hasSwitchToGain1[threadIdx.x + 1]; + shr_hasSwitchToGain0_tmp[threadIdx.x] = + shr_hasSwitchToGain0_tmp[threadIdx.x] || shr_hasSwitchToGain0_tmp[threadIdx.x + 1]; + + hasSwitchToGain6[ch] = shr_hasSwitchToGain6[threadIdx.x]; + hasSwitchToGain1[ch] = shr_hasSwitchToGain1[threadIdx.x]; + + // set only for the threadIdx.x corresponding to sample==0 + check_hasSwitchToGain0 = shr_hasSwitchToGain0_tmp[threadIdx.x]; + + shr_isSaturated[threadIdx.x + 3] = shr_isSaturated[threadIdx.x] || shr_isSaturated[threadIdx.x + 1]; + isSaturated[ch] = shr_isSaturated[threadIdx.x + 3]; + } + + // TODO: w/o this sync, there is a race + // if (threadIdx == sample_max) below uses max sample thread, not for 0 sample + // check if we can remove it + __syncthreads(); + + // TODO: divergent branch + if (gainId == 0 || gainId == 3) { + pedestal = mean_x1[hashedId]; + gainratio = gain6Over1[hashedId] * gain12Over6[hashedId]; + gainsNoise[ch](sample) = 2; + } else if (gainId == 1) { + pedestal = mean_x12[hashedId]; + gainratio = 1.; + gainsNoise[ch](sample) = 0; + } else if (gainId == 2) { + pedestal = mean_x6[hashedId]; + gainratio = gain12Over6[hashedId]; + gainsNoise[ch](sample) = 1; + } + + // TODO: compile time constant -> branch should be non-divergent + if (dynamicPedestal) + amplitude = static_cast(adc) * gainratio; + else + amplitude = (static_cast(adc) - pedestal) * gainratio; + amplitudes[ch][sample] = amplitude; + +#ifdef ECAL_RECO_CUDA_DEBUG + printf("%d %d %d %d %f %f %f\n", tx, ch, sample, adc, amplitude, pedestal, gainratio); + if (adc == 0) + printf("adc is zero\n"); +#endif + + // + // initialization + // + amplitudesForMinimization[inputCh](sample) = 0; + bxs[ch](sample) = sample - 5; + + // select the thread for the max sample + //---> hardcoded above to be 5th sample, ok + if (sample == sample_max) { + // + // initialization + // + acState[ch] = static_cast(MinimizationState::NotFinished); + energies[inputCh] = 0; + chi2[inputCh] = 0; + g_pedestal[inputCh] = 0; + uint32_t flag = 0; + dids_out[inputCh] = did.rawId(); + + // start of this channel in shared mem + int const chStart = threadIdx.x - sample_max; + // thread for the max sample in shared mem + int const threadMax = threadIdx.x; + auto const gainSwitchUseMaxSample = isBarrel ? gainSwitchUseMaxSampleEB : gainSwitchUseMaxSampleEE; + + // this flag setting is applied to all of the cases + if (shr_hasSwitchToGain6[chStart]) + flag |= 0x1 << EcalUncalibratedRecHit::kHasSwitchToGain6; + if (shr_hasSwitchToGain1[chStart]) + flag |= 0x1 << EcalUncalibratedRecHit::kHasSwitchToGain1; + + // this corresponds to cpu branching on lastSampleBeforeSaturation + // likely false + if (check_hasSwitchToGain0) { + // assign for the case some sample having gainId == 0 + //energies[inputCh] = amplitudes[ch][sample_max]; + energies[inputCh] = amplitude; + + // check if samples before sample_max have true + bool saturated_before_max = false; + CMS_UNROLL_LOOP + for (char ii = 0; ii < 5; ii++) + saturated_before_max = saturated_before_max || shr_hasSwitchToGain0[chStart + ii]; + + // if saturation is in the max sample and not in the first 5 + if (!saturated_before_max && shr_hasSwitchToGain0[threadMax]) + energies[inputCh] = 49140; // 4095 * 12 (maximum ADC range * MultiGainPreAmplifier (MGPA) gain) + // This is the actual maximum range that is set when we saturate. + //---- AM FIXME : no pedestal subtraction??? + //It should be "(4095. - pedestal) * gainratio" + + // set state flag to terminate further processing of this channel + acState[ch] = static_cast(MinimizationState::Precomputed); + flag |= 0x1 << EcalUncalibratedRecHit::kSaturated; + flags[inputCh] = flag; + return; + } + + // according to cpu version + // auto max_amplitude = amplitudes[ch][sample_max]; + auto const max_amplitude = amplitude; + // according to cpu version + auto shape_value = shapes_in[hashedId].pdfval[full_pulse_max - 7]; + // note, no syncing as the same thread will be accessing here + bool hasGainSwitch = + shr_hasSwitchToGain6[chStart] || shr_hasSwitchToGain1[chStart] || shr_isSaturated[chStart + 3]; + + // pedestal is final unconditionally + g_pedestal[inputCh] = pedestal; + if (hasGainSwitch && gainSwitchUseMaxSample) { + // thread for sample=0 will access the right guys + energies[inputCh] = max_amplitude / shape_value; + acState[ch] = static_cast(MinimizationState::Precomputed); + flags[inputCh] = flag; + return; + } + + // this happens cause sometimes rms_x12 is 0... + // needs to be checkec why this is the case + // general case here is that noisecov is a Zero matrix + if (rmsForChecking == 0) { + acState[ch] = static_cast(MinimizationState::Precomputed); + flags[inputCh] = flag; + return; + } + + // for the case when no shortcuts were taken + flags[inputCh] = flag; + } + } + } + + /// + /// assume kernel launch configuration is + /// ([MAXSAMPLES, MAXSAMPLES], nchannels) + /// + __global__ void kernel_prep_2d(SampleGainVector const* gainNoise, + uint32_t const* dids_eb, + uint32_t const* dids_ee, + float const* rms_x12, + float const* rms_x6, + float const* rms_x1, + float const* gain12Over6, + float const* gain6Over1, + double const* G12SamplesCorrelationEB, + double const* G6SamplesCorrelationEB, + double const* G1SamplesCorrelationEB, + double const* G12SamplesCorrelationEE, + double const* G6SamplesCorrelationEE, + double const* G1SamplesCorrelationEE, + SampleMatrix* noisecov, + PulseMatrixType* pulse_matrix, + EcalPulseShape const* pulse_shape, + bool const* hasSwitchToGain6, + bool const* hasSwitchToGain1, + bool const* isSaturated, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs) { + int const ch = blockIdx.x; + int const tx = threadIdx.x; + int const ty = threadIdx.y; + constexpr float addPedestalUncertainty = 0.f; + constexpr bool dynamicPedestal = false; + constexpr bool simplifiedNoiseModelForGainSwitch = true; //---- default is true + + // to access input arrays (ids and digis only) + int const inputCh = ch >= offsetForInputs ? ch - offsetForInputs : ch; + auto const* dids = ch >= offsetForInputs ? dids_ee : dids_eb; + + bool tmp0 = hasSwitchToGain6[ch]; + bool tmp1 = hasSwitchToGain1[ch]; + auto const did = DetId{dids[inputCh]}; + auto const isBarrel = did.subdetId() == EcalBarrel; + auto const hashedId = isBarrel ? ecal::reconstruction::hashedIndexEB(did.rawId()) + : offsetForHashes + ecal::reconstruction::hashedIndexEE(did.rawId()); + auto const G12SamplesCorrelation = isBarrel ? G12SamplesCorrelationEB : G12SamplesCorrelationEE; + auto const* G6SamplesCorrelation = isBarrel ? G6SamplesCorrelationEB : G6SamplesCorrelationEE; + auto const* G1SamplesCorrelation = isBarrel ? G1SamplesCorrelationEB : G1SamplesCorrelationEE; + bool tmp2 = isSaturated[ch]; + bool hasGainSwitch = tmp0 || tmp1 || tmp2; + auto const vidx = std::abs(ty - tx); + + // non-divergent branch for all threads per block + if (hasGainSwitch) { + // TODO: did not include simplified noise model + float noise_value = 0; + + // non-divergent branch - all threads per block + // TODO: all of these constants indicate that + // that these parts could be splitted into completely different + // kernels and run one of them only depending on the config + if (simplifiedNoiseModelForGainSwitch) { + int isample_max = 5; // according to cpu defs + int gainidx = gainNoise[ch][isample_max]; + + // non-divergent branches + if (gainidx == 0) + noise_value = rms_x12[hashedId] * rms_x12[hashedId] * G12SamplesCorrelation[vidx]; + if (gainidx == 1) + noise_value = gain12Over6[hashedId] * gain12Over6[hashedId] * rms_x6[hashedId] * rms_x6[hashedId] * + G6SamplesCorrelation[vidx]; + if (gainidx == 2) + noise_value = gain12Over6[hashedId] * gain12Over6[hashedId] * gain6Over1[hashedId] * gain6Over1[hashedId] * + rms_x1[hashedId] * rms_x1[hashedId] * G1SamplesCorrelation[vidx]; + if (!dynamicPedestal && addPedestalUncertainty > 0.f) + noise_value += addPedestalUncertainty * addPedestalUncertainty; + } else { + int gainidx = 0; + char mask = gainidx; + int pedestal = gainNoise[ch][ty] == mask ? 1 : 0; + // NB: gainratio is 1, that is why it does not appear in the formula + noise_value += rms_x12[hashedId] * rms_x12[hashedId] * pedestal * G12SamplesCorrelation[vidx]; + // non-divergent branch + if (!dynamicPedestal && addPedestalUncertainty > 0.f) { + noise_value += addPedestalUncertainty * addPedestalUncertainty * pedestal; // gainratio is 1 + } + + // + gainidx = 1; + mask = gainidx; + pedestal = gainNoise[ch][ty] == mask ? 1 : 0; + noise_value += gain12Over6[hashedId] * gain12Over6[hashedId] * rms_x6[hashedId] * rms_x6[hashedId] * + pedestal * G6SamplesCorrelation[vidx]; + // non-divergent branch + if (!dynamicPedestal && addPedestalUncertainty > 0.f) { + noise_value += gain12Over6[hashedId] * gain12Over6[hashedId] * addPedestalUncertainty * + addPedestalUncertainty * pedestal; + } + + // + gainidx = 2; + mask = gainidx; + pedestal = gainNoise[ch][ty] == mask ? 1 : 0; + float tmp = gain6Over1[hashedId] * gain12Over6[hashedId]; + noise_value += tmp * tmp * rms_x1[hashedId] * rms_x1[hashedId] * pedestal * G1SamplesCorrelation[vidx]; + // non-divergent branch + if (!dynamicPedestal && addPedestalUncertainty > 0.f) { + noise_value += tmp * tmp * addPedestalUncertainty * addPedestalUncertainty * pedestal; + } + } + + noisecov[ch](ty, tx) = noise_value; + } else { + auto rms = rms_x12[hashedId]; + float noise_value = rms * rms * G12SamplesCorrelation[vidx]; + if (!dynamicPedestal && addPedestalUncertainty > 0.f) { + //---- add fully correlated component to noise covariance to inflate pedestal uncertainty + noise_value += addPedestalUncertainty * addPedestalUncertainty; + } + noisecov[ch](ty, tx) = noise_value; + } + + // pulse matrix + int const posToAccess = 9 - tx + ty; // see cpu for reference + float const value = posToAccess >= 7 ? pulse_shape[hashedId].pdfval[posToAccess - 7] : 0; + pulse_matrix[ch](ty, tx) = value; + } + + __global__ void kernel_permute_results(SampleVector* amplitudes, + BXVectorType const* activeBXs, + ::ecal::reco::StorageScalarType* energies, + char const* acState, + int const nchannels) { + // constants + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + int const tx = threadIdx.x + blockIdx.x * blockDim.x; + int const ch = tx / nsamples; + int const sampleidx = tx % nsamples; // this is to address activeBXs + + if (ch >= nchannels) + return; + + // channels that have amplitude precomputed do not need results to be permuted + auto const state = static_cast(acState[ch]); + if (state == MinimizationState::Precomputed) + return; + + // configure shared memory and cp into it + extern __shared__ char smem[]; + SampleVector::Scalar* values = reinterpret_cast(smem); + values[threadIdx.x] = amplitudes[ch](sampleidx); + __syncthreads(); + + // get the sample for this bx + auto const sample = static_cast(activeBXs[ch](sampleidx)) + 5; + + // store back to global + amplitudes[ch](sample) = values[threadIdx.x]; + + // store sample 5 separately + // only for the case when minimization was performed + // not for cases with precomputed amplitudes + if (sample == 5) + energies[ch] = values[threadIdx.x]; + } + + } // namespace multifit +} // namespace ecal diff --git a/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationCommonKernels.h b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationCommonKernels.h new file mode 100644 index 0000000000000..479c623e83f62 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationCommonKernels.h @@ -0,0 +1,104 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_AmplitudeComputationCommonKernels_h +#define RecoLocalCalo_EcalRecProducers_plugins_AmplitudeComputationCommonKernels_h + +#include "Common.h" +#include "DeclsForKernels.h" +#include "EigenMatrixTypes_gpu.h" + +class EcalPulseShape; +// this flag setting is applied to all of the cases +class EcalPulseCovariance; +class EcalUncalibratedRecHit; + +namespace ecal { + namespace multifit { + + /// + /// assume kernel launch configuration is + /// (MAXSAMPLES * nchannels, blocks) + /// TODO: is there a point to split this kernel further to separate reductions + /// + __global__ void kernel_prep_1d_and_initialize(EcalPulseShape const* shapes_in, + uint16_t const* digis_in_eb, + uint32_t const* dids_eb, + uint16_t const* digis_in_ee, + uint32_t const* dids_ee, + SampleVector* amplitudes, + SampleVector* amplitudesForMinimizationEB, + SampleVector* amplitudesForMinimizationEE, + SampleGainVector* gainsNoise, + float const* mean_x1, + float const* mean_x12, + float const* rms_x12, + float const* mean_x6, + float const* gain6Over1, + float const* gain12Over6, + bool* hasSwitchToGain6, + bool* hasSwitchToGain1, + bool* isSaturated, + ::ecal::reco::StorageScalarType* energiesEB, + ::ecal::reco::StorageScalarType* energiesEE, + ::ecal::reco::StorageScalarType* chi2EB, + ::ecal::reco::StorageScalarType* chi2EE, + ::ecal::reco::StorageScalarType* pedestalEB, + ::ecal::reco::StorageScalarType* pedestalEE, + uint32_t* dids_outEB, + uint32_t* dids_outEE, + uint32_t* flagsEB, + uint32_t* flagsEE, + char* acState, + BXVectorType* bxs, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs, + bool const gainSwitchUseMaxSampleEB, + bool const gainSwitchUseMaxSampleEE, + int const nchannels); + + /// + /// assume kernel launch configuration is + /// ([MAXSAMPLES, MAXSAMPLES], nchannels) + /// + __global__ void kernel_prep_2d(SampleGainVector const* gainNoise, + uint32_t const* dids_eb, + uint32_t const* dids_ee, + float const* rms_x12, + float const* rms_x6, + float const* rms_x1, + float const* gain12Over6, + float const* gain6Over1, + double const* G12SamplesCorrelationEB, + double const* G6SamplesCorrelationEB, + double const* G1SamplesCorrelationEB, + double const* G12SamplesCorrelationEE, + double const* G6SamplesCorrelationEE, + double const* G1SamplesCorrelationEE, + SampleMatrix* noisecov, + PulseMatrixType* pulse_matrix, + EcalPulseShape const* pulse_shape, + bool const* hasSwitchToGain6, + bool const* hasSwitchToGain1, + bool const* isSaturated, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs); + + __global__ void kernel_permute_results(SampleVector* amplitudes, + BXVectorType const* activeBXs, + ::ecal::reco::StorageScalarType* energies, + char const* acState, + int const nchannels); + +/// +/// Build an Ecal RecHit. +/// TODO: Use SoA data structures on the host directly +/// the reason for removing this from minimize kernel is to isolate the minimize + +/// again, building an aos rec hit involves strides... -> bad memory access pattern +/// +#ifdef RUN_BUILD_AOS_RECHIT + __global__ void kernel_build_rechit( + float const* energies, float const* chi2s, uint32_t* dids, EcalUncalibratedRecHit* rechits, int nchannels); +#endif // RUN_BUILD_AOS_RECHIT + + } // namespace multifit +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_AmplitudeComputationCommonKernels_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationKernels.cu b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationKernels.cu new file mode 100644 index 0000000000000..e5eff86d15ec7 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationKernels.cu @@ -0,0 +1,305 @@ +#include +#include + +#include + +#include "CondFormats/EcalObjects/interface/EcalPulseCovariances.h" +#include "CondFormats/EcalObjects/interface/EcalPulseShapes.h" +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/Math/interface/approx_exp.h" +#include "DataFormats/Math/interface/approx_log.h" +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" + +#include "AmplitudeComputationCommonKernels.h" +#include "AmplitudeComputationKernels.h" +#include "KernelHelpers.h" + +namespace ecal { + namespace multifit { + + template + __device__ __forceinline__ bool update_covariance(EcalPulseCovariance const& pulse_covariance, + MatrixType& inverse_cov, + SampleVector const& amplitudes) { + constexpr int nsamples = SampleVector::RowsAtCompileTime; + constexpr int npulses = BXVectorType::RowsAtCompileTime; + + CMS_UNROLL_LOOP + for (unsigned int ipulse = 0; ipulse < npulses; ipulse++) { + auto const amplitude = amplitudes.coeff(ipulse); + if (amplitude == 0) + continue; + + // FIXME: ipulse - 5 -> ipulse - firstOffset + int bx = ipulse - 5; + int first_sample_t = std::max(0, bx + 3); + int offset = -3 - bx; + + auto const value_sq = amplitude * amplitude; + + for (int col = first_sample_t; col < nsamples; col++) { + for (int row = col; row < nsamples; row++) { + inverse_cov(row, col) += value_sq * __ldg(&pulse_covariance.covval[row + offset][col + offset]); + } + } + } + + return true; + } + + /// + /// launch ctx parameters are (nchannels / block, blocks) + /// TODO: trivial impl for now, there must be a way to improve + /// + /// Conventions: + /// - amplitudes -> solution vector, what we are fitting for + /// - samples -> raw detector responses + /// - passive constraint - satisfied constraint + /// - active constraint - unsatisfied (yet) constraint + /// + __global__ void kernel_minimize(uint32_t const* dids_eb, + uint32_t const* dids_ee, + SampleMatrix const* __restrict__ noisecov, + EcalPulseCovariance const* __restrict__ pulse_covariance, + BXVectorType* bxs, + SampleVector const* __restrict__ samples, + SampleVector* amplitudesEB, + SampleVector* amplitudesEE, + PulseMatrixType const* __restrict__ pulse_matrix, + ::ecal::reco::StorageScalarType* chi2sEB, + ::ecal::reco::StorageScalarType* chi2sEE, + ::ecal::reco::StorageScalarType* energiesEB, + ::ecal::reco::StorageScalarType* energiesEE, + char* acState, + int nchannels, + int max_iterations, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs) { + // FIXME: ecal has 10 samples and 10 pulses.... + // but this needs to be properly treated and renamed everywhere + constexpr auto NSAMPLES = SampleMatrix::RowsAtCompileTime; + constexpr auto NPULSES = SampleMatrix::ColsAtCompileTime; + static_assert(NSAMPLES == NPULSES); + + using DataType = SampleVector::Scalar; + + extern __shared__ char shrmem[]; + DataType* shrMatrixLForFnnlsStorage = + reinterpret_cast(shrmem) + calo::multifit::MapSymM::total * threadIdx.x; + DataType* shrAtAStorage = reinterpret_cast(shrmem) + + calo::multifit::MapSymM::total * (threadIdx.x + blockDim.x); + + // channel + int idx = threadIdx.x + blockDim.x * blockIdx.x; + +// ref the right ptr +#define ARRANGE(var) auto* var = idx >= offsetForInputs ? var##EE : var##EB + ARRANGE(amplitudes); + ARRANGE(chi2s); + ARRANGE(energies); +#undef ARRANGE + + if (idx < nchannels) { + if (static_cast(acState[idx]) == MinimizationState::Precomputed) + return; + + // get the hash + int const inputCh = idx >= offsetForInputs ? idx - offsetForInputs : idx; + auto const* dids = idx >= offsetForInputs ? dids_ee : dids_eb; + auto const did = DetId{dids[inputCh]}; + auto const isBarrel = did.subdetId() == EcalBarrel; + auto const hashedId = isBarrel ? ecal::reconstruction::hashedIndexEB(did.rawId()) + : offsetForHashes + ecal::reconstruction::hashedIndexEE(did.rawId()); + + // inits + int iter = 0; + int npassive = 0; + + calo::multifit::ColumnVector pulseOffsets; + CMS_UNROLL_LOOP + for (int i = 0; i < NPULSES; ++i) + pulseOffsets(i) = i; + + calo::multifit::ColumnVector resultAmplitudes; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NPULSES; counter++) + resultAmplitudes(counter) = 0; + + // inits + //SampleDecompLLT covariance_decomposition; + //SampleMatrix inverse_cov; + // SampleVector::Scalar chi2 = 0, chi2_now = 0; + float chi2 = 0, chi2_now = 0; + + // loop until ocnverge + while (true) { + if (iter >= max_iterations) + break; + + //inverse_cov = noisecov[idx]; + //DataType covMatrixStorage[MapSymM::total]; + DataType* covMatrixStorage = shrMatrixLForFnnlsStorage; + calo::multifit::MapSymM covMatrix{covMatrixStorage}; + int counter = 0; + CMS_UNROLL_LOOP + for (int col = 0; col < NSAMPLES; col++) { + CMS_UNROLL_LOOP + for (int row = col; row < NSAMPLES; row++) + covMatrixStorage[counter++] = __ldg(&noisecov[idx].coeffRef(row, col)); + } + update_covariance(pulse_covariance[hashedId], covMatrix, resultAmplitudes); + + // compute actual covariance decomposition + //covariance_decomposition.compute(inverse_cov); + //auto const& matrixL = covariance_decomposition.matrixL(); + DataType matrixLStorage[calo::multifit::MapSymM::total]; + calo::multifit::MapSymM matrixL{matrixLStorage}; + calo::multifit::compute_decomposition_unrolled(matrixL, covMatrix); + + // L * A = P + calo::multifit::ColMajorMatrix A; + calo::multifit::solve_forward_subst_matrix(A, pulse_matrix[idx], matrixL); + + // L b = s + float reg_b[NSAMPLES]; + calo::multifit::solve_forward_subst_vector(reg_b, samples[idx], matrixL); + + // FIXME: shared mem + //DataType AtAStorage[MapSymM::total]; + calo::multifit::MapSymM AtA{shrAtAStorage}; + //SampleMatrix AtA; + SampleVector Atb; + CMS_UNROLL_LOOP + for (int icol = 0; icol < NPULSES; icol++) { + float reg_ai[NSAMPLES]; + + // load column icol + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + reg_ai[counter] = A(counter, icol); + + // compute diagoanl + float sum = 0.f; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + sum += reg_ai[counter] * reg_ai[counter]; + + // store + AtA(icol, icol) = sum; + + // go thru the other columns + CMS_UNROLL_LOOP + for (int j = icol + 1; j < NPULSES; j++) { + // load column j + float reg_aj[NSAMPLES]; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + reg_aj[counter] = A(counter, j); + + // accum + float sum = 0.f; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + sum += reg_aj[counter] * reg_ai[counter]; + + // store + //AtA(icol, j) = sum; + AtA(j, icol) = sum; + } + + // Atb accum + float sum_atb = 0.f; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + sum_atb += reg_ai[counter] * reg_b[counter]; + + // store atb + Atb(icol) = sum_atb; + } + + // FIXME: shared mem + //DataType matrixLForFnnlsStorage[MapSymM::total]; + calo::multifit::MapSymM matrixLForFnnls{shrMatrixLForFnnlsStorage}; + + calo::multifit::fnnls(AtA, + Atb, + //amplitudes[idx], + resultAmplitudes, + npassive, + pulseOffsets, + matrixLForFnnls, + 1e-11, + 500, + 16, + 2); + + calo::multifit::calculateChiSq(matrixL, pulse_matrix[idx], resultAmplitudes, samples[idx], chi2_now); + + auto deltachi2 = chi2_now - chi2; + chi2 = chi2_now; + + if (std::abs(deltachi2) < 1e-3) + break; + + //---- AM: TEST + //---- it was 3 lines above, now here as in the CPU version + ++iter; + } + + // store to global output values + // FIXME: amplitudes are used in global directly + chi2s[inputCh] = chi2; + energies[inputCh] = resultAmplitudes(5); + + CMS_UNROLL_LOOP + for (int counter = 0; counter < NPULSES; counter++) + amplitudes[inputCh](counter) = resultAmplitudes(counter); + } + } + + namespace v1 { + + void minimization_procedure(EventInputDataGPU const& eventInputGPU, + EventOutputDataGPU& eventOutputGPU, + EventDataForScratchGPU& scratch, + ConditionsProducts const& conditions, + ConfigurationParameters const& configParameters, + cudaStream_t cudaStream) { + using DataType = SampleVector::Scalar; + unsigned int totalChannels = eventInputGPU.ebDigis.size + eventInputGPU.eeDigis.size; + // unsigned int threads_min = conf.threads.x; + // TODO: configure from python + unsigned int threads_min = configParameters.kernelMinimizeThreads[0]; + unsigned int blocks_min = threads_min > totalChannels ? 1 : (totalChannels + threads_min - 1) / threads_min; + uint32_t const offsetForHashes = conditions.offsetForHashes; + uint32_t const offsetForInputs = eventInputGPU.ebDigis.size; + auto const nbytesShared = 2 * threads_min * + calo::multifit::MapSymM::total * + sizeof(DataType); + kernel_minimize<<>>( + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.ids.get(), + (SampleMatrix*)scratch.noisecov.get(), + conditions.pulseCovariances.values, + (BXVectorType*)scratch.activeBXs.get(), + (SampleVector*)scratch.samples.get(), + (SampleVector*)eventOutputGPU.recHitsEB.amplitudesAll.get(), + (SampleVector*)eventOutputGPU.recHitsEE.amplitudesAll.get(), + (PulseMatrixType*)scratch.pulse_matrix.get(), + eventOutputGPU.recHitsEB.chi2.get(), + eventOutputGPU.recHitsEE.chi2.get(), + eventOutputGPU.recHitsEB.amplitude.get(), + eventOutputGPU.recHitsEE.amplitude.get(), + scratch.acState.get(), + totalChannels, + 50, + offsetForHashes, + offsetForInputs); + cudaCheck(cudaGetLastError()); + } + + } // namespace v1 + + } // namespace multifit +} // namespace ecal diff --git a/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationKernels.h b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationKernels.h new file mode 100644 index 0000000000000..b8202f75b653b --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/AmplitudeComputationKernels.h @@ -0,0 +1,29 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_AmplitudeComputationKernels_h +#define RecoLocalCalo_EcalRecProducers_plugins_AmplitudeComputationKernels_h + +#include "Common.h" +#include "DeclsForKernels.h" +#include "EigenMatrixTypes_gpu.h" + +class EcalPulseShape; +class EcalPulseCovariance; +class EcalUncalibratedRecHit; + +namespace ecal { + namespace multifit { + + namespace v1 { + + void minimization_procedure(EventInputDataGPU const& eventInputGPU, + EventOutputDataGPU& eventOutputGPU, + EventDataForScratchGPU& scratch, + ConditionsProducts const& conditions, + ConfigurationParameters const& configParameters, + cudaStream_t cudaStream); + + } + + } // namespace multifit +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_AmplitudeComputationKernels_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/BuildFile.xml b/RecoLocalCalo/EcalRecProducers/plugins/BuildFile.xml index 25fe21603e864..61eed4689fd20 100644 --- a/RecoLocalCalo/EcalRecProducers/plugins/BuildFile.xml +++ b/RecoLocalCalo/EcalRecProducers/plugins/BuildFile.xml @@ -1,18 +1,22 @@ - - - - - - + + + + + + - - - + + + + + + - + - + + diff --git a/RecoLocalCalo/EcalRecProducers/plugins/Common.h b/RecoLocalCalo/EcalRecProducers/plugins/Common.h new file mode 100644 index 0000000000000..55f5f613ed356 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/Common.h @@ -0,0 +1,17 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_Common_h +#define RecoLocalCalo_EcalRecProducers_plugins_Common_h + +// a workaround for std::abs not being a constexpr function +namespace ecal { + + // temporary + namespace mgpa { + + constexpr int adc(uint16_t sample) { return sample & 0xfff; } + constexpr int gainId(uint16_t sample) { return (sample >> 12) & 0x3; } + + } // namespace mgpa + +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_Common_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/DeclsForKernels.h b/RecoLocalCalo/EcalRecProducers/plugins/DeclsForKernels.h new file mode 100644 index 0000000000000..cac63b6b30112 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/DeclsForKernels.h @@ -0,0 +1,325 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_DeclsForKernels_h +#define RecoLocalCalo_EcalRecProducers_plugins_DeclsForKernels_h + +#include + +#include +#include + +#include "CUDADataFormats/EcalDigi/interface/DigisCollection.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h" +#include "CondFormats/EcalObjects/interface/EcalChannelStatus.h" +#include "CondFormats/EcalObjects/interface/EcalChannelStatusCode.h" +#include "CondFormats/EcalObjects/interface/EcalGainRatios.h" +#include "CondFormats/EcalObjects/interface/EcalPedestals.h" +#include "CondFormats/EcalObjects/interface/EcalTimeBiasCorrections.h" +#include "CondFormats/EcalObjects/interface/EcalTimeOffsetConstant.h" +#include "CondFormats/EcalObjects/interface/EcalWeightSet.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h" + +#include "EigenMatrixTypes_gpu.h" + +struct EcalPulseShape; +class EcalSampleMask; +class EcalTimeBiasCorrections; +struct EcalPulseCovariance; +class EcalDigiCollection; +class EcalXtalGroupId; +class EcalSamplesCorrelation; +class EBDigiCollection; +class EEDigiCollection; + +namespace ecal { + namespace multifit { + + enum class TimeComputationState : char { NotFinished = 0, Finished = 1 }; + enum class MinimizationState : char { + NotFinished = 0, + Finished = 1, + Precomputed = 2, + }; + + // + struct EventInputDataGPU { + ecal::DigisCollection const& ebDigis; + ecal::DigisCollection const& eeDigis; + }; + + // parameters have a fixed type + // Can we go by with single precision + struct ConfigurationParameters { + using type = double; + // device ptrs + type *amplitudeFitParametersEB = nullptr, *amplitudeFitParametersEE = nullptr; + + uint32_t timeFitParametersSizeEB, timeFitParametersSizeEE; + // device ptrs + type *timeFitParametersEB = nullptr, *timeFitParametersEE = nullptr; + + type timeFitLimitsFirstEB, timeFitLimitsFirstEE; + type timeFitLimitsSecondEB, timeFitLimitsSecondEE; + + type timeConstantTermEB, timeConstantTermEE; + + type timeNconstEB, timeNconstEE; + + type amplitudeThreshEE, amplitudeThreshEB; + + type outOfTimeThreshG12pEB, outOfTimeThreshG12mEB; + type outOfTimeThreshG12pEE, outOfTimeThreshG12mEE; + type outOfTimeThreshG61pEE, outOfTimeThreshG61mEE; + type outOfTimeThreshG61pEB, outOfTimeThreshG61mEB; + + std::array kernelMinimizeThreads; + + bool shouldRunTimingComputation; + + uint32_t maxNumberHitsEB; + uint32_t maxNumberHitsEE; + }; + + struct EventOutputDataGPU { + UncalibratedRecHit<::calo::common::DevStoragePolicy> recHitsEB, recHitsEE; + + void allocate(ConfigurationParameters const& configParameters, cudaStream_t cudaStream) { + auto const sizeEB = configParameters.maxNumberHitsEB; + recHitsEB.amplitudesAll = cms::cuda::make_device_unique( + sizeEB * EcalDataFrame::MAXSAMPLES, cudaStream); + recHitsEB.amplitude = cms::cuda::make_device_unique(sizeEB, cudaStream); + recHitsEB.chi2 = cms::cuda::make_device_unique(sizeEB, cudaStream); + recHitsEB.pedestal = cms::cuda::make_device_unique(sizeEB, cudaStream); + + if (configParameters.shouldRunTimingComputation) { + recHitsEB.jitter = cms::cuda::make_device_unique(sizeEB, cudaStream); + recHitsEB.jitterError = cms::cuda::make_device_unique(sizeEB, cudaStream); + } + + recHitsEB.did = cms::cuda::make_device_unique(sizeEB, cudaStream); + recHitsEB.flags = cms::cuda::make_device_unique(sizeEB, cudaStream); + + auto const sizeEE = configParameters.maxNumberHitsEE; + recHitsEE.amplitudesAll = cms::cuda::make_device_unique( + sizeEE * EcalDataFrame::MAXSAMPLES, cudaStream); + recHitsEE.amplitude = cms::cuda::make_device_unique(sizeEE, cudaStream); + recHitsEE.chi2 = cms::cuda::make_device_unique(sizeEE, cudaStream); + recHitsEE.pedestal = cms::cuda::make_device_unique(sizeEE, cudaStream); + + if (configParameters.shouldRunTimingComputation) { + recHitsEE.jitter = cms::cuda::make_device_unique(sizeEE, cudaStream); + recHitsEE.jitterError = cms::cuda::make_device_unique(sizeEE, cudaStream); + } + + recHitsEE.did = cms::cuda::make_device_unique(sizeEE, cudaStream); + recHitsEE.flags = cms::cuda::make_device_unique(sizeEE, cudaStream); + } + }; + + template + constexpr auto getLength() -> uint32_t { + return EigenM::RowsAtCompileTime * EigenM::ColsAtCompileTime; + } + + struct EventDataForScratchGPU { + using SVT = SampleVector::Scalar; + using SGVT = SampleGainVector::Scalar; + using SMT = SampleMatrix::Scalar; + using PMT = PulseMatrixType::Scalar; + using BXVT = BXVectorType::Scalar; + + cms::cuda::device::unique_ptr samples; + cms::cuda::device::unique_ptr gainsNoise; + + cms::cuda::device::unique_ptr noisecov; + cms::cuda::device::unique_ptr pulse_matrix; + cms::cuda::device::unique_ptr activeBXs; + cms::cuda::device::unique_ptr acState; + + cms::cuda::device::unique_ptr hasSwitchToGain6, hasSwitchToGain1, isSaturated; + + cms::cuda::device::unique_ptr sample_values, sample_value_errors; + cms::cuda::device::unique_ptr useless_sample_values; + cms::cuda::device::unique_ptr chi2sNullHypot; + cms::cuda::device::unique_ptr sum0sNullHypot; + cms::cuda::device::unique_ptr sumAAsNullHypot; + cms::cuda::device::unique_ptr pedestal_nums; + cms::cuda::device::unique_ptr tMaxAlphaBetas, tMaxErrorAlphaBetas; + cms::cuda::device::unique_ptr accTimeMax, accTimeWgt; + cms::cuda::device::unique_ptr ampMaxAlphaBeta, ampMaxError; + cms::cuda::device::unique_ptr timeMax, timeError; + cms::cuda::device::unique_ptr tcState; + + void allocate(ConfigurationParameters const& configParameters, cudaStream_t cudaStream) { + constexpr auto svlength = getLength(); + constexpr auto sgvlength = getLength(); + constexpr auto smlength = getLength(); + constexpr auto pmlength = getLength(); + constexpr auto bxvlength = getLength(); + auto const size = configParameters.maxNumberHitsEB + configParameters.maxNumberHitsEE; + + auto alloc = [cudaStream](auto& var, uint32_t size) { + using element_type = typename std::remove_reference_t::element_type; + var = cms::cuda::make_device_unique(size, cudaStream); + }; + + alloc(samples, size * svlength); + alloc(gainsNoise, size * sgvlength); + + alloc(noisecov, size * smlength); + alloc(pulse_matrix, size * pmlength); + alloc(activeBXs, size * bxvlength); + alloc(acState, size); + + alloc(hasSwitchToGain6, size); + alloc(hasSwitchToGain1, size); + alloc(isSaturated, size); + + if (configParameters.shouldRunTimingComputation) { + alloc(sample_values, size * svlength); + alloc(sample_value_errors, size * svlength); + alloc(useless_sample_values, size * EcalDataFrame::MAXSAMPLES); + alloc(chi2sNullHypot, size); + alloc(sum0sNullHypot, size); + alloc(sumAAsNullHypot, size); + alloc(pedestal_nums, size); + + alloc(tMaxAlphaBetas, size); + alloc(tMaxErrorAlphaBetas, size); + alloc(accTimeMax, size); + alloc(accTimeWgt, size); + alloc(ampMaxAlphaBeta, size); + alloc(ampMaxError, size); + alloc(timeMax, size); + alloc(timeError, size); + alloc(tcState, size); + } + } + }; + + // const refs products to conditions + struct ConditionsProducts { + EcalPedestalsGPU::Product const& pedestals; + EcalGainRatiosGPU::Product const& gainRatios; + EcalPulseShapesGPU::Product const& pulseShapes; + EcalPulseCovariancesGPU::Product const& pulseCovariances; + EcalSamplesCorrelationGPU::Product const& samplesCorrelation; + EcalTimeBiasCorrectionsGPU::Product const& timeBiasCorrections; + EcalTimeCalibConstantsGPU::Product const& timeCalibConstants; + EcalSampleMask const& sampleMask; + EcalTimeOffsetConstant const& timeOffsetConstant; + uint32_t offsetForHashes; + EcalMultifitParametersGPU::Product const& multifitParameters; + }; + + struct xyz { + int x, y, z; + }; + + struct conf_data { + xyz threads; + bool runV1; + cudaStream_t cuStream; + }; + + } // namespace multifit +} // namespace ecal + +// +// ECAL Rechit producer +// + +namespace ecal { + namespace rechit { + + // parameters that are read in the configuration file for rechit producer + struct ConfigurationParameters { + // device ptrs + int* ChannelStatusToBeExcluded = nullptr; + uint32_t ChannelStatusToBeExcludedSize; + + bool killDeadChannels; + + bool recoverEBIsolatedChannels; + bool recoverEEIsolatedChannels; + bool recoverEBVFE; + bool recoverEEVFE; + bool recoverEBFE; + bool recoverEEFE; + + float EBLaserMIN; + float EELaserMIN; + float EBLaserMAX; + float EELaserMAX; + + int* expanded_v_DB_reco_flags; + uint32_t* expanded_Sizes_v_DB_reco_flags; + uint32_t* expanded_flagbit_v_DB_reco_flags; + uint32_t expanded_v_DB_reco_flagsSize; + + uint32_t flagmask; + uint32_t maxNumberHitsEB; + uint32_t maxNumberHitsEE; + }; + + struct EventOutputDataGPU { + RecHit<::calo::common::DevStoragePolicy> recHitsEB, recHitsEE; + + void allocate(ConfigurationParameters const& configParameters, cudaStream_t cudaStream) { + //---- configParameters -> needed only to decide if to save the timing information or not + auto const sizeEB = configParameters.maxNumberHitsEB; + recHitsEB.energy = cms::cuda::make_device_unique<::ecal::reco::StorageScalarType[]>(sizeEB, cudaStream); + recHitsEB.time = cms::cuda::make_device_unique<::ecal::reco::StorageScalarType[]>(sizeEB, cudaStream); + recHitsEB.chi2 = cms::cuda::make_device_unique<::ecal::reco::StorageScalarType[]>(sizeEB, cudaStream); + recHitsEB.flagBits = cms::cuda::make_device_unique(sizeEB, cudaStream); + recHitsEB.extra = cms::cuda::make_device_unique(sizeEB, cudaStream); + recHitsEB.did = cms::cuda::make_device_unique(sizeEB, cudaStream); + + auto const sizeEE = configParameters.maxNumberHitsEE; + recHitsEE.energy = cms::cuda::make_device_unique<::ecal::reco::StorageScalarType[]>(sizeEE, cudaStream); + recHitsEE.time = cms::cuda::make_device_unique<::ecal::reco::StorageScalarType[]>(sizeEE, cudaStream); + recHitsEE.chi2 = cms::cuda::make_device_unique<::ecal::reco::StorageScalarType[]>(sizeEE, cudaStream); + recHitsEE.flagBits = cms::cuda::make_device_unique(sizeEE, cudaStream); + recHitsEE.extra = cms::cuda::make_device_unique(sizeEE, cudaStream); + recHitsEE.did = cms::cuda::make_device_unique(sizeEE, cudaStream); + } + }; + + struct EventInputDataGPU { + ecal::UncalibratedRecHit const& ebUncalibRecHits; + ecal::UncalibratedRecHit const& eeUncalibRecHits; + }; + + // const refs products to conditions + struct ConditionsProducts { + EcalRechitADCToGeVConstantGPU::Product const& ADCToGeV; + EcalIntercalibConstantsGPU::Product const& Intercalib; + EcalRechitChannelStatusGPU::Product const& ChannelStatus; + + EcalLaserAPDPNRatiosGPU::Product const& LaserAPDPNRatios; + EcalLaserAPDPNRatiosRefGPU::Product const& LaserAPDPNRatiosRef; + EcalLaserAlphasGPU::Product const& LaserAlphas; + EcalLinearCorrectionsGPU::Product const& LinearCorrections; + + uint32_t offsetForHashes; + }; + + } // namespace rechit +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_DeclsForKernels_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalCPURecHitProducer.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalCPURecHitProducer.cc new file mode 100644 index 0000000000000..528b6bfaae74d --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalCPURecHitProducer.cc @@ -0,0 +1,163 @@ +//#define ECAL_RECO_CUDA_DEBUG + +#ifdef ECAL_RECO_CUDA_DEBUG +#include +#endif + +// framework +#include "FWCore/Framework/interface/stream/EDProducer.h" + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" + +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" + +// algorithm specific + +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" + +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" + + +class EcalCPURecHitProducer : public edm::stream::EDProducer { +public: + explicit EcalCPURecHitProducer(edm::ParameterSet const& ps); + ~EcalCPURecHitProducer() override = default; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + using InputProduct = cms::cuda::Product>; + edm::EDGetTokenT recHitsInEBToken_, recHitsInEEToken_; + using OutputProduct = ecal::RecHit>; + edm::EDPutTokenT recHitsOutEBToken_, recHitsOutEEToken_; + + OutputProduct recHitsEB_, recHitsEE_; + bool containsTimingInformation_; +}; + +void EcalCPURecHitProducer::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("recHitsInLabelEB", edm::InputTag{"ecalRecHitProducerGPU", "EcalRecHitsEB"}); + desc.add("recHitsInLabelEE", edm::InputTag{"ecalRecHitProducerGPU", "EcalRecHitsEE"}); + desc.add("recHitsOutLabelEB", "EcalRecHitsEB"); + desc.add("recHitsOutLabelEE", "EcalRecHitsEE"); + desc.add("containsTimingInformation", false); + + confDesc.addWithDefaultLabel(desc); +} + +EcalCPURecHitProducer::EcalCPURecHitProducer(const edm::ParameterSet& ps) + : recHitsInEBToken_{consumes(ps.getParameter("recHitsInLabelEB"))}, + recHitsInEEToken_{consumes(ps.getParameter("recHitsInLabelEE"))}, + recHitsOutEBToken_{produces(ps.getParameter("recHitsOutLabelEB"))}, + recHitsOutEEToken_{produces(ps.getParameter("recHitsOutLabelEE"))}, + containsTimingInformation_{ps.getParameter("containsTimingInformation")} {} + +void EcalCPURecHitProducer::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder taskHolder) { + // retrieve data/ctx + auto const& ebRecHitsProduct = event.get(recHitsInEBToken_); + auto const& eeRecHitsProduct = event.get(recHitsInEEToken_); + cms::cuda::ScopedContextAcquire ctx{ebRecHitsProduct, std::move(taskHolder)}; + auto const& ebRecHits = ctx.get(ebRecHitsProduct); + auto const& eeRecHits = ctx.get(eeRecHitsProduct); + + // resize the output buffers + recHitsEB_.resize(ebRecHits.size); + recHitsEE_.resize(eeRecHits.size); + +#ifdef ECAL_RECO_CUDA_DEBUG + std::cout << " [EcalCPURecHitProducer::acquire] ebRecHits.size = " << ebRecHits.size << std::endl; + std::cout << " [EcalCPURecHitProducer::acquire] eeRecHits.size = " << eeRecHits.size << std::endl; +#endif + + // enqeue transfers + cudaCheck(cudaMemcpyAsync(recHitsEB_.did.data(), + ebRecHits.did.get(), + recHitsEB_.did.size() * sizeof(uint32_t), + cudaMemcpyDeviceToHost, + ctx.stream())); + cudaCheck(cudaMemcpyAsync(recHitsEE_.did.data(), + eeRecHits.did.get(), + recHitsEE_.did.size() * sizeof(uint32_t), + cudaMemcpyDeviceToHost, + ctx.stream())); + // + // ./CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h:using StorageScalarType = float; + // + + cudaCheck(cudaMemcpyAsync(recHitsEB_.energy.data(), + ebRecHits.energy.get(), + recHitsEB_.energy.size() * sizeof(::ecal::reco::StorageScalarType), + cudaMemcpyDeviceToHost, + ctx.stream())); + cudaCheck(cudaMemcpyAsync(recHitsEE_.energy.data(), + eeRecHits.energy.get(), + recHitsEE_.energy.size() * sizeof(::ecal::reco::StorageScalarType), + cudaMemcpyDeviceToHost, + ctx.stream())); + + cudaCheck(cudaMemcpyAsync(recHitsEB_.chi2.data(), + ebRecHits.chi2.get(), + recHitsEB_.chi2.size() * sizeof(::ecal::reco::StorageScalarType), + cudaMemcpyDeviceToHost, + ctx.stream())); + cudaCheck(cudaMemcpyAsync(recHitsEE_.chi2.data(), + eeRecHits.chi2.get(), + recHitsEE_.chi2.size() * sizeof(::ecal::reco::StorageScalarType), + cudaMemcpyDeviceToHost, + ctx.stream())); + + cudaCheck(cudaMemcpyAsync(recHitsEB_.extra.data(), + ebRecHits.extra.get(), + recHitsEB_.extra.size() * sizeof(uint32_t), + cudaMemcpyDeviceToHost, + ctx.stream())); + cudaCheck(cudaMemcpyAsync(recHitsEE_.extra.data(), + eeRecHits.extra.get(), + recHitsEE_.extra.size() * sizeof(uint32_t), + cudaMemcpyDeviceToHost, + ctx.stream())); + + cudaCheck(cudaMemcpyAsync(recHitsEB_.flagBits.data(), + ebRecHits.flagBits.get(), + recHitsEB_.flagBits.size() * sizeof(uint32_t), + cudaMemcpyDeviceToHost, + ctx.stream())); + cudaCheck(cudaMemcpyAsync(recHitsEE_.flagBits.data(), + eeRecHits.flagBits.get(), + recHitsEE_.flagBits.size() * sizeof(uint32_t), + cudaMemcpyDeviceToHost, + ctx.stream())); + +#ifdef ECAL_RECO_CUDA_DEBUG + for (unsigned int ieb = 0; ieb < ebRecHits.size ; ieb++) { + if (recHitsEB_.extra[ieb] != 0 ) std::cout << " [ " << ieb << " :: " << ebRecHits.size << " ] [ " << recHitsEB_.did[ieb] << " ] eb extra = " << recHitsEB_.extra[ieb] << std::endl; + } + + for (unsigned int ieb = 0; ieb < ebRecHits.size ; ieb++) { + if (recHitsEB_.energy[ieb] != 0 ) std::cout << " [ " << ieb << " :: " << ebRecHits.size << " ] [ " << recHitsEB_.did[ieb] << " ] eb energy = " << recHitsEB_.energy[ieb] << std::endl; + } + + for (unsigned int iee = 0; iee < eeRecHits.size ; iee++) { + if (recHitsEE_.energy[iee] != 0 ) std::cout << " [ " << iee << " :: " << eeRecHits.size << " ] [ " << recHitsEE_.did[iee] << " ] ee energy = " << recHitsEE_.energy[iee] << std::endl; + } +#endif +} + +void EcalCPURecHitProducer::produce(edm::Event& event, edm::EventSetup const& setup) { + // put into event + event.emplace(recHitsOutEBToken_, std::move(recHitsEB_)); + event.emplace(recHitsOutEEToken_, std::move(recHitsEE_)); +} + +DEFINE_FWK_MODULE(EcalCPURecHitProducer); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalCPUUncalibRecHitProducer.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalCPUUncalibRecHitProducer.cc new file mode 100644 index 0000000000000..801d378c7c391 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalCPUUncalibRecHitProducer.cc @@ -0,0 +1,120 @@ +#include + +// framework +#include "FWCore/Framework/interface/stream/EDProducer.h" + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" + +// algorithm specific + +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" + +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" + +class EcalCPUUncalibRecHitProducer : public edm::stream::EDProducer { +public: + explicit EcalCPUUncalibRecHitProducer(edm::ParameterSet const& ps); + ~EcalCPUUncalibRecHitProducer() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + using InputProduct = cms::cuda::Product>; + edm::EDGetTokenT recHitsInEBToken_, recHitsInEEToken_; + using OutputProduct = ecal::UncalibratedRecHit>; + edm::EDPutTokenT recHitsOutEBToken_, recHitsOutEEToken_; + + OutputProduct recHitsEB_, recHitsEE_; + bool containsTimingInformation_; +}; + +void EcalCPUUncalibRecHitProducer::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("recHitsInLabelEB", edm::InputTag{"ecalUncalibRecHitProducerGPU", "EcalUncalibRecHitsEB"}); + desc.add("recHitsInLabelEE", edm::InputTag{"ecalUncalibRecHitProducerGPU", "EcalUncalibRecHitsEE"}); + desc.add("recHitsOutLabelEB", "EcalUncalibRecHitsEB"); + desc.add("recHitsOutLabelEE", "EcalUncalibRecHitsEE"); + desc.add("containsTimingInformation", false); + + confDesc.add("ecalCPUUncalibRecHitProducer", desc); +} + +EcalCPUUncalibRecHitProducer::EcalCPUUncalibRecHitProducer(const edm::ParameterSet& ps) + : recHitsInEBToken_{consumes(ps.getParameter("recHitsInLabelEB"))}, + recHitsInEEToken_{consumes(ps.getParameter("recHitsInLabelEE"))}, + recHitsOutEBToken_{produces(ps.getParameter("recHitsOutLabelEB"))}, + recHitsOutEEToken_{produces(ps.getParameter("recHitsOutLabelEE"))}, + containsTimingInformation_{ps.getParameter("containsTimingInformation")} {} + +EcalCPUUncalibRecHitProducer::~EcalCPUUncalibRecHitProducer() {} + +void EcalCPUUncalibRecHitProducer::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder taskHolder) { + // retrieve data/ctx + auto const& ebRecHitsProduct = event.get(recHitsInEBToken_); + auto const& eeRecHitsProduct = event.get(recHitsInEEToken_); + cms::cuda::ScopedContextAcquire ctx{ebRecHitsProduct, std::move(taskHolder)}; + auto const& ebRecHits = ctx.get(ebRecHitsProduct); + auto const& eeRecHits = ctx.get(eeRecHitsProduct); + + // resize the output buffers + recHitsEB_.resize(ebRecHits.size); + recHitsEE_.resize(eeRecHits.size); + + auto lambdaToTransfer = [&ctx](auto& dest, auto* src) { + using vector_type = typename std::remove_reference::type; + using type = typename vector_type::value_type; + using src_type = typename std::remove_pointer::type; + static_assert(std::is_same::value && "dst and src data types do not match"); + cudaCheck(cudaMemcpyAsync(dest.data(), src, dest.size() * sizeof(type), cudaMemcpyDeviceToHost, ctx.stream())); + }; + + // enqeue transfers + lambdaToTransfer(recHitsEB_.did, ebRecHits.did.get()); + lambdaToTransfer(recHitsEE_.did, eeRecHits.did.get()); + + lambdaToTransfer(recHitsEB_.amplitudesAll, ebRecHits.amplitudesAll.get()); + lambdaToTransfer(recHitsEE_.amplitudesAll, eeRecHits.amplitudesAll.get()); + + lambdaToTransfer(recHitsEB_.amplitude, ebRecHits.amplitude.get()); + lambdaToTransfer(recHitsEE_.amplitude, eeRecHits.amplitude.get()); + + lambdaToTransfer(recHitsEB_.chi2, ebRecHits.chi2.get()); + lambdaToTransfer(recHitsEE_.chi2, eeRecHits.chi2.get()); + + lambdaToTransfer(recHitsEB_.pedestal, ebRecHits.pedestal.get()); + lambdaToTransfer(recHitsEE_.pedestal, eeRecHits.pedestal.get()); + + lambdaToTransfer(recHitsEB_.flags, ebRecHits.flags.get()); + lambdaToTransfer(recHitsEE_.flags, eeRecHits.flags.get()); + + if (containsTimingInformation_) { + lambdaToTransfer(recHitsEB_.jitter, ebRecHits.jitter.get()); + lambdaToTransfer(recHitsEE_.jitter, eeRecHits.jitter.get()); + + lambdaToTransfer(recHitsEB_.jitterError, ebRecHits.jitterError.get()); + lambdaToTransfer(recHitsEE_.jitterError, eeRecHits.jitterError.get()); + } +} + +void EcalCPUUncalibRecHitProducer::produce(edm::Event& event, edm::EventSetup const& setup) { + // tmp vectors + auto recHitsOutEB = std::make_unique(std::move(recHitsEB_)); + auto recHitsOutEE = std::make_unique(std::move(recHitsEE_)); + + // put into event + event.put(recHitsOutEBToken_, std::move(recHitsOutEB)); + event.put(recHitsOutEEToken_, std::move(recHitsOutEE)); +} + +DEFINE_FWK_MODULE(EcalCPUUncalibRecHitProducer); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalESProducersGPUDefs.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalESProducersGPUDefs.cc new file mode 100644 index 0000000000000..3118d54c6a7e9 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalESProducersGPUDefs.cc @@ -0,0 +1,88 @@ +#include "CondFormats/DataRecord/interface/EcalADCToGeVConstantRcd.h" +#include "CondFormats/DataRecord/interface/EcalChannelStatusRcd.h" +#include "CondFormats/DataRecord/interface/EcalGainRatiosRcd.h" +#include "CondFormats/DataRecord/interface/EcalIntercalibConstantsRcd.h" +#include "CondFormats/DataRecord/interface/EcalLaserAPDPNRatiosRcd.h" +#include "CondFormats/DataRecord/interface/EcalLaserAPDPNRatiosRefRcd.h" +#include "CondFormats/DataRecord/interface/EcalLaserAlphasRcd.h" +#include "CondFormats/DataRecord/interface/EcalLinearCorrectionsRcd.h" +#include "CondFormats/DataRecord/interface/EcalPedestalsRcd.h" +#include "CondFormats/DataRecord/interface/EcalPulseCovariancesRcd.h" +#include "CondFormats/DataRecord/interface/EcalPulseShapesRcd.h" +#include "CondFormats/DataRecord/interface/EcalSamplesCorrelationRcd.h" +#include "CondFormats/DataRecord/interface/EcalTimeBiasCorrectionsRcd.h" +#include "CondFormats/DataRecord/interface/EcalTimeCalibConstantsRcd.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "HeterogeneousCore/CUDACore/interface/ConvertingESProducerT.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h" + +using EcalPedestalsGPUESProducer = ConvertingESProducerT; + +using EcalGainRatiosGPUESProducer = ConvertingESProducerT; + +using EcalPulseShapesGPUESProducer = ConvertingESProducerT; + +using EcalPulseCovariancesGPUESProducer = + ConvertingESProducerT; + +using EcalSamplesCorrelationGPUESProducer = + ConvertingESProducerT; + +using EcalTimeBiasCorrectionsGPUESProducer = + ConvertingESProducerT; + +using EcalTimeCalibConstantsGPUESProducer = + ConvertingESProducerT; + +using EcalRechitADCToGeVConstantGPUESProducer = + ConvertingESProducerT; + +using EcalIntercalibConstantsGPUESProducer = + ConvertingESProducerT; + +using EcalRechitChannelStatusGPUESProducer = + ConvertingESProducerT; + +using EcalLaserAPDPNRatiosGPUESProducer = + ConvertingESProducerT; + +using EcalLaserAPDPNRatiosRefGPUESProducer = + ConvertingESProducerT; + +using EcalLaserAlphasGPUESProducer = ConvertingESProducerT; + +using EcalLinearCorrectionsGPUESProducer = + ConvertingESProducerT; + +// +// This below also creates the .py config files, as described in HeterogeneousCore/CUDACore/interface/ConvertingESProducerT.h +// + +DEFINE_FWK_EVENTSETUP_MODULE(EcalPedestalsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalGainRatiosGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalPulseShapesGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalPulseCovariancesGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalSamplesCorrelationGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalTimeBiasCorrectionsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalTimeCalibConstantsGPUESProducer); + +DEFINE_FWK_EVENTSETUP_MODULE(EcalRechitADCToGeVConstantGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalIntercalibConstantsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalRechitChannelStatusGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalLaserAPDPNRatiosGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalLaserAPDPNRatiosRefGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalLaserAlphasGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(EcalLinearCorrectionsGPUESProducer); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalMultifitParametersGPUESProducer.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalMultifitParametersGPUESProducer.cc new file mode 100644 index 0000000000000..1743df5aa945d --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalMultifitParametersGPUESProducer.cc @@ -0,0 +1,78 @@ +#include +#include +#include + +#include "FWCore/Framework/interface/ESProducer.h" +#include "FWCore/Framework/interface/ESProductHost.h" +#include "FWCore/Framework/interface/ESTransientHandle.h" +#include "FWCore/Framework/interface/EventSetupRecordIntervalFinder.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/ModuleFactory.h" +#include "FWCore/Framework/interface/SourceFactory.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/ReusableObjectHolder.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDACore/interface/JobConfigurationGPURecord.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h" + +class EcalMultifitParametersGPUESProducer : public edm::ESProducer, public edm::EventSetupRecordIntervalFinder { +public: + EcalMultifitParametersGPUESProducer(edm::ParameterSet const&); + ~EcalMultifitParametersGPUESProducer() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions&); + std::unique_ptr produce(JobConfigurationGPURecord const&); + +protected: + void setIntervalFor(const edm::eventsetup::EventSetupRecordKey&, + const edm::IOVSyncValue&, + edm::ValidityInterval&) override; + +private: + edm::ParameterSet const pset_; +}; + +EcalMultifitParametersGPUESProducer::EcalMultifitParametersGPUESProducer(edm::ParameterSet const& pset) : pset_{pset} { + setWhatProduced(this); + findingRecord(); +} + +void EcalMultifitParametersGPUESProducer::setIntervalFor(const edm::eventsetup::EventSetupRecordKey& iKey, + const edm::IOVSyncValue& iTime, + edm::ValidityInterval& oInterval) { + oInterval = edm::ValidityInterval(edm::IOVSyncValue::beginOfTime(), edm::IOVSyncValue::endOfTime()); +} + +void EcalMultifitParametersGPUESProducer::fillDescriptions(edm::ConfigurationDescriptions& desc) { + edm::ParameterSetDescription d; + d.add>("pulseOffsets", {-3, -2, -1, 0, 1, 2, 3, 4}); + d.add>("EBtimeFitParameters", + {-2.015452e+00, + 3.130702e+00, + -1.234730e+01, + 4.188921e+01, + -8.283944e+01, + 9.101147e+01, + -5.035761e+01, + 1.105621e+01}); + d.add>("EEtimeFitParameters", + {-2.390548e+00, + 3.553628e+00, + -1.762341e+01, + 6.767538e+01, + -1.332130e+02, + 1.407432e+02, + -7.541106e+01, + 1.620277e+01}); + d.add>("EBamplitudeFitParameters", {1.138, 1.652}); + d.add>("EEamplitudeFitParameters", {1.890, 1.400}); + desc.addWithDefaultLabel(d); +} + +std::unique_ptr EcalMultifitParametersGPUESProducer::produce( + JobConfigurationGPURecord const&) { + return std::make_unique(pset_); +} + +DEFINE_FWK_EVENTSETUP_SOURCE(EcalMultifitParametersGPUESProducer); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitBuilderKernels.cu b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitBuilderKernels.cu new file mode 100644 index 0000000000000..2f5006d4dcc7b --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitBuilderKernels.cu @@ -0,0 +1,675 @@ +#include + +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" + +#include "EcalRecHitBuilderKernels.h" +#include "KernelHelpers.h" + +namespace ecal { + namespace rechit { + + // uncalibrecHit flags + enum UncalibRecHitFlags { + kGood = -1, // channel is good (mutually exclusive with other states) setFlagBit(kGood) reset flags_ to zero + kPoorReco, // channel has been badly reconstructed (e.g. bad shape, bad chi2 etc.) + kSaturated, // saturated channel + kOutOfTime, // channel out of time + kLeadingEdgeRecovered, // saturated channel: energy estimated from the leading edge before saturation + kHasSwitchToGain6, // at least one data frame is in G6 + kHasSwitchToGain1 // at least one data frame is in G1 + }; + + // recHit flags + enum RecHitFlags { + RecHitFlags_kGood = 0, // channel ok, the energy and time measurement are reliable + RecHitFlags_kPoorReco, // the energy is available from the UncalibRecHit, but approximate (bad shape, large chi2) + RecHitFlags_kOutOfTime, // the energy is available from the UncalibRecHit (sync reco), but the event is out of time + RecHitFlags_kFaultyHardware, // The energy is available from the UncalibRecHit, channel is faulty at some hardware level (e.g. noisy) + RecHitFlags_kNoisy, // the channel is very noisy + RecHitFlags_kPoorCalib, // the energy is available from the UncalibRecHit, but the calibration of the channel is poor + RecHitFlags_kSaturated, // saturated channel (recovery not tried) + RecHitFlags_kLeadingEdgeRecovered, // saturated channel: energy estimated from the leading edge before saturation + RecHitFlags_kNeighboursRecovered, // saturated/isolated dead: energy estimated from neighbours + RecHitFlags_kTowerRecovered, // channel in TT with no data link, info retrieved from Trigger Primitive + RecHitFlags_kDead, // channel is dead and any recovery fails + RecHitFlags_kKilled, // MC only flag: the channel is killed in the real detector + RecHitFlags_kTPSaturated, // the channel is in a region with saturated TP + RecHitFlags_kL1SpikeFlag, // the channel is in a region with TP with sFGVB = 0 + RecHitFlags_kWeird, // the signal is believed to originate from an anomalous deposit (spike) + RecHitFlags_kDiWeird, // the signal is anomalous, and neighbors another anomalous signal + RecHitFlags_kHasSwitchToGain6, // at least one data frame is in G6 + RecHitFlags_kHasSwitchToGain1, // at least one data frame is in G1 + // + RecHitFlags_kUnknown // to ease the interface with functions returning flags. + }; + + // status code + enum EcalChannelStatusCode_Code { + kOk = 0, + kDAC, + kNoLaser, + kNoisy, + kNNoisy, + kNNNoisy, + kNNNNoisy, + kNNNNNoisy, + kFixedG6, + kFixedG1, + kFixedG0, + kNonRespondingIsolated, + kDeadVFE, + kDeadFE, + kNoDataNoTP + }; + + __global__ void kernel_create_ecal_rehit( + // configuration + int const* ChannelStatusToBeExcluded, + uint32_t ChannelStatusToBeExcludedSize, + bool const killDeadChannels, + bool const recoverEBIsolatedChannels, + bool const recoverEEIsolatedChannels, + bool const recoverEBVFE, + bool const recoverEEVFE, + bool const recoverEBFE, + bool const recoverEEFE, + float const EBLaserMIN, + float const EELaserMIN, + float const EBLaserMAX, + float const EELaserMAX, + // for flags setting + int const* expanded_v_DB_reco_flags, // FIXME AM: to be checked + uint32_t const* expanded_Sizes_v_DB_reco_flags, + uint32_t const* expanded_flagbit_v_DB_reco_flags, + uint32_t expanded_v_DB_reco_flagsSize, + uint32_t flagmask, + // conditions + float const* adc2gev, + float const* intercalib, + uint16_t const* status, + float const* apdpnrefs, + float const* alphas, + // input for transparency corrections + float const* p1, + float const* p2, + float const* p3, + edm::TimeValue_t const* t1, + edm::TimeValue_t const* t2, + edm::TimeValue_t const* t3, + // input for linear corrections + float const* lp1, + float const* lp2, + float const* lp3, + edm::TimeValue_t const* lt1, + edm::TimeValue_t const* lt2, + edm::TimeValue_t const* lt3, + // time, used for time dependent corrections + edm::TimeValue_t const event_time, + // input + uint32_t const* did_eb, + uint32_t const* did_ee, + ::ecal::reco::StorageScalarType const* amplitude_eb, // in adc counts + ::ecal::reco::StorageScalarType const* amplitude_ee, // in adc counts + ::ecal::reco::StorageScalarType const* time_eb, + ::ecal::reco::StorageScalarType const* time_ee, + ::ecal::reco::StorageScalarType const* chi2_eb, + ::ecal::reco::StorageScalarType const* chi2_ee, + uint32_t const* flags_eb, + uint32_t const* flags_ee, + // output + uint32_t* didEB, + uint32_t* didEE, + ::ecal::reco::StorageScalarType* energyEB, // in energy [GeV] + ::ecal::reco::StorageScalarType* energyEE, // in energy [GeV] + ::ecal::reco::StorageScalarType* timeEB, + ::ecal::reco::StorageScalarType* timeEE, + ::ecal::reco::StorageScalarType* chi2EB, + ::ecal::reco::StorageScalarType* chi2EE, + uint32_t* flagBitsEB, + uint32_t* flagBitsEE, + uint32_t* extraEB, + uint32_t* extraEE, + // other + int const nchannels, + uint32_t const nChannelsBarrel, + uint32_t const offsetForHashes) { + // + // NB: energy "type_wrapper::type" most likely std::vector + // + + for (int ch = threadIdx.x + blockDim.x * blockIdx.x; ch < nchannels; ch += blockDim.x * gridDim.x) { + bool isEndcap = (ch >= nChannelsBarrel); + + int const inputCh = isEndcap ? ch - nChannelsBarrel : ch; + + uint32_t const* didCh = isEndcap ? did_ee : did_eb; + + // arrange to access the right ptrs +#define ARRANGE(var) auto* var = isEndcap ? var##EE : var##EB + ARRANGE(did); + ARRANGE(energy); + ARRANGE(chi2); + ARRANGE(flagBits); + ARRANGE(extra); +#undef ARRANGE + + // only two values, EB or EE + // AM : FIXME : why not using "isBarrel" ? isBarrel ? adc2gev[0] : adc2gev[1] + float adc2gev_to_use = isEndcap ? adc2gev[1] // ee + : adc2gev[0]; // eb + + // first EB and then EE + + ::ecal::reco::StorageScalarType const* amplitude = isEndcap ? amplitude_ee : amplitude_eb; + + ::ecal::reco::StorageScalarType const* chi2_in = isEndcap ? chi2_ee : chi2_eb; + + uint32_t const* flags_in = isEndcap ? flags_ee : flags_eb; + + // simple copy + did[inputCh] = didCh[inputCh]; + + auto const did_to_use = DetId{didCh[inputCh]}; + + auto const isBarrel = did_to_use.subdetId() == EcalBarrel; + auto const hashedId = isBarrel ? ecal::reconstruction::hashedIndexEB(did_to_use.rawId()) + : offsetForHashes + ecal::reconstruction::hashedIndexEE(did_to_use.rawId()); + + float const intercalib_to_use = intercalib[hashedId]; + + // get laser coefficient + float lasercalib = 1.; + + // + // AM: ideas + // + // One possibility is to create the map of laser corrections once on CPU + // for all crystals and push them on GPU. + // Then only if the LS is different, update the laser correction + // The variation within a LS is not worth pursuing (<< 0.1% !!) + // and below the precision we can claim on the laser corrections (right?). + // This will save quite some time (also for the CPU version?) + // + + int iLM = 1; + + if (isBarrel) { + iLM = ecal::reconstruction::laser_monitoring_region_EB(did_to_use.rawId()); + } else { + iLM = ecal::reconstruction::laser_monitoring_region_EE(did_to_use.rawId()); + } + + long long t_i = 0, t_f = 0; + float p_i = 0, p_f = 0; + long long lt_i = 0, lt_f = 0; + float lp_i = 0, lp_f = 0; + + // laser + if (event_time >= t1[iLM - 1] && event_time < t2[iLM - 1]) { + t_i = t1[iLM - 1]; + t_f = t2[iLM - 1]; + p_i = p1[hashedId]; + p_f = p2[hashedId]; + } else if (event_time >= t2[iLM - 1] && event_time <= t3[iLM - 1]) { + t_i = t2[iLM - 1]; + t_f = t3[iLM - 1]; + p_i = p2[hashedId]; + p_f = p3[hashedId]; + } else if (event_time < t1[iLM - 1]) { + t_i = t1[iLM - 1]; + t_f = t2[iLM - 1]; + p_i = p1[hashedId]; + p_f = p2[hashedId]; + + } else if (event_time > t3[iLM - 1]) { + t_i = t2[iLM - 1]; + t_f = t3[iLM - 1]; + p_i = p2[hashedId]; + p_f = p3[hashedId]; + } + + // linear corrections + if (event_time >= lt1[iLM - 1] && event_time < lt2[iLM - 1]) { + lt_i = lt1[iLM - 1]; + lt_f = lt2[iLM - 1]; + lp_i = lp1[hashedId]; + lp_f = lp2[hashedId]; + } else if (event_time >= lt2[iLM - 1] && event_time <= lt3[iLM - 1]) { + lt_i = lt2[iLM - 1]; + lt_f = lt3[iLM - 1]; + lp_i = lp2[hashedId]; + lp_f = lp3[hashedId]; + } else if (event_time < lt1[iLM - 1]) { + lt_i = lt1[iLM - 1]; + lt_f = lt2[iLM - 1]; + lp_i = lp1[hashedId]; + lp_f = lp2[hashedId]; + + } else if (event_time > lt3[iLM - 1]) { + lt_i = lt2[iLM - 1]; + lt_f = lt3[iLM - 1]; + lp_i = lp2[hashedId]; + lp_f = lp3[hashedId]; + } + + // apdpnref and alpha + float apdpnref = apdpnrefs[hashedId]; + float alpha = alphas[hashedId]; + + // now calculate transparency correction + if (apdpnref != 0 && (t_i - t_f) != 0 && (lt_i - lt_f) != 0) { + long long tt = event_time; // never subtract two unsigned! + float interpolatedLaserResponse = + p_i / apdpnref + float(tt - t_i) * (p_f - p_i) / (apdpnref * float(t_f - t_i)); + + float interpolatedLinearResponse = + lp_i / apdpnref + float(tt - lt_i) * (lp_f - lp_i) / (apdpnref * float(lt_f - lt_i)); // FIXED BY FC + + if (interpolatedLinearResponse > 2.f || interpolatedLinearResponse < 0.1f) { + interpolatedLinearResponse = 1.f; + } + if (interpolatedLaserResponse <= 0.) { + // AM : how the heck is it possible? + // interpolatedLaserResponse = 0.0001; + lasercalib = 1.; + + } else { + float interpolatedTransparencyResponse = interpolatedLaserResponse / interpolatedLinearResponse; + + // ... and now this: + lasercalib = 1.f / (std::pow(interpolatedTransparencyResponse, alpha) * interpolatedLinearResponse); + } + } + + // + // Check for channels to be excluded from reconstruction + // + // + // Default energy? Not to be updated if "ChannelStatusToBeExcluded" + // Exploited later by the module "EcalRecHitConvertGPU2CPUFormat" + // + energy[inputCh] = -1; //---- AM: default, un-physical, ok + + // truncate the chi2 + if (chi2_in[inputCh] > 64) + chi2[inputCh] = 64; + else + chi2[inputCh] = chi2_in[inputCh]; + + // default values for the flags + flagBits[inputCh] = 0; + extra[inputCh] = 0; + + static const int chStatusMask = 0x1f; + // ChannelStatusToBeExcluded is a "int" then I put "dbstatus" to be the same + int dbstatus = EcalChannelStatusCode_Code((status[hashedId]) & chStatusMask); + if (ChannelStatusToBeExcludedSize != 0) { + bool skip_this_channel = false; + for (int ich_to_check = 0; ich_to_check < ChannelStatusToBeExcludedSize; ich_to_check++) { + if (ChannelStatusToBeExcluded[ich_to_check] == dbstatus) { + skip_this_channel = true; + break; + } + } + if (skip_this_channel) { + // skip this channel + continue; + } + } + + // Take our association map of dbstatuses-> recHit flagbits and return the apporpriate flagbit word + + // + // AM: get the smaller "flagbit_counter" with match + // + + uint32_t temporary_flagBits = 0; + + int iterator_flags = 0; + bool need_to_exit = false; + int flagbit_counter = 0; + while (!need_to_exit) { + iterator_flags = 0; + for (unsigned int i = 0; i != expanded_v_DB_reco_flagsSize; ++i) { + // check the correct "flagbit" + if (expanded_flagbit_v_DB_reco_flags[i] == flagbit_counter) { + for (unsigned int j = 0; j < expanded_Sizes_v_DB_reco_flags[i]; j++) { + if (expanded_v_DB_reco_flags[iterator_flags] == dbstatus) { + temporary_flagBits = 0x1 << expanded_flagbit_v_DB_reco_flags[i]; + need_to_exit = true; + break; // also from the big loop!!! + } + iterator_flags++; + } + } else { + // if not, got to the next bunch directly + iterator_flags += expanded_Sizes_v_DB_reco_flags[i]; + } + + if (need_to_exit) { + break; + } + } + flagbit_counter += 1; + } + + flagBits[inputCh] = temporary_flagBits; + + if ((flagmask & temporary_flagBits) && killDeadChannels) { + // skip this channel + continue; + } + + // + // multiply the adc counts with factors to get GeV + // + + // energy[ch] = amplitude[inputCh] * adc2gev_to_use * intercalib_to_use ; + energy[inputCh] = amplitude[inputCh] * adc2gev_to_use * intercalib_to_use * lasercalib; + + // Time is not saved so far, FIXME + // time[ch] = time_in[inputCh]; + + // NB: calculate the "flagBits extra" --> not really "flags", but actually an encoded version of energy uncertainty, time unc., ... + + // + // extra packing ... + // + + uint32_t offset; + uint32_t width; + uint32_t value; + + float chi2_temp = chi2[inputCh]; + if (chi2_temp > 64) + chi2_temp = 64; + // use 7 bits + uint32_t rawChi2 = lround(chi2_temp / 64. * ((1 << 7) - 1)); + + offset = 0; + width = 7; + value = 0; + + uint32_t mask = ((1 << width) - 1) << offset; + value &= ~mask; + value |= (rawChi2 & ((1U << width) - 1)) << offset; + + // rawEnergy is actually "error" !!! + uint32_t rawEnergy = 0; + + // AM: FIXME: this is not propagated currently to the uncalibrecHit collection SOA + // if you want to store this in "extra", we need first to add it to the uncalibrecHit results + // then it will be something like the following + // amplitudeError[inputCh] * adc2gev_to_use * intercalib_to_use * lasercalib + // + // + + float amplitudeError_ch = 0.; // amplitudeError[ch]; + + if (amplitudeError_ch > 0.001) { + static constexpr float p10[] = {1.e-2f, 1.e-1f, 1.f, 1.e1f, 1.e2f, 1.e3f, 1.e4f, 1.e5f, 1.e6f}; + int b = amplitudeError_ch < p10[4] ? 0 : 5; + for (; b < 9; ++b) + if (amplitudeError_ch < p10[b]) + break; + + uint16_t exponent = b; + + static constexpr float ip10[] = {1.e5f, 1.e4f, 1.e3f, 1.e2f, 1.e1f, 1.e0f, 1.e-1f, 1.e-2f, 1.e-3f, 1.e-4}; + uint16_t significand = lround(amplitudeError_ch * ip10[exponent]); + // use 13 bits (3 exponent, 10 significand) + rawEnergy = exponent << 10 | significand; + } + + offset = 8; + width = 13; + // value from last change, ok + + mask = ((1 << width) - 1) << offset; + value &= ~mask; + value |= (rawEnergy & ((1U << width) - 1)) << offset; + + uint32_t jitterErrorBits = 0; + jitterErrorBits = jitterErrorBits & 0xFF; + + offset = 24; + width = 8; + // value from last change, ok + + mask = ((1 << width) - 1) << offset; + value &= ~mask; + value |= (jitterErrorBits & ((1U << width) - 1)) << offset; + + // + // now finally set "extra[ch]" + // + extra[inputCh] = value; + + // + // additional flags setting + // + // using correctly the flags as calculated at the UncalibRecHit stage + // + // Now fill flags + + bool good = true; + + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kLeadingEdgeRecovered))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kLeadingEdgeRecovered)); + good = false; + } + + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kSaturated))) { + // leading edge recovery failed - still keep the information + // about the saturation and do not flag as dead + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kSaturated)); + good = false; + } + + // + // AM: why do we have two tests one after the other checking almost the same thing??? + // Please clean up the code, ... also the original one! + // + // uncalibRH.isSaturated() ---> + // + // bool EcalUncalibratedRecHit::isSaturated() const { + // return EcalUncalibratedRecHit::checkFlag(kSaturated); + // } + // + // + + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kSaturated))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kSaturated)); + good = false; + } + + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kOutOfTime))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kOutOfTime)); + good = false; + } + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kPoorReco))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kPoorReco)); + good = false; + } + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kHasSwitchToGain6))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kHasSwitchToGain6)); + } + if (flags_in[inputCh] & (0x1 << (UncalibRecHitFlags::kHasSwitchToGain1))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kHasSwitchToGain1)); + } + + if (good) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kGood)); + } + + if ((isBarrel && (lasercalib < EBLaserMIN || lasercalib > EBLaserMAX)) || (!isBarrel && (lasercalib < EELaserMIN || lasercalib > EELaserMAX))) { + flagBits[inputCh] |= (0x1 << (RecHitFlags::RecHitFlags_kPoorCalib)); + } + + // recover, killing, and other stuff + + // + // Structure: + // EB + // EE + // + // + // - single MVA + // - democratic sharing + // - kill all the other cases + // + + bool is_Single = false; + bool is_FE = false; + bool is_VFE = false; + + bool is_recoverable = false; // DetIdToBeRecovered + + if (dbstatus == 10 || dbstatus == 11 || dbstatus == 12) { + is_recoverable = true; + } + + if (is_recoverable) { + if (dbstatus == EcalChannelStatusCode_Code::kDeadVFE) { + is_VFE = true; + } else if (dbstatus == EcalChannelStatusCode_Code::kDeadVFE) { + is_FE = true; + } else { + is_Single = true; + } + + // EB + if (isBarrel) { + if (is_Single || is_FE || is_VFE) { + // single MVA + if (is_Single && (recoverEBIsolatedChannels || !killDeadChannels)) { + } + // decmocratic sharing + else if (is_FE && (recoverEBFE || !killDeadChannels)) { + } + // kill all the other cases + else { + energy[inputCh] = 0.; // Need to set also the flags ... + } + } + } + // EE + else { + if (is_Single || is_FE || is_VFE) { + // single MVA + if (is_Single && (recoverEBIsolatedChannels || !killDeadChannels)) { + } + // decmocratic sharing + else if (is_FE && (recoverEBFE || !killDeadChannels)) { + // + // Code is definitely too long ... + // + + } + // kill all the other cases + else { + energy[inputCh] = 0.; // Need to set also the flags ... + } + } + } + } + + } // end channel + } + + // host version, to be called by the plugin + void create_ecal_rehit(EventInputDataGPU const& eventInputGPU, + EventOutputDataGPU& eventOutputGPU, + // eventDataForScratchGPU_, + ConditionsProducts const& conditions, + ConfigurationParameters const& configParameters, + uint32_t const nChannelsBarrel, + edm::TimeValue_t const event_time, + cudaStream_t cudaStream) { + int nchannels = eventInputGPU.ebUncalibRecHits.size + eventInputGPU.eeUncalibRecHits.size; + + unsigned int nchannels_per_block = 16; + unsigned int threads_min = nchannels_per_block; + unsigned int blocks_min = (nchannels + threads_min - 1) / threads_min; // TEST : to be optimized (AM) + + // + // kernel create rechit + // + + kernel_create_ecal_rehit<<>>( + // configuration + configParameters.ChannelStatusToBeExcluded, + configParameters.ChannelStatusToBeExcludedSize, + configParameters.killDeadChannels, + configParameters.recoverEBIsolatedChannels, + configParameters.recoverEEIsolatedChannels, + configParameters.recoverEBVFE, + configParameters.recoverEEVFE, + configParameters.recoverEBFE, + configParameters.recoverEEFE, + configParameters.EBLaserMIN, + configParameters.EELaserMIN, + configParameters.EBLaserMAX, + configParameters.EELaserMAX, + // for flags setting + configParameters.expanded_v_DB_reco_flags, + configParameters.expanded_Sizes_v_DB_reco_flags, + configParameters.expanded_flagbit_v_DB_reco_flags, + configParameters.expanded_v_DB_reco_flagsSize, + configParameters.flagmask, + // conditions + conditions.ADCToGeV.adc2gev, + conditions.Intercalib.values, + conditions.ChannelStatus.status, + conditions.LaserAPDPNRatiosRef.values, + conditions.LaserAlphas.values, + // input for transparency corrections + conditions.LaserAPDPNRatios.p1, + conditions.LaserAPDPNRatios.p2, + conditions.LaserAPDPNRatios.p3, + conditions.LaserAPDPNRatios.t1, + conditions.LaserAPDPNRatios.t2, + conditions.LaserAPDPNRatios.t3, + // input for linear corrections + conditions.LinearCorrections.p1, + conditions.LinearCorrections.p2, + conditions.LinearCorrections.p3, + conditions.LinearCorrections.t1, + conditions.LinearCorrections.t2, + conditions.LinearCorrections.t3, + // time, used for time dependent corrections + event_time, + // input + eventInputGPU.ebUncalibRecHits.did.get(), + eventInputGPU.eeUncalibRecHits.did.get(), + eventInputGPU.ebUncalibRecHits.amplitude.get(), + eventInputGPU.eeUncalibRecHits.amplitude.get(), + eventInputGPU.ebUncalibRecHits.jitter.get(), + eventInputGPU.eeUncalibRecHits.jitter.get(), + eventInputGPU.ebUncalibRecHits.chi2.get(), + eventInputGPU.eeUncalibRecHits.chi2.get(), + eventInputGPU.ebUncalibRecHits.flags.get(), + eventInputGPU.eeUncalibRecHits.flags.get(), + // output + eventOutputGPU.recHitsEB.did.get(), + eventOutputGPU.recHitsEE.did.get(), + eventOutputGPU.recHitsEB.energy.get(), + eventOutputGPU.recHitsEE.energy.get(), + eventOutputGPU.recHitsEB.time.get(), + eventOutputGPU.recHitsEE.time.get(), + eventOutputGPU.recHitsEB.chi2.get(), + eventOutputGPU.recHitsEE.chi2.get(), + eventOutputGPU.recHitsEB.flagBits.get(), + eventOutputGPU.recHitsEE.flagBits.get(), + eventOutputGPU.recHitsEB.extra.get(), + eventOutputGPU.recHitsEE.extra.get(), + // other + nchannels, + nChannelsBarrel, + conditions.offsetForHashes); + } + + } // namespace rechit + +} // namespace ecal diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitBuilderKernels.h b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitBuilderKernels.h new file mode 100644 index 0000000000000..cb9c7f435d7b3 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitBuilderKernels.h @@ -0,0 +1,93 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_EcalRecHitBuilderKernels_h +#define RecoLocalCalo_EcalRecProducers_plugins_EcalRecHitBuilderKernels_h + +// +// Builder of ECAL RecHits on GPU +// + +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" +#include "DataFormats/Provenance/interface/Timestamp.h" + +#include "Common.h" +#include "DeclsForKernels.h" + +namespace ecal { + namespace rechit { + + __global__ void kernel_create_ecal_rehit( + // configuration + int const* ChannelStatusToBeExcluded, + uint32_t ChannelStatusToBeExcludedSize, + bool killDeadChannels, + bool const recoverEBIsolatedChannels, + bool const recoverEEIsolatedChannels, + bool const recoverEBVFE, + bool const recoverEEVFE, + bool const recoverEBFE, + bool const recoverEEFE, + // for flags setting + int const* expanded_v_DB_reco_flags, + uint32_t const* expanded_Sizes_v_DB_reco_flags, + uint32_t const* expanded_flagbit_v_DB_reco_flags, + uint32_t expanded_v_DB_reco_flagsSize, + uint32_t flagmask, + // conditions + float const* adc2gev, + float const* intercalib, + uint16_t const* status, + float const* apdpnrefs, + float const* alphas, + // input for transparency corrections + float const* p1, + float const* p2, + float const* p3, + edm::TimeValue_t const* t1, + edm::TimeValue_t const* t2, + edm::TimeValue_t const* t3, + // input for linear corrections + float const* lp1, + float const* lp2, + float const* lp3, + edm::TimeValue_t const* lt1, + edm::TimeValue_t const* lt2, + edm::TimeValue_t const* lt3, + // time, used for time dependent corrections + edm::TimeValue_t const event_time, + // input + uint32_t const* did_eb, + uint32_t const* did_ee, + ::ecal::reco::StorageScalarType const* amplitude_eb, // in adc counts + ::ecal::reco::StorageScalarType const* amplitude_ee, // in adc counts + ::ecal::reco::StorageScalarType const* time_eb, + ::ecal::reco::StorageScalarType const* time_ee, + ::ecal::reco::StorageScalarType const* chi2_eb, + ::ecal::reco::StorageScalarType const* chi2_ee, + uint32_t const* flags_eb, + uint32_t const* flags_ee, + // output + uint32_t* did, + ::ecal::reco::StorageScalarType* energy, // in energy [GeV] + ::ecal::reco::StorageScalarType* time, + ::ecal::reco::StorageScalarType* chi2, + uint32_t* flagBits, + uint32_t* extra, + int const nchannels, + uint32_t const nChannelsBarrel, + uint32_t const offsetForHashes); + + // host version, to be called by the plugin + + void create_ecal_rehit(EventInputDataGPU const& eventInputGPU, + EventOutputDataGPU& eventOutputGPU, + ConditionsProducts const& conditions, + ConfigurationParameters const& configParameters, + uint32_t const nChannelsBarrel, + edm::TimeValue_t const event_time, + cudaStream_t cudaStream); + + } // namespace rechit + +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_EcalRecHitBuilderKernels_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitConvertGPU2CPUFormat.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitConvertGPU2CPUFormat.cc new file mode 100644 index 0000000000000..6df36f4a8b592 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitConvertGPU2CPUFormat.cc @@ -0,0 +1,98 @@ +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHit.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHitCollections.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "Common.h" + +class EcalRecHitConvertGPU2CPUFormat : public edm::stream::EDProducer<> { +public: + explicit EcalRecHitConvertGPU2CPUFormat(edm::ParameterSet const& ps); + ~EcalRecHitConvertGPU2CPUFormat() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + using InputProduct = ecal::RecHit>; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + const edm::EDGetTokenT recHitsGPUEB_; + const edm::EDGetTokenT recHitsGPUEE_; + + const std::string recHitsLabelCPUEB_, recHitsLabelCPUEE_; +}; + +void EcalRecHitConvertGPU2CPUFormat::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("recHitsLabelGPUEB", edm::InputTag("ecalRecHitProducerGPU", "EcalRecHitsGPUEB")); + desc.add("recHitsLabelGPUEE", edm::InputTag("ecalRecHitProducerGPU", "EcalRecHitsGPUEE")); + + desc.add("recHitsLabelCPUEB", "EcalRecHitsEB"); + desc.add("recHitsLabelCPUEE", "EcalRecHitsEE"); + + confDesc.addWithDefaultLabel(desc); +} + +EcalRecHitConvertGPU2CPUFormat::EcalRecHitConvertGPU2CPUFormat(const edm::ParameterSet& ps) + : recHitsGPUEB_{consumes(ps.getParameter("recHitsLabelGPUEB"))}, + recHitsGPUEE_{consumes(ps.getParameter("recHitsLabelGPUEE"))}, + recHitsLabelCPUEB_{ps.getParameter("recHitsLabelCPUEB")}, + recHitsLabelCPUEE_{ps.getParameter("recHitsLabelCPUEE")} { + produces(recHitsLabelCPUEB_); + produces(recHitsLabelCPUEE_); +} + +EcalRecHitConvertGPU2CPUFormat::~EcalRecHitConvertGPU2CPUFormat() {} + +void EcalRecHitConvertGPU2CPUFormat::produce(edm::Event& event, edm::EventSetup const& setup) { + auto const& hRecHitsGPUEB = event.get(recHitsGPUEB_); + auto const& hRecHitsGPUEE = event.get(recHitsGPUEE_); + + auto recHitsCPUEB = std::make_unique(); + auto recHitsCPUEE = std::make_unique(); + recHitsCPUEB->reserve(hRecHitsGPUEB.energy.size()); + recHitsCPUEE->reserve(hRecHitsGPUEE.energy.size()); + + for (uint32_t i = 0; i < hRecHitsGPUEB.energy.size(); ++i) { + // + // Save only if energy is >= 0 ! + // This is extremely important because the channels that were supposed + // to be excluded get "-1" as energy + // + + if (hRecHitsGPUEB.energy[i] >= 0) { + recHitsCPUEB->emplace_back(DetId{hRecHitsGPUEB.did[i]}, + hRecHitsGPUEB.energy[i], + hRecHitsGPUEB.time[i], + hRecHitsGPUEB.extra[i], + hRecHitsGPUEB.flagBits[i]); + } + } + + for (uint32_t i = 0; i < hRecHitsGPUEE.energy.size(); ++i) { + // + // Save only if energy is >= 0 ! + // This is extremely important because the channels that were supposed + // to be excluded get "-1" as energy + // + + if (hRecHitsGPUEE.energy[i] >= 0) { + recHitsCPUEE->emplace_back(DetId{hRecHitsGPUEE.did[i]}, + hRecHitsGPUEE.energy[i], + hRecHitsGPUEE.time[i], + hRecHitsGPUEE.extra[i], + hRecHitsGPUEE.flagBits[i]); + } + } + + event.put(std::move(recHitsCPUEB), recHitsLabelCPUEB_); + event.put(std::move(recHitsCPUEE), recHitsLabelCPUEE_); +} + +DEFINE_FWK_MODULE(EcalRecHitConvertGPU2CPUFormat); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitParametersGPUESProducer.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitParametersGPUESProducer.cc new file mode 100644 index 0000000000000..a63ed42cb2b70 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitParametersGPUESProducer.cc @@ -0,0 +1,83 @@ +#include +#include +#include + +#include "FWCore/Framework/interface/ESProducer.h" +#include "FWCore/Framework/interface/ESProductHost.h" +#include "FWCore/Framework/interface/ESTransientHandle.h" +#include "FWCore/Framework/interface/EventSetupRecordIntervalFinder.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/ModuleFactory.h" +#include "FWCore/Framework/interface/SourceFactory.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/ReusableObjectHolder.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDACore/interface/JobConfigurationGPURecord.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRecHitParametersGPU.h" + +class EcalRecHitParametersGPUESProducer : public edm::ESProducer, public edm::EventSetupRecordIntervalFinder { +public: + EcalRecHitParametersGPUESProducer(edm::ParameterSet const&); + ~EcalRecHitParametersGPUESProducer() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions&); + std::unique_ptr produce(JobConfigurationGPURecord const&); + +protected: + void setIntervalFor(const edm::eventsetup::EventSetupRecordKey&, + const edm::IOVSyncValue&, + edm::ValidityInterval&) override; + +private: + edm::ParameterSet const pset_; +}; + +EcalRecHitParametersGPUESProducer::EcalRecHitParametersGPUESProducer(edm::ParameterSet const& pset) : pset_{pset} { + setWhatProduced(this); + findingRecord(); +} + +void EcalRecHitParametersGPUESProducer::setIntervalFor(const edm::eventsetup::EventSetupRecordKey& iKey, + const edm::IOVSyncValue& iTime, + edm::ValidityInterval& oInterval) { + oInterval = edm::ValidityInterval(edm::IOVSyncValue::beginOfTime(), edm::IOVSyncValue::endOfTime()); +} + +void EcalRecHitParametersGPUESProducer::fillDescriptions(edm::ConfigurationDescriptions& desc) { + edm::ParameterSetDescription d; + + //---- db statuses to be exluded from reconstruction + d.add>("ChannelStatusToBeExcluded", + { + "kDAC", + "kNoisy", + "kNNoisy", + "kFixedG6", + "kFixedG1", + "kFixedG0", + "kNonRespondingIsolated", + "kDeadVFE", + "kDeadFE", + "kNoDataNoTP", + }); + + // reco flags association to DB flag + edm::ParameterSetDescription desc_list_flagsMapDBReco; + desc_list_flagsMapDBReco.add>("kGood", {"kOk", "kDAC", "kNoLaser", "kNoisy"}); + desc_list_flagsMapDBReco.add>("kNoisy", {"kNNoisy", "kFixedG6", "kFixedG1"}); + desc_list_flagsMapDBReco.add>("kNeighboursRecovered", + {"kFixedG0", "kNonRespondingIsolated", "kDeadVFE"}); + desc_list_flagsMapDBReco.add>("kTowerRecovered", {"kDeadFE"}); + desc_list_flagsMapDBReco.add>("kDead", {"kNoDataNoTP"}); + + d.add("flagsMapDBReco", desc_list_flagsMapDBReco); + + desc.addWithDefaultLabel(d); +} + +std::unique_ptr EcalRecHitParametersGPUESProducer::produce(JobConfigurationGPURecord const&) { + return std::make_unique(pset_); +} + +DEFINE_FWK_EVENTSETUP_SOURCE(EcalRecHitParametersGPUESProducer); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitProducerGPU.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitProducerGPU.cc new file mode 100644 index 0000000000000..a6dabd37f8439 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalRecHitProducerGPU.cc @@ -0,0 +1,244 @@ +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" +#include "CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h" +#include "CommonTools/Utils/interface/StringToEnumValue.h" +#include "CondFormats/DataRecord/interface/EcalADCToGeVConstantRcd.h" +#include "CondFormats/DataRecord/interface/EcalChannelStatusRcd.h" +#include "CondFormats/DataRecord/interface/EcalIntercalibConstantsRcd.h" +#include "CondFormats/DataRecord/interface/EcalLaserAPDPNRatiosRcd.h" +#include "CondFormats/DataRecord/interface/EcalLaserAPDPNRatiosRefRcd.h" +#include "CondFormats/DataRecord/interface/EcalLaserAlphasRcd.h" +#include "CondFormats/DataRecord/interface/EcalLinearCorrectionsRcd.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHit.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/CUDACore/interface/JobConfigurationGPURecord.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalIntercalibConstantsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAPDPNRatiosRefGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLaserAlphasGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalLinearCorrectionsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRecHitParametersGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitADCToGeVConstantGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalRechitChannelStatusGPU.h" + +#include "EcalRecHitBuilderKernels.h" + +class EcalRecHitProducerGPU : public edm::stream::EDProducer { +public: + explicit EcalRecHitProducerGPU(edm::ParameterSet const& ps); + ~EcalRecHitProducerGPU() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + // data + uint32_t neb_, nee_; // extremely important, in particular neb_ + + // gpu input + using InputProduct = cms::cuda::Product>; + edm::EDGetTokenT uncalibRecHitsInEBToken_; + edm::EDGetTokenT uncalibRecHitsInEEToken_; + + // event data + ecal::rechit::EventOutputDataGPU eventOutputDataGPU_; + + cms::cuda::ContextState cudaState_; + + // gpu output + using OutputProduct = cms::cuda::Product>; + edm::EDPutTokenT recHitsTokenEB_, recHitsTokenEE_; + + // configuration parameters + ecal::rechit::ConfigurationParameters configParameters_; + + // conditions handles + edm::ESHandle ADCToGeVConstantHandle_; + edm::ESHandle IntercalibConstantsHandle_; + edm::ESHandle ChannelStatusHandle_; + + edm::ESHandle LaserAPDPNRatiosHandle_; + edm::ESHandle LaserAPDPNRatiosRefHandle_; + edm::ESHandle LaserAlphasHandle_; + edm::ESHandle LinearCorrectionsHandle_; + edm::ESHandle recHitParametersHandle_; + + // Associate reco flagbit (outer vector) to many db status flags (inner vector) + std::vector + expanded_v_DB_reco_flags_; // Transform a map in a vector // FIXME AM: int or uint32 to be checked + std::vector expanded_Sizes_v_DB_reco_flags_; // Saving the size for each piece + std::vector expanded_flagbit_v_DB_reco_flags_; // And the "key" for each key + + uint32_t flagmask_; // do not propagate channels with these flags on +}; + +void EcalRecHitProducerGPU::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("uncalibrecHitsInLabelEB", + edm::InputTag("ecalUncalibRecHitProducerGPU", "EcalUncalibRecHitsEB")); + desc.add("uncalibrecHitsInLabelEE", + edm::InputTag("ecalUncalibRecHitProducerGPU", "EcalUncalibRecHitsEE")); + + desc.add("recHitsLabelEB", "EcalRecHitsGPUEB"); + desc.add("recHitsLabelEE", "EcalRecHitsGPUEE"); + + desc.add("killDeadChannels", true); + + desc.add("EBLaserMIN", 0.01); + desc.add("EELaserMIN", 0.01); + desc.add("EBLaserMAX", 30.0); + desc.add("EELaserMAX", 30.0); + + desc.add("maxNumberHitsEB", 61200); + desc.add("maxNumberHitsEE", 14648); +} + +EcalRecHitProducerGPU::EcalRecHitProducerGPU(const edm::ParameterSet& ps) { + //---- input + uncalibRecHitsInEBToken_ = consumes(ps.getParameter("uncalibrecHitsInLabelEB")); + uncalibRecHitsInEEToken_ = consumes(ps.getParameter("uncalibrecHitsInLabelEE")); + + //---- output + recHitsTokenEB_ = produces(ps.getParameter("recHitsLabelEB")); + recHitsTokenEE_ = produces(ps.getParameter("recHitsLabelEE")); + + bool killDeadChannels = ps.getParameter("killDeadChannels"); + configParameters_.killDeadChannels = killDeadChannels; + + configParameters_.EBLaserMIN = ps.getParameter("EBLaserMIN"); + configParameters_.EELaserMIN = ps.getParameter("EELaserMIN"); + configParameters_.EBLaserMAX = ps.getParameter("EBLaserMAX"); + configParameters_.EELaserMAX = ps.getParameter("EELaserMAX"); + + // max number of digis to allocate for + configParameters_.maxNumberHitsEB = ps.getParameter("maxNumberHitsEB"); + configParameters_.maxNumberHitsEE = ps.getParameter("maxNumberHitsEE"); + + flagmask_ = 0; + flagmask_ |= 0x1 << EcalRecHit::kNeighboursRecovered; + flagmask_ |= 0x1 << EcalRecHit::kTowerRecovered; + flagmask_ |= 0x1 << EcalRecHit::kDead; + flagmask_ |= 0x1 << EcalRecHit::kKilled; + flagmask_ |= 0x1 << EcalRecHit::kTPSaturated; + flagmask_ |= 0x1 << EcalRecHit::kL1SpikeFlag; + + configParameters_.flagmask = flagmask_; + + // for recovery and killing + + configParameters_.recoverEBIsolatedChannels = ps.getParameter("recoverEBIsolatedChannels"); + configParameters_.recoverEEIsolatedChannels = ps.getParameter("recoverEEIsolatedChannels"); + configParameters_.recoverEBVFE = ps.getParameter("recoverEBVFE"); + configParameters_.recoverEEVFE = ps.getParameter("recoverEEVFE"); + configParameters_.recoverEBFE = ps.getParameter("recoverEBFE"); + configParameters_.recoverEEFE = ps.getParameter("recoverEEFE"); +} + +EcalRecHitProducerGPU::~EcalRecHitProducerGPU() {} + +void EcalRecHitProducerGPU::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder holder) { + // cuda products + auto const& ebUncalibRecHitsProduct = event.get(uncalibRecHitsInEBToken_); + auto const& eeUncalibRecHitsProduct = event.get(uncalibRecHitsInEEToken_); + // raii + cms::cuda::ScopedContextAcquire ctx{ebUncalibRecHitsProduct, std::move(holder), cudaState_}; + // get actual object + auto const& ebUncalibRecHits = ctx.get(ebUncalibRecHitsProduct); + auto const& eeUncalibRecHits = ctx.get(eeUncalibRecHitsProduct); + + ecal::rechit::EventInputDataGPU inputDataGPU{ebUncalibRecHits, eeUncalibRecHits}; + + neb_ = ebUncalibRecHits.size; + nee_ = eeUncalibRecHits.size; + + if ((neb_ > configParameters_.maxNumberHitsEB) || (nee_ > configParameters_.maxNumberHitsEE)) { + edm::LogError("EcalRecHitProducerGPU") + << "max number of channels exceeded. See options 'maxNumberHitsEB and maxNumberHitsEE' "; + } + + int nchannelsEB = ebUncalibRecHits.size; // --> offsetForInput, first EB and then EE + + // conditions + // - laser correction + // - IC + // - adt2gev + + // + setup.get().get(ADCToGeVConstantHandle_); + setup.get().get(IntercalibConstantsHandle_); + setup.get().get(ChannelStatusHandle_); + + setup.get().get(LaserAPDPNRatiosHandle_); + setup.get().get(LaserAPDPNRatiosRefHandle_); + setup.get().get(LaserAlphasHandle_); + setup.get().get(LinearCorrectionsHandle_); + setup.get().get(recHitParametersHandle_); + + auto const& ADCToGeVConstantProduct = ADCToGeVConstantHandle_->getProduct(ctx.stream()); + auto const& IntercalibConstantsProduct = IntercalibConstantsHandle_->getProduct(ctx.stream()); + auto const& ChannelStatusProduct = ChannelStatusHandle_->getProduct(ctx.stream()); + + auto const& LaserAPDPNRatiosProduct = LaserAPDPNRatiosHandle_->getProduct(ctx.stream()); + auto const& LaserAPDPNRatiosRefProduct = LaserAPDPNRatiosRefHandle_->getProduct(ctx.stream()); + auto const& LaserAlphasProduct = LaserAlphasHandle_->getProduct(ctx.stream()); + auto const& LinearCorrectionsProduct = LinearCorrectionsHandle_->getProduct(ctx.stream()); + auto const& recHitParametersProduct = recHitParametersHandle_->getProduct(ctx.stream()); + + // set config ptrs : this is done to avoid changing things downstream + configParameters_.ChannelStatusToBeExcluded = recHitParametersProduct.ChannelStatusToBeExcluded; + configParameters_.ChannelStatusToBeExcludedSize = std::get<0>(recHitParametersHandle_->getValues()).get().size(); + configParameters_.expanded_v_DB_reco_flags = recHitParametersProduct.expanded_v_DB_reco_flags; + configParameters_.expanded_Sizes_v_DB_reco_flags = recHitParametersProduct.expanded_Sizes_v_DB_reco_flags; + configParameters_.expanded_flagbit_v_DB_reco_flags = recHitParametersProduct.expanded_flagbit_v_DB_reco_flags; + configParameters_.expanded_v_DB_reco_flagsSize = std::get<3>(recHitParametersHandle_->getValues()).get().size(); + + // bundle up conditions + ecal::rechit::ConditionsProducts conditions{ADCToGeVConstantProduct, + IntercalibConstantsProduct, + ChannelStatusProduct, + LaserAPDPNRatiosProduct, + LaserAPDPNRatiosRefProduct, + LaserAlphasProduct, + LinearCorrectionsProduct, + IntercalibConstantsHandle_->getOffset()}; + + // dev mem + eventOutputDataGPU_.allocate(configParameters_, ctx.stream()); + + // + // schedule algorithms + // + + edm::TimeValue_t event_time = event.time().value(); + + ecal::rechit::create_ecal_rehit( + inputDataGPU, eventOutputDataGPU_, conditions, configParameters_, nchannelsEB, event_time, ctx.stream()); + + cudaCheck(cudaGetLastError()); +} + +void EcalRecHitProducerGPU::produce(edm::Event& event, edm::EventSetup const& setup) { + cms::cuda::ScopedContextProduce ctx{cudaState_}; + + eventOutputDataGPU_.recHitsEB.size = neb_; + eventOutputDataGPU_.recHitsEE.size = nee_; + + // put into the event + ctx.emplace(event, recHitsTokenEB_, std::move(eventOutputDataGPU_.recHitsEB)); + ctx.emplace(event, recHitsTokenEE_, std::move(eventOutputDataGPU_.recHitsEE)); +} + +DEFINE_FWK_MODULE(EcalRecHitProducerGPU); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitConvertGPU2CPUFormat.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitConvertGPU2CPUFormat.cc new file mode 100644 index 0000000000000..f7e57a61fdd96 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitConvertGPU2CPUFormat.cc @@ -0,0 +1,93 @@ +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/EcalRecHit/interface/EcalRecHitCollections.h" +#include "DataFormats/EcalRecHit/interface/EcalUncalibratedRecHit.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include "Common.h" + +class EcalUncalibRecHitConvertGPU2CPUFormat : public edm::stream::EDProducer<> { +public: + explicit EcalUncalibRecHitConvertGPU2CPUFormat(edm::ParameterSet const& ps); + ~EcalUncalibRecHitConvertGPU2CPUFormat() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + using InputProduct = ecal::UncalibratedRecHit>; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + const edm::EDGetTokenT recHitsGPUEB_; + const edm::EDGetTokenT recHitsGPUEE_; + + const std::string recHitsLabelCPUEB_, recHitsLabelCPUEE_; +}; + +void EcalUncalibRecHitConvertGPU2CPUFormat::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("recHitsLabelGPUEB", edm::InputTag("ecalUncalibRecHitProducerGPU", "EcalUncalibRecHitsEB")); + desc.add("recHitsLabelGPUEE", edm::InputTag("ecalUncalibRecHitProducerGPU", "EcalUncalibRecHitsEE")); + + desc.add("recHitsLabelCPUEB", "EcalUncalibRecHitsEB"); + desc.add("recHitsLabelCPUEE", "EcalUncalibRecHitsEE"); + + confDesc.add("ecalUncalibRecHitConvertGPU2CPUFormat", desc); +} + +EcalUncalibRecHitConvertGPU2CPUFormat::EcalUncalibRecHitConvertGPU2CPUFormat(const edm::ParameterSet& ps) + : recHitsGPUEB_{consumes(ps.getParameter("recHitsLabelGPUEB"))}, + recHitsGPUEE_{consumes(ps.getParameter("recHitsLabelGPUEE"))}, + recHitsLabelCPUEB_{ps.getParameter("recHitsLabelCPUEB")}, + recHitsLabelCPUEE_{ps.getParameter("recHitsLabelCPUEE")} { + produces(recHitsLabelCPUEB_); + produces(recHitsLabelCPUEE_); +} + +EcalUncalibRecHitConvertGPU2CPUFormat::~EcalUncalibRecHitConvertGPU2CPUFormat() {} + +void EcalUncalibRecHitConvertGPU2CPUFormat::produce(edm::Event& event, edm::EventSetup const& setup) { + edm::Handle hRecHitsGPUEB, hRecHitsGPUEE; + event.getByToken(recHitsGPUEB_, hRecHitsGPUEB); + event.getByToken(recHitsGPUEE_, hRecHitsGPUEE); + + auto recHitsCPUEB = std::make_unique(); + auto recHitsCPUEE = std::make_unique(); + recHitsCPUEB->reserve(hRecHitsGPUEB->amplitude.size()); + recHitsCPUEE->reserve(hRecHitsGPUEE->amplitude.size()); + + for (uint32_t i = 0; i < hRecHitsGPUEB->amplitude.size(); ++i) { + recHitsCPUEB->emplace_back(DetId{hRecHitsGPUEB->did[i]}, + hRecHitsGPUEB->amplitude[i], + hRecHitsGPUEB->pedestal[i], + hRecHitsGPUEB->jitter[i], + hRecHitsGPUEB->chi2[i], + hRecHitsGPUEB->flags[i]); + (*recHitsCPUEB)[i].setJitterError(hRecHitsGPUEB->jitterError[i]); + auto const offset = i * EcalDataFrame::MAXSAMPLES; + for (uint32_t sample = 0; sample < EcalDataFrame::MAXSAMPLES; ++sample) + (*recHitsCPUEB)[i].setOutOfTimeAmplitude(sample, hRecHitsGPUEB->amplitudesAll[offset + sample]); + } + + for (uint32_t i = 0; i < hRecHitsGPUEE->amplitude.size(); ++i) { + recHitsCPUEE->emplace_back(DetId{hRecHitsGPUEE->did[i]}, + hRecHitsGPUEE->amplitude[i], + hRecHitsGPUEE->pedestal[i], + hRecHitsGPUEE->jitter[i], + hRecHitsGPUEE->chi2[i], + hRecHitsGPUEE->flags[i]); + (*recHitsCPUEE)[i].setJitterError(hRecHitsGPUEE->jitterError[i]); + auto const offset = i * EcalDataFrame::MAXSAMPLES; + for (uint32_t sample = 0; sample < EcalDataFrame::MAXSAMPLES; ++sample) + (*recHitsCPUEE)[i].setOutOfTimeAmplitude(sample, hRecHitsGPUEE->amplitudesAll[offset + sample]); + } + + event.put(std::move(recHitsCPUEB), recHitsLabelCPUEB_); + event.put(std::move(recHitsCPUEE), recHitsLabelCPUEE_); +} + +DEFINE_FWK_MODULE(EcalUncalibRecHitConvertGPU2CPUFormat); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitMultiFitAlgoGPU.cu b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitMultiFitAlgoGPU.cu new file mode 100644 index 0000000000000..9d5a8a2ad1bd3 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitMultiFitAlgoGPU.cu @@ -0,0 +1,305 @@ +#include +#include + +#include + +#include "CondFormats/EcalObjects/interface/EcalMGPAGainRatio.h" +#include "CondFormats/EcalObjects/interface/EcalPedestals.h" +#include "CondFormats/EcalObjects/interface/EcalPulseCovariances.h" +#include "CondFormats/EcalObjects/interface/EcalPulseShapes.h" +#include "CondFormats/EcalObjects/interface/EcalSampleMask.h" +#include "CondFormats/EcalObjects/interface/EcalSamplesCorrelation.h" +#include "CondFormats/EcalObjects/interface/EcalXtalGroupId.h" +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" + +#include "AmplitudeComputationCommonKernels.h" +#include "AmplitudeComputationKernels.h" +#include "Common.h" +#include "EcalUncalibRecHitMultiFitAlgoGPU.h" +#include "TimeComputationKernels.h" + +//#define DEBUG + +//#define ECAL_RECO_CUDA_DEBUG + +namespace ecal { + namespace multifit { + + void entryPoint(EventInputDataGPU const& eventInputGPU, + EventOutputDataGPU& eventOutputGPU, + EventDataForScratchGPU& scratch, + ConditionsProducts const& conditions, + ConfigurationParameters const& configParameters, + cudaStream_t cudaStream) { + using digis_type = std::vector; + using dids_type = std::vector; + // accodring to the cpu setup //----> hardcoded + bool const gainSwitchUseMaxSampleEB = true; + // accodring to the cpu setup //----> hardcoded + bool const gainSwitchUseMaxSampleEE = false; + + uint32_t const offsetForHashes = conditions.offsetForHashes; + uint32_t const offsetForInputs = eventInputGPU.ebDigis.size; + unsigned int totalChannels = eventInputGPU.ebDigis.size + eventInputGPU.eeDigis.size; + + // + // 1d preparation kernel + // + unsigned int nchannels_per_block = 32; + unsigned int threads_1d = 10 * nchannels_per_block; + unsigned int blocks_1d = threads_1d > 10 * totalChannels ? 1 : (totalChannels * 10 + threads_1d - 1) / threads_1d; + int shared_bytes = nchannels_per_block * EcalDataFrame::MAXSAMPLES * + (sizeof(bool) + sizeof(bool) + sizeof(bool) + sizeof(bool) + sizeof(char) + sizeof(bool)); + kernel_prep_1d_and_initialize<<>>( + conditions.pulseShapes.values, + eventInputGPU.ebDigis.data.get(), + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.data.get(), + eventInputGPU.eeDigis.ids.get(), + (SampleVector*)scratch.samples.get(), + (SampleVector*)eventOutputGPU.recHitsEB.amplitudesAll.get(), + (SampleVector*)eventOutputGPU.recHitsEE.amplitudesAll.get(), + (SampleGainVector*)scratch.gainsNoise.get(), + conditions.pedestals.mean_x1, + conditions.pedestals.mean_x12, + conditions.pedestals.rms_x12, + conditions.pedestals.mean_x6, + conditions.gainRatios.gain6Over1, + conditions.gainRatios.gain12Over6, + scratch.hasSwitchToGain6.get(), + scratch.hasSwitchToGain1.get(), + scratch.isSaturated.get(), + eventOutputGPU.recHitsEB.amplitude.get(), + eventOutputGPU.recHitsEE.amplitude.get(), + eventOutputGPU.recHitsEB.chi2.get(), + eventOutputGPU.recHitsEE.chi2.get(), + eventOutputGPU.recHitsEB.pedestal.get(), + eventOutputGPU.recHitsEE.pedestal.get(), + eventOutputGPU.recHitsEB.did.get(), + eventOutputGPU.recHitsEE.did.get(), + eventOutputGPU.recHitsEB.flags.get(), + eventOutputGPU.recHitsEE.flags.get(), + scratch.acState.get(), + (BXVectorType*)scratch.activeBXs.get(), + offsetForHashes, + offsetForInputs, + gainSwitchUseMaxSampleEB, + gainSwitchUseMaxSampleEE, + totalChannels); + cudaCheck(cudaGetLastError()); + + // + // 2d preparation kernel + // + int blocks_2d = totalChannels; + dim3 threads_2d{10, 10}; + kernel_prep_2d<<>>((SampleGainVector*)scratch.gainsNoise.get(), + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.ids.get(), + conditions.pedestals.rms_x12, + conditions.pedestals.rms_x6, + conditions.pedestals.rms_x1, + conditions.gainRatios.gain12Over6, + conditions.gainRatios.gain6Over1, + conditions.samplesCorrelation.EBG12SamplesCorrelation, + conditions.samplesCorrelation.EBG6SamplesCorrelation, + conditions.samplesCorrelation.EBG1SamplesCorrelation, + conditions.samplesCorrelation.EEG12SamplesCorrelation, + conditions.samplesCorrelation.EEG6SamplesCorrelation, + conditions.samplesCorrelation.EEG1SamplesCorrelation, + (SampleMatrix*)scratch.noisecov.get(), + (PulseMatrixType*)scratch.pulse_matrix.get(), + conditions.pulseShapes.values, + scratch.hasSwitchToGain6.get(), + scratch.hasSwitchToGain1.get(), + scratch.isSaturated.get(), + offsetForHashes, + offsetForInputs); + cudaCheck(cudaGetLastError()); + + // run minimization kernels + v1::minimization_procedure(eventInputGPU, eventOutputGPU, scratch, conditions, configParameters, cudaStream); + + if (configParameters.shouldRunTimingComputation) { + // + // TODO: this guy can run concurrently with other kernels, + // there is no dependence on the order of execution + // + unsigned int threads_time_init = threads_1d; + unsigned int blocks_time_init = blocks_1d; + int sharedBytesInit = 2 * threads_time_init * sizeof(SampleVector::Scalar); + kernel_time_computation_init<<>>( + eventInputGPU.ebDigis.data.get(), + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.data.get(), + eventInputGPU.eeDigis.ids.get(), + conditions.pedestals.rms_x12, + conditions.pedestals.rms_x6, + conditions.pedestals.rms_x1, + conditions.pedestals.mean_x12, + conditions.pedestals.mean_x6, + conditions.pedestals.mean_x1, + conditions.gainRatios.gain12Over6, + conditions.gainRatios.gain6Over1, + scratch.sample_values.get(), + scratch.sample_value_errors.get(), + scratch.ampMaxError.get(), + scratch.useless_sample_values.get(), + scratch.pedestal_nums.get(), + offsetForHashes, + offsetForInputs, + conditions.sampleMask.getEcalSampleMaskRecordEB(), + conditions.sampleMask.getEcalSampleMaskRecordEE(), + totalChannels); + cudaCheck(cudaGetLastError()); + + // + // TODO: small kernel only for EB. It needs to be checked if + /// fusing such small kernels is beneficial in here + // + // we are running only over EB digis + // therefore we need to create threads/blocks only for that + unsigned int const threadsFixMGPA = threads_1d; + unsigned int const blocksFixMGPA = + threadsFixMGPA > 10 * eventInputGPU.ebDigis.size + ? 1 + : (10 * eventInputGPU.ebDigis.size + threadsFixMGPA - 1) / threadsFixMGPA; + kernel_time_compute_fixMGPAslew<<>>( + eventInputGPU.ebDigis.data.get(), + eventInputGPU.eeDigis.data.get(), + scratch.sample_values.get(), + scratch.sample_value_errors.get(), + scratch.useless_sample_values.get(), + conditions.sampleMask.getEcalSampleMaskRecordEB(), + totalChannels, + offsetForInputs); + cudaCheck(cudaGetLastError()); + + int sharedBytes = EcalDataFrame::MAXSAMPLES * nchannels_per_block * 4 * sizeof(SampleVector::Scalar); + auto const threads_nullhypot = threads_1d; + auto const blocks_nullhypot = blocks_1d; + kernel_time_compute_nullhypot<<>>( + scratch.sample_values.get(), + scratch.sample_value_errors.get(), + scratch.useless_sample_values.get(), + scratch.chi2sNullHypot.get(), + scratch.sum0sNullHypot.get(), + scratch.sumAAsNullHypot.get(), + totalChannels); + cudaCheck(cudaGetLastError()); + + unsigned int nchannels_per_block_makeratio = 10; + unsigned int threads_makeratio = 45 * nchannels_per_block_makeratio; + unsigned int blocks_makeratio = threads_makeratio > 45 * totalChannels + ? 1 + : (totalChannels * 45 + threads_makeratio - 1) / threads_makeratio; + int sharedBytesMakeRatio = 5 * threads_makeratio * sizeof(SampleVector::Scalar); + kernel_time_compute_makeratio<<>>( + scratch.sample_values.get(), + scratch.sample_value_errors.get(), + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.ids.get(), + scratch.useless_sample_values.get(), + scratch.pedestal_nums.get(), + configParameters.amplitudeFitParametersEB, + configParameters.amplitudeFitParametersEE, + configParameters.timeFitParametersEB, + configParameters.timeFitParametersEE, + scratch.sumAAsNullHypot.get(), + scratch.sum0sNullHypot.get(), + scratch.tMaxAlphaBetas.get(), + scratch.tMaxErrorAlphaBetas.get(), + scratch.accTimeMax.get(), + scratch.accTimeWgt.get(), + scratch.tcState.get(), + configParameters.timeFitParametersSizeEB, + configParameters.timeFitParametersSizeEE, + configParameters.timeFitLimitsFirstEB, + configParameters.timeFitLimitsFirstEE, + configParameters.timeFitLimitsSecondEB, + configParameters.timeFitLimitsSecondEE, + totalChannels, + offsetForInputs); + cudaCheck(cudaGetLastError()); + + auto const threads_findamplchi2 = threads_1d; + auto const blocks_findamplchi2 = blocks_1d; + int const sharedBytesFindAmplChi2 = 2 * threads_findamplchi2 * sizeof(SampleVector::Scalar); + kernel_time_compute_findamplchi2_and_finish<<>>(scratch.sample_values.get(), + scratch.sample_value_errors.get(), + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.ids.get(), + scratch.useless_sample_values.get(), + scratch.tMaxAlphaBetas.get(), + scratch.tMaxErrorAlphaBetas.get(), + scratch.accTimeMax.get(), + scratch.accTimeWgt.get(), + configParameters.amplitudeFitParametersEB, + configParameters.amplitudeFitParametersEE, + scratch.sumAAsNullHypot.get(), + scratch.sum0sNullHypot.get(), + scratch.chi2sNullHypot.get(), + scratch.tcState.get(), + scratch.ampMaxAlphaBeta.get(), + scratch.ampMaxError.get(), + scratch.timeMax.get(), + scratch.timeError.get(), + totalChannels, + offsetForInputs); + cudaCheck(cudaGetLastError()); + + auto const threads_timecorr = 32; + auto const blocks_timecorr = + threads_timecorr > totalChannels ? 1 : (totalChannels + threads_timecorr - 1) / threads_timecorr; + kernel_time_correction_and_finalize<<>>( + eventOutputGPU.recHitsEB.amplitude.get(), + eventOutputGPU.recHitsEE.amplitude.get(), + eventInputGPU.ebDigis.data.get(), + eventInputGPU.ebDigis.ids.get(), + eventInputGPU.eeDigis.data.get(), + eventInputGPU.eeDigis.ids.get(), + conditions.timeBiasCorrections.EBTimeCorrAmplitudeBins, + conditions.timeBiasCorrections.EETimeCorrAmplitudeBins, + conditions.timeBiasCorrections.EBTimeCorrShiftBins, + conditions.timeBiasCorrections.EETimeCorrShiftBins, + scratch.timeMax.get(), + scratch.timeError.get(), + conditions.pedestals.rms_x12, + conditions.timeCalibConstants.values, + eventOutputGPU.recHitsEB.jitter.get(), + eventOutputGPU.recHitsEE.jitter.get(), + eventOutputGPU.recHitsEB.jitterError.get(), + eventOutputGPU.recHitsEE.jitterError.get(), + eventOutputGPU.recHitsEB.flags.get(), + eventOutputGPU.recHitsEE.flags.get(), + conditions.timeBiasCorrections.EBTimeCorrAmplitudeBinsSize, + conditions.timeBiasCorrections.EETimeCorrAmplitudeBinsSize, + configParameters.timeConstantTermEB, + configParameters.timeConstantTermEE, + conditions.timeOffsetConstant.getEBValue(), + conditions.timeOffsetConstant.getEEValue(), + configParameters.timeNconstEB, + configParameters.timeNconstEE, + configParameters.amplitudeThreshEB, + configParameters.amplitudeThreshEE, + configParameters.outOfTimeThreshG12pEB, + configParameters.outOfTimeThreshG12pEE, + configParameters.outOfTimeThreshG12mEB, + configParameters.outOfTimeThreshG12mEE, + configParameters.outOfTimeThreshG61pEB, + configParameters.outOfTimeThreshG61pEE, + configParameters.outOfTimeThreshG61mEB, + configParameters.outOfTimeThreshG61mEE, + offsetForHashes, + offsetForInputs, + totalChannels); + cudaCheck(cudaGetLastError()); + } + } + + } // namespace multifit +} // namespace ecal diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitMultiFitAlgoGPU.h b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitMultiFitAlgoGPU.h new file mode 100644 index 0000000000000..c84047a8bf8e7 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitMultiFitAlgoGPU.h @@ -0,0 +1,23 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_EcalUncalibRecHitMultiFitAlgoGPU_h +#define RecoLocalCalo_EcalRecProducers_plugins_EcalUncalibRecHitMultiFitAlgoGPU_h + +#include + +#include + +#include "DeclsForKernels.h" + +namespace ecal { + namespace multifit { + + void entryPoint(EventInputDataGPU const&, + EventOutputDataGPU&, + EventDataForScratchGPU&, + ConditionsProducts const&, + ConfigurationParameters const&, + cudaStream_t); + + } // namespace multifit +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_EcalUncalibRecHitMultiFitAlgoGPU_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitProducerGPU.cc b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitProducerGPU.cc new file mode 100644 index 0000000000000..a321f35144c39 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EcalUncalibRecHitProducerGPU.cc @@ -0,0 +1,279 @@ +#include "CUDADataFormats/EcalRecHitSoA/interface/EcalUncalibratedRecHit.h" +#include "CondFormats/DataRecord/interface/EcalGainRatiosRcd.h" +#include "CondFormats/DataRecord/interface/EcalPedestalsRcd.h" +#include "CondFormats/DataRecord/interface/EcalPulseCovariancesRcd.h" +#include "CondFormats/DataRecord/interface/EcalPulseShapesRcd.h" +#include "CondFormats/DataRecord/interface/EcalSampleMaskRcd.h" +#include "CondFormats/DataRecord/interface/EcalSamplesCorrelationRcd.h" +#include "CondFormats/DataRecord/interface/EcalTimeBiasCorrectionsRcd.h" +#include "CondFormats/DataRecord/interface/EcalTimeCalibConstantsRcd.h" +#include "CondFormats/DataRecord/interface/EcalTimeOffsetConstantRcd.h" +#include "CondFormats/EcalObjects/interface/EcalTimeOffsetConstant.h" +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "HeterogeneousCore/CUDACore/interface/JobConfigurationGPURecord.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalGainRatiosGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalMultifitParametersGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPedestalsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseCovariancesGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalPulseShapesGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalSamplesCorrelationGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeBiasCorrectionsGPU.h" +#include "RecoLocalCalo/EcalRecAlgos/interface/EcalTimeCalibConstantsGPU.h" + +#include "Common.h" +#include "DeclsForKernels.h" +#include "EcalUncalibRecHitMultiFitAlgoGPU.h" + +class EcalUncalibRecHitProducerGPU : public edm::stream::EDProducer { +public: + explicit EcalUncalibRecHitProducerGPU(edm::ParameterSet const& ps); + ~EcalUncalibRecHitProducerGPU() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + using InputProduct = cms::cuda::Product>; + const edm::EDGetTokenT digisTokenEB_, digisTokenEE_; + using OutputProduct = cms::cuda::Product>; + const edm::EDPutTokenT recHitsTokenEB_, recHitsTokenEE_; + + // conditions tokens + const edm::ESGetToken pedestalsToken_; + const edm::ESGetToken gainRatiosToken_; + const edm::ESGetToken pulseShapesToken_; + const edm::ESGetToken pulseCovariancesToken_; + const edm::ESGetToken samplesCorrelationToken_; + const edm::ESGetToken timeBiasCorrectionsToken_; + const edm::ESGetToken timeCalibConstantsToken_; + const edm::ESGetToken sampleMaskToken_; + const edm::ESGetToken timeOffsetConstantToken_; + const edm::ESGetToken multifitParametersToken_; + + // configuration parameters + ecal::multifit::ConfigurationParameters configParameters_; + + // event data + ecal::multifit::EventOutputDataGPU eventOutputDataGPU_; + + cms::cuda::ContextState cudaState_; + + uint32_t neb_, nee_; +}; + +void EcalUncalibRecHitProducerGPU::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("digisLabelEB", edm::InputTag("ecalRawToDigiGPU", "ebDigis")); + desc.add("digisLabelEE", edm::InputTag("ecalRawToDigiGPU", "eeDigis")); + + desc.add("recHitsLabelEB", "EcalUncalibRecHitsEB"); + desc.add("recHitsLabelEE", "EcalUncalibRecHitsEE"); + + desc.add("EBtimeFitLimits_Lower", 0.2); + desc.add("EBtimeFitLimits_Upper", 1.4); + desc.add("EEtimeFitLimits_Lower", 0.2); + desc.add("EEtimeFitLimits_Upper", 1.4); + desc.add("EBtimeConstantTerm", .6); + desc.add("EEtimeConstantTerm", 1.0); + desc.add("EBtimeNconst", 28.5); + desc.add("EEtimeNconst", 31.8); + desc.add("outOfTimeThresholdGain12pEB", 5); + desc.add("outOfTimeThresholdGain12mEB", 5); + desc.add("outOfTimeThresholdGain61pEB", 5); + desc.add("outOfTimeThresholdGain61mEB", 5); + desc.add("outOfTimeThresholdGain12pEE", 1000); + desc.add("outOfTimeThresholdGain12mEE", 1000); + desc.add("outOfTimeThresholdGain61pEE", 1000); + desc.add("outOfTimeThresholdGain61mEE", 1000); + desc.add("amplitudeThresholdEB", 10); + desc.add("amplitudeThresholdEE", 10); + desc.add("maxNumberHitsEB", 61200); + desc.add("maxNumberHitsEE", 14648); + desc.addUntracked>("kernelMinimizeThreads", {32, 1, 1}); + desc.add("shouldRunTimingComputation", true); + confDesc.addWithDefaultLabel(desc); +} + +EcalUncalibRecHitProducerGPU::EcalUncalibRecHitProducerGPU(const edm::ParameterSet& ps) + : digisTokenEB_{consumes(ps.getParameter("digisLabelEB"))}, + digisTokenEE_{consumes(ps.getParameter("digisLabelEE"))}, + recHitsTokenEB_{produces(ps.getParameter("recHitsLabelEB"))}, + recHitsTokenEE_{produces(ps.getParameter("recHitsLabelEE"))}, + pedestalsToken_{esConsumes()}, + gainRatiosToken_{esConsumes()}, + pulseShapesToken_{esConsumes()}, + pulseCovariancesToken_{esConsumes()}, + samplesCorrelationToken_{esConsumes()}, + timeBiasCorrectionsToken_{esConsumes()}, + timeCalibConstantsToken_{esConsumes()}, + sampleMaskToken_{esConsumes()}, + timeOffsetConstantToken_{esConsumes()}, + multifitParametersToken_{esConsumes()} { + std::pair EBtimeFitLimits, EEtimeFitLimits; + EBtimeFitLimits.first = ps.getParameter("EBtimeFitLimits_Lower"); + EBtimeFitLimits.second = ps.getParameter("EBtimeFitLimits_Upper"); + EEtimeFitLimits.first = ps.getParameter("EEtimeFitLimits_Lower"); + EEtimeFitLimits.second = ps.getParameter("EEtimeFitLimits_Upper"); + + auto EBtimeConstantTerm = ps.getParameter("EBtimeConstantTerm"); + auto EEtimeConstantTerm = ps.getParameter("EEtimeConstantTerm"); + auto EBtimeNconst = ps.getParameter("EBtimeNconst"); + auto EEtimeNconst = ps.getParameter("EEtimeNconst"); + + auto outOfTimeThreshG12pEB = ps.getParameter("outOfTimeThresholdGain12pEB"); + auto outOfTimeThreshG12mEB = ps.getParameter("outOfTimeThresholdGain12mEB"); + auto outOfTimeThreshG61pEB = ps.getParameter("outOfTimeThresholdGain61pEB"); + auto outOfTimeThreshG61mEB = ps.getParameter("outOfTimeThresholdGain61mEB"); + auto outOfTimeThreshG12pEE = ps.getParameter("outOfTimeThresholdGain12pEE"); + auto outOfTimeThreshG12mEE = ps.getParameter("outOfTimeThresholdGain12mEE"); + auto outOfTimeThreshG61pEE = ps.getParameter("outOfTimeThresholdGain61pEE"); + auto outOfTimeThreshG61mEE = ps.getParameter("outOfTimeThresholdGain61mEE"); + auto amplitudeThreshEB = ps.getParameter("amplitudeThresholdEB"); + auto amplitudeThreshEE = ps.getParameter("amplitudeThresholdEE"); + + // max number of digis to allocate for + configParameters_.maxNumberHitsEB = ps.getParameter("maxNumberHitsEB"); + configParameters_.maxNumberHitsEE = ps.getParameter("maxNumberHitsEE"); + + // switch to run timing computation kernels + configParameters_.shouldRunTimingComputation = ps.getParameter("shouldRunTimingComputation"); + + // minimize kernel launch conf + auto threadsMinimize = ps.getUntrackedParameter>("kernelMinimizeThreads"); + configParameters_.kernelMinimizeThreads[0] = threadsMinimize[0]; + configParameters_.kernelMinimizeThreads[1] = threadsMinimize[1]; + configParameters_.kernelMinimizeThreads[2] = threadsMinimize[2]; + + // + // configuration and physics parameters: done once + // assume there is a single device + // use sync copying + // + + // time fit parameters and limits + configParameters_.timeFitLimitsFirstEB = EBtimeFitLimits.first; + configParameters_.timeFitLimitsSecondEB = EBtimeFitLimits.second; + configParameters_.timeFitLimitsFirstEE = EEtimeFitLimits.first; + configParameters_.timeFitLimitsSecondEE = EEtimeFitLimits.second; + + // time constant terms + configParameters_.timeConstantTermEB = EBtimeConstantTerm; + configParameters_.timeConstantTermEE = EEtimeConstantTerm; + + // time N const + configParameters_.timeNconstEB = EBtimeNconst; + configParameters_.timeNconstEE = EEtimeNconst; + + // amplitude threshold for time flags + configParameters_.amplitudeThreshEB = amplitudeThreshEB; + configParameters_.amplitudeThreshEE = amplitudeThreshEE; + + // out of time thresholds gain-dependent + configParameters_.outOfTimeThreshG12pEB = outOfTimeThreshG12pEB; + configParameters_.outOfTimeThreshG12pEE = outOfTimeThreshG12pEE; + configParameters_.outOfTimeThreshG61pEB = outOfTimeThreshG61pEB; + configParameters_.outOfTimeThreshG61pEE = outOfTimeThreshG61pEE; + configParameters_.outOfTimeThreshG12mEB = outOfTimeThreshG12mEB; + configParameters_.outOfTimeThreshG12mEE = outOfTimeThreshG12mEE; + configParameters_.outOfTimeThreshG61mEB = outOfTimeThreshG61mEB; + configParameters_.outOfTimeThreshG61mEE = outOfTimeThreshG61mEE; +} + +EcalUncalibRecHitProducerGPU::~EcalUncalibRecHitProducerGPU() {} + +void EcalUncalibRecHitProducerGPU::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder holder) { + // cuda products + auto const& ebDigisProduct = event.get(digisTokenEB_); + auto const& eeDigisProduct = event.get(digisTokenEE_); + + // raii + cms::cuda::ScopedContextAcquire ctx{ebDigisProduct, std::move(holder), cudaState_}; + + // get actual obj + auto const& ebDigis = ctx.get(ebDigisProduct); + auto const& eeDigis = ctx.get(eeDigisProduct); + ecal::multifit::EventInputDataGPU inputDataGPU{ebDigis, eeDigis}; + neb_ = ebDigis.size; + nee_ = eeDigis.size; + + if ((neb_ > configParameters_.maxNumberHitsEB) || (nee_ > configParameters_.maxNumberHitsEE)) { + edm::LogError("EcalUncalibRecHitProducerGPU") + << "max number of channels exceeded. See options 'maxNumberHitsEB and maxNumberHitsEE' "; + } + + // conditions + auto const& timeCalibConstantsData = setup.getData(timeCalibConstantsToken_); + auto const& sampleMaskData = setup.getData(sampleMaskToken_); + auto const& timeOffsetConstantData = setup.getData(timeOffsetConstantToken_); + auto const& multifitParametersData = setup.getData(multifitParametersToken_); + + auto const& pedestals = setup.getData(pedestalsToken_).getProduct(ctx.stream()); + auto const& gainRatios = setup.getData(gainRatiosToken_).getProduct(ctx.stream()); + auto const& pulseShapes = setup.getData(pulseShapesToken_).getProduct(ctx.stream()); + auto const& pulseCovariances = setup.getData(pulseCovariancesToken_).getProduct(ctx.stream()); + auto const& samplesCorrelation = setup.getData(samplesCorrelationToken_).getProduct(ctx.stream()); + auto const& timeBiasCorrections = setup.getData(timeBiasCorrectionsToken_).getProduct(ctx.stream()); + auto const& timeCalibConstants = timeCalibConstantsData.getProduct(ctx.stream()); + auto const& multifitParameters = multifitParametersData.getProduct(ctx.stream()); + + // assign ptrs/values: this is done not to change how things look downstream + configParameters_.amplitudeFitParametersEB = multifitParameters.amplitudeFitParametersEB; + configParameters_.amplitudeFitParametersEE = multifitParameters.amplitudeFitParametersEE; + configParameters_.timeFitParametersEB = multifitParameters.timeFitParametersEB; + configParameters_.timeFitParametersEE = multifitParameters.timeFitParametersEE; + configParameters_.timeFitParametersSizeEB = multifitParametersData.getValues()[2].get().size(); + configParameters_.timeFitParametersSizeEE = multifitParametersData.getValues()[3].get().size(); + + // bundle up conditions + ecal::multifit::ConditionsProducts conditions{pedestals, + gainRatios, + pulseShapes, + pulseCovariances, + samplesCorrelation, + timeBiasCorrections, + timeCalibConstants, + sampleMaskData, + timeOffsetConstantData, + timeCalibConstantsData.getOffset(), + multifitParameters}; + + // dev mem + eventOutputDataGPU_.allocate(configParameters_, ctx.stream()); + + // scratch mem + ecal::multifit::EventDataForScratchGPU eventDataForScratchGPU; + eventDataForScratchGPU.allocate(configParameters_, ctx.stream()); + + // + // schedule algorithms + // + ecal::multifit::entryPoint( + inputDataGPU, eventOutputDataGPU_, eventDataForScratchGPU, conditions, configParameters_, ctx.stream()); +} + +void EcalUncalibRecHitProducerGPU::produce(edm::Event& event, edm::EventSetup const& setup) { + //DurationMeasurer timer{std::string{"produce duration"}}; + cms::cuda::ScopedContextProduce ctx{cudaState_}; + + // set the size of eb and ee + eventOutputDataGPU_.recHitsEB.size = neb_; + eventOutputDataGPU_.recHitsEE.size = nee_; + + // put into the event + ctx.emplace(event, recHitsTokenEB_, std::move(eventOutputDataGPU_.recHitsEB)); + ctx.emplace(event, recHitsTokenEE_, std::move(eventOutputDataGPU_.recHitsEE)); +} + +DEFINE_FWK_MODULE(EcalUncalibRecHitProducerGPU); diff --git a/RecoLocalCalo/EcalRecProducers/plugins/EigenMatrixTypes_gpu.h b/RecoLocalCalo/EcalRecProducers/plugins/EigenMatrixTypes_gpu.h new file mode 100644 index 0000000000000..bbf9cb0dbb5c9 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/EigenMatrixTypes_gpu.h @@ -0,0 +1,49 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_EigenMatrixTypes_gpu_h +#define RecoLocalCalo_EcalRecProducers_plugins_EigenMatrixTypes_gpu_h + +#include + +#include + +#include "CUDADataFormats/EcalRecHitSoA/interface/RecoTypes.h" + +namespace ecal { + namespace multifit { + + constexpr int SampleVectorSize = 10; + constexpr int FullSampleVectorSize = 19; + constexpr int PulseVectorSize = 12; + constexpr int NGains = 3; + + using data_type = ::ecal::reco::ComputationScalarType; + + typedef Eigen::Matrix PulseMatrixType; + typedef Eigen::Matrix BXVectorType; + using SampleMatrixD = Eigen::Matrix; + + typedef Eigen::Matrix SampleVector; + typedef Eigen::Matrix FullSampleVector; + typedef Eigen::Matrix PulseVector; + typedef Eigen::Matrix BXVector; + typedef Eigen::Matrix SampleGainVector; + typedef Eigen::Matrix SampleMatrix; + typedef Eigen::Matrix FullSampleMatrix; + typedef Eigen::Matrix PulseMatrix; + typedef Eigen::Matrix + SamplePulseMatrix; + typedef Eigen::LLT SampleDecompLLT; + typedef Eigen::LLT SampleDecompLLTD; + typedef Eigen::LLT PulseDecompLLT; + typedef Eigen::LDLT PulseDecompLDLT; + + typedef Eigen::Matrix SingleMatrix; + typedef Eigen::Matrix SingleVector; + + typedef std::array SampleMatrixGainArray; + + using PermutationMatrix = Eigen::PermutationMatrix; + + } // namespace multifit +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_EigenMatrixTypes_gpu_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/KernelHelpers.cu b/RecoLocalCalo/EcalRecProducers/plugins/KernelHelpers.cu new file mode 100644 index 0000000000000..5316ed87d6ecc --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/KernelHelpers.cu @@ -0,0 +1,308 @@ +#include "DataFormats/EcalDetId/interface/EBDetId.h" +#include "DataFormats/EcalDetId/interface/EEDetId.h" + +#include "KernelHelpers.h" + +namespace ecal { + namespace reconstruction { + + namespace internal { + + namespace barrel { + + __device__ __forceinline__ bool positiveZ(uint32_t id) { return id & 0x10000; } + + __device__ __forceinline__ uint32_t ietaAbs(uint32_t id) { return (id >> 9) & 0x7F; } + + __device__ __forceinline__ uint32_t iphi(uint32_t id) { return id & 0x1FF; } + + __device__ int dccFromSm(int ism) { + int iz = 1; + if (ism > 18) + iz = -1; + if (iz == -1) + ism -= 18; + int idcc = 9 + ism; + if (iz == +1) + idcc += 18; + return idcc; + } + + __device__ int sm(int ieta, int iphi) { + int iz = 1; + if (ieta < 0) + iz = -1; + ieta *= iz; + int iphi_ = iphi; + if (iphi_ > 360) + iphi_ -= 360; + int ism = (iphi_ - 1) / 20 + 1; + if (iz == -1) + ism += 18; + return ism; + } + + __device__ int dcc(int ieta, int iphi) { + int ism = sm(ieta, iphi); + return dccFromSm(ism); + } + + // + // ---- why on hell things are so complex and not simple ??? + // + + __device__ int lm_channel(int iX, int iY) { + static const int idx_[] = { + // clang-format off + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + 1, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 8, 8, 8, 8, // 3 + 1, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 8, 8, 8, 8, // 2 + 1, 3, 3, 3, 3, 5, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 9, // 1 + 1, 3, 3, 3, 3, 5, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 9 // 0 + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + // clang-format on + }; + + int il, ic, ii; + const int iym = 4; + const int ixm = 17; + int iX_ = iX + 1; + int iY_ = iY + 1; + il = iym - iY_; + ic = iX_ - 1; + ii = il * ixm + ic; + if (ii < 0 || ii > (int)(sizeof(idx_) / sizeof(int))) { + return -1; + }; + return idx_[ii]; + } + + __device__ int localCoord_x(int ieta, int iphi) { + int iz = 1; + if (ieta < 0) { + iz = -1; + } + ieta *= iz; + int ix = ieta - 1; + + return ix; + } + + __device__ int localCoord_y(int ieta, int iphi) { + int iz = 1; + if (ieta < 0) { + iz = -1; + } + int iphi_ = iphi; + if (iphi_ > 360) { + iphi_ -= 360; + } + int iy = (iphi_ - 1) % 20; + if (iz == -1) { + iy = 19 - iy; + } + + return iy; + } + + __device__ int lmmod(int ieta, int iphi) { + int ix = localCoord_x(ieta, iphi); + int iy = localCoord_y(ieta, iphi); + + return lm_channel(ix / 5, iy / 5); + } + + __device__ int side(int ieta, int iphi) { + int ilmmod = lmmod(ieta, iphi); + return (ilmmod % 2 == 0) ? 1 : 0; + } + + } // namespace barrel + + } // namespace internal + + __device__ uint32_t hashedIndexEB(uint32_t id) { + using namespace internal::barrel; + return (EBDetId::MAX_IETA + (positiveZ(id) ? ietaAbs(id) - 1 : -ietaAbs(id))) * EBDetId::MAX_IPHI + iphi(id) - 1; + } + + // + // https://cmssdt.cern.ch/lxr/source/CalibCalorimetry/EcalLaserAnalyzer/src/MEEBGeom.cc + // function: "lmr" + + __device__ int laser_monitoring_region_EB(uint32_t id) { + using namespace internal::barrel; + + int ieta; + if (positiveZ(id)) { + ieta = ietaAbs(id); + } else { + ieta = -ietaAbs(id); + } + + int idcc = dcc(ieta, (int)(iphi(id))); + int ism = idcc - 9; + + int iside = side(ieta, (int)(iphi(id))); + + return (1 + 2 * (ism - 1) + iside); + } + + namespace internal { + + namespace endcap { + + __device__ __forceinline__ uint32_t ix(uint32_t id) { return (id >> 7) & 0x7F; } + + __device__ __forceinline__ uint32_t iy(uint32_t id) { return id & 0x7F; } + + __device__ __forceinline__ bool positiveZ(uint32_t id) { return id & 0x4000; } + + // these constants come from EE Det Id + __constant__ const unsigned short kxf[] = { + 41, 51, 41, 51, 41, 51, 36, 51, 36, 51, 26, 51, 26, 51, 26, 51, 21, 51, 21, 51, 21, 51, 21, 51, 21, + 51, 16, 51, 16, 51, 14, 51, 14, 51, 14, 51, 14, 51, 14, 51, 9, 51, 9, 51, 9, 51, 9, 51, 9, 51, + 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 4, 51, 4, 51, 4, + 51, 4, 51, 4, 56, 1, 58, 1, 59, 1, 60, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, + 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 61, 1, 61, 1, 60, 1, 59, 1, 58, 4, 56, 4, 51, 4, + 51, 4, 51, 4, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, 6, 51, + 9, 51, 9, 51, 9, 51, 9, 51, 9, 51, 14, 51, 14, 51, 14, 51, 14, 51, 14, 51, 16, 51, 16, 51, 21, + 51, 21, 51, 21, 51, 21, 51, 21, 51, 26, 51, 26, 51, 26, 51, 36, 51, 36, 51, 41, 51, 41, 51, 41, 51}; + + __constant__ const unsigned short kdi[] = { + 0, 10, 20, 30, 40, 50, 60, 75, 90, 105, 120, 145, 170, 195, 220, 245, 270, + 300, 330, 360, 390, 420, 450, 480, 510, 540, 570, 605, 640, 675, 710, 747, 784, 821, + 858, 895, 932, 969, 1006, 1043, 1080, 1122, 1164, 1206, 1248, 1290, 1332, 1374, 1416, 1458, 1500, + 1545, 1590, 1635, 1680, 1725, 1770, 1815, 1860, 1905, 1950, 1995, 2040, 2085, 2130, 2175, 2220, 2265, + 2310, 2355, 2400, 2447, 2494, 2541, 2588, 2635, 2682, 2729, 2776, 2818, 2860, 2903, 2946, 2988, 3030, + 3071, 3112, 3152, 3192, 3232, 3272, 3311, 3350, 3389, 3428, 3467, 3506, 3545, 3584, 3623, 3662, 3701, + 3740, 3779, 3818, 3857, 3896, 3935, 3974, 4013, 4052, 4092, 4132, 4172, 4212, 4253, 4294, 4336, 4378, + 4421, 4464, 4506, 4548, 4595, 4642, 4689, 4736, 4783, 4830, 4877, 4924, 4969, 5014, 5059, 5104, 5149, + 5194, 5239, 5284, 5329, 5374, 5419, 5464, 5509, 5554, 5599, 5644, 5689, 5734, 5779, 5824, 5866, 5908, + 5950, 5992, 6034, 6076, 6118, 6160, 6202, 6244, 6281, 6318, 6355, 6392, 6429, 6466, 6503, 6540, 6577, + 6614, 6649, 6684, 6719, 6754, 6784, 6814, 6844, 6874, 6904, 6934, 6964, 6994, 7024, 7054, 7079, 7104, + 7129, 7154, 7179, 7204, 7219, 7234, 7249, 7264, 7274, 7284, 7294, 7304, 7314}; + + __device__ int quadrant(int iX, int iY) { + bool near = iX >= 11; + bool far = !near; + bool top = iY >= 11; + bool bot = !top; + + int iquad = 0; + if (near && top) + iquad = 1; + if (far && top) + iquad = 2; + if (far && bot) + iquad = 3; + if (near && bot) + iquad = 4; + + return iquad; + } + + __device__ int sector(int iX, int iY) { + // Y (towards the surface) + // T + // | + // | + // | + // o---------| X (towards center of LHC) + // + static const int idx_[] = { + // clang-format off + // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, // 20 + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, // 19 + 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 8, 0, 0, 0, // 18 + 0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 8, 8, 8, 0, 0, // 17 + 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 8, 8, 8, 8, 0, // 16 + 0, 2, 2, 2, 2, 2, 1, 1, 1, 1, 9, 9, 9, 9, 8, 8, 8, 8, 8, 0, // 15 + 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 9, 9, 9, 8, 8, 8, 8, 8, 8, 0, // 14 + 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // 13 + 3, 3, 2, 2, 2, 2, 2, 2, 2, 0, 0, 8, 8, 8, 8, 8, 8, 8, 7, 7, // 12 + 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 0, 0, 8, 7, 7, 7, 7, 7, 7, 7, // 11 + 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, // 10 + 3, 3, 3, 3, 3, 3, 3, 4, 4, 0, 0, 6, 6, 7, 7, 7, 7, 7, 7, 7, // 9 + 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, // 8 + 0, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 0, // 7 + 0, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 0, // 6 + 0, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 0, // 5 + 0, 0, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 0, 0, // 4 + 0, 0, 0, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 0, 0, 0, // 3 + 0, 0, 0, 0, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0 // 1 + // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + // clang-format on + }; + + int iym, ixm, il, ic, ii; + iym = 20; + ixm = 20; + int iX_ = iX; + int iY_ = iY; + il = iym - iY_; + ic = iX_ - 1; + ii = il * ixm + ic; + + if (ii < 0 || ii > (int)(sizeof(idx_) / sizeof(int)) || idx_[ii] == 0) { + return -1; + }; + return idx_[ii]; + } + + } // namespace endcap + + } // namespace internal + + __device__ uint32_t hashedIndexEE(uint32_t id) { + using namespace internal::endcap; + + const uint32_t jx(ix(id)); + const uint32_t jd(2 * (iy(id) - 1) + (jx - 1) / 50); + return ((positiveZ(id) ? EEDetId::kEEhalf : 0) + kdi[jd] + jx - kxf[jd]); + } + + // + // https://cmssdt.cern.ch/lxr/source/CalibCalorimetry/EcalLaserAnalyzer/src/MEEEGeom.cc + // https://github.com/cms-sw/cmssw/blob/master/CalibCalorimetry/EcalLaserCorrection/src/EcalLaserDbService.cc + // + + __device__ int laser_monitoring_region_EE(uint32_t id) { + using namespace internal::endcap; + + // SuperCrysCoord + uint32_t iX = (ix(id) - 1) / 5 + 1; + uint32_t iY = (iy(id) - 1) / 5 + 1; + + // Correct convention + // * @param iz iz/zside index: -1 for EE-, +1 for EE+ + // https://github.com/cms-sw/cmssw/blob/master/DataFormats/EcalDetId/interface/EEDetId.h#L68-L71 + // zside in https://github.com/cms-sw/cmssw/blob/master/CalibCalorimetry/EcalLaserCorrection/src/EcalLaserDbService.cc#L63 + // + int iz = positiveZ(id) ? 1 : -1; + + int iquad = quadrant(iX, iY); + int isect = sector(iX, iY); + if (isect < 0) + return -1; + + int ilmr = 0; + ilmr = isect - 6; + if (ilmr <= 0) + ilmr += 9; + if (ilmr == 9) + ilmr++; + if (ilmr == 8 && iquad == 4) + ilmr++; + if (iz == +1) + ilmr += 72; + else + ilmr += 82; + + return ilmr; + } + + } // namespace reconstruction +} // namespace ecal diff --git a/RecoLocalCalo/EcalRecProducers/plugins/KernelHelpers.h b/RecoLocalCalo/EcalRecProducers/plugins/KernelHelpers.h new file mode 100644 index 0000000000000..74c5b68d8e137 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/KernelHelpers.h @@ -0,0 +1,26 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_KernelHelpers_h +#define RecoLocalCalo_EcalRecProducers_plugins_KernelHelpers_h + +#include "DataFormats/CaloRecHit/interface/MultifitComputations.h" + +#include +#include +#include + +#include + +namespace ecal { + namespace reconstruction { + + __device__ uint32_t hashedIndexEB(uint32_t id); + + __device__ uint32_t hashedIndexEE(uint32_t id); + + __device__ int laser_monitoring_region_EB(uint32_t id); + + __device__ int laser_monitoring_region_EE(uint32_t id); + + } // namespace reconstruction +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_KernelHelpers_h diff --git a/RecoLocalCalo/EcalRecProducers/plugins/TimeComputationKernels.cu b/RecoLocalCalo/EcalRecProducers/plugins/TimeComputationKernels.cu new file mode 100644 index 0000000000000..9c2d2fc986c08 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/TimeComputationKernels.cu @@ -0,0 +1,1133 @@ +#include +#include + +#include + +#include "DataFormats/EcalDigi/interface/EcalDataFrame.h" +#include "DataFormats/EcalRecHit/interface/EcalUncalibratedRecHit.h" +#include "DataFormats/Math/interface/approx_exp.h" +#include "DataFormats/Math/interface/approx_log.h" +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" + +#include "Common.h" +#include "TimeComputationKernels.h" +#include "KernelHelpers.h" + +//#define DEBUG + +//#define ECAL_RECO_CUDA_DEBUG + +namespace ecal { + namespace multifit { + + __device__ __forceinline__ bool use_sample(unsigned int sample_mask, unsigned int sample) { + return sample_mask & (0x1 << (EcalDataFrame::MAXSAMPLES - (sample + 1))); + } + + __global__ void kernel_time_compute_nullhypot(SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + bool const* useless_sample_values, + SampleVector::Scalar* chi2s, + SampleVector::Scalar* sum0s, + SampleVector::Scalar* sumAAs, + const int nchannels) { + using ScalarType = SampleVector::Scalar; + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + int tx = threadIdx.x + blockDim.x * blockIdx.x; + int ltx = threadIdx.x; + int ch = tx / nsamples; + int nchannels_per_block = blockDim.x / nsamples; + + // threads that return here should not affect the __syncthreads() below since they have exitted the kernel + if (ch >= nchannels) + return; + + int sample = tx % nsamples; + + // shared mem inits + extern __shared__ char sdata[]; + char* s_sum0 = sdata; + SampleVector::Scalar* s_sum1 = reinterpret_cast(s_sum0 + nchannels_per_block * nsamples); + SampleVector::Scalar* s_sumA = s_sum1 + nchannels_per_block * nsamples; + SampleVector::Scalar* s_sumAA = s_sumA + nchannels_per_block * nsamples; + + // TODO make sure no div by 0 + const auto inv_error = + useless_sample_values[tx] ? 0.0 : 1.0 / (sample_value_errors[tx] * sample_value_errors[tx]); + const auto sample_value = sample_values[tx]; + s_sum0[ltx] = useless_sample_values[tx] ? 0 : 1; + s_sum1[ltx] = inv_error; + s_sumA[ltx] = sample_value * inv_error; + s_sumAA[ltx] = sample_value * sample_value * inv_error; + __syncthreads(); + + // 5 threads for [0, 4] samples + if (sample < 5) { + s_sum0[ltx] += s_sum0[ltx + 5]; + s_sum1[ltx] += s_sum1[ltx + 5]; + s_sumA[ltx] += s_sumA[ltx + 5]; + s_sumAA[ltx] += s_sumAA[ltx + 5]; + } + __syncthreads(); + + if (sample < 2) { + // note double counting of sample 3 + s_sum0[ltx] += s_sum0[ltx + 2] + s_sum0[ltx + 3]; + s_sum1[ltx] += s_sum1[ltx + 2] + s_sum1[ltx + 3]; + s_sumA[ltx] += s_sumA[ltx + 2] + s_sumA[ltx + 3]; + s_sumAA[ltx] += s_sumAA[ltx + 2] + s_sumAA[ltx + 3]; + } + __syncthreads(); + + if (sample == 0) { + // note, subtract to remove the double counting of sample == 3 + const auto sum0 = s_sum0[ltx] + s_sum0[ltx + 1] - s_sum0[ltx + 3]; + const auto sum1 = s_sum1[ltx] + s_sum1[ltx + 1] - s_sum1[ltx + 3]; + const auto sumA = s_sumA[ltx] + s_sumA[ltx + 1] - s_sumA[ltx + 3]; + const auto sumAA = s_sumAA[ltx] + s_sumAA[ltx + 1] - s_sumAA[ltx + 3]; + const auto chi2 = sum0 > 0 ? (sumAA - sumA * sumA / sum1) / sum0 : static_cast(0); + chi2s[ch] = chi2; + sum0s[ch] = sum0; + sumAAs[ch] = sumAA; + +#ifdef DEBUG_TC_NULLHYPOT + if (ch == 0) { + printf("chi2 = %f sum0 = %d sumAA = %f\n", chi2, static_cast(sum0), sumAA); + } +#endif + } + } + + constexpr float fast_expf(float x) { return unsafe_expf<6>(x); } + constexpr float fast_logf(float x) { return unsafe_logf<7>(x); } + + //#define DEBUG_TC_MAKERATIO + // + // launch ctx parameters are + // 45 threads per channel, X channels per block, Y blocks + // 45 comes from: 10 samples for i <- 0 to 9 and for j <- i+1 to 9 + // TODO: it might be much beter to use 32 threads per channel instead of 45 + // to simplify the synchronization + // + __global__ void kernel_time_compute_makeratio(SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + uint32_t const* dids_eb, + uint32_t const* dids_ee, + bool const* useless_sample_values, + char const* pedestal_nums, + ConfigurationParameters::type const* amplitudeFitParametersEB, + ConfigurationParameters::type const* amplitudeFitParametersEE, + ConfigurationParameters::type const* timeFitParametersEB, + ConfigurationParameters::type const* timeFitParametersEE, + SampleVector::Scalar const* sumAAsNullHypot, + SampleVector::Scalar const* sum0sNullHypot, + SampleVector::Scalar* tMaxAlphaBetas, + SampleVector::Scalar* tMaxErrorAlphaBetas, + SampleVector::Scalar* g_accTimeMax, + SampleVector::Scalar* g_accTimeWgt, + TimeComputationState* g_state, + unsigned const int timeFitParameters_sizeEB, + unsigned const int timeFitParameters_sizeEE, + ConfigurationParameters::type const timeFitLimits_firstEB, + ConfigurationParameters::type const timeFitLimits_firstEE, + ConfigurationParameters::type const timeFitLimits_secondEB, + ConfigurationParameters::type const timeFitLimits_secondEE, + const int nchannels, + uint32_t const offsetForInputs) { + using ScalarType = SampleVector::Scalar; + + // constants + constexpr int nthreads_per_channel = 45; // n=10, n(n-1)/2 + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + const int gtx = threadIdx.x + blockDim.x * blockIdx.x; + const int ch = gtx / nthreads_per_channel; + const int ltx = threadIdx.x % nthreads_per_channel; + const int ch_start = ch * nsamples; + const auto* dids = ch >= offsetForInputs ? dids_ee : dids_eb; + const int inputCh = ch >= offsetForInputs ? ch - offsetForInputs : ch; + + // remove inactive threads + // threads that return here should not affect the __syncthreads() below since they have exitted the kernel + if (ch >= nchannels) + return; + + const auto did = DetId{dids[inputCh]}; + const auto isBarrel = did.subdetId() == EcalBarrel; + const auto* amplitudeFitParameters = isBarrel ? amplitudeFitParametersEB : amplitudeFitParametersEE; + const auto* timeFitParameters = isBarrel ? timeFitParametersEB : timeFitParametersEE; + const auto timeFitParameters_size = isBarrel ? timeFitParameters_sizeEB : timeFitParameters_sizeEE; + const auto timeFitLimits_first = isBarrel ? timeFitLimits_firstEB : timeFitLimits_firstEE; + const auto timeFitLimits_second = isBarrel ? timeFitLimits_secondEB : timeFitLimits_secondEE; + + extern __shared__ char smem[]; + ScalarType* shr_chi2s = reinterpret_cast(smem); + ScalarType* shr_time_wgt = shr_chi2s + blockDim.x; + ScalarType* shr_time_max = shr_time_wgt + blockDim.x; + ScalarType* shrTimeMax = shr_time_max + blockDim.x; + ScalarType* shrTimeWgt = shrTimeMax + blockDim.x; + + // map tx -> (sample_i, sample_j) + int sample_i, sample_j = 0; + if (ltx >= 0 && ltx <= 8) { + sample_i = 0; + sample_j = 1 + ltx; + } else if (ltx <= 16) { + sample_i = 1; + sample_j = 2 + ltx - 9; + } else if (ltx <= 23) { + sample_i = 2; + sample_j = 3 + ltx - 17; + } else if (ltx <= 29) { + sample_i = 3; + sample_j = 4 + ltx - 24; + } else if (ltx <= 34) { + sample_i = 4; + sample_j = 5 + ltx - 30; + } else if (ltx <= 38) { + sample_i = 5; + sample_j = 6 + ltx - 35; + } else if (ltx <= 41) { + sample_i = 6; + sample_j = 7 + ltx - 39; + } else if (ltx <= 43) { + sample_i = 7; + sample_j = 8 + ltx - 42; + } else if (ltx <= 44) { + sample_i = 8; + sample_j = 9; + } else + assert(false); + + const auto tx_i = ch_start + sample_i; + const auto tx_j = ch_start + sample_j; + + // + // note, given the way we partition the block, with 45 threads per channel + // we will end up with inactive threads which need to be dragged along + // through the synching point + // + bool const condForUselessSamples = useless_sample_values[tx_i] || useless_sample_values[tx_j] || + sample_values[tx_i] <= 1 || sample_values[tx_j] <= 1; + + // + // see cpu implementation for explanation + // + ScalarType chi2 = std::numeric_limits::max(); + ScalarType tmax = 0; + ScalarType tmaxerr = 0; + shrTimeMax[threadIdx.x] = 0; + shrTimeWgt[threadIdx.x] = 0; + bool internalCondForSkipping1 = true; + bool internalCondForSkipping2 = true; + if (!condForUselessSamples) { + const auto rtmp = sample_values[tx_i] / sample_values[tx_j]; + const auto invampl_i = 1.0 / sample_values[tx_i]; + const auto relErr2_i = sample_value_errors[tx_i] * sample_value_errors[tx_i] * invampl_i * invampl_i; + const auto invampl_j = 1.0 / sample_values[tx_j]; + const auto relErr2_j = sample_value_errors[tx_j] * sample_value_errors[tx_j] * invampl_j * invampl_j; + const auto err1 = rtmp * rtmp * (relErr2_i + relErr2_j); + auto err2 = sample_value_errors[tx_j] * (sample_values[tx_i] - sample_values[tx_j]) * (invampl_j * invampl_j); + // TODO non-divergent branch for a block if each block has 1 channel + // otherwise non-divergent for groups of 45 threads + // at this point, pedestal_nums[ch] can be either 0, 1 or 2 + if (pedestal_nums[ch] == 2) + err2 *= err2 * 0.5; + const auto err3 = (0.289 * 0.289) * (invampl_j * invampl_j); + const auto total_error = std::sqrt(err1 + err2 + err3); + + const auto alpha = amplitudeFitParameters[0]; + const auto beta = amplitudeFitParameters[1]; + const auto alphabeta = alpha * beta; + const auto invalphabeta = 1.0 / alphabeta; + + // variables instead of a struct + const auto ratio_index = sample_i; + const auto ratio_step = sample_j - sample_i; + const auto ratio_value = rtmp; + const auto ratio_error = total_error; + + const auto rlim_i_j = fast_expf(static_cast(sample_j - sample_i) / beta) - 0.001; + internalCondForSkipping1 = !(total_error < 1.0 && rtmp > 0.001 && rtmp < rlim_i_j); + if (!internalCondForSkipping1) { + // + // precompute. + // in cpu version this was done conditionally + // however easier to do it here (precompute) and then just filter out + // if not needed + // + const auto l_timeFitLimits_first = timeFitLimits_first; + const auto l_timeFitLimits_second = timeFitLimits_second; + if (ratio_step == 1 && ratio_value >= l_timeFitLimits_first && ratio_value <= l_timeFitLimits_second) { + const auto time_max_i = static_cast(ratio_index); + auto u = timeFitParameters[timeFitParameters_size - 1]; + CMS_UNROLL_LOOP + for (int k = timeFitParameters_size - 2; k >= 0; k--) + u = u * ratio_value + timeFitParameters[k]; + + auto du = (timeFitParameters_size - 1) * (timeFitParameters[timeFitParameters_size - 1]); + for (int k = timeFitParameters_size - 2; k >= 1; k--) + du = du * ratio_value + k * timeFitParameters[k]; + + const auto error2 = ratio_error * ratio_error * du * du; + const auto time_max = error2 > 0 ? (time_max_i - u) / error2 : static_cast(0); + const auto time_wgt = error2 > 0 ? 1.0 / error2 : static_cast(0); + + // store into shared mem + // note, this name is essentially identical to the one used + // below. + shrTimeMax[threadIdx.x] = error2 > 0 ? time_max : 0; + shrTimeWgt[threadIdx.x] = error2 > 0 ? time_wgt : 0; + } else { + shrTimeMax[threadIdx.x] = 0; + shrTimeWgt[threadIdx.x] = 0; + } + + // continue with ratios + const auto stepOverBeta = static_cast(ratio_step) / beta; + const auto offset = static_cast(ratio_index) + alphabeta; + const auto rmin = std::max(ratio_value - ratio_error, 0.001); + const auto rmax = std::min(ratio_value + ratio_error, + fast_expf(static_cast(ratio_step) / beta) - 0.001); + const auto time1 = offset - ratio_step / (fast_expf((stepOverBeta - fast_logf(rmin)) / alpha) - 1.0); + const auto time2 = offset - ratio_step / (fast_expf((stepOverBeta - fast_logf(rmax)) / alpha) - 1.0); + + // set these guys + tmax = 0.5 * (time1 + time2); + tmaxerr = 0.5 * std::sqrt((time1 - time2) * (time1 - time2)); +#ifdef DEBUG_TC_MAKERATIO + if (ch == 1 || ch == 0) + printf("ch = %d ltx = %d tmax = %f tmaxerr = %f time1 = %f time2 = %f offset = %f rmin = %f rmax = %f\n", + ch, + ltx, + tmax, + tmaxerr, + time1, + time2, + offset, + rmin, + rmax); +#endif + + SampleVector::Scalar sumAf = 0; + SampleVector::Scalar sumff = 0; + const int itmin = std::max(-1, static_cast(std::floor(tmax - alphabeta))); + auto loffset = (static_cast(itmin) - tmax) * invalphabeta; + // TODO: data dependence + for (int it = itmin + 1; it < nsamples; it++) { + loffset += invalphabeta; + if (useless_sample_values[ch_start + it]) + continue; + const auto inverr2 = 1.0 / (sample_value_errors[ch_start + it] * sample_value_errors[ch_start + it]); + const auto term1 = 1.0 + loffset; + const auto f = (term1 > 1e-6) ? fast_expf(alpha * (fast_logf(term1) - loffset)) : 0; + sumAf += sample_values[ch_start + it] * (f * inverr2); + sumff += f * (f * inverr2); + } + + const auto sumAA = sumAAsNullHypot[ch]; + const auto sum0 = sum0sNullHypot[ch]; + chi2 = sumAA; + // TODO: sum0 can not be 0 below, need to introduce the check upfront + if (sumff > 0) { + chi2 = sumAA - sumAf * (sumAf / sumff); + } + chi2 /= sum0; + +#ifdef DEBUG_TC_MAKERATIO + if (ch == 1 || ch == 0) + printf("ch = %d ltx = %d sumAf = %f sumff = %f sumAA = %f sum0 = %d tmax = %f tmaxerr = %f chi2 = %f\n", + ch, + ltx, + sumAf, + sumff, + sumAA, + static_cast(sum0), + tmax, + tmaxerr, + chi2); +#endif + + if (chi2 > 0 && tmax > 0 && tmaxerr > 0) + internalCondForSkipping2 = false; + else + chi2 = std::numeric_limits::max(); + } + } + + // store into smem + shr_chi2s[threadIdx.x] = chi2; + __syncthreads(); + + // find min chi2 - quite crude for now + // TODO validate/check + char iter = nthreads_per_channel / 2 + nthreads_per_channel % 2; + bool oddElements = nthreads_per_channel % 2; + CMS_UNROLL_LOOP + while (iter >= 1) { + if (ltx < iter) + // for odd ns, the last guy will just store itself + // exception is for ltx == 0 and iter==1 + shr_chi2s[threadIdx.x] = oddElements && (ltx == iter - 1 && ltx > 0) + ? shr_chi2s[threadIdx.x] + : std::min(shr_chi2s[threadIdx.x], shr_chi2s[threadIdx.x + iter]); + __syncthreads(); + oddElements = iter % 2; + iter = iter == 1 ? iter / 2 : iter / 2 + iter % 2; + } + + // filter out inactive or useless samples threads + if (!condForUselessSamples && !internalCondForSkipping1 && !internalCondForSkipping2) { + // min chi2, now compute weighted average of tmax measurements + // see cpu version for more explanation + const auto chi2min = shr_chi2s[threadIdx.x - ltx]; + const auto chi2Limit = chi2min + 1.0; + const auto inverseSigmaSquared = chi2 < chi2Limit ? 1.0 / (tmaxerr * tmaxerr) : 0.0; + +#ifdef DEBUG_TC_MAKERATIO + if (ch == 1 || ch == 0) + printf("ch = %d ltx = %d chi2min = %f chi2Limit = %f inverseSigmaSquared = %f\n", + ch, + ltx, + chi2min, + chi2Limit, + inverseSigmaSquared); +#endif + + // store into shared mem and run reduction + // TODO: check if cooperative groups would be better + // TODO: check if shuffling intrinsics are better + shr_time_wgt[threadIdx.x] = inverseSigmaSquared; + shr_time_max[threadIdx.x] = tmax * inverseSigmaSquared; + } else { + shr_time_wgt[threadIdx.x] = 0; + shr_time_max[threadIdx.x] = 0; + } + __syncthreads(); + + // reduce to compute time_max and time_wgt + iter = nthreads_per_channel / 2 + nthreads_per_channel % 2; + oddElements = nthreads_per_channel % 2; + CMS_UNROLL_LOOP + while (iter >= 1) { + if (ltx < iter) { + shr_time_wgt[threadIdx.x] = oddElements && (ltx == iter - 1 && ltx > 0) + ? shr_time_wgt[threadIdx.x] + : shr_time_wgt[threadIdx.x] + shr_time_wgt[threadIdx.x + iter]; + shr_time_max[threadIdx.x] = oddElements && (ltx == iter - 1 && ltx > 0) + ? shr_time_max[threadIdx.x] + : shr_time_max[threadIdx.x] + shr_time_max[threadIdx.x + iter]; + shrTimeMax[threadIdx.x] = oddElements && (ltx == iter - 1 && ltx > 0) + ? shrTimeMax[threadIdx.x] + : shrTimeMax[threadIdx.x] + shrTimeMax[threadIdx.x + iter]; + shrTimeWgt[threadIdx.x] = oddElements && (ltx == iter - 1 && ltx > 0) + ? shrTimeWgt[threadIdx.x] + : shrTimeWgt[threadIdx.x] + shrTimeWgt[threadIdx.x + iter]; + } + + __syncthreads(); + oddElements = iter % 2; + iter = iter == 1 ? iter / 2 : iter / 2 + iter % 2; + } + + // load from shared memory the 0th guy (will contain accumulated values) + // compute + // store into global mem + if (ltx == 0) { + const auto tmp_time_max = shr_time_max[threadIdx.x]; + const auto tmp_time_wgt = shr_time_wgt[threadIdx.x]; + + // we are done if there number of time ratios is 0 + if (tmp_time_wgt == 0 && tmp_time_max == 0) { + g_state[ch] = TimeComputationState::Finished; + return; + } + + // no div by 0 + const auto tMaxAlphaBeta = tmp_time_max / tmp_time_wgt; + const auto tMaxErrorAlphaBeta = 1.0 / std::sqrt(tmp_time_wgt); + + tMaxAlphaBetas[ch] = tMaxAlphaBeta; + tMaxErrorAlphaBetas[ch] = tMaxErrorAlphaBeta; + g_accTimeMax[ch] = shrTimeMax[threadIdx.x]; + g_accTimeWgt[ch] = shrTimeWgt[threadIdx.x]; + g_state[ch] = TimeComputationState::NotFinished; + +#ifdef DEBUG_TC_MAKERATIO + printf("ch = %d time_max = %f time_wgt = %f\n", ch, tmp_time_max, tmp_time_wgt); + printf("ch = %d tMaxAlphaBeta = %f tMaxErrorAlphaBeta = %f timeMax = %f timeWgt = %f\n", + ch, + tMaxAlphaBeta, + tMaxErrorAlphaBeta, + shrTimeMax[threadIdx.x], + shrTimeWgt[threadIdx.x]); +#endif + } + } + + /// launch ctx parameters are + /// 10 threads per channel, N channels per block, Y blocks + /// TODO: do we need to keep the state around or can be removed?! + //#define DEBUG_FINDAMPLCHI2_AND_FINISH + __global__ void kernel_time_compute_findamplchi2_and_finish( + SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + uint32_t const* dids_eb, + uint32_t const* dids_ee, + bool const* useless_samples, + SampleVector::Scalar const* g_tMaxAlphaBeta, + SampleVector::Scalar const* g_tMaxErrorAlphaBeta, + SampleVector::Scalar const* g_accTimeMax, + SampleVector::Scalar const* g_accTimeWgt, + ConfigurationParameters::type const* amplitudeFitParametersEB, + ConfigurationParameters::type const* amplitudeFitParametersEE, + SampleVector::Scalar const* sumAAsNullHypot, + SampleVector::Scalar const* sum0sNullHypot, + SampleVector::Scalar const* chi2sNullHypot, + TimeComputationState* g_state, + SampleVector::Scalar* g_ampMaxAlphaBeta, + SampleVector::Scalar* g_ampMaxError, + SampleVector::Scalar* g_timeMax, + SampleVector::Scalar* g_timeError, + const int nchannels, + uint32_t const offsetForInputs) { + using ScalarType = SampleVector::Scalar; + + // constants + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + const int gtx = threadIdx.x + blockIdx.x * blockDim.x; + const int ch = gtx / nsamples; + const int sample = threadIdx.x % nsamples; + const auto* dids = ch >= offsetForInputs ? dids_ee : dids_eb; + const int inputCh = ch >= offsetForInputs ? ch - offsetForInputs : ch; + + // configure shared mem + // per block, we need #threads per block * 2 * sizeof(ScalarType) + // we run with N channels per block + extern __shared__ char smem[]; + ScalarType* shr_sumAf = reinterpret_cast(smem); + ScalarType* shr_sumff = shr_sumAf + blockDim.x; + + if (ch >= nchannels) + return; + + auto state = g_state[ch]; + const auto did = DetId{dids[inputCh]}; + const auto* amplitudeFitParameters = + did.subdetId() == EcalBarrel ? amplitudeFitParametersEB : amplitudeFitParametersEE; + + // TODO is that better than storing into global and launching another kernel + // for the first 10 threads + if (state == TimeComputationState::NotFinished) { + const auto alpha = amplitudeFitParameters[0]; + const auto beta = amplitudeFitParameters[1]; + const auto alphabeta = alpha * beta; + const auto invalphabeta = 1.0 / alphabeta; + const auto tMaxAlphaBeta = g_tMaxAlphaBeta[ch]; + const auto sample_value = sample_values[gtx]; + const auto sample_value_error = sample_value_errors[gtx]; + const auto inverr2 = + useless_samples[gtx] ? static_cast(0) : 1.0 / (sample_value_error * sample_value_error); + const auto offset = (static_cast(sample) - tMaxAlphaBeta) * invalphabeta; + const auto term1 = 1.0 + offset; + const auto f = term1 > 1e-6 ? fast_expf(alpha * (fast_logf(term1) - offset)) : static_cast(0.0); + const auto sumAf = sample_value * (f * inverr2); + const auto sumff = f * (f * inverr2); + + // store into shared mem + shr_sumAf[threadIdx.x] = sumAf; + shr_sumff[threadIdx.x] = sumff; + } else { + shr_sumAf[threadIdx.x] = 0; + shr_sumff[threadIdx.x] = 0; + } + __syncthreads(); + + // reduce + // unroll completely here (but hardcoded) + if (sample < 5) { + shr_sumAf[threadIdx.x] += shr_sumAf[threadIdx.x + 5]; + shr_sumff[threadIdx.x] += shr_sumff[threadIdx.x + 5]; + } + __syncthreads(); + + if (sample < 2) { + // will need to subtract for ltx = 3, we double count here + shr_sumAf[threadIdx.x] += shr_sumAf[threadIdx.x + 2] + shr_sumAf[threadIdx.x + 3]; + shr_sumff[threadIdx.x] += shr_sumff[threadIdx.x + 2] + shr_sumff[threadIdx.x + 3]; + } + __syncthreads(); + + if (sample == 0) { + // exit if the state is done + // note, we do not exit before all __synchtreads are finished + if (state == TimeComputationState::Finished) { + g_timeMax[ch] = 5; + g_timeError[ch] = -999; + return; + } + + // subtract to avoid double counting + const auto sumff = shr_sumff[threadIdx.x] + shr_sumff[threadIdx.x + 1] - shr_sumff[threadIdx.x + 3]; + const auto sumAf = shr_sumAf[threadIdx.x] + shr_sumAf[threadIdx.x + 1] - shr_sumAf[threadIdx.x + 3]; + + const auto ampMaxAlphaBeta = sumff > 0 ? sumAf / sumff : 0; + const auto sumAA = sumAAsNullHypot[ch]; + const auto sum0 = sum0sNullHypot[ch]; + const auto nullChi2 = chi2sNullHypot[ch]; + if (sumff > 0) { + const auto chi2AlphaBeta = (sumAA - sumAf * sumAf / sumff) / sum0; + if (chi2AlphaBeta > nullChi2) { + // null hypothesis is better + state = TimeComputationState::Finished; +#ifdef DEBUG_FINDAMPLCHI2_AND_FINISH + printf("ch = %d chi2AlphaBeta = %f nullChi2 = %f sumAA = %f sumAf = %f sumff = %f sum0 = %f\n", + ch, + chi2AlphaBeta, + nullChi2, + sumAA, + sumAf, + sumff, + sum0); +#endif + } + + // store to global + g_ampMaxAlphaBeta[ch] = ampMaxAlphaBeta; + } else { +#ifdef DEBUG_FINDAMPLCHI2_AND_FINISH + printf("ch = %d sum0 = %f sumAA = %f sumff = %f sumAf = %f\n", ch, sum0, sumAA, sumff, sumAf); +#endif + state = TimeComputationState::Finished; + } + + // store the state to global and finish calcs + g_state[ch] = state; + if (state == TimeComputationState::Finished) { + // store default values into global + g_timeMax[ch] = 5; + g_timeError[ch] = -999; +#ifdef DEBUG_FINDAMPLCHI2_AND_FINISH + printf("ch = %d finished state\n", ch); +#endif + return; + } + + const auto ampMaxError = g_ampMaxError[ch]; + const auto test_ratio = ampMaxAlphaBeta / ampMaxError; + const auto accTimeMax = g_accTimeMax[ch]; + const auto accTimeWgt = g_accTimeWgt[ch]; + const auto tMaxAlphaBeta = g_tMaxAlphaBeta[ch]; + const auto tMaxErrorAlphaBeta = g_tMaxErrorAlphaBeta[ch]; + // branch to separate large vs small pulses + // see cpu version for more info + if (test_ratio > 5.0 && accTimeWgt > 0) { + const auto tMaxRatio = accTimeWgt > 0 ? accTimeMax / accTimeWgt : static_cast(0); + const auto tMaxErrorRatio = accTimeWgt > 0 ? 1.0 / std::sqrt(accTimeWgt) : static_cast(0); + + if (test_ratio > 10.0) { + g_timeMax[ch] = tMaxRatio; + g_timeError[ch] = tMaxErrorRatio; + +#ifdef DEBUG_FINDAMPLCHI2_AND_FINISH + printf("ch = %d tMaxRatio = %f tMaxErrorRatio = %f\n", ch, tMaxRatio, tMaxErrorRatio); +#endif + } else { + const auto timeMax = (tMaxAlphaBeta * (10.0 - ampMaxAlphaBeta / ampMaxError) + + tMaxRatio * (ampMaxAlphaBeta / ampMaxError - 5.0)) / + 5.0; + const auto timeError = (tMaxErrorAlphaBeta * (10.0 - ampMaxAlphaBeta / ampMaxError) + + tMaxErrorRatio * (ampMaxAlphaBeta / ampMaxError - 5.0)) / + 5.0; + state = TimeComputationState::Finished; + g_state[ch] = state; + g_timeMax[ch] = timeMax; + g_timeError[ch] = timeError; + +#ifdef DEBUG_FINDAMPLCHI2_AND_FINISH + printf("ch = %d timeMax = %f timeError = %f\n", ch, timeMax, timeError); +#endif + } + } else { + state = TimeComputationState::Finished; + g_state[ch] = state; + g_timeMax[ch] = tMaxAlphaBeta; + g_timeError[ch] = tMaxErrorAlphaBeta; + +#ifdef DEBUG_FINDAMPLCHI2_AND_FINISH + printf("ch = %d tMaxAlphaBeta = %f tMaxErrorAlphaBeta = %f\n", ch, tMaxAlphaBeta, tMaxErrorAlphaBeta); +#endif + } + } + } + + __global__ void kernel_time_compute_fixMGPAslew(uint16_t const* digis_eb, + uint16_t const* digis_ee, + SampleVector::Scalar* sample_values, + SampleVector::Scalar* sample_value_errors, + bool* useless_sample_values, + unsigned const int sample_mask, + const int nchannels, + uint32_t const offsetForInputs) { + using ScalarType = SampleVector::Scalar; + + // constants + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + const int gtx = threadIdx.x + blockIdx.x * blockDim.x; + const int ch = gtx / nsamples; + const int sample = threadIdx.x % nsamples; + const int inputGtx = ch >= offsetForInputs ? gtx - offsetForInputs * nsamples : gtx; + const auto* digis = ch >= offsetForInputs ? digis_ee : digis_eb; + + // remove thread for sample 0, oversubscribing is easier than .... + if (ch >= nchannels || sample == 0) + return; + + if (!use_sample(sample_mask, sample)) + return; + + const auto gainIdPrev = ecal::mgpa::gainId(digis[inputGtx - 1]); + const auto gainIdNext = ecal::mgpa::gainId(digis[inputGtx]); + if (gainIdPrev >= 1 && gainIdPrev <= 3 && gainIdNext >= 1 && gainIdNext <= 3 && gainIdPrev < gainIdNext) { + sample_values[gtx - 1] = 0; + sample_value_errors[gtx - 1] = 1e+9; + useless_sample_values[gtx - 1] = true; + } + } + + __global__ void kernel_time_compute_ampl(SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + uint32_t const* dids, + bool const* useless_samples, + SampleVector::Scalar const* g_timeMax, + SampleVector::Scalar const* amplitudeFitParametersEB, + SampleVector::Scalar const* amplitudeFitParametersEE, + SampleVector::Scalar* g_amplitudeMax, + const int nchannels) { + using ScalarType = SampleVector::Scalar; + + // constants + constexpr ScalarType corr4 = 1.; + constexpr ScalarType corr6 = 1.; + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + const int gtx = threadIdx.x + blockIdx.x * blockDim.x; + const int ch = gtx / nsamples; + const int sample = threadIdx.x % nsamples; + + if (ch >= nchannels) + return; + + const auto did = DetId{dids[ch]}; + const auto* amplitudeFitParameters = + did.subdetId() == EcalBarrel ? amplitudeFitParametersEB : amplitudeFitParametersEE; + + // configure shared mem + extern __shared__ char smem[]; + ScalarType* shr_sum1 = reinterpret_cast(smem); + auto* shr_sumA = shr_sum1 + blockDim.x; + auto* shr_sumF = shr_sumA + blockDim.x; + auto* shr_sumAF = shr_sumF + blockDim.x; + auto* shr_sumFF = shr_sumAF + blockDim.x; + + const auto alpha = amplitudeFitParameters[0]; + const auto beta = amplitudeFitParameters[1]; + const auto timeMax = g_timeMax[ch]; + const auto pedestalLimit = timeMax - (alpha * beta) - 1.0; + const auto sample_value = sample_values[gtx]; + const auto sample_value_error = sample_value_errors[gtx]; + const auto inverr2 = + sample_value_error > 0 ? 1. / (sample_value_error * sample_value_error) : static_cast(0); + const auto termOne = 1 + (sample - timeMax) / (alpha * beta); + const auto f = termOne > 1.e-5 ? fast_expf(alpha * fast_logf(termOne) - (sample - timeMax) / beta) + : static_cast(0.); + + bool const cond = ((sample < pedestalLimit) || (f > 0.6 * corr6 && sample <= timeMax) || + (f > 0.4 * corr4 && sample >= timeMax)) && + !useless_samples[gtx]; + + // store into shared mem + shr_sum1[threadIdx.x] = cond ? inverr2 : static_cast(0); + shr_sumA[threadIdx.x] = cond ? sample_value * inverr2 : static_cast(0); + shr_sumF[threadIdx.x] = cond ? f * inverr2 : static_cast(0); + shr_sumAF[threadIdx.x] = cond ? (f * inverr2) * sample_value : static_cast(0); + shr_sumFF[threadIdx.x] = cond ? f * (f * inverr2) : static_cast(0); + + // reduction + if (sample <= 4) { + shr_sum1[threadIdx.x] += shr_sum1[threadIdx.x + 5]; + shr_sumA[threadIdx.x] += shr_sumA[threadIdx.x + 5]; + shr_sumF[threadIdx.x] += shr_sumF[threadIdx.x + 5]; + shr_sumAF[threadIdx.x] += shr_sumAF[threadIdx.x + 5]; + shr_sumFF[threadIdx.x] += shr_sumFF[threadIdx.x + 5]; + } + __syncthreads(); + + if (sample < 2) { + // note: we double count sample 3 + shr_sum1[threadIdx.x] += shr_sum1[threadIdx.x + 2] + shr_sum1[threadIdx.x + 3]; + shr_sumA[threadIdx.x] += shr_sumA[threadIdx.x + 2] + shr_sumA[threadIdx.x + 3]; + shr_sumF[threadIdx.x] += shr_sumF[threadIdx.x + 2] + shr_sumF[threadIdx.x + 3]; + shr_sumAF[threadIdx.x] += shr_sumAF[threadIdx.x + 2] + shr_sumAF[threadIdx.x + 3]; + shr_sumFF[threadIdx.x] += shr_sumFF[threadIdx.x + 2] + shr_sumFF[threadIdx.x + 3]; + } + __syncthreads(); + + if (sample == 0) { + const auto sum1 = shr_sum1[threadIdx.x] + shr_sum1[threadIdx.x + 1] - shr_sum1[threadIdx.x + 3]; + const auto sumA = shr_sumA[threadIdx.x] + shr_sumA[threadIdx.x + 1] - shr_sumA[threadIdx.x + 3]; + const auto sumF = shr_sumF[threadIdx.x] + shr_sumF[threadIdx.x + 1] - shr_sumF[threadIdx.x + 3]; + const auto sumAF = shr_sumAF[threadIdx.x] + shr_sumAF[threadIdx.x + 1] - shr_sumAF[threadIdx.x + 3]; + const auto sumFF = shr_sumFF[threadIdx.x] + shr_sumFF[threadIdx.x + 1] - shr_sumFF[threadIdx.x + 3]; + + const auto denom = sumFF * sum1 - sumF * sumF; + const auto condForDenom = sum1 > 0 && std::abs(denom) > 1.e-20; + const auto amplitudeMax = condForDenom ? (sumAF * sum1 - sumA * sumF) / denom : static_cast(0.); + + // store into global mem + g_amplitudeMax[ch] = amplitudeMax; + } + } + + //#define ECAL_RECO_CUDA_TC_INIT_DEBUG + __global__ void kernel_time_computation_init(uint16_t const* digis_eb, + uint32_t const* dids_eb, + uint16_t const* digis_ee, + uint32_t const* dids_ee, + float const* rms_x12, + float const* rms_x6, + float const* rms_x1, + float const* mean_x12, + float const* mean_x6, + float const* mean_x1, + float const* gain12Over6, + float const* gain6Over1, + SampleVector::Scalar* sample_values, + SampleVector::Scalar* sample_value_errors, + SampleVector::Scalar* ampMaxError, + bool* useless_sample_values, + char* pedestal_nums, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs, + unsigned const int sample_maskEB, + unsigned const int sample_maskEE, + int nchannels) { + using ScalarType = SampleVector::Scalar; + + // constants + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + const int tx = threadIdx.x + blockDim.x * blockIdx.x; + const int ch = tx / nsamples; + const int inputTx = ch >= offsetForInputs ? tx - offsetForInputs * nsamples : tx; + const int inputCh = ch >= offsetForInputs ? ch - offsetForInputs : ch; + const auto* digis = ch >= offsetForInputs ? digis_ee : digis_eb; + const auto* dids = ch >= offsetForInputs ? dids_ee : dids_eb; + + // threads that return here should not affect the __syncthreads() below since they have exitted the kernel + if (ch >= nchannels) + return; + + // indices/inits + const int sample = tx % nsamples; + const int input_ch_start = inputCh * nsamples; + SampleVector::Scalar pedestal = 0.; + int num = 0; + + // configure shared mem + extern __shared__ char smem[]; + ScalarType* shrSampleValues = reinterpret_cast(smem); + ScalarType* shrSampleValueErrors = shrSampleValues + blockDim.x; + + // 0 and 1 sample values + const auto adc0 = ecal::mgpa::adc(digis[input_ch_start]); + const auto gainId0 = ecal::mgpa::gainId(digis[input_ch_start]); + const auto adc1 = ecal::mgpa::adc(digis[input_ch_start + 1]); + const auto gainId1 = ecal::mgpa::gainId(digis[input_ch_start + 1]); + const auto did = DetId{dids[inputCh]}; + const auto isBarrel = did.subdetId() == EcalBarrel; + const auto sample_mask = did.subdetId() == EcalBarrel ? sample_maskEB : sample_maskEE; + const auto hashedId = isBarrel ? ecal::reconstruction::hashedIndexEB(did.rawId()) + : offsetForHashes + ecal::reconstruction::hashedIndexEE(did.rawId()); + + // set pedestal + // TODO this branch is non-divergent for a group of 10 threads + if (gainId0 == 1 && use_sample(sample_mask, 0)) { + pedestal = static_cast(adc0); + num = 1; + + const auto diff = adc1 - adc0; + if (gainId1 == 1 && use_sample(sample_mask, 1) && std::abs(diff) < 3 * rms_x12[hashedId]) { + pedestal = (pedestal + static_cast(adc1)) / 2.0; + num = 2; + } + } else { + pedestal = mean_x12[ch]; + } + + // ped subtracted and gain-renormalized samples. + const auto gainId = ecal::mgpa::gainId(digis[inputTx]); + const auto adc = ecal::mgpa::adc(digis[inputTx]); + + bool bad = false; + SampleVector::Scalar sample_value, sample_value_error; + // TODO divergent branch + // TODO: piece below is general both for amplitudes and timing + // potentially there is a way to reduce the amount of code... + if (!use_sample(sample_mask, sample)) { + bad = true; + sample_value = 0; + sample_value_error = 0; + } else if (gainId == 1) { + sample_value = static_cast(adc) - pedestal; + sample_value_error = rms_x12[hashedId]; + } else if (gainId == 2) { + sample_value = (static_cast(adc) - mean_x6[hashedId]) * gain12Over6[hashedId]; + sample_value_error = rms_x6[hashedId] * gain12Over6[hashedId]; + } else if (gainId == 3) { + sample_value = + (static_cast(adc) - mean_x1[hashedId]) * gain6Over1[hashedId] * gain12Over6[hashedId]; + sample_value_error = rms_x1[hashedId] * gain6Over1[hashedId] * gain12Over6[hashedId]; + } else { + sample_value = 0; + sample_value_error = 0; + bad = true; + } + + // TODO: make sure we save things correctly when sample is useless + const auto useless_sample = (sample_value_error <= 0) | bad; + useless_sample_values[tx] = useless_sample; + sample_values[tx] = sample_value; + sample_value_errors[tx] = useless_sample ? 1e+9 : sample_value_error; + + // DEBUG +#ifdef ECAL_RECO_CUDA_TC_INIT_DEBUG + if (ch == 0) { + printf("sample = %d sample_value = %f sample_value_error = %f useless = %c\n", + sample, + sample_value, + sample_value_error, + useless_sample ? '1' : '0'); + } +#endif + + // store into the shared mem + shrSampleValues[threadIdx.x] = sample_value_error > 0 ? sample_value : std::numeric_limits::min(); + shrSampleValueErrors[threadIdx.x] = sample_value_error; + __syncthreads(); + + // perform the reduction with min + if (sample < 5) { + // note, if equal -> we keep the value with lower sample as for cpu + shrSampleValueErrors[threadIdx.x] = shrSampleValues[threadIdx.x] < shrSampleValues[threadIdx.x + 5] + ? shrSampleValueErrors[threadIdx.x + 5] + : shrSampleValueErrors[threadIdx.x]; + shrSampleValues[threadIdx.x] = std::max(shrSampleValues[threadIdx.x], shrSampleValues[threadIdx.x + 5]); + } + __syncthreads(); + + // a bit of an overkill, but easier than to compare across 3 values + if (sample < 3) { + shrSampleValueErrors[threadIdx.x] = shrSampleValues[threadIdx.x] < shrSampleValues[threadIdx.x + 3] + ? shrSampleValueErrors[threadIdx.x + 3] + : shrSampleValueErrors[threadIdx.x]; + shrSampleValues[threadIdx.x] = std::max(shrSampleValues[threadIdx.x], shrSampleValues[threadIdx.x + 3]); + } + __syncthreads(); + + if (sample < 2) { + shrSampleValueErrors[threadIdx.x] = shrSampleValues[threadIdx.x] < shrSampleValues[threadIdx.x + 2] + ? shrSampleValueErrors[threadIdx.x + 2] + : shrSampleValueErrors[threadIdx.x]; + shrSampleValues[threadIdx.x] = std::max(shrSampleValues[threadIdx.x], shrSampleValues[threadIdx.x + 2]); + } + __syncthreads(); + + if (sample == 0) { + // we only needd the max error + const auto maxSampleValueError = shrSampleValues[threadIdx.x] < shrSampleValues[threadIdx.x + 1] + ? shrSampleValueErrors[threadIdx.x + 1] + : shrSampleValueErrors[threadIdx.x]; + + // # pedestal samples used + pedestal_nums[ch] = num; + // this is used downstream + ampMaxError[ch] = maxSampleValueError; + + // DEBUG +#ifdef ECAL_RECO_CUDA_TC_INIT_DEBUG + if (ch == 0) { + printf("pedestal_nums = %d ampMaxError = %f\n", num, maxSampleValueError); + } +#endif + } + } + + /// + /// launch context parameters: 1 thread per channel + /// + //#define DEBUG_TIME_CORRECTION + __global__ void kernel_time_correction_and_finalize( + // SampleVector::Scalar const* g_amplitude, + ::ecal::reco::StorageScalarType const* g_amplitudeEB, + ::ecal::reco::StorageScalarType const* g_amplitudeEE, + uint16_t const* digis_eb, + uint32_t const* dids_eb, + uint16_t const* digis_ee, + uint32_t const* dids_ee, + float const* amplitudeBinsEB, + float const* amplitudeBinsEE, + float const* shiftBinsEB, + float const* shiftBinsEE, + SampleVector::Scalar const* g_timeMax, + SampleVector::Scalar const* g_timeError, + float const* g_rms_x12, + float const* timeCalibConstant, + float* g_jitterEB, + float* g_jitterEE, + float* g_jitterErrorEB, + float* g_jitterErrorEE, + uint32_t* flagsEB, + uint32_t* flagsEE, + const int amplitudeBinsSizeEB, + const int amplitudeBinsSizeEE, + ConfigurationParameters::type const timeConstantTermEB, + ConfigurationParameters::type const timeConstantTermEE, + float const offsetTimeValueEB, + float const offsetTimeValueEE, + ConfigurationParameters::type const timeNconstEB, + ConfigurationParameters::type const timeNconstEE, + ConfigurationParameters::type const amplitudeThresholdEB, + ConfigurationParameters::type const amplitudeThresholdEE, + ConfigurationParameters::type const outOfTimeThreshG12pEB, + ConfigurationParameters::type const outOfTimeThreshG12pEE, + ConfigurationParameters::type const outOfTimeThreshG12mEB, + ConfigurationParameters::type const outOfTimeThreshG12mEE, + ConfigurationParameters::type const outOfTimeThreshG61pEB, + ConfigurationParameters::type const outOfTimeThreshG61pEE, + ConfigurationParameters::type const outOfTimeThreshG61mEB, + ConfigurationParameters::type const outOfTimeThreshG61mEE, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs, + const int nchannels) { + using ScalarType = SampleVector::Scalar; + + // constants + constexpr int nsamples = EcalDataFrame::MAXSAMPLES; + + // indices + const int gtx = threadIdx.x + blockIdx.x * blockDim.x; + const int inputGtx = gtx >= offsetForInputs ? gtx - offsetForInputs : gtx; + const auto* dids = gtx >= offsetForInputs ? dids_ee : dids_eb; + const auto& digis = gtx >= offsetForInputs ? digis_ee : digis_eb; + + // filter out outside of range threads + if (gtx >= nchannels) + return; + +// need to ref the right ptrs +#define ARRANGE(var) auto* var = gtx >= offsetForInputs ? var##EE : var##EB + ARRANGE(g_amplitude); + ARRANGE(g_jitter); + ARRANGE(g_jitterError); + ARRANGE(flags); +#undef ARRANGE + + const auto did = DetId{dids[inputGtx]}; + const auto isBarrel = did.subdetId() == EcalBarrel; + const auto hashedId = isBarrel ? ecal::reconstruction::hashedIndexEB(did.rawId()) + : offsetForHashes + ecal::reconstruction::hashedIndexEE(did.rawId()); + const auto* amplitudeBins = isBarrel ? amplitudeBinsEB : amplitudeBinsEE; + const auto* shiftBins = isBarrel ? shiftBinsEB : shiftBinsEE; + const auto amplitudeBinsSize = isBarrel ? amplitudeBinsSizeEB : amplitudeBinsSizeEE; + const auto timeConstantTerm = isBarrel ? timeConstantTermEB : timeConstantTermEE; + const auto timeNconst = isBarrel ? timeNconstEB : timeNconstEE; + const auto offsetTimeValue = isBarrel ? offsetTimeValueEB : offsetTimeValueEE; + const auto amplitudeThreshold = isBarrel ? amplitudeThresholdEB : amplitudeThresholdEE; + const auto outOfTimeThreshG12p = isBarrel ? outOfTimeThreshG12pEB : outOfTimeThreshG12pEE; + const auto outOfTimeThreshG12m = isBarrel ? outOfTimeThreshG12mEB : outOfTimeThreshG12mEE; + const auto outOfTimeThreshG61p = isBarrel ? outOfTimeThreshG61pEB : outOfTimeThreshG61pEE; + const auto outOfTimeThreshG61m = isBarrel ? outOfTimeThreshG61mEB : outOfTimeThreshG61mEE; + + // load some + const auto amplitude = g_amplitude[inputGtx]; + const auto rms_x12 = g_rms_x12[hashedId]; + const auto timeCalibConst = timeCalibConstant[hashedId]; + + int myBin = -1; + for (int bin = 0; bin < amplitudeBinsSize; bin++) { + if (amplitude > amplitudeBins[bin]) + myBin = bin; + else + break; + } + + ScalarType correction = 0; + if (myBin == -1) { + correction = shiftBins[0]; + } else if (myBin == amplitudeBinsSize - 1) { + correction = shiftBins[myBin]; + } else { + correction = shiftBins[myBin + 1] - shiftBins[myBin]; + correction *= (amplitude - amplitudeBins[myBin]) / (amplitudeBins[myBin + 1] - amplitudeBins[myBin]); + correction += shiftBins[myBin]; + } + + // correction * 1./25. + correction = correction * 0.04; + const auto timeMax = g_timeMax[gtx]; + const auto timeError = g_timeError[gtx]; + const auto jitter = timeMax - 5 + correction; + const auto jitterError = + std::sqrt(timeError * timeError + timeConstantTerm * timeConstantTerm * 0.04 * 0.04); // 0.04 = 1./25. + +#ifdef DEBUG_TIME_CORRECTION + printf("ch = %d timeMax = %f timeError = %f jitter = %f correction = %f\n", + gtx, + timeMax, + timeError, + jitter, + correction); +// } +#endif + + // store back to global + g_jitter[inputGtx] = jitter; + g_jitterError[inputGtx] = jitterError; + + // set the flag + // TODO: replace with something more efficient (if required), + // for now just to make it work + if (amplitude > amplitudeThreshold * rms_x12) { + auto threshP = outOfTimeThreshG12p; + auto threshM = outOfTimeThreshG12m; + if (amplitude > 3000.) { + for (int isample = 0; isample < nsamples; isample++) { + int gainid = ecal::mgpa::gainId(digis[nsamples * inputGtx + isample]); + if (gainid != 1) { + threshP = outOfTimeThreshG61p; + threshM = outOfTimeThreshG61m; + break; + } + } + } + + const auto correctedTime = (timeMax - 5) * 25 + timeCalibConst + offsetTimeValue; + const auto nterm = timeNconst * rms_x12 / amplitude; + const auto sigmat = std::sqrt(nterm * nterm + timeConstantTerm * timeConstantTerm); + if (correctedTime > sigmat * threshP || correctedTime < -sigmat * threshM) + flags[inputGtx] |= 0x1 << EcalUncalibratedRecHit::kOutOfTime; + } + } + + } // namespace multifit +} // namespace ecal diff --git a/RecoLocalCalo/EcalRecProducers/plugins/TimeComputationKernels.h b/RecoLocalCalo/EcalRecProducers/plugins/TimeComputationKernels.h new file mode 100644 index 0000000000000..a9b1c69678abd --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/plugins/TimeComputationKernels.h @@ -0,0 +1,186 @@ +#ifndef RecoLocalCalo_EcalRecProducers_plugins_TimeComputationKernels_h +#define RecoLocalCalo_EcalRecProducers_plugins_TimeComputationKernels_h + +#include +#include + +#include + +#include "DataFormats/Math/interface/approx_exp.h" +#include "DataFormats/Math/interface/approx_log.h" + +#include "Common.h" +#include "DeclsForKernels.h" +#include "EigenMatrixTypes_gpu.h" + +//#define DEBUG + +//#define ECAL_RECO_CUDA_DEBUG + +namespace ecal { + namespace multifit { + + __global__ void kernel_time_compute_nullhypot(SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + bool const* useless_sample_values, + SampleVector::Scalar* chi2s, + SampleVector::Scalar* sum0s, + SampleVector::Scalar* sumAAs, + int const nchannels); + // + // launch ctx parameters are + // 45 threads per channel, X channels per block, Y blocks + // 45 comes from: 10 samples for i <- 0 to 9 and for j <- i+1 to 9 + // TODO: it might be much beter to use 32 threads per channel instead of 45 + // to simplify the synchronization + // + __global__ void kernel_time_compute_makeratio(SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + uint32_t const* dids_eb, + uint32_t const* dids_ee, + bool const* useless_sample_values, + char const* pedestal_nums, + ConfigurationParameters::type const* amplitudeFitParametersEB, + ConfigurationParameters::type const* amplitudeFitParametersEE, + ConfigurationParameters::type const* timeFitParametersEB, + ConfigurationParameters::type const* timeFitParametersEE, + SampleVector::Scalar const* sumAAsNullHypot, + SampleVector::Scalar const* sum0sNullHypot, + SampleVector::Scalar* tMaxAlphaBetas, + SampleVector::Scalar* tMaxErrorAlphaBetas, + SampleVector::Scalar* g_accTimeMax, + SampleVector::Scalar* g_accTimeWgt, + TimeComputationState* g_state, + unsigned int const timeFitParameters_sizeEB, + unsigned int const timeFitParameters_sizeEE, + ConfigurationParameters::type const timeFitLimits_firstEB, + ConfigurationParameters::type const timeFitLimits_firstEE, + ConfigurationParameters::type const timeFitLimits_secondEB, + ConfigurationParameters::type const timeFitLimits_secondEE, + int const nchannels, + uint32_t const offsetForInputs); + + /// launch ctx parameters are + /// 10 threads per channel, N channels per block, Y blocks + /// TODO: do we need to keep the state around or can be removed?! + //#define DEBUG_FINDAMPLCHI2_AND_FINISH + __global__ void kernel_time_compute_findamplchi2_and_finish( + SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + uint32_t const* dids_eb, + uint32_t const* dids_ee, + bool const* useless_samples, + SampleVector::Scalar const* g_tMaxAlphaBeta, + SampleVector::Scalar const* g_tMaxErrorAlphaBeta, + SampleVector::Scalar const* g_accTimeMax, + SampleVector::Scalar const* g_accTimeWgt, + ConfigurationParameters::type const* amplitudeFitParametersEB, + ConfigurationParameters::type const* amplitudeFitParametersEE, + SampleVector::Scalar const* sumAAsNullHypot, + SampleVector::Scalar const* sum0sNullHypot, + SampleVector::Scalar const* chi2sNullHypot, + TimeComputationState* g_state, + SampleVector::Scalar* g_ampMaxAlphaBeta, + SampleVector::Scalar* g_ampMaxError, + SampleVector::Scalar* g_timeMax, + SampleVector::Scalar* g_timeError, + int const nchannels, + uint32_t const offsetForInputs); + + __global__ void kernel_time_compute_fixMGPAslew(uint16_t const* digis_eb, + uint16_t const* digis_ee, + SampleVector::Scalar* sample_values, + SampleVector::Scalar* sample_value_errors, + bool* useless_sample_values, + unsigned int const sample_mask, + int const nchannels, + uint32_t const offsetForInputs); + + __global__ void kernel_time_compute_ampl(SampleVector::Scalar const* sample_values, + SampleVector::Scalar const* sample_value_errors, + uint32_t const* dids_eb, + uint32_t const* dids_ed, + bool const* useless_samples, + SampleVector::Scalar const* g_timeMax, + SampleVector::Scalar const* amplitudeFitParametersEB, + SampleVector::Scalar const* amplitudeFitParametersEE, + SampleVector::Scalar* g_amplitudeMax, + int const nchannels, + uint32_t const offsetForInputs); + + //#define ECAL_RECO_CUDA_TC_INIT_DEBUG + __global__ void kernel_time_computation_init(uint16_t const* digis_eb, + uint32_t const* dids_eb, + uint16_t const* digis_ee, + uint32_t const* dids_ee, + float const* rms_x12, + float const* rms_x6, + float const* rms_x1, + float const* mean_x12, + float const* mean_x6, + float const* mean_x1, + float const* gain12Over6, + float const* gain6Over1, + SampleVector::Scalar* sample_values, + SampleVector::Scalar* sample_value_errors, + SampleVector::Scalar* ampMaxError, + bool* useless_sample_values, + char* pedestal_nums, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs, + unsigned int const sample_maskEB, + unsigned int const sample_maskEE, + int nchannels); + + /// + /// launch context parameters: 1 thread per channel + /// + //#define DEBUG_TIME_CORRECTION + __global__ void kernel_time_correction_and_finalize( + // SampleVector::Scalar const* g_amplitude, + ::ecal::reco::StorageScalarType const* g_amplitudeEB, + ::ecal::reco::StorageScalarType const* g_amplitudeEE, + uint16_t const* digis_eb, + uint32_t const* dids_eb, + uint16_t const* digis_ee, + uint32_t const* dids_ee, + float const* amplitudeBinsEB, + float const* amplitudeBinsEE, + float const* shiftBinsEB, + float const* shiftBinsEE, + SampleVector::Scalar const* g_timeMax, + SampleVector::Scalar const* g_timeError, + float const* g_rms_x12, + float const* timeCalibConstant, + ::ecal::reco::StorageScalarType* g_jitterEB, + ::ecal::reco::StorageScalarType* g_jitterEE, + ::ecal::reco::StorageScalarType* g_jitterErrorEB, + ::ecal::reco::StorageScalarType* g_jitterErrorEE, + uint32_t* flagsEB, + uint32_t* flagsEE, + int const amplitudeBinsSizeEB, + int const amplitudeBinsSizeEE, + ConfigurationParameters::type const timeConstantTermEB, + ConfigurationParameters::type const timeConstantTermEE, + float const offsetTimeValueEB, + float const offsetTimeValueEE, + ConfigurationParameters::type const timeNconstEB, + ConfigurationParameters::type const timeNconstEE, + ConfigurationParameters::type const amplitudeThresholdEB, + ConfigurationParameters::type const amplitudeThresholdEE, + ConfigurationParameters::type const outOfTimeThreshG12pEB, + ConfigurationParameters::type const outOfTimeThreshG12pEE, + ConfigurationParameters::type const outOfTimeThreshG12mEB, + ConfigurationParameters::type const outOfTimeThreshG12mEE, + ConfigurationParameters::type const outOfTimeThreshG61pEB, + ConfigurationParameters::type const outOfTimeThreshG61pEE, + ConfigurationParameters::type const outOfTimeThreshG61mEB, + ConfigurationParameters::type const outOfTimeThreshG61mEE, + uint32_t const offsetForHashes, + uint32_t const offsetForInputs, + int const nchannels); + + } // namespace multifit +} // namespace ecal + +#endif // RecoLocalCalo_EcalRecProducers_plugins_TimeComputationKernels_h diff --git a/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_cff.py b/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_cff.py index 1eef78d42e940..72a3efaae38ba 100644 --- a/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_cff.py +++ b/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_cff.py @@ -1,6 +1,57 @@ import FWCore.ParameterSet.Config as cms +from Configuration.ProcessModifiers.gpu_cff import gpu # ECAL multifit running on CPU from RecoLocalCalo.EcalRecProducers.ecalMultiFitUncalibRecHit_cfi import ecalMultiFitUncalibRecHit ecalMultiFitUncalibRecHitTask = cms.Task(ecalMultiFitUncalibRecHit) + +# ECAL conditions used by the multifit running on GPU +from RecoLocalCalo.EcalRecProducers.ecalPedestalsGPUESProducer_cfi import ecalPedestalsGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalGainRatiosGPUESProducer_cfi import ecalGainRatiosGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalPulseShapesGPUESProducer_cfi import ecalPulseShapesGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalPulseCovariancesGPUESProducer_cfi import ecalPulseCovariancesGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalSamplesCorrelationGPUESProducer_cfi import ecalSamplesCorrelationGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalTimeBiasCorrectionsGPUESProducer_cfi import ecalTimeBiasCorrectionsGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalTimeCalibConstantsGPUESProducer_cfi import ecalTimeCalibConstantsGPUESProducer +from RecoLocalCalo.EcalRecProducers.ecalMultifitParametersGPUESProducer_cfi import ecalMultifitParametersGPUESProducer + +# ECAL multifit running on GPU +from RecoLocalCalo.EcalRecProducers.ecalUncalibRecHitProducerGPU_cfi import ecalUncalibRecHitProducerGPU as _ecalUncalibRecHitProducerGPU +ecalMultiFitUncalibRecHitGPU = _ecalUncalibRecHitProducerGPU.clone( + digisLabelEB = cms.InputTag('ecalDigisGPU', 'ebDigis'), + digisLabelEE = cms.InputTag('ecalDigisGPU', 'eeDigis'), +) + +# copy the uncalibrated rechits from GPU to CPU +from RecoLocalCalo.EcalRecProducers.ecalCPUUncalibRecHitProducer_cfi import ecalCPUUncalibRecHitProducer as _ecalCPUUncalibRecHitProducer +ecalMultiFitUncalibRecHitSoA = _ecalCPUUncalibRecHitProducer.clone( + recHitsInLabelEB = cms.InputTag('ecalMultiFitUncalibRecHitGPU', 'EcalUncalibRecHitsEB'), + recHitsInLabelEE = cms.InputTag('ecalMultiFitUncalibRecHitGPU', 'EcalUncalibRecHitsEE'), +) + +# convert the uncalibrated rechits from SoA to legacy format +from RecoLocalCalo.EcalRecProducers.ecalUncalibRecHitConvertGPU2CPUFormat_cfi import ecalUncalibRecHitConvertGPU2CPUFormat as _ecalUncalibRecHitConvertGPU2CPUFormat +_ecalMultiFitUncalibRecHit_gpu = _ecalUncalibRecHitConvertGPU2CPUFormat.clone( + recHitsLabelGPUEB = cms.InputTag('ecalMultiFitUncalibRecHitSoA', 'EcalUncalibRecHitsEB'), + recHitsLabelGPUEE = cms.InputTag('ecalMultiFitUncalibRecHitSoA', 'EcalUncalibRecHitsEE'), +) +gpu.toReplaceWith(ecalMultiFitUncalibRecHit, _ecalMultiFitUncalibRecHit_gpu) + +gpu.toReplaceWith(ecalMultiFitUncalibRecHitTask, cms.Task( + # ECAL conditions used by the multifit running on GPU + ecalPedestalsGPUESProducer, + ecalGainRatiosGPUESProducer, + ecalPulseShapesGPUESProducer, + ecalPulseCovariancesGPUESProducer, + ecalSamplesCorrelationGPUESProducer, + ecalTimeBiasCorrectionsGPUESProducer, + ecalTimeCalibConstantsGPUESProducer, + ecalMultifitParametersGPUESProducer, + # ECAL multifit running on GP + ecalMultiFitUncalibRecHitGPU, + # copy the uncalibrated rechits from GPU to CPU + ecalMultiFitUncalibRecHitSoA, + # convert the uncalibrated rechits legacy format + ecalMultiFitUncalibRecHit, +)) diff --git a/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_gpu_new_cfi.py b/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_gpu_new_cfi.py new file mode 100644 index 0000000000000..84a0c6f9cbe8a --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/python/ecalMultiFitUncalibRecHit_gpu_new_cfi.py @@ -0,0 +1,83 @@ +import FWCore.ParameterSet.Config as cms + +from RecoLocalCalo.EcalRecProducers.ecalPulseShapeParameters_cff import * + +ecalMultiFitUncalibRecHitgpu = cms.EDProducer("EcalUncalibRecHitProducerGPUNew", + EBdigiCollection = cms.InputTag("ecalDigis","ebDigis"), + EEdigiCollection = cms.InputTag("ecalDigis","eeDigis"), + EBhitCollection = cms.string("EcalUncalibRecHitsEBgpunew"), + EBhitCollection_soa = cms.string("EcalUncalibRecHitsEBgpunew"), + EEhitCollection = cms.string('EcalUncalibRecHitsEEgpunew'), + EEhitCollection_soa = cms.string('EcalUncalibRecHitsEEgpunew'), + algo = cms.string("EcalUncalibRecHitWorkerMultiFitGPUNew"), + algoPSet = cms.PSet( + # for multifit method + EcalPulseShapeParameters = cms.PSet( ecal_pulse_shape_parameters ), + activeBXs = cms.vint32(-5,-4,-3,-2,-1,0,1,2,3,4), + ampErrorCalculation = cms.bool(True), + useLumiInfoRunHeader = cms.bool(True), + + doPrefitEB = cms.bool(False), + doPrefitEE = cms.bool(False), + prefitMaxChiSqEB = cms.double(25.), + prefitMaxChiSqEE = cms.double(10.), + + dynamicPedestalsEB = cms.bool(False), + dynamicPedestalsEE = cms.bool(False), + mitigateBadSamplesEB = cms.bool(False), + mitigateBadSamplesEE = cms.bool(False), + gainSwitchUseMaxSampleEB = cms.bool(True), + gainSwitchUseMaxSampleEE = cms.bool(False), + selectiveBadSampleCriteriaEB = cms.bool(False), + selectiveBadSampleCriteriaEE = cms.bool(False), + simplifiedNoiseModelForGainSwitch = cms.bool(True), + addPedestalUncertaintyEB = cms.double(0.), + addPedestalUncertaintyEE = cms.double(0.), + + # decide which algorithm to be use to calculate the jitter + timealgo = cms.string("RatioMethod"), + + # for ratio method + EBtimeFitParameters = cms.vdouble(-2.015452e+00, 3.130702e+00, -1.234730e+01, 4.188921e+01, -8.283944e+01, 9.101147e+01, -5.035761e+01, 1.105621e+01), + EEtimeFitParameters = cms.vdouble(-2.390548e+00, 3.553628e+00, -1.762341e+01, 6.767538e+01, -1.332130e+02, 1.407432e+02, -7.541106e+01, 1.620277e+01), + EBamplitudeFitParameters = cms.vdouble(1.138,1.652), + EEamplitudeFitParameters = cms.vdouble(1.890,1.400), + EBtimeFitLimits_Lower = cms.double(0.2), + EBtimeFitLimits_Upper = cms.double(1.4), + EEtimeFitLimits_Lower = cms.double(0.2), + EEtimeFitLimits_Upper = cms.double(1.4), + # for time error + EBtimeConstantTerm= cms.double(.6), + EEtimeConstantTerm= cms.double(1.0), + + # for kOutOfTime flag + EBtimeNconst = cms.double(28.5), + EEtimeNconst = cms.double(31.8), + outOfTimeThresholdGain12pEB = cms.double(5), # times estimated precision + outOfTimeThresholdGain12mEB = cms.double(5), # times estimated precision + outOfTimeThresholdGain61pEB = cms.double(5), # times estimated precision + outOfTimeThresholdGain61mEB = cms.double(5), # times estimated precision + outOfTimeThresholdGain12pEE = cms.double(1000), # times estimated precision + outOfTimeThresholdGain12mEE = cms.double(1000), # times estimated precision + outOfTimeThresholdGain61pEE = cms.double(1000), # times estimated precision + outOfTimeThresholdGain61mEE = cms.double(1000), # times estimated precision + amplitudeThresholdEB = cms.double(10), + amplitudeThresholdEE = cms.double(10), + + ebSpikeThreshold = cms.double(1.042), + + # these are now taken from DB. Here the MC parameters for backward compatibility + ebPulseShape = cms.vdouble( 5.2e-05,-5.26e-05 , 6.66e-05, 0.1168, 0.7575, 1., 0.8876, 0.6732, 0.4741, 0.3194 ), + eePulseShape = cms.vdouble( 5.2e-05,-5.26e-05 , 6.66e-05, 0.1168, 0.7575, 1., 0.8876, 0.6732, 0.4741, 0.3194 ), + + # for kPoorReco flag + kPoorRecoFlagEB = cms.bool(True), + kPoorRecoFlagEE = cms.bool(False), + chi2ThreshEB_ = cms.double(65.0), + chi2ThreshEE_ = cms.double(50.0), + + # threads/blocks config + threads = cms.vint32(256, 1, 1), + runV1 = cms.bool(True), + ) +) diff --git a/RecoLocalCalo/EcalRecProducers/python/ecalRecHitGPU_cfi.py b/RecoLocalCalo/EcalRecProducers/python/ecalRecHitGPU_cfi.py new file mode 100644 index 0000000000000..a9b5599fd970f --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/python/ecalRecHitGPU_cfi.py @@ -0,0 +1,69 @@ +import FWCore.ParameterSet.Config as cms + +from RecoLocalCalo.EcalRecAlgos.ecalCleaningAlgo import cleaningAlgoConfig + +# rechit producer +ecalRecHitGPU = cms.EDProducer("EcalRecHitProducerGPU", + + uncalibrecHitsInLabelEB = cms.InputTag("ecalUncalibRecHitProducerGPU","EcalUncalibRecHitsEB"), + uncalibrecHitsInLabelEE = cms.InputTag("ecalUncalibRecHitProducerGPU","EcalUncalibRecHitsEE"), + + recHitsLabelEB = cms.string("EcalRecHitsEB"), + recHitsLabelEE = cms.string("EcalRecHitsEE"), + + maxNumberHitsEB = cms.uint32(61200), + maxNumberHitsEE = cms.uint32(14648), + + ## db statuses to be exluded from reconstruction (some will be recovered) + ChannelStatusToBeExcluded = cms.vstring( 'kDAC', + 'kNoisy', + 'kNNoisy', + 'kFixedG6', + 'kFixedG1', + 'kFixedG0', + 'kNonRespondingIsolated', + 'kDeadVFE', + 'kDeadFE', + 'kNoDataNoTP', + # + # AM should I add them here????? + # next ones from "flagsMapDBReco" + # but not defined in "EcalChannelStatusCode.h" + # but they are defined in "EcalRecHit.h" + # + #'kKilled', + #'kTPSaturated', + #'kL1SpikeFlag', + ), + + ## avoid propagation of dead channels other than after recovery + killDeadChannels = cms.bool(True), + + ## define maximal and minimal values for the laser corrections + + EBLaserMIN = cms.double(0.01), + EELaserMIN = cms.double(0.01), + + EBLaserMAX = cms.double(30.0), + EELaserMAX = cms.double(30.0), + + ## reco flags association to DB flag + flagsMapDBReco = cms.PSet( + kGood = cms.vstring('kOk','kDAC','kNoLaser','kNoisy'), + kNoisy = cms.vstring('kNNoisy','kFixedG6','kFixedG1'), + kNeighboursRecovered = cms.vstring('kFixedG0', + 'kNonRespondingIsolated', + 'kDeadVFE'), + kTowerRecovered = cms.vstring('kDeadFE'), + kDead = cms.vstring('kNoDataNoTP') + ), + + ## for channel recovery + recoverEBIsolatedChannels = cms.bool(False), + recoverEEIsolatedChannels = cms.bool(False), + recoverEBVFE = cms.bool(False), + recoverEEVFE = cms.bool(False), + recoverEBFE = cms.bool(True), + recoverEEFE = cms.bool(True), +) + diff --git a/RecoLocalCalo/EcalRecProducers/test/ecalRawDecodingAndMultifit.py b/RecoLocalCalo/EcalRecProducers/test/ecalRawDecodingAndMultifit.py new file mode 100644 index 0000000000000..a3d04e836f020 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/test/ecalRawDecodingAndMultifit.py @@ -0,0 +1,172 @@ +import FWCore.ParameterSet.Config as cms +from Configuration.StandardSequences.Eras import eras + +process = cms.Process('RECO', eras.Run2_2018) + +# import of standard configurations +process.load('Configuration.StandardSequences.Services_cff') +process.load('FWCore.MessageService.MessageLogger_cfi') +process.load('HeterogeneousCore.CUDAServices.CUDAService_cfi') +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load('Configuration.StandardSequences.MagneticField_AutoFromDBCurrent_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') + +# Other statements +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, '102X_dataRun2_HLT_v2', '') + + +process.maxEvents = cms.untracked.PSet( + input = cms.untracked.int32(100) +) + +# load data using the DAQ source +import sys, os, inspect +sys.path.append(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) +process.load('sourceFromRawCmggpu_cff') + +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load("RecoLocalCalo.Configuration.hcalLocalReco_cff") +process.load("EventFilter.HcalRawToDigi.HcalRawToDigi_cfi") +process.load("EventFilter.EcalRawToDigi.EcalUnpackerData_cfi") +process.load("RecoLuminosity.LumiProducer.bunchSpacingProducer_cfi") + +# load both cpu and gpu plugins +process.load("RecoLocalCalo.EcalRecProducers.ecalUncalibRecHitProducerGPU_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalMultiFitUncalibRecHit_cfi") + +# for validation of gpu multifit products +process.load("RecoLocalCalo.EcalRecProducers.ecalCPUUncalibRecHitProducer_cfi") +process.load("EventFilter.EcalRawToDigi.ecalCPUDigisProducer_cfi") + +process.load("EventFilter.EcalRawToDigi.ecalRawToDigiGPU_cfi") +process.load("EventFilter.EcalRawToDigi.ecalElectronicsMappingGPUESProducer_cfi") + +process.load("RecoLocalCalo.EcalRecProducers.ecalPedestalsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalGainRatiosGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalPulseShapesGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalPulseCovariancesGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalSamplesCorrelationGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalTimeBiasCorrectionsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalTimeCalibConstantsGPUESProducer_cfi") + +process.load("RecoLocalCalo.EcalRecProducers.ecalRechitADCToGeVConstantGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalIntercalibConstantsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalRechitChannelStatusGPUESProducer_cfi") + +process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosRefGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAlphasGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalLinearCorrectionsGPUESProducer_cfi") + +# force HLT configuration for ecalMultiFitUncalibRecHit +process.ecalMultiFitUncalibRecHit.algoPSet = cms.PSet( + ebSpikeThreshold = cms.double(1.042), + EBtimeFitLimits_Upper = cms.double(1.4), + EEtimeFitLimits_Lower = cms.double(0.2), + timealgo = cms.string("None"), + EBtimeNconst = cms.double(28.5), + prefitMaxChiSqEE = cms.double(10.0), + outOfTimeThresholdGain12mEB = cms.double(5.0), + outOfTimeThresholdGain12mEE = cms.double(1000.0), + EEtimeFitParameters = cms.vdouble(-2.390548, 3.553628, -17.62341, 67.67538, -133.213, 140.7432, -75.41106, 16.20277), + prefitMaxChiSqEB = cms.double(25.0), + simplifiedNoiseModelForGainSwitch = cms.bool(True), + EBtimeFitParameters = cms.vdouble(-2.015452, 3.130702, -12.3473, 41.88921, -82.83944, 91.01147, -50.35761, 11.05621), + selectiveBadSampleCriteriaEB = cms.bool(False), + dynamicPedestalsEB = cms.bool(False), + useLumiInfoRunHeader = cms.bool(False), + EBamplitudeFitParameters = cms.vdouble(1.138, 1.652), + doPrefitEE = cms.bool(False), + dynamicPedestalsEE = cms.bool(False), + selectiveBadSampleCriteriaEE = cms.bool(False), + outOfTimeThresholdGain61pEE = cms.double(1000.0), + outOfTimeThresholdGain61pEB = cms.double(5.0), + activeBXs = cms.vint32(-5, -4, -3, -2, -1, 0, 1, 2, 3, 4), + EcalPulseShapeParameters = cms.PSet( + EEPulseShapeTemplate = cms.vdouble(0.116442, 0.756246, 1.0, 0.897182, 0.686831, 0.491506, 0.344111, 0.245731, 0.174115, 0.123361, 0.0874288, 0.061957), + EEdigiCollection = cms.string(""), + EcalPreMixStage2 = cms.bool(False), + EcalPreMixStage1 = cms.bool(False), + EBPulseShapeCovariance = cms.vdouble(3.001E-6, 1.233E-5, 0.0, -4.416E-6, -4.571E-6, -3.614E-6, -2.636E-6, -1.286E-6, -8.41E-7, -5.296E-7, 0.0, 0.0, 1.233E-5, 6.154E-5, 0.0, -2.2E-5, -2.309E-5, -1.838E-5, -1.373E-5, -7.334E-6, -5.088E-6, -3.745E-6, -2.428E-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.416E-6, -2.2E-5, 0.0, 8.319E-6, 8.545E-6, 6.792E-6, 5.059E-6, 2.678E-6, 1.816E-6, 1.223E-6, 8.245E-7, 5.589E-7, -4.571E-6, -2.309E-5, 0.0, 8.545E-6, 9.182E-6, 7.219E-6, 5.388E-6, 2.853E-6, 1.944E-6, 1.324E-6, 9.083E-7, 6.335E-7, -3.614E-6, -1.838E-5, 0.0, 6.792E-6, 7.219E-6, 6.016E-6, 4.437E-6, 2.385E-6, 1.636E-6, 1.118E-6, 7.754E-7, 5.556E-7, -2.636E-6, -1.373E-5, 0.0, 5.059E-6, 5.388E-6, 4.437E-6, 3.602E-6, 1.917E-6, 1.322E-6, 9.079E-7, 6.529E-7, 4.752E-7, -1.286E-6, -7.334E-6, 0.0, 2.678E-6, 2.853E-6, 2.385E-6, 1.917E-6, 1.375E-6, 9.1E-7, 6.455E-7, 4.693E-7, 3.657E-7, -8.41E-7, -5.088E-6, 0.0, 1.816E-6, 1.944E-6, 1.636E-6, 1.322E-6, 9.1E-7, 9.115E-7, 6.062E-7, 4.436E-7, 3.422E-7, -5.296E-7, -3.745E-6, 0.0, 1.223E-6, 1.324E-6, 1.118E-6, 9.079E-7, 6.455E-7, 6.062E-7, 7.217E-7, 4.862E-7, 3.768E-7, 0.0, -2.428E-6, 0.0, 8.245E-7, 9.083E-7, 7.754E-7, 6.529E-7, 4.693E-7, 4.436E-7, 4.862E-7, 6.509E-7, 4.418E-7, 0.0, 0.0, 0.0, 5.589E-7, 6.335E-7, 5.556E-7, 4.752E-7, 3.657E-7, 3.422E-7, 3.768E-7, 4.418E-7, 6.142E-7), + ESdigiCollection = cms.string(""), + EBdigiCollection = cms.string(""), + EBCorrNoiseMatrixG01 = cms.vdouble(1.0, 0.73354, 0.64442, 0.58851, 0.55425, 0.53082, 0.51916, 0.51097, 0.50732, 0.50409), + EBCorrNoiseMatrixG12 = cms.vdouble(1.0, 0.71073, 0.55721, 0.46089, 0.40449, 0.35931, 0.33924, 0.32439, 0.31581, 0.30481), + EBCorrNoiseMatrixG06 = cms.vdouble(1.0, 0.70946, 0.58021, 0.49846, 0.45006, 0.41366, 0.39699, 0.38478, 0.37847, 0.37055), + EEPulseShapeCovariance = cms.vdouble(3.941E-5, 3.333E-5, 0.0, -1.449E-5, -1.661E-5, -1.424E-5, -1.183E-5, -6.842E-6, -4.915E-6, -3.411E-6, 0.0, 0.0, 3.333E-5, 2.862E-5, 0.0, -1.244E-5, -1.431E-5, -1.233E-5, -1.032E-5, -5.883E-6, -4.154E-6, -2.902E-6, -2.128E-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.449E-5, -1.244E-5, 0.0, 5.84E-6, 6.649E-6, 5.72E-6, 4.812E-6, 2.708E-6, 1.869E-6, 1.33E-6, 9.186E-7, 6.446E-7, -1.661E-5, -1.431E-5, 0.0, 6.649E-6, 7.966E-6, 6.898E-6, 5.794E-6, 3.157E-6, 2.184E-6, 1.567E-6, 1.084E-6, 7.575E-7, -1.424E-5, -1.233E-5, 0.0, 5.72E-6, 6.898E-6, 6.341E-6, 5.347E-6, 2.859E-6, 1.991E-6, 1.431E-6, 9.839E-7, 6.886E-7, -1.183E-5, -1.032E-5, 0.0, 4.812E-6, 5.794E-6, 5.347E-6, 4.854E-6, 2.628E-6, 1.809E-6, 1.289E-6, 9.02E-7, 6.146E-7, -6.842E-6, -5.883E-6, 0.0, 2.708E-6, 3.157E-6, 2.859E-6, 2.628E-6, 1.863E-6, 1.296E-6, 8.882E-7, 6.108E-7, 4.283E-7, -4.915E-6, -4.154E-6, 0.0, 1.869E-6, 2.184E-6, 1.991E-6, 1.809E-6, 1.296E-6, 1.217E-6, 8.669E-7, 5.751E-7, 3.882E-7, -3.411E-6, -2.902E-6, 0.0, 1.33E-6, 1.567E-6, 1.431E-6, 1.289E-6, 8.882E-7, 8.669E-7, 9.522E-7, 6.717E-7, 4.293E-7, 0.0, -2.128E-6, 0.0, 9.186E-7, 1.084E-6, 9.839E-7, 9.02E-7, 6.108E-7, 5.751E-7, 6.717E-7, 7.911E-7, 5.493E-7, 0.0, 0.0, 0.0, 6.446E-7, 7.575E-7, 6.886E-7, 6.146E-7, 4.283E-7, 3.882E-7, 4.293E-7, 5.493E-7, 7.027E-7), + EBPulseShapeTemplate = cms.vdouble(0.0113979, 0.758151, 1.0, 0.887744, 0.673548, 0.474332, 0.319561, 0.215144, 0.147464, 0.101087, 0.0693181, 0.0475044), + EECorrNoiseMatrixG01 = cms.vdouble(1.0, 0.72698, 0.62048, 0.55691, 0.51848, 0.49147, 0.47813, 0.47007, 0.46621, 0.46265), + EECorrNoiseMatrixG12 = cms.vdouble(1.0, 0.71373, 0.44825, 0.30152, 0.21609, 0.14786, 0.11772, 0.10165, 0.09465, 0.08098), + UseLCcorrection = cms.untracked.bool(True), + EECorrNoiseMatrixG06 = cms.vdouble(1.0, 0.71217, 0.47464, 0.34056, 0.26282, 0.20287, 0.17734, 0.16256, 0.15618, 0.14443) + ), + doPrefitEB = cms.bool(False), + addPedestalUncertaintyEE = cms.double(0.0), + addPedestalUncertaintyEB = cms.double(0.0), + gainSwitchUseMaxSampleEB = cms.bool(True), + EEtimeNconst = cms.double(31.8), + EEamplitudeFitParameters = cms.vdouble(1.89, 1.4), + chi2ThreshEE_ = cms.double(50.0), + eePulseShape = cms.vdouble(5.2E-5, -5.26E-5, 6.66E-5, 0.1168, 0.7575, 1.0, 0.8876, 0.6732, 0.4741, 0.3194), + outOfTimeThresholdGain12pEB = cms.double(5.0), + gainSwitchUseMaxSampleEE = cms.bool(False), + mitigateBadSamplesEB = cms.bool(False), + outOfTimeThresholdGain12pEE = cms.double(1000.0), + ebPulseShape = cms.vdouble(5.2E-5, -5.26E-5, 6.66E-5, 0.1168, 0.7575, 1.0, 0.8876, 0.6732, 0.4741, 0.3194), + ampErrorCalculation = cms.bool(False), + mitigateBadSamplesEE = cms.bool(False), + amplitudeThresholdEB = cms.double(10.0), + kPoorRecoFlagEB = cms.bool(True), + amplitudeThresholdEE = cms.double(10.0), + EBtimeFitLimits_Lower = cms.double(0.2), + kPoorRecoFlagEE = cms.bool(False), + EEtimeFitLimits_Upper = cms.double(1.4), + outOfTimeThresholdGain61mEE = cms.double(1000.0), + EEtimeConstantTerm = cms.double(1.0), + EBtimeConstantTerm = cms.double(0.6), + chi2ThreshEB_ = cms.double(65.0), + outOfTimeThresholdGain61mEB = cms.double(5.0) +) + +process.ecalDigis = process.ecalEBunpacker.clone() +process.ecalDigis.InputLabel = cms.InputTag('rawDataCollector') + +process.out = cms.OutputModule("PoolOutputModule", + fileName = cms.untracked.string("test.root") +) + +process.finalize = cms.EndPath(process.out) + +process.bunchSpacing = cms.Path( + process.bunchSpacingProducer +) + +process.digiPath = cms.Path( + process.ecalDigis + + process.ecalRawToDigiGPU + + process.ecalCPUDigisProducer +) + +process.recoPath = cms.Path( + process.ecalMultiFitUncalibRecHit + + process.ecalUncalibRecHitProducerGPU + + process.ecalCPUUncalibRecHitProducer +) + +process.schedule = cms.Schedule( + process.bunchSpacing, + process.digiPath, + process.recoPath, + process.finalize +) + +process.options = cms.untracked.PSet( + numberOfThreads = cms.untracked.uint32(4), + numberOfStreams = cms.untracked.uint32(4), + SkipEvent = cms.untracked.vstring('ProductNotFound'), + wantSummary = cms.untracked.bool(True) +) + +# report CUDAService messages +process.MessageLogger.categories.append("CUDAService") diff --git a/RecoLocalCalo/EcalRecProducers/test/sourceFromRawCmggpu_cff.py b/RecoLocalCalo/EcalRecProducers/test/sourceFromRawCmggpu_cff.py new file mode 100644 index 0000000000000..e993a7573b689 --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/test/sourceFromRawCmggpu_cff.py @@ -0,0 +1,151 @@ +import FWCore.ParameterSet.Config as cms + +# input +FastMonitoringService = cms.Service( "FastMonitoringService", + filePerFwkStream = cms.untracked.bool( False ), + fastMonIntervals = cms.untracked.uint32( 2 ), + sleepTime = cms.untracked.int32( 1 ) +) + +EvFDaqDirector = cms.Service( "EvFDaqDirector", + runNumber = cms.untracked.uint32( 321177 ), + + baseDir = cms.untracked.string( "tmp" ), + buBaseDir = cms.untracked.string( "tmp" ), + + useFileBroker = cms.untracked.bool( False ), + fileBrokerKeepAlive = cms.untracked.bool( True ), + fileBrokerPort = cms.untracked.string( "8080" ), + fileBrokerUseLocalLock = cms.untracked.bool( True ), + fuLockPollInterval = cms.untracked.uint32( 2000 ), + + requireTransfersPSet = cms.untracked.bool( False ), + selectedTransferMode = cms.untracked.string( "" ), + mergingPset = cms.untracked.string( "" ), + + outputAdler32Recheck = cms.untracked.bool( False ), +) + +source = cms.Source( "FedRawDataInputSource", + runNumber = cms.untracked.uint32( 321177 ), + getLSFromFilename = cms.untracked.bool(True), + testModeNoBuilderUnit = cms.untracked.bool(False), + verifyAdler32 = cms.untracked.bool( True ), + verifyChecksum = cms.untracked.bool( True ), + useL1EventID = cms.untracked.bool( False ), # True + alwaysStartFromfirstLS = cms.untracked.uint32( 0 ), + + eventChunkBlock = cms.untracked.uint32( 240 ), # 32 + eventChunkSize = cms.untracked.uint32( 240), # 32 + maxBufferedFiles = cms.untracked.uint32( 8 ), # 2 + numBuffers = cms.untracked.uint32( 8 ), # 2 + + fileListMode = cms.untracked.bool( True ), # False + fileNames = cms.untracked.vstring( + #'/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0142_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0142_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0142_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0142_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0142_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0143_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0143_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0143_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0143_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0143_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0144_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0144_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0144_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0144_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0144_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0145_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0145_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0145_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0145_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0145_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0146_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0146_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0146_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0146_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0146_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0147_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0147_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0147_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0147_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0147_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0148_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0148_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0148_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0148_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0148_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0149_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0149_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0149_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0149_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0149_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0150_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0150_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0150_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0150_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0150_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0151_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0151_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0151_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0151_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0151_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0152_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0152_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0152_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0152_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0152_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0153_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0153_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0153_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0153_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0153_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0154_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0154_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0154_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0154_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0154_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0155_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0155_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0155_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0155_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0155_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0156_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0156_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0156_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0156_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0156_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0157_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0157_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0157_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0157_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0157_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0158_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0158_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0158_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0158_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0158_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0159_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0159_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0159_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0159_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0159_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0160_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0160_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0160_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0160_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0160_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0161_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0161_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0161_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0161_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0161_index000004.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0162_index000000.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0162_index000001.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0162_index000002.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0162_index000003.raw', + '/data/patatrack/store/raw/Run2018D/JetHT/RAW/v1/000/321/177/00000/run321177_ls0162_index000004.raw', + ), +) \ No newline at end of file diff --git a/RecoLocalCalo/EcalRecProducers/test/testEcalRechitProducer_cfg.py b/RecoLocalCalo/EcalRecProducers/test/testEcalRechitProducer_cfg.py new file mode 100644 index 0000000000000..c70572ff3b89d --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/test/testEcalRechitProducer_cfg.py @@ -0,0 +1,322 @@ +import FWCore.ParameterSet.Config as cms + +from Configuration.StandardSequences.Eras import eras +#from Configuration.ProcessModifiers.gpu_cff import gpu + +process = cms.Process('RECO', eras.Run2_2018) + +# import of standard configurations +process.load('Configuration.StandardSequences.Services_cff') +#process.load('SimGeneral.HepPDTESSource.pythiapdt_cfi') +process.load('FWCore.MessageService.MessageLogger_cfi') +process.load('HeterogeneousCore.CUDAServices.CUDAService_cfi') +#process.load('Configuration.EventContent.EventContent_cff') +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load('Configuration.StandardSequences.MagneticField_AutoFromDBCurrent_cff') +#process.load('Configuration.StandardSequences.RawToDigi_Data_cff') +#process.load('Configuration.StandardSequences.Reconstruction_Data_cff') +#process.load('DQMOffline.Configuration.DQMOffline_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') + + + + + + +# Other statements +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, '102X_dataRun2_HLT_v2', '') + + +process.maxEvents = cms.untracked.PSet( + input = cms.untracked.int32(1000) +) + +# load data using the DAQ source +import sys, os, inspect +sys.path.append(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) +process.load('sourceFromRawCmggpu_cff') + +#----------------------------------------- +# CMSSW/Hcal non-DQM Related Module import +#----------------------------------------- +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load("RecoLocalCalo.Configuration.hcalLocalReco_cff") +#process.load("RecoLocalCalo.Configuration.ecalLocalRecoSequence_cff") +process.load("EventFilter.HcalRawToDigi.HcalRawToDigi_cfi") +process.load("EventFilter.EcalRawToDigi.EcalUnpackerData_cfi") +process.load("RecoLuminosity.LumiProducer.bunchSpacingProducer_cfi") + +# load both cpu and gpu plugins +# +# ../cfipython/slc7_amd64_gcc700/RecoLocalCalo/EcalRecProducers/ecalUncalibRecHitProducerGPU_cfi.py +# +process.load("RecoLocalCalo.EcalRecProducers.ecalUncalibRecHitProducerGPU_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalMultiFitUncalibRecHit_cfi") + +# for validation of gpu multifit products +process.load("RecoLocalCalo.EcalRecProducers.ecalCPUUncalibRecHitProducer_cfi") +process.load("EventFilter.EcalRawToDigi.ecalCPUDigisProducer_cfi") + +process.load("EventFilter.EcalRawToDigi.ecalRawToDigiGPU_cfi") +process.load("EventFilter.EcalRawToDigi.ecalElectronicsMappingGPUESProducer_cfi") + +#process.ecalUncalibRecHitProducerGPU.kernelsVersion = 0 +#process.ecalUncalibRecHitProducerGPU.kernelMinimizeThreads = cms.vuint32(16, 1, 1) + +process.load("RecoLocalCalo.EcalRecProducers.ecalPedestalsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalGainRatiosGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalPulseShapesGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalPulseCovariancesGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalSamplesCorrelationGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalTimeBiasCorrectionsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalTimeCalibConstantsGPUESProducer_cfi") + +#process.ecalMultiFitUncalibRecHitgpu.algoPSet.threads = cms.vint32(256, 1, 1) + +#from RecoLocalCalo.EcalRecProducers.ecalMultifitParametersGPUESProducer_cfi import ecalMultifitParametersGPUESProducer +process.load("RecoLocalCalo.EcalRecProducers.ecalMultifitParametersGPUESProducer_cfi") + +# +# +# No "JobConfigurationGPURecord" record found in the EventSetup.n +# #---> +# +process.load("RecoLocalCalo.EcalRecProducers.ecalRecHitParametersGPUESProducer_cfi") +#ecalRecHitParametersGPUESProducer_cfi.py + + +## +## force HLT configuration for ecalMultiFitUncalibRecHit +## + +process.ecalMultiFitUncalibRecHit.algoPSet = cms.PSet( + ebSpikeThreshold = cms.double( 1.042 ), + EBtimeFitLimits_Upper = cms.double( 1.4 ), + EEtimeFitLimits_Lower = cms.double( 0.2 ), + timealgo = cms.string( "None" ), + EBtimeNconst = cms.double( 28.5 ), + prefitMaxChiSqEE = cms.double( 10.0 ), + outOfTimeThresholdGain12mEB = cms.double( 5.0 ), + outOfTimeThresholdGain12mEE = cms.double( 1000.0 ), + EEtimeFitParameters = cms.vdouble( -2.390548, 3.553628, -17.62341, 67.67538, -133.213, 140.7432, -75.41106, 16.20277 ), + prefitMaxChiSqEB = cms.double( 25.0 ), + simplifiedNoiseModelForGainSwitch = cms.bool( True ), + EBtimeFitParameters = cms.vdouble( -2.015452, 3.130702, -12.3473, 41.88921, -82.83944, 91.01147, -50.35761, 11.05621 ), + selectiveBadSampleCriteriaEB = cms.bool( False ), + dynamicPedestalsEB = cms.bool( False ), + useLumiInfoRunHeader = cms.bool( False ), + EBamplitudeFitParameters = cms.vdouble( 1.138, 1.652 ), + doPrefitEE = cms.bool( False ), + dynamicPedestalsEE = cms.bool( False ), + selectiveBadSampleCriteriaEE = cms.bool( False ), + outOfTimeThresholdGain61pEE = cms.double( 1000.0 ), + outOfTimeThresholdGain61pEB = cms.double( 5.0 ), + activeBXs = cms.vint32( -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 ), + EcalPulseShapeParameters = cms.PSet( + EEPulseShapeTemplate = cms.vdouble( 0.116442, 0.756246, 1.0, 0.897182, 0.686831, 0.491506, 0.344111, 0.245731, 0.174115, 0.123361, 0.0874288, 0.061957 ), + EEdigiCollection = cms.string( "" ), + EcalPreMixStage2 = cms.bool( False ), + EcalPreMixStage1 = cms.bool( False ), + EBPulseShapeCovariance = cms.vdouble( 3.001E-6, 1.233E-5, 0.0, -4.416E-6, -4.571E-6, -3.614E-6, -2.636E-6, -1.286E-6, -8.41E-7, -5.296E-7, 0.0, 0.0, 1.233E-5, 6.154E-5, 0.0, -2.2E-5, -2.309E-5, -1.838E-5, -1.373E-5, -7.334E-6, -5.088E-6, -3.745E-6, -2.428E-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.416E-6, -2.2E-5, 0.0, 8.319E-6, 8.545E-6, 6.792E-6, 5.059E-6, 2.678E-6, 1.816E-6, 1.223E-6, 8.245E-7, 5.589E-7, -4.571E-6, -2.309E-5, 0.0, 8.545E-6, 9.182E-6, 7.219E-6, 5.388E-6, 2.853E-6, 1.944E-6, 1.324E-6, 9.083E-7, 6.335E-7, -3.614E-6, -1.838E-5, 0.0, 6.792E-6, 7.219E-6, 6.016E-6, 4.437E-6, 2.385E-6, 1.636E-6, 1.118E-6, 7.754E-7, 5.556E-7, -2.636E-6, -1.373E-5, 0.0, 5.059E-6, 5.388E-6, 4.437E-6, 3.602E-6, 1.917E-6, 1.322E-6, 9.079E-7, 6.529E-7, 4.752E-7, -1.286E-6, -7.334E-6, 0.0, 2.678E-6, 2.853E-6, 2.385E-6, 1.917E-6, 1.375E-6, 9.1E-7, 6.455E-7, 4.693E-7, 3.657E-7, -8.41E-7, -5.088E-6, 0.0, 1.816E-6, 1.944E-6, 1.636E-6, 1.322E-6, 9.1E-7, 9.115E-7, 6.062E-7, 4.436E-7, 3.422E-7, -5.296E-7, -3.745E-6, 0.0, 1.223E-6, 1.324E-6, 1.118E-6, 9.079E-7, 6.455E-7, 6.062E-7, 7.217E-7, 4.862E-7, 3.768E-7, 0.0, -2.428E-6, 0.0, 8.245E-7, 9.083E-7, 7.754E-7, 6.529E-7, 4.693E-7, 4.436E-7, 4.862E-7, 6.509E-7, 4.418E-7, 0.0, 0.0, 0.0, 5.589E-7, 6.335E-7, 5.556E-7, 4.752E-7, 3.657E-7, 3.422E-7, 3.768E-7, 4.418E-7, 6.142E-7 ), + ESdigiCollection = cms.string( "" ), + EBdigiCollection = cms.string( "" ), + EBCorrNoiseMatrixG01 = cms.vdouble( 1.0, 0.73354, 0.64442, 0.58851, 0.55425, 0.53082, 0.51916, 0.51097, 0.50732, 0.50409 ), + EBCorrNoiseMatrixG12 = cms.vdouble( 1.0, 0.71073, 0.55721, 0.46089, 0.40449, 0.35931, 0.33924, 0.32439, 0.31581, 0.30481 ), + EBCorrNoiseMatrixG06 = cms.vdouble( 1.0, 0.70946, 0.58021, 0.49846, 0.45006, 0.41366, 0.39699, 0.38478, 0.37847, 0.37055 ), + EEPulseShapeCovariance = cms.vdouble( 3.941E-5, 3.333E-5, 0.0, -1.449E-5, -1.661E-5, -1.424E-5, -1.183E-5, -6.842E-6, -4.915E-6, -3.411E-6, 0.0, 0.0, 3.333E-5, 2.862E-5, 0.0, -1.244E-5, -1.431E-5, -1.233E-5, -1.032E-5, -5.883E-6, -4.154E-6, -2.902E-6, -2.128E-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.449E-5, -1.244E-5, 0.0, 5.84E-6, 6.649E-6, 5.72E-6, 4.812E-6, 2.708E-6, 1.869E-6, 1.33E-6, 9.186E-7, 6.446E-7, -1.661E-5, -1.431E-5, 0.0, 6.649E-6, 7.966E-6, 6.898E-6, 5.794E-6, 3.157E-6, 2.184E-6, 1.567E-6, 1.084E-6, 7.575E-7, -1.424E-5, -1.233E-5, 0.0, 5.72E-6, 6.898E-6, 6.341E-6, 5.347E-6, 2.859E-6, 1.991E-6, 1.431E-6, 9.839E-7, 6.886E-7, -1.183E-5, -1.032E-5, 0.0, 4.812E-6, 5.794E-6, 5.347E-6, 4.854E-6, 2.628E-6, 1.809E-6, 1.289E-6, 9.02E-7, 6.146E-7, -6.842E-6, -5.883E-6, 0.0, 2.708E-6, 3.157E-6, 2.859E-6, 2.628E-6, 1.863E-6, 1.296E-6, 8.882E-7, 6.108E-7, 4.283E-7, -4.915E-6, -4.154E-6, 0.0, 1.869E-6, 2.184E-6, 1.991E-6, 1.809E-6, 1.296E-6, 1.217E-6, 8.669E-7, 5.751E-7, 3.882E-7, -3.411E-6, -2.902E-6, 0.0, 1.33E-6, 1.567E-6, 1.431E-6, 1.289E-6, 8.882E-7, 8.669E-7, 9.522E-7, 6.717E-7, 4.293E-7, 0.0, -2.128E-6, 0.0, 9.186E-7, 1.084E-6, 9.839E-7, 9.02E-7, 6.108E-7, 5.751E-7, 6.717E-7, 7.911E-7, 5.493E-7, 0.0, 0.0, 0.0, 6.446E-7, 7.575E-7, 6.886E-7, 6.146E-7, 4.283E-7, 3.882E-7, 4.293E-7, 5.493E-7, 7.027E-7 ), + EBPulseShapeTemplate = cms.vdouble( 0.0113979, 0.758151, 1.0, 0.887744, 0.673548, 0.474332, 0.319561, 0.215144, 0.147464, 0.101087, 0.0693181, 0.0475044 ), + EECorrNoiseMatrixG01 = cms.vdouble( 1.0, 0.72698, 0.62048, 0.55691, 0.51848, 0.49147, 0.47813, 0.47007, 0.46621, 0.46265 ), + EECorrNoiseMatrixG12 = cms.vdouble( 1.0, 0.71373, 0.44825, 0.30152, 0.21609, 0.14786, 0.11772, 0.10165, 0.09465, 0.08098 ), + UseLCcorrection = cms.untracked.bool( True ), + EECorrNoiseMatrixG06 = cms.vdouble( 1.0, 0.71217, 0.47464, 0.34056, 0.26282, 0.20287, 0.17734, 0.16256, 0.15618, 0.14443 ) + ), + doPrefitEB = cms.bool( False ), + addPedestalUncertaintyEE = cms.double( 0.0 ), + addPedestalUncertaintyEB = cms.double( 0.0 ), + gainSwitchUseMaxSampleEB = cms.bool( True ), + EEtimeNconst = cms.double( 31.8 ), + EEamplitudeFitParameters = cms.vdouble( 1.89, 1.4 ), + chi2ThreshEE_ = cms.double( 50.0 ), + eePulseShape = cms.vdouble( 5.2E-5, -5.26E-5, 6.66E-5, 0.1168, 0.7575, 1.0, 0.8876, 0.6732, 0.4741, 0.3194 ), + outOfTimeThresholdGain12pEB = cms.double( 5.0 ), + gainSwitchUseMaxSampleEE = cms.bool( False ), + mitigateBadSamplesEB = cms.bool( False ), + outOfTimeThresholdGain12pEE = cms.double( 1000.0 ), + ebPulseShape = cms.vdouble( 5.2E-5, -5.26E-5, 6.66E-5, 0.1168, 0.7575, 1.0, 0.8876, 0.6732, 0.4741, 0.3194 ), + ampErrorCalculation = cms.bool( False ), + mitigateBadSamplesEE = cms.bool( False ), + amplitudeThresholdEB = cms.double( 10.0 ), + kPoorRecoFlagEB = cms.bool( True ), + amplitudeThresholdEE = cms.double( 10.0 ), + EBtimeFitLimits_Lower = cms.double( 0.2 ), + kPoorRecoFlagEE = cms.bool( False ), + EEtimeFitLimits_Upper = cms.double( 1.4 ), + outOfTimeThresholdGain61mEE = cms.double( 1000.0 ), + EEtimeConstantTerm = cms.double( 1.0 ), + EBtimeConstantTerm = cms.double( 0.6 ), + chi2ThreshEB_ = cms.double( 65.0 ), + outOfTimeThresholdGain61mEB = cms.double( 5.0 ) +) + +## + + + +process.load('Configuration.StandardSequences.Reconstruction_cff') +#process.ecalRecHit + + +process.load("RecoLocalCalo.EcalRecProducers.ecalRechitADCToGeVConstantGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalRechitChannelStatusGPUESProducer_cfi") +#process.load("RecoLocalCalo.EcalRecProducers.ecalADCToGeVConstantGPUESProducer_cfi") +#process.load("RecoLocalCalo.EcalRecProducers.ecalChannelStatusGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalIntercalibConstantsGPUESProducer_cfi") + +process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAPDPNRatiosRefGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalLaserAlphasGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalLinearCorrectionsGPUESProducer_cfi") + +process.load("RecoLocalCalo.EcalRecProducers.ecalRecHitGPU_cfi") +process.ecalRecHitProducerGPU = process.ecalRecHitGPU.clone() + + +process.load("RecoLocalCalo.EcalRecProducers.ecalCPURecHitProducer_cfi") + + +# +# AM : TEST to see if the number of rechits matches +# +#process.ecalRecHit.killDeadChannels = cms.bool(False) +# +#process.ecalRecHit.recoverEBFE = cms.bool(False) +#process.ecalRecHit.recoverEBIsolatedChannels = cms.bool(False) +#process.ecalRecHit.recoverEBVFE = cms.bool(False) +## +#process.ecalRecHit.recoverEEFE = cms.bool(False) +#process.ecalRecHit.recoverEEIsolatedChannels = cms.bool(False) +#process.ecalRecHit.recoverEEVFE = cms.bool(False) +# +#process.ecalRecHit.skipTimeCalib = cms.bool(True) +# +#process.ecalRecHitProducerGPU.killDeadChannels = cms.bool(False) +# +# +#process.ecalRecHitProducerGPU.recoverEBFE = cms.bool(False) +#process.ecalRecHitProducerGPU.recoverEBIsolatedChannels = cms.bool(False) +#process.ecalRecHitProducerGPU.recoverEBVFE = cms.bool(False) +#process.ecalRecHitProducerGPU.recoverEEFE = cms.bool(False) +#process.ecalRecHitProducerGPU.recoverEEIsolatedChannels = cms.bool(False) +#process.ecalRecHitProducerGPU.recoverEEVFE = cms.bool(False) +# +# +# +# TEST +# +#process.ecalRecHit.ChannelStatusToBeExcluded = cms.vstring( + #'kDAC', + #'kNoisy', + #'kNNoisy', + #'kFixedG6', + #'kFixedG1', + #'kFixedG0', + #'kNonRespondingIsolated', + #'kDeadVFE', + #'kDeadFE', + #'kNoDataNoTP' + #) +#process.ecalRecHitProducerGPU.ChannelStatusToBeExcluded = cms.vstring( + #'kDAC', + #'kNoisy', + #'kNNoisy', + #'kFixedG6', + #'kFixedG1', + #'kFixedG0', + #'kNonRespondingIsolated', + #'kDeadVFE', + #'kDeadFE', + #'kNoDataNoTP' + #) +# +# + + #ChannelStatusToBeExcluded = cms.vstring( + #'kDAC', + #'kNoisy', + #'kNNoisy', + #'kFixedG6', + #'kFixedG1', + #'kFixedG0', + #'kNonRespondingIsolated', + #'kDeadVFE', + #'kDeadFE', + #'kNoDataNoTP' + #), + + + +#process.hcalDigis.silent = cms.untracked.bool(False) +#process.hcalDigis.InputLabel = rawTag +process.ecalDigis = process.ecalEBunpacker.clone() +process.ecalDigis.InputLabel = cms.InputTag('rawDataCollector') +#process.hbheprerecogpu.processQIE11 = cms.bool(True) + +process.out = cms.OutputModule( + "PoolOutputModule", + fileName = cms.untracked.string("testRechit.root") +) + +#process.out = cms.OutputModule("AsciiOutputModule", +# outputCommands = cms.untracked.vstring( +# 'keep *_ecalMultiFitUncalibRecHit_*_*', +# ), +# verbosity = cms.untracked.uint32(0) +#) +process.finalize = cms.EndPath(process.out) + +process.bunchSpacing = cms.Path( + process.bunchSpacingProducer +) + +process.digiPath = cms.Path( + #process.hcalDigis + process.ecalDigis + *process.ecalRawToDigiGPU + *process.ecalCPUDigisProducer +) + +process.recoPath = cms.Path( + (process.ecalMultiFitUncalibRecHit+process.ecalDetIdToBeRecovered) + #process.ecalMultiFitUncalibRecHit + *process.ecalRecHit +# gpu + *process.ecalUncalibRecHitProducerGPU + *process.ecalCPUUncalibRecHitProducer + *process.ecalRecHitProducerGPU + *process.ecalCPURecHitProducer +) + +process.schedule = cms.Schedule( + process.bunchSpacing, + process.digiPath, + process.recoPath, +# process.ecalecalLocalRecoSequence + process.finalize +) + +process.options = cms.untracked.PSet( + numberOfThreads = cms.untracked.uint32(4), + numberOfStreams = cms.untracked.uint32(4), + SkipEvent = cms.untracked.vstring('ProductNotFound'), + wantSummary = cms.untracked.bool(True) +) + +# report CUDAService messages +process.MessageLogger.categories.append("CUDAService") + + +# +#process.DependencyGraph = cms.Service("DependencyGraph") + + diff --git a/RecoLocalCalo/EcalRecProducers/test/testEcalUncalibRechitProducer_cfg.py b/RecoLocalCalo/EcalRecProducers/test/testEcalUncalibRechitProducer_cfg.py new file mode 100644 index 0000000000000..ffb665d7bc96a --- /dev/null +++ b/RecoLocalCalo/EcalRecProducers/test/testEcalUncalibRechitProducer_cfg.py @@ -0,0 +1,233 @@ +import FWCore.ParameterSet.Config as cms + +from Configuration.StandardSequences.Eras import eras +#from Configuration.ProcessModifiers.gpu_cff import gpu + +process = cms.Process('RECO', eras.Run2_2018) + +# import of standard configurations +process.load('Configuration.StandardSequences.Services_cff') +#process.load('SimGeneral.HepPDTESSource.pythiapdt_cfi') +process.load('FWCore.MessageService.MessageLogger_cfi') +process.load('HeterogeneousCore.CUDAServices.CUDAService_cfi') +#process.load('Configuration.EventContent.EventContent_cff') +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load('Configuration.StandardSequences.MagneticField_AutoFromDBCurrent_cff') +#process.load('Configuration.StandardSequences.RawToDigi_Data_cff') +#process.load('Configuration.StandardSequences.Reconstruction_Data_cff') +#process.load('DQMOffline.Configuration.DQMOffline_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') + + + + + + +# Other statements +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, '102X_dataRun2_HLT_v2', '') + + +process.maxEvents = cms.untracked.PSet( + #input = cms.untracked.int32(100) + input = cms.untracked.int32(1000) +) + +# load data using the DAQ source +import sys, os, inspect +sys.path.append(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) +process.load('sourceFromRawCmggpu_cff') + +#----------------------------------------- +# CMSSW/Hcal non-DQM Related Module import +#----------------------------------------- +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load("RecoLocalCalo.Configuration.hcalLocalReco_cff") +#process.load("RecoLocalCalo.Configuration.ecalLocalRecoSequence_cff") +process.load("EventFilter.HcalRawToDigi.HcalRawToDigi_cfi") +process.load("EventFilter.EcalRawToDigi.EcalUnpackerData_cfi") +process.load("RecoLuminosity.LumiProducer.bunchSpacingProducer_cfi") + +# load both cpu and gpu plugins +# +# ../cfipython/slc7_amd64_gcc700/RecoLocalCalo/EcalRecProducers/ecalUncalibRecHitProducerGPU_cfi.py +# +process.load("RecoLocalCalo.EcalRecProducers.ecalUncalibRecHitProducerGPU_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalMultiFitUncalibRecHit_cfi") + +# for validation of gpu multifit products +process.load("RecoLocalCalo.EcalRecProducers.ecalCPUUncalibRecHitProducer_cfi") +# +# ../cfipython/slc7_amd64_gcc700/RecoLocalCalo/EcalRecProducers/ecalCPUUncalibRecHitProducer_cfi.py +# + +process.load("EventFilter.EcalRawToDigi.ecalRawToDigiGPU_cfi") +process.load("EventFilter.EcalRawToDigi.ecalElectronicsMappingGPUESProducer_cfi") + +#process.ecalUncalibRecHitProducerGPU.kernelsVersion = 0 +#process.ecalUncalibRecHitProducerGPU.kernelMinimizeThreads = cms.vuint32(16, 1, 1) +# +# process.ecalUncalibRecHitProducerGPU.shouldRunTimingComputation = cms.bool(False) +# + + +process.load("RecoLocalCalo.EcalRecProducers.ecalPedestalsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalGainRatiosGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalPulseShapesGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalPulseCovariancesGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalSamplesCorrelationGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalTimeBiasCorrectionsGPUESProducer_cfi") +process.load("RecoLocalCalo.EcalRecProducers.ecalTimeCalibConstantsGPUESProducer_cfi") + +#process.ecalMultiFitUncalibRecHitgpu.algoPSet.threads = cms.vint32(256, 1, 1) + + +process.load("RecoLocalCalo.EcalRecProducers.ecalMultifitParametersGPUESProducer_cfi") + + + +## +## force HLT configuration for ecalMultiFitUncalibRecHit +## + +process.ecalMultiFitUncalibRecHit.algoPSet = cms.PSet( + ebSpikeThreshold = cms.double( 1.042 ), + EBtimeFitLimits_Upper = cms.double( 1.4 ), + EEtimeFitLimits_Lower = cms.double( 0.2 ), + timealgo = cms.string( "None" ), # ----> no timing computation for CPU version + EBtimeNconst = cms.double( 28.5 ), + prefitMaxChiSqEE = cms.double( 10.0 ), + outOfTimeThresholdGain12mEB = cms.double( 5.0 ), + outOfTimeThresholdGain12mEE = cms.double( 1000.0 ), + EEtimeFitParameters = cms.vdouble( -2.390548, 3.553628, -17.62341, 67.67538, -133.213, 140.7432, -75.41106, 16.20277 ), + prefitMaxChiSqEB = cms.double( 25.0 ), + simplifiedNoiseModelForGainSwitch = cms.bool( True ), + EBtimeFitParameters = cms.vdouble( -2.015452, 3.130702, -12.3473, 41.88921, -82.83944, 91.01147, -50.35761, 11.05621 ), + selectiveBadSampleCriteriaEB = cms.bool( False ), + dynamicPedestalsEB = cms.bool( False ), + useLumiInfoRunHeader = cms.bool( False ), + EBamplitudeFitParameters = cms.vdouble( 1.138, 1.652 ), + doPrefitEE = cms.bool( False ), + dynamicPedestalsEE = cms.bool( False ), + selectiveBadSampleCriteriaEE = cms.bool( False ), + outOfTimeThresholdGain61pEE = cms.double( 1000.0 ), + outOfTimeThresholdGain61pEB = cms.double( 5.0 ), + activeBXs = cms.vint32( -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 ), + EcalPulseShapeParameters = cms.PSet( + EEPulseShapeTemplate = cms.vdouble( 0.116442, 0.756246, 1.0, 0.897182, 0.686831, 0.491506, 0.344111, 0.245731, 0.174115, 0.123361, 0.0874288, 0.061957 ), + EEdigiCollection = cms.string( "" ), + EcalPreMixStage2 = cms.bool( False ), + EcalPreMixStage1 = cms.bool( False ), + EBPulseShapeCovariance = cms.vdouble( 3.001E-6, 1.233E-5, 0.0, -4.416E-6, -4.571E-6, -3.614E-6, -2.636E-6, -1.286E-6, -8.41E-7, -5.296E-7, 0.0, 0.0, 1.233E-5, 6.154E-5, 0.0, -2.2E-5, -2.309E-5, -1.838E-5, -1.373E-5, -7.334E-6, -5.088E-6, -3.745E-6, -2.428E-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.416E-6, -2.2E-5, 0.0, 8.319E-6, 8.545E-6, 6.792E-6, 5.059E-6, 2.678E-6, 1.816E-6, 1.223E-6, 8.245E-7, 5.589E-7, -4.571E-6, -2.309E-5, 0.0, 8.545E-6, 9.182E-6, 7.219E-6, 5.388E-6, 2.853E-6, 1.944E-6, 1.324E-6, 9.083E-7, 6.335E-7, -3.614E-6, -1.838E-5, 0.0, 6.792E-6, 7.219E-6, 6.016E-6, 4.437E-6, 2.385E-6, 1.636E-6, 1.118E-6, 7.754E-7, 5.556E-7, -2.636E-6, -1.373E-5, 0.0, 5.059E-6, 5.388E-6, 4.437E-6, 3.602E-6, 1.917E-6, 1.322E-6, 9.079E-7, 6.529E-7, 4.752E-7, -1.286E-6, -7.334E-6, 0.0, 2.678E-6, 2.853E-6, 2.385E-6, 1.917E-6, 1.375E-6, 9.1E-7, 6.455E-7, 4.693E-7, 3.657E-7, -8.41E-7, -5.088E-6, 0.0, 1.816E-6, 1.944E-6, 1.636E-6, 1.322E-6, 9.1E-7, 9.115E-7, 6.062E-7, 4.436E-7, 3.422E-7, -5.296E-7, -3.745E-6, 0.0, 1.223E-6, 1.324E-6, 1.118E-6, 9.079E-7, 6.455E-7, 6.062E-7, 7.217E-7, 4.862E-7, 3.768E-7, 0.0, -2.428E-6, 0.0, 8.245E-7, 9.083E-7, 7.754E-7, 6.529E-7, 4.693E-7, 4.436E-7, 4.862E-7, 6.509E-7, 4.418E-7, 0.0, 0.0, 0.0, 5.589E-7, 6.335E-7, 5.556E-7, 4.752E-7, 3.657E-7, 3.422E-7, 3.768E-7, 4.418E-7, 6.142E-7 ), + ESdigiCollection = cms.string( "" ), + EBdigiCollection = cms.string( "" ), + EBCorrNoiseMatrixG01 = cms.vdouble( 1.0, 0.73354, 0.64442, 0.58851, 0.55425, 0.53082, 0.51916, 0.51097, 0.50732, 0.50409 ), + EBCorrNoiseMatrixG12 = cms.vdouble( 1.0, 0.71073, 0.55721, 0.46089, 0.40449, 0.35931, 0.33924, 0.32439, 0.31581, 0.30481 ), + EBCorrNoiseMatrixG06 = cms.vdouble( 1.0, 0.70946, 0.58021, 0.49846, 0.45006, 0.41366, 0.39699, 0.38478, 0.37847, 0.37055 ), + EEPulseShapeCovariance = cms.vdouble( 3.941E-5, 3.333E-5, 0.0, -1.449E-5, -1.661E-5, -1.424E-5, -1.183E-5, -6.842E-6, -4.915E-6, -3.411E-6, 0.0, 0.0, 3.333E-5, 2.862E-5, 0.0, -1.244E-5, -1.431E-5, -1.233E-5, -1.032E-5, -5.883E-6, -4.154E-6, -2.902E-6, -2.128E-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.449E-5, -1.244E-5, 0.0, 5.84E-6, 6.649E-6, 5.72E-6, 4.812E-6, 2.708E-6, 1.869E-6, 1.33E-6, 9.186E-7, 6.446E-7, -1.661E-5, -1.431E-5, 0.0, 6.649E-6, 7.966E-6, 6.898E-6, 5.794E-6, 3.157E-6, 2.184E-6, 1.567E-6, 1.084E-6, 7.575E-7, -1.424E-5, -1.233E-5, 0.0, 5.72E-6, 6.898E-6, 6.341E-6, 5.347E-6, 2.859E-6, 1.991E-6, 1.431E-6, 9.839E-7, 6.886E-7, -1.183E-5, -1.032E-5, 0.0, 4.812E-6, 5.794E-6, 5.347E-6, 4.854E-6, 2.628E-6, 1.809E-6, 1.289E-6, 9.02E-7, 6.146E-7, -6.842E-6, -5.883E-6, 0.0, 2.708E-6, 3.157E-6, 2.859E-6, 2.628E-6, 1.863E-6, 1.296E-6, 8.882E-7, 6.108E-7, 4.283E-7, -4.915E-6, -4.154E-6, 0.0, 1.869E-6, 2.184E-6, 1.991E-6, 1.809E-6, 1.296E-6, 1.217E-6, 8.669E-7, 5.751E-7, 3.882E-7, -3.411E-6, -2.902E-6, 0.0, 1.33E-6, 1.567E-6, 1.431E-6, 1.289E-6, 8.882E-7, 8.669E-7, 9.522E-7, 6.717E-7, 4.293E-7, 0.0, -2.128E-6, 0.0, 9.186E-7, 1.084E-6, 9.839E-7, 9.02E-7, 6.108E-7, 5.751E-7, 6.717E-7, 7.911E-7, 5.493E-7, 0.0, 0.0, 0.0, 6.446E-7, 7.575E-7, 6.886E-7, 6.146E-7, 4.283E-7, 3.882E-7, 4.293E-7, 5.493E-7, 7.027E-7 ), + EBPulseShapeTemplate = cms.vdouble( 0.0113979, 0.758151, 1.0, 0.887744, 0.673548, 0.474332, 0.319561, 0.215144, 0.147464, 0.101087, 0.0693181, 0.0475044 ), + EECorrNoiseMatrixG01 = cms.vdouble( 1.0, 0.72698, 0.62048, 0.55691, 0.51848, 0.49147, 0.47813, 0.47007, 0.46621, 0.46265 ), + EECorrNoiseMatrixG12 = cms.vdouble( 1.0, 0.71373, 0.44825, 0.30152, 0.21609, 0.14786, 0.11772, 0.10165, 0.09465, 0.08098 ), + UseLCcorrection = cms.untracked.bool( True ), + EECorrNoiseMatrixG06 = cms.vdouble( 1.0, 0.71217, 0.47464, 0.34056, 0.26282, 0.20287, 0.17734, 0.16256, 0.15618, 0.14443 ) + ), + doPrefitEB = cms.bool( False ), + addPedestalUncertaintyEE = cms.double( 0.0 ), + addPedestalUncertaintyEB = cms.double( 0.0 ), + gainSwitchUseMaxSampleEB = cms.bool( True ), + EEtimeNconst = cms.double( 31.8 ), + EEamplitudeFitParameters = cms.vdouble( 1.89, 1.4 ), + chi2ThreshEE_ = cms.double( 50.0 ), + eePulseShape = cms.vdouble( 5.2E-5, -5.26E-5, 6.66E-5, 0.1168, 0.7575, 1.0, 0.8876, 0.6732, 0.4741, 0.3194 ), + outOfTimeThresholdGain12pEB = cms.double( 5.0 ), + gainSwitchUseMaxSampleEE = cms.bool( False ), + mitigateBadSamplesEB = cms.bool( False ), + outOfTimeThresholdGain12pEE = cms.double( 1000.0 ), + ebPulseShape = cms.vdouble( 5.2E-5, -5.26E-5, 6.66E-5, 0.1168, 0.7575, 1.0, 0.8876, 0.6732, 0.4741, 0.3194 ), + ampErrorCalculation = cms.bool( False ), + mitigateBadSamplesEE = cms.bool( False ), + amplitudeThresholdEB = cms.double( 10.0 ), + kPoorRecoFlagEB = cms.bool( True ), + amplitudeThresholdEE = cms.double( 10.0 ), + EBtimeFitLimits_Lower = cms.double( 0.2 ), + kPoorRecoFlagEE = cms.bool( False ), + EEtimeFitLimits_Upper = cms.double( 1.4 ), + outOfTimeThresholdGain61mEE = cms.double( 1000.0 ), + EEtimeConstantTerm = cms.double( 1.0 ), + EBtimeConstantTerm = cms.double( 0.6 ), + chi2ThreshEB_ = cms.double( 65.0 ), + outOfTimeThresholdGain61mEB = cms.double( 5.0 ) +) + +## + + + +#process.load('Configuration.StandardSequences.Reconstruction_cff') +#process.ecalRecHit + + + +#process.load("RecoLocalCalo.EcalRecProducers.ecalRecHitGPU_cfi") +#process.ecalRecHitGPU + + + +#process.hcalDigis.silent = cms.untracked.bool(False) +#process.hcalDigis.InputLabel = rawTag +process.ecalDigis = process.ecalEBunpacker.clone() +process.ecalDigis.InputLabel = cms.InputTag('rawDataCollector') +#process.hbheprerecogpu.processQIE11 = cms.bool(True) + +process.out = cms.OutputModule( + "PoolOutputModule", + fileName = cms.untracked.string("test_uncalib.root") +) + +#process.out = cms.OutputModule("AsciiOutputModule", +# outputCommands = cms.untracked.vstring( +# 'keep *_ecalMultiFitUncalibRecHit_*_*', +# ), +# verbosity = cms.untracked.uint32(0) +#) +process.finalize = cms.EndPath(process.out) + +process.bunchSpacing = cms.Path( + process.bunchSpacingProducer +) + +process.digiPath = cms.Path( + #process.hcalDigis + process.ecalDigis + *process.ecalRawToDigiGPU +) + +process.recoPath = cms.Path( + #(process.ecalMultiFitUncalibRecHit+process.ecalDetIdToBeRecovered) + process.ecalMultiFitUncalibRecHit + #*process.ecalRecHit +# gpu + *process.ecalUncalibRecHitProducerGPU + *process.ecalCPUUncalibRecHitProducer + #*process.ecalRecHitGPU +) + +process.schedule = cms.Schedule( + process.bunchSpacing, + process.digiPath, + process.recoPath, +# process.ecalecalLocalRecoSequence + process.finalize +) + +process.options = cms.untracked.PSet( + numberOfThreads = cms.untracked.uint32(8), + numberOfStreams = cms.untracked.uint32(8), + SkipEvent = cms.untracked.vstring('ProductNotFound'), + wantSummary = cms.untracked.bool(True) +) + +# report CUDAService messages +process.MessageLogger.categories.append("CUDAService") + + diff --git a/RecoLocalCalo/HcalRecAlgos/BuildFile.xml b/RecoLocalCalo/HcalRecAlgos/BuildFile.xml index ac94d61e12494..2c2ee20aff7d5 100644 --- a/RecoLocalCalo/HcalRecAlgos/BuildFile.xml +++ b/RecoLocalCalo/HcalRecAlgos/BuildFile.xml @@ -1,19 +1,26 @@ - + + + + + + + + + + - - - - - + + + + + - - diff --git a/RecoLocalCalo/HcalRecAlgos/bin/BuildFile.xml b/RecoLocalCalo/HcalRecAlgos/bin/BuildFile.xml new file mode 100644 index 0000000000000..30f76131622bd --- /dev/null +++ b/RecoLocalCalo/HcalRecAlgos/bin/BuildFile.xml @@ -0,0 +1,2 @@ + + diff --git a/RecoLocalCalo/HcalRecAlgos/bin/generateQIEShapes.cc b/RecoLocalCalo/HcalRecAlgos/bin/generateQIEShapes.cc new file mode 100644 index 0000000000000..39d5195e7438d --- /dev/null +++ b/RecoLocalCalo/HcalRecAlgos/bin/generateQIEShapes.cc @@ -0,0 +1,82 @@ +#include +#include +#include + +// +// Pregenerate QIE Shapes using hardcoded arrays +// This is taken directly from CondFormats/HcalObjects/srcHcalQIEData.cc +// This generation is running upon conditions retrieval typically for the cpu workload +// +// For the GPU workload, it is better to put generated values into constant memory. +// Either this or just use global memory (for global mem, we need getters...). +// Choosign constant memory as thsese +// values are statically known and never change. Any change in any case requires +// recompilation! +// + +const float binMin[32] = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 16, 18, 20, 22, 24, 26, 28, 31, 34, 37, 40, 44, 48, 52, 57, 62}; + +const float binMin2[64] = {-0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, // 16 bins with width 1x + 15.5, 17.5, 19.5, 21.5, 23.5, 25.5, 27.5, 29.5, 31.5, 33.5, 35.5, + 37.5, 39.5, 41.5, 43.5, 45.5, 47.5, 49.5, 51.5, 53.5, // 20 bins with width 2x + 55.5, 59.5, 63.5, 67.5, 71.5, 75.5, 79.5, 83.5, 87.5, 91.5, 95.5, + 99.5, 103.5, 107.5, 111.5, 115.5, 119.5, 123.5, 127.5, 131.5, 135.5, // 21 bins with width 4x + 139.5, 147.5, 155.5, 163.5, 171.5, 179.5, 187.5}; // 7 bins with width 8x + +constexpr uint32_t nbins_qie8 = 32; +constexpr uint32_t nbins_qie11 = 64; + +void dump(std::vector const& vec, std::string const& name) { + std::stringstream str; + str << "float const " << name << "[" << vec.size() << "] = {"; + uint32_t counter = 0; + for (auto const& value : vec) { + if (counter % 8 == 0) + str << std::endl; + if (counter == vec.size() - 1) + str << value; + else + str << value << ", "; + counter++; + } + str << "};"; + std::cout << str.str() << std::endl; +} + +void generate(uint32_t const nbins, float const* initValues, std::vector& values) { + // preset the first range + for (uint32_t adc = 0; adc < nbins; adc++) + values[adc] = initValues[adc]; + + // do the rest + int scale = 1; + for (uint32_t range = 1; range < 4; range++) { + int factor = nbins == 32 ? 5 : 8; + scale *= factor; + + auto const index_offset = range * nbins; + uint32_t const overlap = nbins == 32 ? 2 : 3; + values[index_offset] = values[index_offset - overlap]; + + for (uint32_t i = 1; i < nbins; i++) + values[index_offset + i] = values[index_offset + i - 1] + scale * (values[i] - values[i - 1]); + } + + values[nbins * 4] = 2 * values[nbins * 4 - 1] - values[nbins * 4 - 2]; +} + +int main(int argc, char* argv[]) { + // + // run 128 bins + // + std::vector valuesqie8(nbins_qie8 * 4 + 1), valuesqie11(nbins_qie11 * 4 + 1); + generate(nbins_qie8, binMin, valuesqie8); + generate(nbins_qie11, binMin2, valuesqie11); + + dump(valuesqie8, std::string{"qie8shape"}); + dump(valuesqie11, std::string{"qie11shape"}); + + return 0; +} diff --git a/RecoLocalCalo/HcalRecAlgos/interface/HcalMahiPulseOffsetsGPU.h b/RecoLocalCalo/HcalRecAlgos/interface/HcalMahiPulseOffsetsGPU.h new file mode 100644 index 0000000000000..98ce9c0b660f1 --- /dev/null +++ b/RecoLocalCalo/HcalRecAlgos/interface/HcalMahiPulseOffsetsGPU.h @@ -0,0 +1,37 @@ +#ifndef RecoLocalCalo_HcalRecAlgos_interface_HcalMahiPulseOffsetsGPU_h +#define RecoLocalCalo_HcalRecAlgos_interface_HcalMahiPulseOffsetsGPU_h + +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalMahiPulseOffsetsGPU { +public: + struct Product { + ~Product(); + int* values; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalMahiPulseOffsetsGPU(edm::ParameterSet const&); + + // will trigger deallocation of Product thru ~Product + ~HcalMahiPulseOffsetsGPU() = default; + + std::vector> const& getValues() const { return values_; } + + // get device pointers + Product const& getProduct(cudaStream_t) const; + +private: + std::vector> values_; + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/RecoLocalCalo/HcalRecAlgos/interface/HcalRecoParamsWithPulseShapesGPU.h b/RecoLocalCalo/HcalRecAlgos/interface/HcalRecoParamsWithPulseShapesGPU.h new file mode 100644 index 0000000000000..965fb873bcf88 --- /dev/null +++ b/RecoLocalCalo/HcalRecAlgos/interface/HcalRecoParamsWithPulseShapesGPU.h @@ -0,0 +1,54 @@ +#ifndef RecoLocalCalo_HcalRecAlgos_interface_HcalRecoParamsWithPulseShapesGPU_h +#define RecoLocalCalo_HcalRecAlgos_interface_HcalRecoParamsWithPulseShapesGPU_h + +#ifndef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#endif + +class HcalRecoParams; + +// +// TODO: HcalPulseShapes will need to be used via ESSource +// This is a workaround: precompute/store/transfer what's needed only +// +class HcalRecoParamsWithPulseShapesGPU { +public: + struct Product { + ~Product(); + uint32_t *param1 = nullptr, *param2 = nullptr; + uint32_t *ids = nullptr; + + // These guys come directly from PulseShapeFunctor class + float *acc25nsVec = nullptr, *diff25nsItvlVec = nullptr, *accVarLenIdxMinusOneVec = nullptr, + *diffVarItvlIdxMinusOneVec = nullptr, *accVarLenIdxZEROVec = nullptr, *diffVarItvlIdxZEROVec = nullptr; + }; + +#ifndef __CUDACC__ + // rearrange reco params + HcalRecoParamsWithPulseShapesGPU(HcalRecoParams const &); + + // will trigger deallocation of Product thru ~Product + ~HcalRecoParamsWithPulseShapesGPU() = default; + + // get device pointers + Product const &getProduct(cudaStream_t) const; + +private: + uint64_t totalChannels_; // hb + he + std::vector> param1_; + std::vector> param2_; + std::vector> ids_; + + std::vector> acc25nsVec_; // 256 + std::vector> diff25nsItvlVec_; // 256 + std::vector> accVarLenIdxMinusOneVec_; // 25 + std::vector> diffVarItvlIdxMinusOneVec_; // 25 + std::vector> accVarLenIdxZEROVec_; // 25 + std::vector> diffVarItvlIdxZEROVec_; // 25 + + cms::cuda::ESProduct product_; +#endif +}; + +#endif diff --git a/RecoLocalCalo/HcalRecAlgos/src/HcalMahiPulseOffsetsGPU.cc b/RecoLocalCalo/HcalRecAlgos/src/HcalMahiPulseOffsetsGPU.cc new file mode 100644 index 0000000000000..005a77932f6df --- /dev/null +++ b/RecoLocalCalo/HcalRecAlgos/src/HcalMahiPulseOffsetsGPU.cc @@ -0,0 +1,35 @@ +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalMahiPulseOffsetsGPU.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +// FIXME: add proper getters to conditions +HcalMahiPulseOffsetsGPU::HcalMahiPulseOffsetsGPU(edm::ParameterSet const& ps) { + auto const& values = ps.getParameter>("pulseOffsets"); + values_.resize(values.size()); + std::copy(values.begin(), values.end(), values_.begin()); +} + +HcalMahiPulseOffsetsGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(values)); +} + +HcalMahiPulseOffsetsGPU::Product const& HcalMahiPulseOffsetsGPU::getProduct(cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](HcalMahiPulseOffsetsGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.values, this->values_.size() * sizeof(int))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.values, + this->values_.data(), + this->values_.size() * sizeof(int), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalMahiPulseOffsetsGPU); diff --git a/RecoLocalCalo/HcalRecAlgos/src/HcalRecoParamsWithPulseShapesGPU.cc b/RecoLocalCalo/HcalRecAlgos/src/HcalRecoParamsWithPulseShapesGPU.cc new file mode 100644 index 0000000000000..b42621b98908e --- /dev/null +++ b/RecoLocalCalo/HcalRecAlgos/src/HcalRecoParamsWithPulseShapesGPU.cc @@ -0,0 +1,222 @@ +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalRecoParamsWithPulseShapesGPU.h" + +#include "CondFormats/HcalObjects/interface/HcalRecoParams.h" +#include "CalibCalorimetry/HcalAlgos/interface/HcalPulseShapes.h" +#include "RecoLocalCalo/HcalRecAlgos/interface/PulseShapeFunctor.h" + +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include + +// FIXME: add proper getters to conditions +HcalRecoParamsWithPulseShapesGPU::HcalRecoParamsWithPulseShapesGPU(HcalRecoParams const& recoParams) + : totalChannels_{recoParams.getAllContainers()[0].second.size() + recoParams.getAllContainers()[1].second.size()}, + param1_(totalChannels_), + param2_(totalChannels_), + ids_(totalChannels_) { +#ifdef HCAL_MAHI_CPUDEBUG + printf("hello from a reco params with pulse shapes\n"); +#endif + + auto const containers = recoParams.getAllContainers(); + + HcalPulseShapes pulseShapes; + std::unordered_map idCache; + + // fill in eb + auto const& barrelValues = containers[0].second; + for (uint64_t i = 0; i < barrelValues.size(); ++i) { + param1_[i] = barrelValues[i].param1(); + param2_[i] = barrelValues[i].param2(); + + auto const pulseShapeId = barrelValues[i].pulseShapeID(); + // FIXME: 0 throws upon look up to HcalPulseShapes + // although comments state that 0 is reserved, + // HcalPulseShapes::getShape throws on 0! + if (pulseShapeId == 0) { + ids_[i] = 0; + continue; + } + if (auto const iter = idCache.find(pulseShapeId); iter == idCache.end()) { + // new guy + auto const newId = idCache.size(); + idCache[pulseShapeId] = newId; + // this will be the id + ids_[i] = newId; + + // resize value arrays + acc25nsVec_.resize(acc25nsVec_.size() + hcal::constants::maxPSshapeBin); + diff25nsItvlVec_.resize(diff25nsItvlVec_.size() + hcal::constants::maxPSshapeBin); + accVarLenIdxMinusOneVec_.resize(accVarLenIdxMinusOneVec_.size() + hcal::constants::nsPerBX); + diffVarItvlIdxMinusOneVec_.resize(diffVarItvlIdxMinusOneVec_.size() + hcal::constants::nsPerBX); + accVarLenIdxZEROVec_.resize(accVarLenIdxZEROVec_.size() + hcal::constants::nsPerBX); + diffVarItvlIdxZEROVec_.resize(diffVarItvlIdxZEROVec_.size() + hcal::constants::nsPerBX); + + // precompute and get values from the functor + auto const& pulseShape = pulseShapes.getShape(pulseShapeId); + FitterFuncs::PulseShapeFunctor functor{pulseShape, false, false, false, 1, 0, 0, hcal::constants::maxSamples}; + auto const offset256 = newId * hcal::constants::maxPSshapeBin; + auto const offset25 = newId * hcal::constants::nsPerBX; + auto const numShapes = newId; + for (int i = 0; i < hcal::constants::maxPSshapeBin; i++) { + acc25nsVec_[offset256 * numShapes + i] = functor.acc25nsVec()[i]; + diff25nsItvlVec_[offset256 * numShapes + i] = functor.diff25nsItvlVec()[i]; + } + + for (int i = 0; i < hcal::constants::nsPerBX; i++) { + accVarLenIdxMinusOneVec_[offset25 * numShapes + i] = functor.accVarLenIdxMinusOneVec()[i]; + diffVarItvlIdxMinusOneVec_[offset25 * numShapes + i] = functor.diffVarItvlIdxMinusOneVec()[i]; + accVarLenIdxZEROVec_[offset25 * numShapes + i] = functor.accVarLenIdxZEROVec()[i]; + diffVarItvlIdxZEROVec_[offset25 * numShapes + i] = functor.diffVarItvlIdxZEROVec()[i]; + } + } else { + // already recorded this pulse shape, just set id + ids_[i] = iter->second; + } +#ifdef HCAL_MAHI_CPUDEBUG + if (barrelValues[i].rawId() == DETID_TO_DEBUG) { + printf("recoShapeId = %u myid = %u\n", pulseShapeId, ids_[i]); + } +#endif + } + + // fill in ee + auto const& endcapValues = containers[1].second; + auto const offset = barrelValues.size(); + for (uint64_t i = 0; i < endcapValues.size(); ++i) { + param1_[i + offset] = endcapValues[i].param1(); + param2_[i + offset] = endcapValues[i].param2(); + + auto const pulseShapeId = endcapValues[i].pulseShapeID(); + // FIXME: 0 throws upon look up to HcalPulseShapes + // although comments state that 0 is reserved, + // HcalPulseShapes::getShape throws on 0! + if (pulseShapeId == 0) { + ids_[i + offset] = 0; + continue; + } + if (auto const iter = idCache.find(pulseShapeId); iter == idCache.end()) { + // new guy + auto const newId = idCache.size(); + idCache[pulseShapeId] = newId; + // this will be the id + ids_[i + offset] = newId; + + // resize value arrays + acc25nsVec_.resize(acc25nsVec_.size() + hcal::constants::maxPSshapeBin); + diff25nsItvlVec_.resize(diff25nsItvlVec_.size() + hcal::constants::maxPSshapeBin); + accVarLenIdxMinusOneVec_.resize(accVarLenIdxMinusOneVec_.size() + hcal::constants::nsPerBX); + diffVarItvlIdxMinusOneVec_.resize(diffVarItvlIdxMinusOneVec_.size() + hcal::constants::nsPerBX); + accVarLenIdxZEROVec_.resize(accVarLenIdxZEROVec_.size() + hcal::constants::nsPerBX); + diffVarItvlIdxZEROVec_.resize(diffVarItvlIdxZEROVec_.size() + hcal::constants::nsPerBX); + + // precompute and get values from the functor + auto const& pulseShape = pulseShapes.getShape(pulseShapeId); + FitterFuncs::PulseShapeFunctor functor{pulseShape, false, false, false, 1, 0, 0, hcal::constants::maxSamples}; + auto const offset256 = newId * hcal::constants::maxPSshapeBin; + auto const offset25 = newId * hcal::constants::nsPerBX; + auto const numShapes = newId; + for (int i = 0; i < hcal::constants::maxPSshapeBin; i++) { + acc25nsVec_[offset256 * numShapes + i] = functor.acc25nsVec()[i]; + diff25nsItvlVec_[offset256 * numShapes + i] = functor.diff25nsItvlVec()[i]; + } + + for (int i = 0; i < hcal::constants::nsPerBX; i++) { + accVarLenIdxMinusOneVec_[offset25 * numShapes + i] = functor.accVarLenIdxMinusOneVec()[i]; + diffVarItvlIdxMinusOneVec_[offset25 * numShapes + i] = functor.diffVarItvlIdxMinusOneVec()[i]; + accVarLenIdxZEROVec_[offset25 * numShapes + i] = functor.accVarLenIdxZEROVec()[i]; + diffVarItvlIdxZEROVec_[offset25 * numShapes + i] = functor.diffVarItvlIdxZEROVec()[i]; + } + } else { + // already recorded this pulse shape, just set id + ids_[i + offset] = iter->second; + } + } + +#ifdef HCAL_MAHI_CPUDEBUG + for (auto const& p : idCache) + printf("recoPulseShapeId = %u id = %u\n", p.first, p.second); +#endif +} + +HcalRecoParamsWithPulseShapesGPU::Product::~Product() { + // deallocation + cudaCheck(cudaFree(param1)); + cudaCheck(cudaFree(param2)); + cudaCheck(cudaFree(ids)); + cudaCheck(cudaFree(acc25nsVec)); + cudaCheck(cudaFree(diff25nsItvlVec)); + cudaCheck(cudaFree(accVarLenIdxMinusOneVec)); + cudaCheck(cudaFree(diffVarItvlIdxMinusOneVec)); + cudaCheck(cudaFree(accVarLenIdxZEROVec)); + cudaCheck(cudaFree(diffVarItvlIdxZEROVec)); +} + +HcalRecoParamsWithPulseShapesGPU::Product const& HcalRecoParamsWithPulseShapesGPU::getProduct( + cudaStream_t cudaStream) const { + auto const& product = product_.dataForCurrentDeviceAsync( + cudaStream, [this](HcalRecoParamsWithPulseShapesGPU::Product& product, cudaStream_t cudaStream) { + // malloc + cudaCheck(cudaMalloc((void**)&product.param1, this->param1_.size() * sizeof(uint32_t))); + cudaCheck(cudaMalloc((void**)&product.param2, this->param2_.size() * sizeof(uint32_t))); + cudaCheck(cudaMalloc((void**)&product.ids, this->ids_.size() * sizeof(uint32_t))); + cudaCheck(cudaMalloc((void**)&product.acc25nsVec, this->acc25nsVec_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.diff25nsItvlVec, this->diff25nsItvlVec_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.accVarLenIdxMinusOneVec, + this->accVarLenIdxMinusOneVec_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.diffVarItvlIdxMinusOneVec, + this->diffVarItvlIdxMinusOneVec_.size() * sizeof(float))); + cudaCheck(cudaMalloc((void**)&product.accVarLenIdxZEROVec, this->accVarLenIdxZEROVec_.size() * sizeof(float))); + cudaCheck( + cudaMalloc((void**)&product.diffVarItvlIdxZEROVec, this->diffVarItvlIdxZEROVec_.size() * sizeof(float))); + + // transfer + cudaCheck(cudaMemcpyAsync(product.param1, + this->param1_.data(), + this->param1_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.param2, + this->param2_.data(), + this->param2_.size() * sizeof(uint32_t), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync( + product.ids, this->ids_.data(), this->ids_.size() * sizeof(uint32_t), cudaMemcpyHostToDevice, cudaStream)); + cudaCheck(cudaMemcpyAsync(product.acc25nsVec, + this->acc25nsVec_.data(), + this->acc25nsVec_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.diff25nsItvlVec, + this->diff25nsItvlVec_.data(), + this->diff25nsItvlVec_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.accVarLenIdxMinusOneVec, + this->accVarLenIdxMinusOneVec_.data(), + this->accVarLenIdxMinusOneVec_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.diffVarItvlIdxMinusOneVec, + this->diffVarItvlIdxMinusOneVec_.data(), + this->diffVarItvlIdxMinusOneVec_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.accVarLenIdxZEROVec, + this->accVarLenIdxZEROVec_.data(), + this->accVarLenIdxZEROVec_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + cudaCheck(cudaMemcpyAsync(product.diffVarItvlIdxZEROVec, + this->diffVarItvlIdxZEROVec_.data(), + this->diffVarItvlIdxZEROVec_.size() * sizeof(float), + cudaMemcpyHostToDevice, + cudaStream)); + }); + + return product; +} + +TYPELOOKUP_DATA_REG(HcalRecoParamsWithPulseShapesGPU); diff --git a/RecoLocalCalo/HcalRecProducers/BuildFile.xml b/RecoLocalCalo/HcalRecProducers/BuildFile.xml index c3ae589a0c0a7..e34037ecbcbb5 100644 --- a/RecoLocalCalo/HcalRecProducers/BuildFile.xml +++ b/RecoLocalCalo/HcalRecProducers/BuildFile.xml @@ -1,8 +1,25 @@ - + + + + + + - - + + + + + + + + + - + + + + + + diff --git a/RecoLocalCalo/HcalRecProducers/bin/BuildFile.xml b/RecoLocalCalo/HcalRecProducers/bin/BuildFile.xml new file mode 100644 index 0000000000000..a804a7fe4b70e --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/bin/BuildFile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/RecoLocalCalo/HcalRecProducers/bin/makeHcalRecHitGpuValidationPlots.cpp b/RecoLocalCalo/HcalRecProducers/bin/makeHcalRecHitGpuValidationPlots.cpp new file mode 100644 index 0000000000000..fe6aabf928aca --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/bin/makeHcalRecHitGpuValidationPlots.cpp @@ -0,0 +1,282 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "DataFormats/HcalRecHit/interface/HcalRecHitCollections.h" +//#include "CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h" + +#define CREATE_HIST_1D(varname, nbins, first, last) auto varname = new TH1D(#varname, #varname, nbins, first, last) + +#define CREATE_HIST_2D(varname, nbins, first, last) \ + auto varname = new TH2D(#varname, #varname, nbins, first, last, nbins, first, last) + +int main(int argc, char* argv[]) { + if (argc < 3) { + std::cout << "run with: ./ \n"; + exit(0); + } + + std::string inFileName{argv[1]}; + std::string outFileName{argv[2]}; + + // branches to use + edm::Wrapper* wcpu = nullptr; + edm::Wrapper* wgpu = nullptr; + // edm::Wrapper>> *wgpu=nullptr; + + // prep output + TFile rfout{outFileName.c_str(), "recreate"}; + + CREATE_HIST_1D(hEnergyM0HBGPU, 1000, 0, 100); + CREATE_HIST_1D(hEnergyM0HEGPU, 1000, 0, 100); + CREATE_HIST_1D(hEnergyM0HBCPU, 1000, 0, 100); + CREATE_HIST_1D(hEnergyM0HECPU, 1000, 0, 100); + + CREATE_HIST_1D(hEnergyHBGPU, 1000, 0, 100); + CREATE_HIST_1D(hEnergyHBCPU, 1000, 0, 100); + CREATE_HIST_1D(hEnergyHEGPU, 1000, 0, 100); + CREATE_HIST_1D(hEnergyHECPU, 1000, 0, 100); + + CREATE_HIST_1D(hChi2HBGPU, 1000, 0, 100); + CREATE_HIST_1D(hChi2HBCPU, 1000, 0, 100); + CREATE_HIST_1D(hChi2HEGPU, 1000, 0, 100); + CREATE_HIST_1D(hChi2HECPU, 1000, 0, 100); + + CREATE_HIST_2D(hEnergyHBGPUvsCPU, 1000, 0, 100); + CREATE_HIST_2D(hEnergyHEGPUvsCPU, 1000, 0, 100); + CREATE_HIST_2D(hChi2HBGPUvsCPU, 1000, 0, 100); + CREATE_HIST_2D(hChi2HEGPUvsCPU, 1000, 0, 100); + + CREATE_HIST_2D(hEnergyM0HBGPUvsCPU, 1000, 0, 100); + CREATE_HIST_2D(hEnergyM0HEGPUvsCPU, 1000, 0, 100); + + // prep input + TFile rfin{inFileName.c_str()}; + TTree* rt = (TTree*)rfin.Get("Events"); + rt->SetBranchAddress("HBHERecHitsSorted_hcalCPURecHitsProducer_recHitsLegacyHBHE_RECO.", &wgpu); + // rt->SetBranchAddress("hcalCUDAHostAllocatorAliashcalcommonVecStoragePolicyhcalRecHitCollection_hcalCPURecHitsProducer_recHitsM0LabelOut_RECO.", &wgpu); + rt->SetBranchAddress("HBHERecHitsSorted_hbheprereco__RECO.", &wcpu); + + // accumulate + auto const nentries = rt->GetEntries(); + std::cout << ">>> nentries = " << nentries << std::endl; + for (int ie = 0; ie < nentries; ++ie) { + rt->GetEntry(ie); + + auto const& gpuProduct = wgpu->bareProduct(); + auto const& cpuProduct = wcpu->bareProduct(); + + auto const ncpu = cpuProduct.size(); + auto const ngpu = gpuProduct.size(); + // auto const ngpu = gpuProduct.energy.size(); + + if (ngpu != ncpu) { + std::cerr << "*** mismatch in number of rec hits for event " << ie << std::endl + << ">>> ngpu = " << ngpu << std::endl + << ">>> ncpu = " << ncpu << std::endl; + } + + for (uint32_t ich = 0; ich < ncpu; ich++) { + auto const& cpurh = cpuProduct[ich]; + auto const& did = cpurh.id(); + auto iter2gpu = gpuProduct.find(did); + // auto iter2idgpu = std::find( + // gpuProduct.did.begin(), gpuProduct.did.end(), did.rawId()); + + if (iter2gpu == gpuProduct.end()) { + std::cerr << "missing " << did << std::endl; + continue; + } + + assert(iter2gpu->id().rawId() == did.rawId()); + + auto const gpu_energy_m0 = iter2gpu->eraw(); + auto const cpu_energy_m0 = cpurh.eraw(); + auto const gpu_energy = iter2gpu->energy(); + auto const cpu_energy = cpurh.energy(); + auto const gpu_chi2 = iter2gpu->chi2(); + auto const cpu_chi2 = cpurh.chi2(); + + if (did.subdetId() == HcalBarrel) { + hEnergyM0HBGPU->Fill(gpu_energy_m0); + hEnergyM0HBCPU->Fill(cpu_energy_m0); + hEnergyM0HBGPUvsCPU->Fill(cpu_energy_m0, gpu_energy_m0); + + hEnergyHBGPU->Fill(gpu_energy); + hEnergyHBCPU->Fill(cpu_energy); + hEnergyHBGPUvsCPU->Fill(cpu_energy, gpu_energy); + hChi2HBGPU->Fill(gpu_chi2); + hChi2HBCPU->Fill(cpu_chi2); + hChi2HBGPUvsCPU->Fill(cpu_chi2, gpu_chi2); + } else if (did.subdetId() == HcalEndcap) { + hEnergyM0HEGPU->Fill(gpu_energy_m0); + hEnergyM0HECPU->Fill(cpu_energy_m0); + hEnergyM0HEGPUvsCPU->Fill(cpu_energy_m0, gpu_energy_m0); + + hEnergyHEGPU->Fill(gpu_energy); + hEnergyHECPU->Fill(cpu_energy); + hEnergyHEGPUvsCPU->Fill(cpu_energy, gpu_energy); + + hChi2HEGPU->Fill(gpu_chi2); + hChi2HECPU->Fill(cpu_chi2); + hChi2HEGPUvsCPU->Fill(cpu_chi2, gpu_chi2); + } + } + } + + { + TCanvas c{"plots", "plots", 4200, 6200}; + c.Divide(4, 3); + c.cd(1); + { + gPad->SetLogy(); + hEnergyM0HBCPU->SetLineColor(kBlack); + hEnergyM0HBCPU->SetLineWidth(1.); + hEnergyM0HBCPU->Draw(""); + hEnergyM0HBGPU->SetLineColor(kBlue); + hEnergyM0HBGPU->SetLineWidth(1.); + hEnergyM0HBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hEnergyM0HBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(2); + { + gPad->SetLogz(); + hEnergyM0HBGPUvsCPU->GetXaxis()->SetTitle("cpu"); + hEnergyM0HBGPUvsCPU->GetYaxis()->SetTitle("gpu"); + hEnergyM0HBGPUvsCPU->Draw("colz"); + } + c.cd(3); + { + gPad->SetLogy(); + hEnergyM0HECPU->SetLineColor(kBlack); + hEnergyM0HECPU->SetLineWidth(1.); + hEnergyM0HECPU->Draw(""); + hEnergyM0HEGPU->SetLineColor(kBlue); + hEnergyM0HEGPU->SetLineWidth(1.); + hEnergyM0HEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hEnergyM0HEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(4); + { + gPad->SetLogz(); + hEnergyM0HEGPUvsCPU->GetXaxis()->SetTitle("cpu"); + hEnergyM0HEGPUvsCPU->GetYaxis()->SetTitle("gpu"); + hEnergyM0HEGPUvsCPU->Draw("colz"); + } + c.cd(5); + { + gPad->SetLogy(); + hEnergyHBCPU->SetLineColor(kBlack); + hEnergyHBCPU->SetLineWidth(1.); + hEnergyHBCPU->Draw(""); + hEnergyHBGPU->SetLineColor(kBlue); + hEnergyHBGPU->SetLineWidth(1.); + hEnergyHBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hEnergyHBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(6); + { + gPad->SetLogz(); + hEnergyHBGPUvsCPU->GetXaxis()->SetTitle("cpu"); + hEnergyHBGPUvsCPU->GetYaxis()->SetTitle("gpu"); + hEnergyHBGPUvsCPU->Draw("colz"); + } + c.cd(7); + { + gPad->SetLogy(); + hEnergyHECPU->SetLineColor(kBlack); + hEnergyHECPU->SetLineWidth(1.); + hEnergyHECPU->Draw(""); + hEnergyHEGPU->SetLineColor(kBlue); + hEnergyHEGPU->SetLineWidth(1.); + hEnergyHEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hEnergyHEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(8); + { + gPad->SetLogz(); + hEnergyHEGPUvsCPU->GetXaxis()->SetTitle("cpu"); + hEnergyHEGPUvsCPU->GetYaxis()->SetTitle("gpu"); + hEnergyHEGPUvsCPU->Draw("colz"); + } + c.cd(9); + { + gPad->SetLogy(); + hChi2HBCPU->SetLineColor(kBlack); + hChi2HBCPU->SetLineWidth(1.); + hChi2HBCPU->Draw(""); + hChi2HBGPU->SetLineColor(kBlue); + hChi2HBGPU->SetLineWidth(1.); + hChi2HBGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hChi2HBGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(10); + { + gPad->SetLogz(); + hChi2HBGPUvsCPU->GetXaxis()->SetTitle("cpu"); + hChi2HBGPUvsCPU->GetYaxis()->SetTitle("gpu"); + hChi2HBGPUvsCPU->Draw("colz"); + } + c.cd(11); + { + gPad->SetLogy(); + hChi2HECPU->SetLineColor(kBlack); + hChi2HECPU->SetLineWidth(1.); + hChi2HECPU->Draw(""); + hChi2HEGPU->SetLineColor(kBlue); + hChi2HEGPU->SetLineWidth(1.); + hChi2HEGPU->Draw("sames"); + gPad->Update(); + auto stats = (TPaveStats*)hChi2HEGPU->FindObject("stats"); + auto y2 = stats->GetY2NDC(); + auto y1 = stats->GetY1NDC(); + stats->SetY2NDC(y1); + stats->SetY1NDC(y1 - (y2 - y1)); + } + c.cd(12); + { + gPad->SetLogz(); + hChi2HEGPUvsCPU->GetXaxis()->SetTitle("cpu"); + hChi2HEGPUvsCPU->GetYaxis()->SetTitle("gpu"); + hChi2HEGPUvsCPU->Draw("colz"); + } + c.SaveAs("plots.pdf"); + } + + rfin.Close(); + rfout.Write(); + rfout.Close(); +} diff --git a/RecoLocalCalo/HcalRecProducers/python/hbheRecHitProducerGPUTask_cff.py b/RecoLocalCalo/HcalRecProducers/python/hbheRecHitProducerGPUTask_cff.py new file mode 100644 index 0000000000000..d938653d5a15e --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/python/hbheRecHitProducerGPUTask_cff.py @@ -0,0 +1,65 @@ +import FWCore.ParameterSet.Config as cms + +# Run 3 HCAL workflow on GPU + +# EventSetup modules used by HBHERecHitProducerGPU +from RecoLocalCalo.HcalRecProducers.hcalGainsGPUESProducer_cfi import hcalGainsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalGainWidthsGPUESProducer_cfi import hcalGainWidthsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalLUTCorrsGPUESProducer_cfi import hcalLUTCorrsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalConvertedPedestalsGPUESProducer_cfi import hcalConvertedPedestalsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalConvertedEffectivePedestalsGPUESProducer_cfi import hcalConvertedEffectivePedestalsGPUESProducer +hcalConvertedEffectivePedestalsGPUESProducer.label0 = "withTopoEff" + +from RecoLocalCalo.HcalRecProducers.hcalConvertedPedestalWidthsGPUESProducer_cfi import hcalConvertedPedestalWidthsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalConvertedEffectivePedestalWidthsGPUESProducer_cfi import hcalConvertedEffectivePedestalWidthsGPUESProducer +hcalConvertedEffectivePedestalWidthsGPUESProducer.label0 = "withTopoEff" +hcalConvertedEffectivePedestalWidthsGPUESProducer.label1 = "withTopoEff" + +from RecoLocalCalo.HcalRecProducers.hcalQIECodersGPUESProducer_cfi import hcalQIECodersGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalRecoParamsWithPulseShapesGPUESProducer_cfi import hcalRecoParamsWithPulseShapesGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalRespCorrsGPUESProducer_cfi import hcalRespCorrsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalTimeCorrsGPUESProducer_cfi import hcalTimeCorrsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalQIETypesGPUESProducer_cfi import hcalQIETypesGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalSiPMParametersGPUESProducer_cfi import hcalSiPMParametersGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalSiPMCharacteristicsGPUESProducer_cfi import hcalSiPMCharacteristicsGPUESProducer +from RecoLocalCalo.HcalRecProducers.hcalMahiPulseOffsetsGPUESProducer_cfi import hcalMahiPulseOffsetsGPUESProducer + +# convert the HBHE digis into SoA format, and copy them from CPU to GPU +from EventFilter.HcalRawToDigi.hcalDigisProducerGPU_cfi import hcalDigisProducerGPU as _hcalDigisProducerGPU +hcalDigisGPU = _hcalDigisProducerGPU.clone( + digisLabelF01HE = "", + digisLabelF5HB = "", + digisLabelF3HB = "" +) + +# run the HCAL local reconstruction (MAHI) on GPU +from RecoLocalCalo.HcalRecProducers.hbheRecHitProducerGPU_cfi import hbheRecHitProducerGPU as _hbheRecHitProducerGPU +hbheRecHitProducerGPU = _hbheRecHitProducerGPU.clone( + digisLabelF01HE = "hcalDigisGPU", + digisLabelF5HB = "hcalDigisGPU", + digisLabelF3HB = "hcalDigisGPU", + recHitsLabelM0HBHE = "" +) + +# Tasks and Sequences +hbheRecHitProducerGPUTask = cms.Task( + hcalGainsGPUESProducer, + hcalGainWidthsGPUESProducer, + hcalLUTCorrsGPUESProducer, + hcalConvertedPedestalsGPUESProducer, + hcalConvertedEffectivePedestalsGPUESProducer, + hcalConvertedPedestalWidthsGPUESProducer, + hcalConvertedEffectivePedestalWidthsGPUESProducer, + hcalQIECodersGPUESProducer, + hcalRecoParamsWithPulseShapesGPUESProducer, + hcalRespCorrsGPUESProducer, + hcalTimeCorrsGPUESProducer, + hcalQIETypesGPUESProducer, + hcalSiPMParametersGPUESProducer, + hcalSiPMCharacteristicsGPUESProducer, + hcalMahiPulseOffsetsGPUESProducer, + hcalDigisGPU, + hbheRecHitProducerGPU +) + +hbheRecHitProducerGPUSequence = cms.Sequence(hbheRecHitProducerGPUTask) diff --git a/RecoLocalCalo/HcalRecProducers/src/DeclsForKernels.h b/RecoLocalCalo/HcalRecProducers/src/DeclsForKernels.h new file mode 100644 index 0000000000000..807c42b057fa3 --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/DeclsForKernels.h @@ -0,0 +1,115 @@ +#ifndef RecoLocalCalo_HcalRecProducers_src_DeclsForKernels_h +#define RecoLocalCalo_HcalRecProducers_src_DeclsForKernels_h + +#include +#include + +#include "CUDADataFormats/HcalDigi/interface/DigiCollection.h" +#include "CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h" +#include "CalibCalorimetry/HcalAlgos/interface/HcalTimeSlew.h" +#include "CondFormats/DataRecord/interface/HcalCombinedRecordsGPU.h" +#include "CondFormats/DataRecord/interface/HcalGainWidthsRcd.h" +#include "CondFormats/DataRecord/interface/HcalGainsRcd.h" +#include "CondFormats/DataRecord/interface/HcalLUTCorrsRcd.h" +#include "CondFormats/DataRecord/interface/HcalQIEDataRcd.h" +#include "CondFormats/DataRecord/interface/HcalQIETypesRcd.h" +#include "CondFormats/DataRecord/interface/HcalRecoParamsRcd.h" +#include "CondFormats/DataRecord/interface/HcalRespCorrsRcd.h" +#include "CondFormats/DataRecord/interface/HcalSiPMCharacteristicsRcd.h" +#include "CondFormats/DataRecord/interface/HcalSiPMParametersRcd.h" +#include "CondFormats/DataRecord/interface/HcalTimeCorrsRcd.h" +#include "CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalWidthsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalGainWidthsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalGainsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalLUTCorrsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalQIECodersGPU.h" +#include "CondFormats/HcalObjects/interface/HcalQIETypesGPU.h" +#include "CondFormats/HcalObjects/interface/HcalRecoParamsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalRespCorrsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMCharacteristicsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMParametersGPU.h" +#include "CondFormats/HcalObjects/interface/HcalTimeCorrsGPU.h" +#include "Geometry/CaloTopology/interface/HcalTopology.h" +#include "Geometry/HcalCommonData/interface/HcalDDDRecConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalMahiPulseOffsetsGPU.h" +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalRecoParamsWithPulseShapesGPU.h" + +namespace hcal { + namespace reconstruction { + + struct ConditionsProducts { + HcalGainWidthsGPU::Product const& gainWidths; + HcalGainsGPU::Product const& gains; + HcalLUTCorrsGPU::Product const& lutCorrs; + HcalConvertedPedestalWidthsGPU::Product const& pedestalWidths; + HcalConvertedEffectivePedestalWidthsGPU::Product const& effectivePedestalWidths; + HcalConvertedPedestalsGPU::Product const& pedestals; + HcalQIECodersGPU::Product const& qieCoders; + HcalRecoParamsWithPulseShapesGPU::Product const& recoParams; + HcalRespCorrsGPU::Product const& respCorrs; + HcalTimeCorrsGPU::Product const& timeCorrs; + HcalQIETypesGPU::Product const& qieTypes; + HcalSiPMParametersGPU::Product const& sipmParameters; + HcalSiPMCharacteristicsGPU::Product const& sipmCharacteristics; + HcalConvertedPedestalsGPU::Product const* convertedEffectivePedestals; + HcalTopology const* topology; + HcalDDDRecConstants const* recConstants; + uint32_t offsetForHashes; + HcalMahiPulseOffsetsGPU::Product const& pulseOffsets; + std::vector> const& pulseOffsetsHost; + }; + + struct ConfigParameters { + uint32_t maxChannels; + uint32_t maxTimeSamples; + uint32_t kprep1dChannelsPerBlock; + int sipmQTSShift; + int sipmQNTStoSum; + int firstSampleShift; + bool useEffectivePedestals; + + float meanTime; + float timeSigmaSiPM, timeSigmaHPD; + float ts4Thresh; + + std::array kernelMinimizeThreads; + + // FIXME: + // - add "getters" to HcalTimeSlew calib formats + // - add ES Producer to consume what is produced above not to replicate. + // which ones to use is hardcoded, therefore no need to send those to the device + bool applyTimeSlew; + float tzeroTimeSlew, slopeTimeSlew, tmaxTimeSlew; + }; + + struct OutputDataGPU { + RecHitCollection<::calo::common::DevStoragePolicy> recHits; + + void allocate(ConfigParameters const& config, cudaStream_t cudaStream) { + recHits.energy = cms::cuda::make_device_unique(config.maxChannels, cudaStream); + recHits.chi2 = cms::cuda::make_device_unique(config.maxChannels, cudaStream); + recHits.energyM0 = cms::cuda::make_device_unique(config.maxChannels, cudaStream); + recHits.timeM0 = cms::cuda::make_device_unique(config.maxChannels, cudaStream); + recHits.did = cms::cuda::make_device_unique(config.maxChannels, cudaStream); + } + }; + + struct ScratchDataGPU { + cms::cuda::device::unique_ptr amplitudes, noiseTerms, pulseMatrices, pulseMatricesM, pulseMatricesP; + cms::cuda::device::unique_ptr soiSamples; + }; + + struct InputDataGPU { + DigiCollection const& f01HEDigis; + DigiCollection const& f5HBDigis; + DigiCollection const& f3HBDigis; + }; + + } // namespace reconstruction +} // namespace hcal + +#endif // RecoLocalCalo_HcalRecProducers_src_DeclsForKernels_h diff --git a/RecoLocalCalo/HcalRecProducers/src/HBHERecHitProducerGPU.cc b/RecoLocalCalo/HcalRecProducers/src/HBHERecHitProducerGPU.cc new file mode 100644 index 0000000000000..af5398e49fa8f --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/HBHERecHitProducerGPU.cc @@ -0,0 +1,246 @@ +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/CUDACore/interface/JobConfigurationGPURecord.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" + +#include "SimpleAlgoGPU.h" + +class HBHERecHitProducerGPU : public edm::stream::EDProducer { +public: + explicit HBHERecHitProducerGPU(edm::ParameterSet const&); + ~HBHERecHitProducerGPU() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + + using IProductTypef01 = cms::cuda::Product>; + edm::EDGetTokenT digisTokenF01HE_; + + using IProductTypef5 = cms::cuda::Product>; + edm::EDGetTokenT digisTokenF5HB_; + + using IProductTypef3 = cms::cuda::Product>; + edm::EDGetTokenT digisTokenF3HB_; + + using RecHitType = hcal::RecHitCollection; + using OProductType = cms::cuda::Product; + edm::EDPutTokenT rechitsM0Token_; + + hcal::reconstruction::ConfigParameters configParameters_; + hcal::reconstruction::OutputDataGPU outputGPU_; + cms::cuda::ContextState cudaState_; +}; + +HBHERecHitProducerGPU::HBHERecHitProducerGPU(edm::ParameterSet const& ps) + : digisTokenF01HE_{consumes(ps.getParameter("digisLabelF01HE"))}, + digisTokenF5HB_{consumes(ps.getParameter("digisLabelF5HB"))}, + digisTokenF3HB_{consumes(ps.getParameter("digisLabelF3HB"))}, + rechitsM0Token_{produces(ps.getParameter("recHitsLabelM0HBHE"))} { + configParameters_.maxChannels = ps.getParameter("maxChannels"); + configParameters_.maxTimeSamples = ps.getParameter("maxTimeSamples"); + configParameters_.kprep1dChannelsPerBlock = ps.getParameter("kprep1dChannelsPerBlock"); + configParameters_.sipmQTSShift = ps.getParameter("sipmQTSShift"); + configParameters_.sipmQNTStoSum = ps.getParameter("sipmQNTStoSum"); + configParameters_.firstSampleShift = ps.getParameter("firstSampleShift"); + configParameters_.useEffectivePedestals = ps.getParameter("useEffectivePedestals"); + + configParameters_.meanTime = ps.getParameter("meanTime"); + configParameters_.timeSigmaSiPM = ps.getParameter("timeSigmaSiPM"); + configParameters_.timeSigmaHPD = ps.getParameter("timeSigmaHPD"); + configParameters_.ts4Thresh = ps.getParameter("ts4Thresh"); + + configParameters_.applyTimeSlew = ps.getParameter("applyTimeSlew"); + auto const tzeroValues = ps.getParameter>("tzeroTimeSlewParameters"); + auto const slopeValues = ps.getParameter>("slopeTimeSlewParameters"); + auto const tmaxValues = ps.getParameter>("tmaxTimeSlewParameters"); + + configParameters_.tzeroTimeSlew = tzeroValues[HcalTimeSlew::Medium]; + configParameters_.slopeTimeSlew = slopeValues[HcalTimeSlew::Medium]; + configParameters_.tmaxTimeSlew = tmaxValues[HcalTimeSlew::Medium]; + + auto threadsMinimize = ps.getParameter>("kernelMinimizeThreads"); + configParameters_.kernelMinimizeThreads[0] = threadsMinimize[0]; + configParameters_.kernelMinimizeThreads[1] = threadsMinimize[1]; + configParameters_.kernelMinimizeThreads[2] = threadsMinimize[2]; +} + +HBHERecHitProducerGPU::~HBHERecHitProducerGPU() {} + +void HBHERecHitProducerGPU::fillDescriptions(edm::ConfigurationDescriptions& cdesc) { + edm::ParameterSetDescription desc; + desc.add("maxChannels", 10000u); + desc.add("maxTimeSamples", 10); + desc.add("kprep1dChannelsPerBlock", 32); + desc.add("digisLabelF01HE", edm::InputTag{"hcalRawToDigiGPU", "f01HEDigisGPU"}); + desc.add("digisLabelF5HB", edm::InputTag{"hcalRawToDigiGPU", "f5HBDigisGPU"}); + desc.add("digisLabelF3HB", edm::InputTag{"hcalRawToDigiGPU", "f3HBDigisGPU"}); + desc.add("recHitsLabelM0HBHE", "recHitsM0HBHE"); + desc.add("sipmQTSShift", 0); + desc.add("sipmQNTStoSum", 3); + desc.add("firstSampleShift", 0); + desc.add("useEffectivePedestals", true); + + desc.add("meanTime", 0.f); + desc.add("timeSigmaSiPM", 2.5f); + desc.add("timeSigmaHPD", 5.0f); + desc.add("ts4Thresh", 0.0); + + desc.add("applyTimeSlew", true); + desc.add>("tzeroTimeSlewParameters", {23.960177, 11.977461, 9.109694}); + desc.add>("slopeTimeSlewParameters", {-3.178648, -1.5610227, -1.075824}); + desc.add>("tmaxTimeSlewParameters", {16.00, 10.00, 6.25}); + desc.add>("kernelMinimizeThreads", {16, 1, 1}); + + cdesc.addWithDefaultLabel(desc); +} + +void HBHERecHitProducerGPU::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder holder) { +#ifdef HCAL_MAHI_CPUDEBUG + auto start = std::chrono::high_resolution_clock::now(); +#endif + + // input + raii + auto const& f01HEProduct = event.get(digisTokenF01HE_); + auto const& f5HBProduct = event.get(digisTokenF5HB_); + auto const& f3HBProduct = event.get(digisTokenF3HB_); + cms::cuda::ScopedContextAcquire ctx{f01HEProduct, std::move(holder), cudaState_}; + auto const& f01HEDigis = ctx.get(f01HEProduct); + auto const& f5HBDigis = ctx.get(f5HBProduct); + auto const& f3HBDigis = ctx.get(f3HBProduct); + + hcal::reconstruction::InputDataGPU inputGPU{f01HEDigis, f5HBDigis, f3HBDigis}; + + // conditions + edm::ESHandle recoParamsHandle; + setup.get().get(recoParamsHandle); + auto const& recoParamsProduct = recoParamsHandle->getProduct(ctx.stream()); + + edm::ESHandle gainWidthsHandle; + setup.get().get(gainWidthsHandle); + auto const& gainWidthsProduct = gainWidthsHandle->getProduct(ctx.stream()); + + edm::ESHandle gainsHandle; + setup.get().get(gainsHandle); + auto const& gainsProduct = gainsHandle->getProduct(ctx.stream()); + + edm::ESHandle lutCorrsHandle; + setup.get().get(lutCorrsHandle); + auto const& lutCorrsProduct = lutCorrsHandle->getProduct(ctx.stream()); + + // use only 1 depending on useEffectivePedestals + edm::ESHandle pedestalWidthsHandle; + edm::ESHandle effectivePedestalWidthsHandle; + setup.get().get(effectivePedestalWidthsHandle); + setup.get().get(pedestalWidthsHandle); + auto const& pedestalWidthsProduct = pedestalWidthsHandle->getProduct(ctx.stream()); + auto const& effectivePedestalWidthsProduct = effectivePedestalWidthsHandle->getProduct(ctx.stream()); + + edm::ESHandle pedestalsHandle; + setup.get().get(pedestalsHandle); + auto const& pedestalsProduct = pedestalsHandle->getProduct(ctx.stream()); + + edm::ESHandle effectivePedestalsHandle; + if (configParameters_.useEffectivePedestals) + setup.get().get(effectivePedestalsHandle); + auto const* effectivePedestalsProduct = + configParameters_.useEffectivePedestals ? &effectivePedestalsHandle->getProduct(ctx.stream()) : nullptr; + + edm::ESHandle qieCodersHandle; + setup.get().get(qieCodersHandle); + auto const& qieCodersProduct = qieCodersHandle->getProduct(ctx.stream()); + + edm::ESHandle respCorrsHandle; + setup.get().get(respCorrsHandle); + auto const& respCorrsProduct = respCorrsHandle->getProduct(ctx.stream()); + + edm::ESHandle timeCorrsHandle; + setup.get().get(timeCorrsHandle); + auto const& timeCorrsProduct = timeCorrsHandle->getProduct(ctx.stream()); + + edm::ESHandle qieTypesHandle; + setup.get().get(qieTypesHandle); + auto const& qieTypesProduct = qieTypesHandle->getProduct(ctx.stream()); + + edm::ESHandle topologyHandle; + setup.get().get(topologyHandle); + edm::ESHandle recConstantsHandle; + setup.get().get(recConstantsHandle); + + edm::ESHandle sipmParametersHandle; + setup.get().get(sipmParametersHandle); + auto const& sipmParametersProduct = sipmParametersHandle->getProduct(ctx.stream()); + + edm::ESHandle sipmCharacteristicsHandle; + setup.get().get(sipmCharacteristicsHandle); + auto const& sipmCharacteristicsProduct = sipmCharacteristicsHandle->getProduct(ctx.stream()); + + edm::ESHandle pulseOffsetsHandle; + setup.get().get(pulseOffsetsHandle); + auto const& pulseOffsetsProduct = pulseOffsetsHandle->getProduct(ctx.stream()); + + // bundle up conditions + hcal::reconstruction::ConditionsProducts conditions{gainWidthsProduct, + gainsProduct, + lutCorrsProduct, + pedestalWidthsProduct, + effectivePedestalWidthsProduct, + pedestalsProduct, + qieCodersProduct, + recoParamsProduct, + respCorrsProduct, + timeCorrsProduct, + qieTypesProduct, + sipmParametersProduct, + sipmCharacteristicsProduct, + effectivePedestalsProduct, + topologyHandle.product(), + recConstantsHandle.product(), + pedestalsHandle->offsetForHashes(), + pulseOffsetsProduct, + pulseOffsetsHandle->getValues()}; + + // scratch mem on device + hcal::reconstruction::ScratchDataGPU scratchGPU = { + cms::cuda::make_device_unique(configParameters_.maxChannels * configParameters_.maxTimeSamples, + ctx.stream()), + cms::cuda::make_device_unique(configParameters_.maxChannels * configParameters_.maxTimeSamples, + ctx.stream()), + cms::cuda::make_device_unique( + configParameters_.maxChannels * configParameters_.maxTimeSamples * configParameters_.maxTimeSamples, + ctx.stream()), + cms::cuda::make_device_unique( + configParameters_.maxChannels * configParameters_.maxTimeSamples * configParameters_.maxTimeSamples, + ctx.stream()), + cms::cuda::make_device_unique( + configParameters_.maxChannels * configParameters_.maxTimeSamples * configParameters_.maxTimeSamples, + ctx.stream()), + cms::cuda::make_device_unique(configParameters_.maxChannels, ctx.stream()), + }; + + // output dev mem + outputGPU_.allocate(configParameters_, ctx.stream()); + + hcal::reconstruction::entryPoint(inputGPU, outputGPU_, conditions, scratchGPU, configParameters_, ctx.stream()); + +#ifdef HCAL_MAHI_CPUDEBUG + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start).count(); + std::cout << "acquire duration = " << duration << std::endl; +#endif +} + +void HBHERecHitProducerGPU::produce(edm::Event& event, edm::EventSetup const& setup) { + cms::cuda::ScopedContextProduce ctx{cudaState_}; + ctx.emplace(event, rechitsM0Token_, std::move(outputGPU_.recHits)); +} + +DEFINE_FWK_MODULE(HBHERecHitProducerGPU); diff --git a/RecoLocalCalo/HcalRecProducers/src/HCALGPUAnalyzer.cc b/RecoLocalCalo/HcalRecProducers/src/HCALGPUAnalyzer.cc new file mode 100644 index 0000000000000..ba3c9de696c47 --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/HCALGPUAnalyzer.cc @@ -0,0 +1,307 @@ +// -*- C++ -*- +// +// Package: ComparisonPlots/HCALGPUAnalyzer +// Class: HCALGPUAnalyzer +// +/**\class HCALGPUAnalyzer HCALGPUAnalyzer.cc ComparisonPlots/HCALGPUAnalyzer/plugins/HCALGPUAnalyzer.cc + + Description: [one line class summary] + + Implementation: + [Notes on implementation] +*/ +// +// Original Author: Mariarosaria D'Alfonso +// Created: Mon, 17 Dec 2018 16:22:58 GMT +// +// + +// system include files +#include +#include +#include +#include +using namespace std; + +// user include files +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/one/EDAnalyzer.h" + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" + +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "CommonTools/UtilAlgos/interface/TFileService.h" + +#include "DataFormats/HcalRecHit/interface/HBHERecHit.h" +#include "DataFormats/HcalRecHit/interface/HcalRecHitCollections.h" +#include "DataFormats/HcalDetId/interface/HcalDetId.h" + +#include "SimDataFormats/CaloHit/interface/PCaloHit.h" +#include "SimDataFormats/CaloHit/interface/PCaloHitContainer.h" + +#include "SimCalorimetry/HcalSimAlgos/interface/HcalSimParameterMap.h" + +#include "TH2F.h" + +// +// class declaration +// + +class HCALGPUAnalyzer : public edm::one::EDAnalyzer { +public: + explicit HCALGPUAnalyzer(const edm::ParameterSet &); + ~HCALGPUAnalyzer() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions &descriptions); + +private: + void beginJob() override; + void analyze(const edm::Event &, const edm::EventSetup &) override; + void endJob() override; + + // ----------member data --------------------------- + // void ClearVariables(); + + // some variables for storing information + double Method0Energy, Method0EnergyGPU; + double RecHitEnergy, RecHitEnergyGPU; + double RecHitTime, RecHitTimeGPU; + double iEta, iEtaGPU; + double iPhi, iPhiGPU; + int depth, depthGPU; + + TH2F *hEnergy_2dMahi; + TH2F *hEnergy_2dM0; + TH2F *hTime_2dMahi; + + TH2F *Unmatched; + TH2F *Matched; + TH1F *hEnergy_cpu; + TH1F *hEnergy_gpu; + TH1F *hEnergy_cpugpu; + TH1F *hEnergy_cpugpu_rel; + TH1F *hEnergyM0_cpu; + TH1F *hEnergyM0_gpu; + TH1F *hTime_cpu; + TH1F *hTime_gpu; + + // create the output file + edm::Service FileService; + // create the token to retrieve hit information + edm::EDGetTokenT hRhToken; + edm::EDGetTokenT hRhTokenGPU; +}; + +// +// constants, enums and typedefs +// + +// +// static data member definitions +// + +// +// constructors and destructor +// +HCALGPUAnalyzer::HCALGPUAnalyzer(const edm::ParameterSet &iConfig) { + usesResource("TFileService"); + + hRhToken = consumes(iConfig.getUntrackedParameter("HBHERecHits", "hbheprereco")); + hRhTokenGPU = consumes( + iConfig.getUntrackedParameter("HBHERecHits", "hcalCPURecHitsProducer:recHitsLegacyHBHE")); + + // + + hEnergy_2dM0 = FileService->make("hEnergy_2dM0", "hEnergy_2dM0", 1000, 0., 100., 1000, 0., 100.); + hEnergy_2dM0->GetXaxis()->SetTitle("Cpu M0 Energy"); + hEnergy_2dM0->GetYaxis()->SetTitle("GPU M0 Energy"); + + hEnergy_2dMahi = FileService->make("hEnergy_2dMahi", "hEnergy_2dMahi", 1000, 0., 100., 1000, 0., 100.); + hEnergy_2dMahi->GetXaxis()->SetTitle("CPU Energy"); + hEnergy_2dMahi->GetYaxis()->SetTitle("GPU Energy"); + + hTime_2dMahi = FileService->make("hTime_2dMahi", "hTime_2dMahi", 250, -12.5, 12.5, 250, -12.5, 12.5); + hTime_2dMahi->GetXaxis()->SetTitle("Mahi Time CPU"); + hTime_2dMahi->GetYaxis()->SetTitle("Mahi Time GPU"); + + // + + hEnergyM0_cpu = FileService->make("hEnergyM0_cpu", "hEnergyM0_cpu", 100, 0., 100.); + hEnergyM0_cpu->GetXaxis()->SetTitle("CPU Energy"); + + hEnergy_cpu = FileService->make("hEnergy_cpu", "hEnergy_cpu", 50, 0., 50.); + hEnergy_cpu->GetXaxis()->SetTitle("CPU Energy"); + + hEnergy_gpu = FileService->make("hEnergy_gpu", "hEnergy_gpu", 50, 0., 50.); + hEnergy_gpu->GetXaxis()->SetTitle("GPU Energy"); + + // + + hEnergy_cpugpu = FileService->make("hEnergy_cpugpu", "hEnergy_cpugpu", 500, -2.5, 2.5); + hEnergy_cpugpu->GetXaxis()->SetTitle("GPU Energy - CPU Energy [GeV]"); + hEnergy_cpugpu->GetYaxis()->SetTitle("# RecHits"); + + hEnergy_cpugpu_rel = + FileService->make("hEnergy_cpugpu_rel", "hEnergy_cpugpu_rel ( E > 0.005 GeV)", 500, -2.5, 2.5); + hEnergy_cpugpu_rel->GetXaxis()->SetTitle("(GPU Energy - CPU Energy) / CPU energy"); + hEnergy_cpugpu_rel->GetYaxis()->SetTitle("# RecHits"); + + // + + hTime_cpu = FileService->make("hTime_cpu", "hTime_cpu", 50, -25., 25.); + hTime_cpu->GetXaxis()->SetTitle("CPU Time"); + + hTime_gpu = FileService->make("hTime_gpu", "hTime_gpu", 50, -25., 25.); + hTime_gpu->GetXaxis()->SetTitle("GPU Time"); + + Unmatched = FileService->make("Unmatched", "Unmatched (eta,phi)", 100, -50., 50., 85, 0., 85.); + Matched = FileService->make("Matched", "Matched (eta,phi)", 100, -50., 50., 85, 0., 85.); + + //now do what ever initialization is needed +} + +// +// member functions +// + +// ------------ method called for each event ------------ +void HCALGPUAnalyzer::analyze(const edm::Event &iEvent, const edm::EventSetup &iSetup) { + using namespace edm; + + // Read events + Handle hRecHits; + iEvent.getByToken(hRhToken, hRecHits); + + Handle hRecHitsGPU; + iEvent.getByToken(hRhTokenGPU, hRecHitsGPU); + + // Loop over all rechits in one event + for (int i = 0; i < (int)hRecHits->size(); i++) { + // get ID information for the reconstructed hit + HcalDetId detID_rh = (*hRecHits)[i].id().rawId(); + + // ID information can get us detector coordinates + depth = (*hRecHits)[i].id().depth(); + iEta = detID_rh.ieta(); + iPhi = detID_rh.iphi(); + + // get some variables + Method0Energy = (*hRecHits)[i].eraw(); + RecHitEnergy = (*hRecHits)[i].energy(); + RecHitTime = (*hRecHits)[i].time(); + + hEnergy_cpu->Fill(RecHitEnergy); + hTime_cpu->Fill(RecHitTime); + + /* + cout << "Run " << i << ": "; + cout << "Method0Energy: " << Method0Energy; + cout << "RecHitEnergy: " << RecHitEnergy; + cout << "depth: " << depth; + cout << "iEta: " << iEta; + cout << "iPhi: " << iPhi; + cout << "RecHitTime" << RecHitTime; + */ + } + + for (int i = 0; i < (int)hRecHitsGPU->size(); i++) { + // get ID information for the reconstructed hit + HcalDetId detID_rh = (*hRecHitsGPU)[i].id().rawId(); + + // ID information can get us detector coordinates + depthGPU = (*hRecHitsGPU)[i].id().depth(); + iEtaGPU = detID_rh.ieta(); + iPhiGPU = detID_rh.iphi(); + + // get some variables + Method0EnergyGPU = (*hRecHitsGPU)[i].eraw(); + RecHitEnergyGPU = (*hRecHitsGPU)[i].energy(); + RecHitTimeGPU = (*hRecHitsGPU)[i].time(); + + hEnergy_gpu->Fill(RecHitEnergyGPU); + hTime_gpu->Fill(RecHitTimeGPU); + + /* + cout << "Run " << i << ": "; + cout << "Method0Energy: " << Method0EnergyGPU; + cout << "RecHitEnergy: " << RecHitEnergyGPU; + cout << "depth: " << depthGPU; + cout << "iEta: " << iEtaGPU; + cout << "iPhi: " << iPhiGPU; + cout << "RecHitTime" << RecHitTimeGPU; + */ + } + + // Loop over all rechits in one event + for (int i = 0; i < (int)hRecHits->size(); i++) { + HcalDetId detID_rh = (*hRecHits)[i].id().rawId(); + + bool unmatched = true; + // cout << "--------------------------------------------------------" << endl; + + for (int j = 0; j < (int)hRecHitsGPU->size(); j++) { + HcalDetId detID_gpu = (*hRecHitsGPU)[j].id().rawId(); + + if ((detID_rh == detID_gpu)) { + /* + cout << "Mtime(cpu)" << (*hRecHits)[i].time() << endl; + cout << " Mtime(gpu)" << (*hRecHitsGPU)[j].time() << endl; + + cout << "M0E(cpu)" << (*hRecHits)[i].eraw() << endl; + cout << " M0E(gpu)" << (*hRecHitsGPU)[j].eraw() << endl; + */ + + auto relValue = ((*hRecHitsGPU)[j].energy() - (*hRecHits)[i].energy()) / (*hRecHits)[i].energy(); + + hEnergy_2dM0->Fill((*hRecHits)[i].eraw(), (*hRecHitsGPU)[j].eraw()); + hEnergy_2dMahi->Fill((*hRecHits)[i].energy(), (*hRecHitsGPU)[j].energy()); + hEnergy_cpugpu->Fill((*hRecHitsGPU)[j].energy() - (*hRecHits)[i].energy()); + if ((*hRecHits)[i].energy() > 0.005) + hEnergy_cpugpu_rel->Fill(relValue); + hTime_2dMahi->Fill((*hRecHits)[i].time(), (*hRecHitsGPU)[j].time()); + + /* + if((relValue < - 0.9) and ((*hRecHits)[i].energy()>0.005)) { + cout << "----------------------------------"<< endl; + cout << " detID = " << detID_rh.rawId() << endl; + cout << "ME(cpu)" << (*hRecHits)[i].energy() << endl; + cout << " ME(gpu)" << (*hRecHitsGPU)[j].energy() << endl; + } + */ + + Matched->Fill(detID_rh.ieta(), detID_rh.iphi()); + + unmatched = false; + } + } + + /// + + if (unmatched) { + Unmatched->Fill(detID_rh.ieta(), detID_rh.iphi()); + // cout << " recHit not matched =" << detID_rh << " E(raw)=" << (*hRecHits)[i].eraw() << " E=" << (*hRecHits)[i].energy() << endl; + } + } +} + +// ------------ method called once each job just before starting event loop ------------ +void HCALGPUAnalyzer::beginJob() {} + +// ------------ method called once each job just after ending the event loop ------------ +void HCALGPUAnalyzer::endJob() {} + +// ------------ method fills 'descriptions' with the allowed parameters for the module ------------ +void HCALGPUAnalyzer::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { + //The following says we do not know what parameters are allowed so do no validation + // Please change this to state exactly what you do use, even if it is no parameters + edm::ParameterSetDescription desc; + desc.setUnknown(); + descriptions.addDefault(desc); +} + +//define this as a plug-in +DEFINE_FWK_MODULE(HCALGPUAnalyzer); diff --git a/RecoLocalCalo/HcalRecProducers/src/HcalCPURecHitsProducer.cc b/RecoLocalCalo/HcalRecProducers/src/HcalCPURecHitsProducer.cc new file mode 100644 index 0000000000000..714ec8b7de5af --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/HcalCPURecHitsProducer.cc @@ -0,0 +1,106 @@ +#include +#include + +#include "CUDADataFormats/HcalRecHitSoA/interface/RecHitCollection.h" +#include "DataFormats/HcalRecHit/interface/HcalRecHitCollections.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +class HcalCPURecHitsProducer : public edm::stream::EDProducer { +public: + explicit HcalCPURecHitsProducer(edm::ParameterSet const& ps); + ~HcalCPURecHitsProducer() override; + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void acquire(edm::Event const&, edm::EventSetup const&, edm::WaitingTaskWithArenaHolder) override; + void produce(edm::Event&, edm::EventSetup const&) override; + +private: + using IProductType = cms::cuda::Product>; + edm::EDGetTokenT recHitsM0TokenIn_; + using OProductType = hcal::RecHitCollection>; + edm::EDPutTokenT recHitsM0TokenOut_; + edm::EDPutTokenT recHitsLegacyTokenOut_; + + // to pass from acquire to produce + OProductType tmpRecHits_; +}; + +void HcalCPURecHitsProducer::fillDescriptions(edm::ConfigurationDescriptions& confDesc) { + edm::ParameterSetDescription desc; + + desc.add("recHitsM0LabelIn", edm::InputTag{"hbheRecHitProducerGPU", "recHitsM0HBHE"}); + desc.add("recHitsM0LabelOut", "recHitsM0HBHE"); + desc.add("recHitsLegacyLabelOut", "recHitsLegacyHBHE"); + + confDesc.addWithDefaultLabel(desc); +} + +HcalCPURecHitsProducer::HcalCPURecHitsProducer(const edm::ParameterSet& ps) + : recHitsM0TokenIn_{consumes(ps.getParameter("recHitsM0LabelIn"))}, + recHitsM0TokenOut_{produces(ps.getParameter("recHitsM0LabelOut"))}, + recHitsLegacyTokenOut_{produces(ps.getParameter("recHitsLegacyLabelOut"))} {} + +HcalCPURecHitsProducer::~HcalCPURecHitsProducer() {} + +void HcalCPURecHitsProducer::acquire(edm::Event const& event, + edm::EventSetup const& setup, + edm::WaitingTaskWithArenaHolder taskHolder) { + // retrieve data/ctx + auto const& recHitsProduct = event.get(recHitsM0TokenIn_); + cms::cuda::ScopedContextAcquire ctx{recHitsProduct, std::move(taskHolder)}; + auto const& recHits = ctx.get(recHitsProduct); + + // resize tmp buffers + tmpRecHits_.resize(recHits.size); + +#ifdef HCAL_MAHI_CPUDEBUG + std::cout << "num rec Hits = " << recHits.size << std::endl; +#endif + + auto lambdaToTransfer = [&ctx](auto& dest, auto* src) { + using vector_type = typename std::remove_reference::type; + using src_data_type = typename std::remove_pointer::type; + using type = typename vector_type::value_type; + static_assert(std::is_same::value && "Dest and Src data types do not match"); + cudaCheck(cudaMemcpyAsync(dest.data(), src, dest.size() * sizeof(type), cudaMemcpyDeviceToHost, ctx.stream())); + }; + + lambdaToTransfer(tmpRecHits_.energy, recHits.energy.get()); + lambdaToTransfer(tmpRecHits_.chi2, recHits.chi2.get()); + lambdaToTransfer(tmpRecHits_.energyM0, recHits.energyM0.get()); + lambdaToTransfer(tmpRecHits_.timeM0, recHits.timeM0.get()); + lambdaToTransfer(tmpRecHits_.did, recHits.did.get()); +} + +void HcalCPURecHitsProducer::produce(edm::Event& event, edm::EventSetup const& setup) { + // populate the legacy collection + auto recHitsLegacy = std::make_unique(); + // did not set size with ctor as there is no setter for did + recHitsLegacy->reserve(tmpRecHits_.did.size()); + for (uint32_t i = 0; i < tmpRecHits_.did.size(); i++) { + recHitsLegacy->emplace_back(HcalDetId{tmpRecHits_.did[i]}, + tmpRecHits_.energy[i], + 0 // timeRising + ); + + // update newly pushed guy + (*recHitsLegacy)[i].setChiSquared(tmpRecHits_.chi2[i]); + (*recHitsLegacy)[i].setRawEnergy(tmpRecHits_.energyM0[i]); + } + + // put a legacy format + event.put(recHitsLegacyTokenOut_, std::move(recHitsLegacy)); + + // put a new format + event.emplace(recHitsM0TokenOut_, std::move(tmpRecHits_)); +} + +DEFINE_FWK_MODULE(HcalCPURecHitsProducer); diff --git a/RecoLocalCalo/HcalRecProducers/src/HcalESProducersGPUDefs.cc b/RecoLocalCalo/HcalRecProducers/src/HcalESProducersGPUDefs.cc new file mode 100644 index 0000000000000..2fc6cc0d19002 --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/HcalESProducersGPUDefs.cc @@ -0,0 +1,120 @@ +#include "CondFormats/DataRecord/interface/HcalCombinedRecordsGPU.h" +#include "CondFormats/DataRecord/interface/HcalGainWidthsRcd.h" +#include "CondFormats/DataRecord/interface/HcalGainsRcd.h" +#include "CondFormats/DataRecord/interface/HcalLUTCorrsRcd.h" +#include "CondFormats/DataRecord/interface/HcalPedestalWidthsRcd.h" +#include "CondFormats/DataRecord/interface/HcalPedestalsRcd.h" +#include "CondFormats/DataRecord/interface/HcalQIEDataRcd.h" +#include "CondFormats/DataRecord/interface/HcalQIETypesRcd.h" +#include "CondFormats/DataRecord/interface/HcalRecoParamsRcd.h" +#include "CondFormats/DataRecord/interface/HcalRespCorrsRcd.h" +#include "CondFormats/DataRecord/interface/HcalSiPMCharacteristicsRcd.h" +#include "CondFormats/DataRecord/interface/HcalSiPMParametersRcd.h" +#include "CondFormats/DataRecord/interface/HcalTimeCorrsRcd.h" +#include "CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalWidthsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalConvertedEffectivePedestalsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalConvertedPedestalWidthsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalConvertedPedestalsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalGainWidths.h" +#include "CondFormats/HcalObjects/interface/HcalGainWidthsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalGains.h" +#include "CondFormats/HcalObjects/interface/HcalGainsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalLUTCorrs.h" +#include "CondFormats/HcalObjects/interface/HcalLUTCorrsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalPedestalWidths.h" +#include "CondFormats/HcalObjects/interface/HcalPedestalWidthsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalPedestals.h" +#include "CondFormats/HcalObjects/interface/HcalPedestalsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalQIECodersGPU.h" +#include "CondFormats/HcalObjects/interface/HcalQIEData.h" +#include "CondFormats/HcalObjects/interface/HcalQIETypes.h" +#include "CondFormats/HcalObjects/interface/HcalQIETypesGPU.h" +#include "CondFormats/HcalObjects/interface/HcalRecoParams.h" +#include "CondFormats/HcalObjects/interface/HcalRecoParamsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalRespCorrs.h" +#include "CondFormats/HcalObjects/interface/HcalRespCorrsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMCharacteristics.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMCharacteristicsGPU.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMParameters.h" +#include "CondFormats/HcalObjects/interface/HcalSiPMParametersGPU.h" +#include "CondFormats/HcalObjects/interface/HcalTimeCorrs.h" +#include "CondFormats/HcalObjects/interface/HcalTimeCorrsGPU.h" +#include "HeterogeneousCore/CUDACore/interface/ConvertingESProducerT.h" +#include "HeterogeneousCore/CUDACore/interface/ConvertingESProducerWithDependenciesT.h" +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalRecoParamsWithPulseShapesGPU.h" + +using HcalRecoParamsGPUESProducer = ConvertingESProducerT; + +using HcalRecoParamsWithPulseShapesGPUESProducer = + ConvertingESProducerT; + +using HcalPedestalsGPUESProducer = ConvertingESProducerT; + +using HcalGainsGPUESProducer = ConvertingESProducerT; + +using HcalLUTCorrsGPUESProducer = ConvertingESProducerT; + +using HcalRespCorrsGPUESProducer = ConvertingESProducerT; + +using HcalTimeCorrsGPUESProducer = ConvertingESProducerT; + +using HcalPedestalWidthsGPUESProducer = + ConvertingESProducerT; + +using HcalGainWidthsGPUESProducer = ConvertingESProducerT; + +using HcalQIECodersGPUESProducer = ConvertingESProducerT; + +using HcalQIETypesGPUESProducer = ConvertingESProducerT; + +using HcalSiPMParametersGPUESProducer = + ConvertingESProducerT; + +using HcalSiPMCharacteristicsGPUESProducer = + ConvertingESProducerT; + +using HcalConvertedPedestalsGPUESProducer = ConvertingESProducerWithDependenciesT; + +using HcalConvertedEffectivePedestalsGPUESProducer = + ConvertingESProducerWithDependenciesT; + +using HcalConvertedPedestalWidthsGPUESProducer = ConvertingESProducerWithDependenciesT; + +using HcalConvertedEffectivePedestalWidthsGPUESProducer = + ConvertingESProducerWithDependenciesT; + +DEFINE_FWK_EVENTSETUP_MODULE(HcalRecoParamsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalRecoParamsWithPulseShapesGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalPedestalsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalGainsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalLUTCorrsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalRespCorrsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalTimeCorrsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalPedestalWidthsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalGainWidthsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalQIECodersGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalQIETypesGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalSiPMParametersGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalSiPMCharacteristicsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalConvertedPedestalsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalConvertedEffectivePedestalsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalConvertedPedestalWidthsGPUESProducer); +DEFINE_FWK_EVENTSETUP_MODULE(HcalConvertedEffectivePedestalWidthsGPUESProducer); diff --git a/RecoLocalCalo/HcalRecProducers/src/HcalMahiPulseOffsetsGPUESProducer.cc b/RecoLocalCalo/HcalRecProducers/src/HcalMahiPulseOffsetsGPUESProducer.cc new file mode 100644 index 0000000000000..0862e0a861d5d --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/HcalMahiPulseOffsetsGPUESProducer.cc @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "FWCore/Framework/interface/ESProducer.h" +#include "FWCore/Framework/interface/ESProductHost.h" +#include "FWCore/Framework/interface/ESTransientHandle.h" +#include "FWCore/Framework/interface/EventSetupRecordIntervalFinder.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/ModuleFactory.h" +#include "FWCore/Framework/interface/SourceFactory.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/ReusableObjectHolder.h" +#include "FWCore/Utilities/interface/typelookup.h" +#include "HeterogeneousCore/CUDACore/interface/JobConfigurationGPURecord.h" +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalMahiPulseOffsetsGPU.h" + +class HcalMahiPulseOffsetsGPUESProducer : public edm::ESProducer, public edm::EventSetupRecordIntervalFinder { +public: + HcalMahiPulseOffsetsGPUESProducer(edm::ParameterSet const&); + ~HcalMahiPulseOffsetsGPUESProducer() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions&); + std::unique_ptr produce(JobConfigurationGPURecord const&); + +protected: + void setIntervalFor(const edm::eventsetup::EventSetupRecordKey&, + const edm::IOVSyncValue&, + edm::ValidityInterval&) override; + +private: + edm::ParameterSet const& pset_; +}; + +HcalMahiPulseOffsetsGPUESProducer::HcalMahiPulseOffsetsGPUESProducer(edm::ParameterSet const& pset) : pset_{pset} { + setWhatProduced(this); + findingRecord(); +} + +void HcalMahiPulseOffsetsGPUESProducer::setIntervalFor(const edm::eventsetup::EventSetupRecordKey& iKey, + const edm::IOVSyncValue& iTime, + edm::ValidityInterval& oInterval) { + oInterval = edm::ValidityInterval(edm::IOVSyncValue::beginOfTime(), edm::IOVSyncValue::endOfTime()); +} + +void HcalMahiPulseOffsetsGPUESProducer::fillDescriptions(edm::ConfigurationDescriptions& desc) { + edm::ParameterSetDescription d; + d.add>("pulseOffsets", {-3, -2, -1, 0, 1, 2, 3, 4}); + desc.addWithDefaultLabel(d); +} + +std::unique_ptr HcalMahiPulseOffsetsGPUESProducer::produce(JobConfigurationGPURecord const&) { + return std::make_unique(pset_); +} + +DEFINE_FWK_EVENTSETUP_SOURCE(HcalMahiPulseOffsetsGPUESProducer); diff --git a/RecoLocalCalo/HcalRecProducers/src/KernelHelpers.h b/RecoLocalCalo/HcalRecProducers/src/KernelHelpers.h new file mode 100644 index 0000000000000..ade221b2c4870 --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/KernelHelpers.h @@ -0,0 +1,220 @@ +#ifndef RecoLocalCalo_HcalRecProducers_src_KernelHelpers_h +#define RecoLocalCalo_HcalRecProducers_src_KernelHelpers_h + +#include "RecoLocalCalo/HcalRecAlgos/interface/HcalConstants.h" + +#include "DeclsForKernels.h" + +namespace hcal { + namespace reconstruction { + + // this is from HcalTimeSlew. + // HcalTimeSlew are values that come in from ESProducer that takes them + // from a python config. see DeclsForKernels for more explanation + __forceinline__ __device__ float compute_time_slew_delay(float const fC, + float const tzero, + float const slope, + float const tmax) { + auto const rawDelay = tzero + slope * std::log(fC); + return rawDelay < 0 ? 0 : (rawDelay > tmax ? tmax : rawDelay); + } + + // HcalQIEShapes are hardcoded in HcalQIEData.cc basically + // + some logic to generate 128 and 256 value arrays... + __constant__ float const qie8shape[129] = { + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, + 18, 20, 22, 24, 26, 28, 31, 34, 37, 40, 44, 48, 52, 57, 62, 57, 62, + 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 142, 152, 162, + 172, 182, 192, 202, 217, 232, 247, 262, 282, 302, 322, 347, 372, 347, 372, 397, 422, + 447, 472, 497, 522, 547, 572, 597, 622, 647, 672, 697, 722, 772, 822, 872, 922, 972, + 1022, 1072, 1147, 1222, 1297, 1372, 1472, 1572, 1672, 1797, 1922, 1797, 1922, 2047, 2172, 2297, 2422, + 2547, 2672, 2797, 2922, 3047, 3172, 3297, 3422, 3547, 3672, 3922, 4172, 4422, 4672, 4922, 5172, 5422, + 5797, 6172, 6547, 6922, 7422, 7922, 8422, 9047, 9672, 10297}; + + __constant__ float const qie11shape[257] = { + -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, + 11.5, 12.5, 13.5, 14.5, 15.5, 17.5, 19.5, 21.5, 23.5, 25.5, 27.5, 29.5, + 31.5, 33.5, 35.5, 37.5, 39.5, 41.5, 43.5, 45.5, 47.5, 49.5, 51.5, 53.5, + 55.5, 59.5, 63.5, 67.5, 71.5, 75.5, 79.5, 83.5, 87.5, 91.5, 95.5, 99.5, + 103.5, 107.5, 111.5, 115.5, 119.5, 123.5, 127.5, 131.5, 135.5, 139.5, 147.5, 155.5, + 163.5, 171.5, 179.5, 187.5, 171.5, 179.5, 187.5, 195.5, 203.5, 211.5, 219.5, 227.5, + 235.5, 243.5, 251.5, 259.5, 267.5, 275.5, 283.5, 291.5, 299.5, 315.5, 331.5, 347.5, + 363.5, 379.5, 395.5, 411.5, 427.5, 443.5, 459.5, 475.5, 491.5, 507.5, 523.5, 539.5, + 555.5, 571.5, 587.5, 603.5, 619.5, 651.5, 683.5, 715.5, 747.5, 779.5, 811.5, 843.5, + 875.5, 907.5, 939.5, 971.5, 1003.5, 1035.5, 1067.5, 1099.5, 1131.5, 1163.5, 1195.5, 1227.5, + 1259.5, 1291.5, 1355.5, 1419.5, 1483.5, 1547.5, 1611.5, 1675.5, 1547.5, 1611.5, 1675.5, 1739.5, + 1803.5, 1867.5, 1931.5, 1995.5, 2059.5, 2123.5, 2187.5, 2251.5, 2315.5, 2379.5, 2443.5, 2507.5, + 2571.5, 2699.5, 2827.5, 2955.5, 3083.5, 3211.5, 3339.5, 3467.5, 3595.5, 3723.5, 3851.5, 3979.5, + 4107.5, 4235.5, 4363.5, 4491.5, 4619.5, 4747.5, 4875.5, 5003.5, 5131.5, 5387.5, 5643.5, 5899.5, + 6155.5, 6411.5, 6667.5, 6923.5, 7179.5, 7435.5, 7691.5, 7947.5, 8203.5, 8459.5, 8715.5, 8971.5, + 9227.5, 9483.5, 9739.5, 9995.5, 10251.5, 10507.5, 11019.5, 11531.5, 12043.5, 12555.5, 13067.5, 13579.5, + 12555.5, 13067.5, 13579.5, 14091.5, 14603.5, 15115.5, 15627.5, 16139.5, 16651.5, 17163.5, 17675.5, 18187.5, + 18699.5, 19211.5, 19723.5, 20235.5, 20747.5, 21771.5, 22795.5, 23819.5, 24843.5, 25867.5, 26891.5, 27915.5, + 28939.5, 29963.5, 30987.5, 32011.5, 33035.5, 34059.5, 35083.5, 36107.5, 37131.5, 38155.5, 39179.5, 40203.5, + 41227.5, 43275.5, 45323.5, 47371.5, 49419.5, 51467.5, 53515.5, 55563.5, 57611.5, 59659.5, 61707.5, 63755.5, + 65803.5, 67851.5, 69899.5, 71947.5, 73995.5, 76043.5, 78091.5, 80139.5, 82187.5, 84235.5, 88331.5, 92427.5, + 96523.5, 100620, 104716, 108812, 112908}; + + // Conditions are transferred once per IOV + // Access is performed based on the det id which is converted to a linear index + // 2 funcs below are taken from HcalTopology (reimplemented here). + // Inputs are constants that are also taken from HcalTopology + // but passed to the kernel as arguments using the HclaTopology itself + constexpr int32_t IPHI_MAX = 72; + + __forceinline__ __device__ uint32_t did2linearIndexHB( + uint32_t const didraw, int const maxDepthHB, int const firstHBRing, int const lastHBRing, int const nEtaHB) { + HcalDetId did{didraw}; + uint32_t const value = (did.depth() - 1) + maxDepthHB * (did.iphi() - 1); + return did.ieta() > 0 ? value + maxDepthHB * hcal::reconstruction::IPHI_MAX * (did.ieta() - firstHBRing) + : value + maxDepthHB * hcal::reconstruction::IPHI_MAX * (did.ieta() + lastHBRing + nEtaHB); + } + + __forceinline__ __device__ uint32_t did2linearIndexHE(uint32_t const didraw, + int const maxDepthHE, + int const maxPhiHE, + int const firstHERing, + int const lastHERing, + int const nEtaHE) { + HcalDetId did{didraw}; + uint32_t const value = (did.depth() - 1) + maxDepthHE * (did.iphi() - 1); + return did.ieta() > 0 ? value + maxDepthHE * maxPhiHE * (did.ieta() - firstHERing) + : value + maxDepthHE * maxPhiHE * (did.ieta() + lastHERing + nEtaHE); + } + + __forceinline__ __device__ uint32_t get_qiecoder_index(uint32_t const capid, uint32_t const range) { + return capid * 4 + range; + } + + __forceinline__ __device__ float compute_reco_correction_factor(float const par1, + float const par2, + float const par3, + float const x) { + return par3 * x * x + par2 * x + par1; + } + + // compute the charge using the adc, qie type and the appropriate qie shape array + __forceinline__ __device__ float compute_coder_charge( + int const qieType, uint8_t const adc, uint8_t const capid, float const* qieOffsets, float const* qieSlopes) { + auto const range = qieType == 0 ? (adc >> 5) & 0x3 : (adc >> 6) & 0x3; + auto const* qieShapeToUse = qieType == 0 ? qie8shape : qie11shape; + auto const nbins = qieType == 0 ? 32 : 64; + auto const center = adc % nbins == nbins - 1 ? 0.5 * (3 * qieShapeToUse[adc] - qieShapeToUse[adc - 1]) + : 0.5 * (qieShapeToUse[adc] + qieShapeToUse[adc + 1]); + auto const index = get_qiecoder_index(capid, range); + return (center - qieOffsets[index]) / qieSlopes[index]; + } + + // this is from + // https://github.com/cms-sw/cmssw/blob/master/RecoLocalCalo/HcalRecProducers/src/HBHEPhase1Reconstructor.cc#L140 + + __forceinline__ __device__ float compute_diff_charge_gain(int const qieType, + uint8_t adc, + uint8_t const capid, + float const* qieOffsets, + float const* qieSlopes, + bool const isqie11) { + constexpr uint32_t mantissaMaskQIE8 = 0x1fu; + constexpr uint32_t mantissaMaskQIE11 = 0x3f; + auto const mantissaMask = isqie11 ? mantissaMaskQIE11 : mantissaMaskQIE8; + auto const q = compute_coder_charge(qieType, adc, capid, qieOffsets, qieSlopes); + auto const mantissa = adc & mantissaMask; + + if (mantissa == 0u || mantissa == mantissaMask - 1u) + return compute_coder_charge(qieType, adc + 1u, capid, qieOffsets, qieSlopes) - q; + else if (mantissa == 1u || mantissa == mantissaMask) + return q - compute_coder_charge(qieType, adc - 1u, capid, qieOffsets, qieSlopes); + else { + auto const qup = compute_coder_charge(qieType, adc + 1u, capid, qieOffsets, qieSlopes); + auto const qdown = compute_coder_charge(qieType, adc - 1u, capid, qieOffsets, qieSlopes); + auto const upgain = qup - q; + auto const downgain = q - qdown; + auto const averagegain = (qup - qdown) / 2.f; + if (std::abs(upgain - downgain) < 0.01f * averagegain) + return averagegain; + else { + auto const q2up = compute_coder_charge(qieType, adc + 2u, capid, qieOffsets, qieSlopes); + auto const q2down = compute_coder_charge(qieType, adc - 2u, capid, qieOffsets, qieSlopes); + auto const upgain2 = q2up - qup; + auto const downgain2 = qdown - q2down; + if (std::abs(upgain2 - upgain) < std::abs(downgain2 - downgain)) + return upgain; + else + return downgain; + } + } + } + + // TODO: remove what's not needed + // originally from from RecoLocalCalo/HcalRecAlgos/src/PulseShapeFunctor.cc + __forceinline__ __device__ float compute_pulse_shape_value(float const pulse_time, + int const sample, + int const shift, + float const* acc25nsVec, + float const* diff25nsItvlVec, + float const* accVarLenIdxMinusOneVec, + float const* diffVarItvlIdxMinusOneVec, + float const* accVarLenIdxZeroVec, + float const* diffVarItvlIdxZeroVec) { + // constants + constexpr float slew = 0.f; + constexpr auto ns_per_bx = hcal::constants::nsPerBX; + + // FIXME: clean up all the rounding... this is coming from original cpu version + float const i_start_float = -hcal::constants::iniTimeShift - pulse_time - slew > 0.f + ? 0.f + : std::abs(-hcal::constants::iniTimeShift - pulse_time - slew) + 1.f; + int i_start = static_cast(i_start_float); + float offset_start = static_cast(i_start) - hcal::constants::iniTimeShift - pulse_time - slew; + // FIXME: do we need a check for nan??? +#ifdef HCAL_MAHI_GPUDEBUG + if (shift == 0) + printf("i_start_float = %f i_start = %d offset_start = %f\n", i_start_float, i_start, offset_start); +#endif + + // boundary + if (offset_start == 1.0f) { + offset_start = 0.f; + i_start -= 1; + } + +#ifdef HCAL_MAHI_GPUDEBUG + if (shift == 0) + printf("i_start_float = %f i_start = %d offset_start = %f\n", i_start_float, i_start, offset_start); +#endif + + int const bin_start = static_cast(offset_start); + auto const bin_start_up = static_cast(bin_start) + 0.5f; + int const bin_0_start = offset_start < bin_start_up ? bin_start - 1 : bin_start; + int const its_start = i_start / ns_per_bx; + int const distTo25ns_start = hcal::constants::nsPerBX - 1 - i_start % ns_per_bx; + auto const factor = offset_start - static_cast(bin_0_start) - 0.5; + +#ifdef HCAL_MAHI_GPUDEBUG + if (shift == 0) { + printf("bin_start = %d bin_0_start = %d its_start = %d distTo25ns_start = %d factor = %f\n", + bin_start, + bin_0_start, + its_start, + distTo25ns_start, + factor); + } +#endif + + auto const sample_over10ts = sample + shift; + float value = 0.0f; + if (sample_over10ts == its_start) { + value = bin_0_start == -1 + ? accVarLenIdxMinusOneVec[distTo25ns_start] + factor * diffVarItvlIdxMinusOneVec[distTo25ns_start] + : accVarLenIdxZeroVec[distTo25ns_start] + factor * diffVarItvlIdxZeroVec[distTo25ns_start]; + } else if (sample_over10ts > its_start) { + int const bin_idx = distTo25ns_start + 1 + (sample_over10ts - its_start - 1) * ns_per_bx + bin_0_start; + value = acc25nsVec[bin_idx] + factor * diff25nsItvlVec[bin_idx]; + } + return value; + } + + } // namespace reconstruction +} // namespace hcal + +#endif // RecoLocalCalo_HcalRecProducers_src_KernelHelpers_h diff --git a/RecoLocalCalo/HcalRecProducers/src/MahiGPU.cu b/RecoLocalCalo/HcalRecProducers/src/MahiGPU.cu new file mode 100644 index 0000000000000..10098a8b3a962 --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/MahiGPU.cu @@ -0,0 +1,1223 @@ +#include + +#include "DataFormats/CaloRecHit/interface/MultifitComputations.h" +// needed to compile with USER_CXXFLAGS="-DCOMPUTE_TDC_TIME" +#include "DataFormats/HcalRecHit/interface/HcalSpecialTimes.h" +#include "FWCore/Utilities/interface/CMSUnrollLoop.h" + +// TODO reuse some of the HCAL constats from +//#include "RecoLocalCalo/HcalRecAlgos/interface/HcalConstants.h" + +#include "SimpleAlgoGPU.h" +#include "KernelHelpers.h" + +#ifdef HCAL_MAHI_GPUDEBUG +#define DETID_TO_DEBUG 1125647428 +#endif + +namespace hcal { + namespace mahi { + + // TODO: provide constants from configuration + // from RecoLocalCalo/HcalRecProducers/python/HBHEMahiParameters_cfi.py + constexpr int nMaxItersMin = 50; + constexpr int nMaxItersNNLS = 500; + constexpr double nnlsThresh = 1e-11; + constexpr float deltaChi2Threashold = 1e-3; + + // from RecoLocalCalo/HcalRecProducers/src/HBHEPhase1Reconstructor.cc + __forceinline__ __device__ float get_raw_charge(double const charge, + double const pedestal, + float const* shrChargeMinusPedestal, + float const* parLin1Values, + float const* parLin2Values, + float const* parLin3Values, + int32_t const nsamplesForCompute, + int32_t const soi, + int const sipmQTSShift, + int const sipmQNTStoSum, + int const sipmType, + float const fcByPE, + bool const isqie11) { + float rawCharge; + + if (!isqie11) + rawCharge = charge; + else { + auto const parLin1 = parLin1Values[sipmType - 1]; + auto const parLin2 = parLin2Values[sipmType - 1]; + auto const parLin3 = parLin3Values[sipmType - 1]; + + int const first = std::max(soi + sipmQTSShift, 0); + int const last = std::min(soi + sipmQNTStoSum, nsamplesForCompute); + float sipmq = 0.0f; + for (auto ts = first; ts < last; ts++) + sipmq += shrChargeMinusPedestal[threadIdx.y * nsamplesForCompute + ts]; + auto const effectivePixelsFired = sipmq / fcByPE; + auto const factor = + hcal::reconstruction::compute_reco_correction_factor(parLin1, parLin2, parLin3, effectivePixelsFired); + rawCharge = (charge - pedestal) * factor + pedestal; + +#ifdef HCAL_MAHI_GPUDEBUG + printf("first = %d last = %d sipmQ = %f factor = %f rawCharge = %f\n", first, last, sipmq, factor, rawCharge); +#endif + } + return rawCharge; + } + + // Assume: same number of samples for HB and HE + // TODO: add/validate restrict (will increase #registers in use by the kernel) + __global__ void kernel_prep1d_sameNumberOfSamples(float* amplitudes, + float* noiseTerms, + float* outputEnergy, + float* outputChi2, + uint16_t const* dataf01HE, + uint16_t const* dataf5HB, + uint16_t const* dataf3HB, + uint32_t const* idsf01HE, + uint32_t const* idsf5HB, + uint32_t const* idsf3HB, + uint32_t const stridef01HE, + uint32_t const stridef5HB, + uint32_t const stridef3HB, + uint32_t const nchannelsf01HE, + uint32_t const nchannelsf5HB, + uint8_t const* npresamplesf5HB, + int8_t* soiSamples, + float* method0Energy, + float* method0Time, + uint32_t* outputdid, + uint32_t const nchannels, + uint32_t const* recoParam1Values, + uint32_t const* recoParam2Values, + float const* qieCoderOffsets, + float const* qieCoderSlopes, + int const* qieTypes, + float const* pedestalWidths, + float const* effectivePedestalWidths, + float const* pedestals, + float const* effectivePedestals, + bool const useEffectivePedestals, + int const* sipmTypeValues, + float const* fcByPEValues, + float const* parLin1Values, + float const* parLin2Values, + float const* parLin3Values, + float const* gainValues, + float const* respCorrectionValues, + int const maxDepthHB, + int const maxDepthHE, + int const maxPhiHE, + int const firstHBRing, + int const lastHBRing, + int const firstHERing, + int const lastHERing, + int const nEtaHB, + int const nEtaHE, + int const sipmQTSShift, + int const sipmQNTStoSum, + int const firstSampleShift, + uint32_t const offsetForHashes, + float const ts4Thresh, + int const startingSample) { + // indices + runtime constants + auto const sample = threadIdx.x + startingSample; + auto const sampleWithinWindow = threadIdx.x; + int32_t const nsamplesForCompute = blockDim.x; + auto const lch = threadIdx.y; + auto const gch = lch + blockDim.y * blockIdx.x; + auto const nchannels_per_block = blockDim.y; + auto const linearThPerBlock = threadIdx.x + threadIdx.y * blockDim.x; + + // remove + if (gch >= nchannels) + return; + + // initialize all output buffers + if (sampleWithinWindow == 0) { + outputdid[gch] = 0; + method0Energy[gch] = 0; + method0Time[gch] = 0; + outputEnergy[gch] = 0; + outputChi2[gch] = 0; + } + +#ifdef HCAL_MAHI_GPUDEBUG +#ifdef HCAL_MAHI_GPUDEBUG_SINGLECHANNEL + if (gch > 0) + return; +#endif +#endif + + // configure shared mem + extern __shared__ char smem[]; + float* shrEnergyM0PerTS = reinterpret_cast(smem); + float* shrChargeMinusPedestal = shrEnergyM0PerTS + nsamplesForCompute * nchannels_per_block; + float* shrMethod0EnergyAccum = shrChargeMinusPedestal + nsamplesForCompute * nchannels_per_block; + float* shrEnergyM0TotalAccum = shrMethod0EnergyAccum + nchannels_per_block; + unsigned long long int* shrMethod0EnergySamplePair = + reinterpret_cast(shrEnergyM0TotalAccum + nchannels_per_block); + if (sampleWithinWindow == 0) { + shrMethod0EnergyAccum[lch] = 0; + shrMethod0EnergySamplePair[lch] = __float_as_uint(std::numeric_limits::min()); + shrEnergyM0TotalAccum[lch] = 0; + } + + // offset output + auto* amplitudesForChannel = amplitudes + nsamplesForCompute * gch; + auto* noiseTermsForChannel = noiseTerms + nsamplesForCompute * gch; + auto const nchannelsf015 = nchannelsf01HE + nchannelsf5HB; + + // get event input quantities + auto const stride = gch < nchannelsf01HE ? stridef01HE : (gch < nchannelsf015 ? stridef5HB : stridef3HB); + auto const nsamples = gch < nchannelsf01HE ? compute_nsamples(stride) + : (gch < nchannelsf015 ? compute_nsamples(stride) + : compute_nsamples(stride)); + +#ifdef HCAL_MAHI_GPUDEBUG + assert(nsamples == nsamplesForCompute || nsamples - startingSample == nsamplesForCompute); +#endif + + auto const id = gch < nchannelsf01HE + ? idsf01HE[gch] + : (gch < nchannelsf015 ? idsf5HB[gch - nchannelsf01HE] : idsf3HB[gch - nchannelsf015]); + auto const did = HcalDetId{id}; + auto const adc = + gch < nchannelsf01HE + ? adc_for_sample(dataf01HE + stride * gch, sample) + : (gch < nchannelsf015 ? adc_for_sample(dataf5HB + stride * (gch - nchannelsf01HE), sample) + : adc_for_sample(dataf3HB + stride * (gch - nchannelsf015), sample)); + auto const capid = + gch < nchannelsf01HE + ? capid_for_sample(dataf01HE + stride * gch, sample) + : (gch < nchannelsf015 ? capid_for_sample(dataf5HB + stride * (gch - nchannelsf01HE), sample) + : capid_for_sample(dataf3HB + stride * (gch - nchannelsf015), sample)); + +#ifdef HCAL_MAHI_GPUDEBUG +#ifdef HCAL_MAHI_GPUDEBUG_FILTERDETID + if (id != DETID_TO_DEBUG) + return; +#endif +#endif + + // compute hash for this did + auto const hashedId = + did.subdetId() == HcalBarrel + ? hcal::reconstruction::did2linearIndexHB(id, maxDepthHB, firstHBRing, lastHBRing, nEtaHB) + : hcal::reconstruction::did2linearIndexHE(id, maxDepthHE, maxPhiHE, firstHERing, lastHERing, nEtaHE) + + offsetForHashes; + + // conditions based on the hash + // FIXME: remove hardcoded values + auto const qieType = qieTypes[hashedId] > 0 ? 1 : 0; // 2 types at this point + auto const* qieOffsets = qieCoderOffsets + hashedId * HcalQIECodersGPU::numValuesPerChannel; + auto const* qieSlopes = qieCoderSlopes + hashedId * HcalQIECodersGPU::numValuesPerChannel; + auto const* pedestalsForChannel = pedestals + hashedId * 4; + auto const* pedestalWidthsForChannel = useEffectivePedestals && (gch < nchannelsf01HE || gch >= nchannelsf015) + ? effectivePedestalWidths + hashedId * 4 + : pedestalWidths + hashedId * 4; + auto const* gains = gainValues + hashedId * 4; + auto const gain = gains[capid]; + auto const gain0 = gains[0]; + auto const respCorrection = respCorrectionValues[hashedId]; + auto const pedestal = pedestalsForChannel[capid]; + auto const pedestalWidth = pedestalWidthsForChannel[capid]; + // if needed, only use effective pedestals for f01 + auto const pedestalToUseForMethod0 = useEffectivePedestals && (gch < nchannelsf01HE || gch >= nchannelsf015) + ? effectivePedestals[hashedId * 4 + capid] + : pedestal; + auto const sipmType = sipmTypeValues[hashedId]; + auto const fcByPE = fcByPEValues[hashedId]; + auto const recoParam1 = recoParam1Values[hashedId]; + auto const recoParam2 = recoParam2Values[hashedId]; + +#ifdef HCAL_MAHI_GPUDEBUG + printf("qieType = %d qieOffset0 = %f qieOffset1 = %f qieSlope0 = %f qieSlope1 = %f\n", + qieType, + qieOffsets[0], + qieOffsets[1], + qieSlopes[0], + qieSlopes[1]); +#endif + + // compute charge + auto const charge = hcal::reconstruction::compute_coder_charge(qieType, adc, capid, qieOffsets, qieSlopes); + + shrChargeMinusPedestal[linearThPerBlock] = charge - pedestal; + if (gch < nchannelsf01HE) { + // NOTE: assume that soi is high only for a single guy! + // which must be the case. cpu version does not check for that + // if that is not the case, we will see that with cuda mmecheck + auto const soibit = soibit_for_sample(dataf01HE + stride * gch, sample); + if (soibit == 1) + soiSamples[gch] = sampleWithinWindow; + } else if (gch >= nchannelsf015) { + auto const soibit = soibit_for_sample(dataf3HB + stride * (gch - nchannelsf015), sample); + if (soibit == 1) + soiSamples[gch] = sampleWithinWindow; + } + __syncthreads(); + int32_t const soi = gch < nchannelsf01HE + ? soiSamples[gch] + : (gch < nchannelsf015 ? npresamplesf5HB[gch - nchannelsf01HE] : soiSamples[gch]); + //int32_t const soi = gch >= nchannelsf01HE + // ? npresamplesf5HB[gch - nchannelsf01HE] + // : soiSamples[gch]; + // this is here just to make things uniform... + if (gch >= nchannelsf01HE && gch < nchannelsf015 && sampleWithinWindow == 0) + soiSamples[gch] = npresamplesf5HB[gch - nchannelsf01HE]; + + // + // compute various quantities (raw charge and tdc stuff) + // NOTE: this branch will be divergent only for a single warp that + // sits on the boundary when flavor 01 channels end and flavor 5 start + // + float const rawCharge = get_raw_charge(charge, + pedestal, + shrChargeMinusPedestal, + parLin1Values, + parLin2Values, + parLin3Values, + nsamplesForCompute, + soi, + sipmQTSShift, + sipmQNTStoSum, + sipmType, + fcByPE, + gch < nchannelsf01HE || gch >= nchannelsf015); + + auto const dfc = hcal::reconstruction::compute_diff_charge_gain( + qieType, adc, capid, qieOffsets, qieSlopes, gch < nchannelsf01HE || gch >= nchannelsf015); + +#ifdef COMPUTE_TDC_TIME + float tdcTime; + if (gch >= nchannelsf01HE && gch < nchannelsf015) { + tdcTime = HcalSpecialTimes::UNKNOWN_T_NOTDC; + } else { + if (gch < nchannelsf01HE) + tdcTime = HcalSpecialTimes::getTDCTime(tdc_for_sample(dataf01HE + stride * gch, sample)); + else if (gch >= nchannelsf015) + tdcTime = + HcalSpecialTimes::getTDCTime(tdc_for_sample(dataf3HB + stride * (gch - nchannelsf015), sample)); + } +#endif // COMPUTE_TDC_TIME + + // compute method 0 quantities + // TODO: need to apply containment + // TODO: need to apply time slew + // TODO: for < run 3, apply HBM legacy energy correction + auto const nsamplesToAdd = recoParam1 < 10 ? recoParam2 : (recoParam1 >> 14) & 0xF; + auto const startSampleTmp = soi + firstSampleShift; + auto const startSample = startSampleTmp < 0 ? 0 : startSampleTmp; + auto const endSample = + startSample + nsamplesToAdd < nsamplesForCompute ? startSample + nsamplesToAdd : nsamplesForCompute; + // NOTE: gain is a small number < 10^-3, multiply it last + auto const energym0_per_ts = gain * ((rawCharge - pedestalToUseForMethod0) * respCorrection); + auto const energym0_per_ts_gain0 = gain0 * ((rawCharge - pedestalToUseForMethod0) * respCorrection); + // store to shared mem + shrEnergyM0PerTS[lch * nsamplesForCompute + sampleWithinWindow] = energym0_per_ts; + atomicAdd(&shrEnergyM0TotalAccum[lch], energym0_per_ts_gain0); + +#ifdef HCAL_MAHI_GPUDEBUG + printf( + "id = %u sample = %d gch = %d hashedId = %u adc = %u capid = %u\n" + " charge = %f rawCharge = %f dfc = %f pedestal = %f\n" + " gain = %f respCorrection = %f energym0_per_ts = %f\n", + id, + sample, + gch, + hashedId, + adc, + capid, + charge, + rawCharge, + dfc, + pedestalToUseForMethod0, + gain, + respCorrection, + energym0_per_ts); + printf( + "startSample = %d endSample = %d param1 = %u param2 = %u\n", startSample, endSample, recoParam1, recoParam2); +#endif + + if (sampleWithinWindow >= startSample && sampleWithinWindow < endSample) { + atomicAdd(&shrMethod0EnergyAccum[lch], energym0_per_ts); + // pack sample, energy as 64 bit value + unsigned long long int old = shrMethod0EnergySamplePair[lch], assumed; + unsigned long long int val = + (static_cast(sampleWithinWindow) << 32) + __float_as_uint(energym0_per_ts); + do { + assumed = old; + // decode energy, sample values + //int const current_sample = (assumed >> 32) & 0xffffffff; + float const current_energy = __uint_as_float(assumed & 0xffffffff); + if (energym0_per_ts > current_energy) + old = atomicCAS(&shrMethod0EnergySamplePair[lch], assumed, val); + else + break; + } while (assumed != old); + } + __syncthreads(); + + // NOTE: must take soi, as values for that thread are used... + if (sampleWithinWindow == soi) { + auto const method0_energy = shrMethod0EnergyAccum[lch]; + auto const val = shrMethod0EnergySamplePair[lch]; + int const max_sample = (val >> 32) & 0xffffffff; + float const max_energy = __uint_as_float(val & 0xffffffff); + float const max_energy_1 = + max_sample < nsamplesForCompute - 1 ? shrEnergyM0PerTS[lch * nsamplesForCompute + max_sample + 1] : 0.f; + float const position = nsamplesToAdd < nsamplesForCompute ? max_sample - soi : max_sample; + auto const sum = max_energy + max_energy_1; + // FIXME: for full comparison with cpu method 0 timing, + // need to correct by slew + // requires an accumulator -> more shared mem -> omit here unless + // really needed + float const time = + max_energy > 0.f && max_energy_1 > 0.f ? 25.f * (position + max_energy_1 / sum) : 25.f * position; + + // store method0 quantities to global mem + outputdid[gch] = id; + method0Energy[gch] = method0_energy; + method0Time[gch] = time; + +#ifdef HCAL_MAHI_GPUDEBUG + printf("tsTOT = %f tstrig = %f ts4Thresh = %f\n", shrEnergyM0TotalAccum[lch], energym0_per_ts_gain0, ts4Thresh); +#endif + + // check as in cpu version if mahi is not needed + // FIXME: KNOWN ISSUE: observed a problem when rawCharge and pedestal + // are basically equal and generate -0.00000... + // needs to be treated properly + if (!(shrEnergyM0TotalAccum[lch] > 0 && energym0_per_ts_gain0 > ts4Thresh)) { + // do not need to run mahi minimization + //outputEnergy[gch] = 0; energy already inited to 0 + outputChi2[gch] = -9999.f; + } + +#ifdef HCAL_MAHI_GPUDEBUG + printf("method0_energy = %f max_sample = %d max_energy = %f time = %f\n", + method0_energy, + max_sample, + max_energy, + time); +#endif + } + + // + // preparations for mahi fit + // + auto const amplitude = rawCharge - pedestalToUseForMethod0; + auto const noiseADC = (1. / std::sqrt(12)) * dfc; + auto const noisePhotoSq = amplitude > pedestalWidth ? (amplitude * fcByPE) : 0.f; + auto const noiseTerm = noiseADC * noiseADC + noisePhotoSq + pedestalWidth * pedestalWidth; + +#ifdef HCAL_MAHI_GPUDEBUG + printf( + "charrge(%d) = %f pedestal(%d) = %f dfc(%d) = %f pedestalWidth(%d) = %f noiseADC(%d) = %f noisPhoto(%d) = " + "%f\n", + sample, + rawCharge, + sample, + pedestalToUseForMethod0, + sample, + dfc, + sample, + pedestalWidth, + sample, + noiseADC, + sample, + noisePhotoSq); +#endif + + // store to global memory + amplitudesForChannel[sampleWithinWindow] = amplitude; + noiseTermsForChannel[sampleWithinWindow] = noiseTerm; + } + + // TODO: need to add an array of offsets for pulses (a la activeBXs...) + // Assume for now 8 pulses + __global__ void kernel_prep_pulseMatrices_sameNumberOfSamples(float* pulseMatrices, + float* pulseMatricesM, + float* pulseMatricesP, + int const* pulseOffsets, + float const* amplitudes, + uint32_t const* idsf01HE, + uint32_t const* idsf5HB, + uint32_t const* idsf3HB, + uint32_t const nchannelsf01HE, + uint32_t const nchannelsf5HB, + uint32_t const nchannelsTotal, + int8_t const* soiSamples, + uint32_t const* recoPulseShapeIds, + float const* acc25nsVecValues, + float const* diff25nsItvlVecValues, + float const* accVarLenIdxMinusOneVecValues, + float const* diffVarItvlIdxMinusOneVecValues, + float const* accVarLenIdxZeroVecValues, + float const* diffVarItvlIdxZeroVecValues, + float const meanTime, + float const timeSigmaSiPM, + float const timeSigmaHPD, + int const maxDepthHB, + int const maxDepthHE, + int const maxPhiHE, + int const firstHBRing, + int const lastHBRing, + int const firstHERing, + int const lastHERing, + int const nEtaHB, + int const nEtaHE, + uint32_t const offsetForHashes, + bool const applyTimeSlew, + float const tzeroTimeSlew, + float const slopeTimeSlew, + float const tmaxTimeSlew) { + // indices + auto const ipulse = threadIdx.y; + auto const npulses = blockDim.y; + auto const sample = threadIdx.x; + auto const nsamples = blockDim.x; + auto const lch = threadIdx.z; + auto const gch = lch + blockIdx.x * blockDim.z; + auto const nchannelsf015 = nchannelsf01HE + nchannelsf5HB; + + if (gch >= nchannelsTotal) + return; + + // conditions + auto const id = gch < nchannelsf01HE + ? idsf01HE[gch] + : (gch < nchannelsf015 ? idsf5HB[gch - nchannelsf01HE] : idsf3HB[gch - nchannelsf015]); + //auto const id = gch >= nchannelsf01HE + // ? idsf5HB[gch - nchannelsf01HE] + // : idsf01HE[gch]; + auto const deltaT = gch >= nchannelsf01HE && gch < nchannelsf015 ? timeSigmaHPD : timeSigmaSiPM; + auto const did = DetId{id}; + auto const hashedId = + did.subdetId() == HcalBarrel + ? hcal::reconstruction::did2linearIndexHB(id, maxDepthHB, firstHBRing, lastHBRing, nEtaHB) + : hcal::reconstruction::did2linearIndexHE(id, maxDepthHE, maxPhiHE, firstHERing, lastHERing, nEtaHE) + + offsetForHashes; + auto const recoPulseShapeId = recoPulseShapeIds[hashedId]; + auto const* acc25nsVec = acc25nsVecValues + recoPulseShapeId * hcal::constants::maxPSshapeBin; + auto const* diff25nsItvlVec = diff25nsItvlVecValues + recoPulseShapeId * hcal::constants::maxPSshapeBin; + auto const* accVarLenIdxMinusOneVec = accVarLenIdxMinusOneVecValues + recoPulseShapeId * hcal::constants::nsPerBX; + auto const* diffVarItvlIdxMinusOneVec = + diffVarItvlIdxMinusOneVecValues + recoPulseShapeId * hcal::constants::nsPerBX; + auto const* accVarLenIdxZeroVec = accVarLenIdxZeroVecValues + recoPulseShapeId * hcal::constants::nsPerBX; + auto const* diffVarItvlIdxZeroVec = diffVarItvlIdxZeroVecValues + recoPulseShapeId * hcal::constants::nsPerBX; + + // offset output arrays + auto* pulseMatrix = pulseMatrices + nsamples * npulses * gch; + auto* pulseMatrixM = pulseMatricesM + nsamples * npulses * gch; + auto* pulseMatrixP = pulseMatricesP + nsamples * npulses * gch; + + // amplitude per ipulse + int const soi = soiSamples[gch]; + int const pulseOffset = pulseOffsets[ipulse]; + auto const amplitude = amplitudes[gch * nsamples + pulseOffset + soi]; + +#ifdef HCAL_MAHI_GPUDEBUG +#ifdef HCAL_MAHI_GPUDEBUG_FILTERDETID + if (id != DETID_TO_DEBUG) + return; +#endif +#endif + +#ifdef HCAL_MAHI_GPUDEBUG + if (sample == 0 && ipulse == 0) { + for (int i = 0; i < 8; i++) + printf("amplitude(%d) = %f\n", i, amplitudes[gch * nsamples + i]); + printf("acc25nsVec and diff25nsItvlVec for recoPulseShapeId = %u\n", recoPulseShapeId); + for (int i = 0; i < 256; i++) { + printf("acc25nsVec(%d) = %f diff25nsItvlVec(%d) = %f\n", i, acc25nsVec[i], i, diff25nsItvlVec[i]); + } + printf("accVarLenIdxZEROVec and accVarLenIdxMinusOneVec\n"); + for (int i = 0; i < 25; i++) { + printf("accVarLenIdxZEROVec(%d) = %f accVarLenIdxMinusOneVec(%d) = %f\n", + i, + accVarLenIdxZeroVec[i], + i, + accVarLenIdxMinusOneVec[i]); + } + printf("diffVarItvlIdxZEROVec and diffVarItvlIdxMinusOneVec\n"); + for (int i = 0; i < 25; i++) { + printf("diffVarItvlIdxZEROVec(%d) = %f diffVarItvlIdxMinusOneVec(%d) = %f\n", + i, + diffVarItvlIdxZeroVec[i], + i, + diffVarItvlIdxMinusOneVec[i]); + } + } +#endif + + auto t0 = meanTime; + if (applyTimeSlew) { + if (amplitude <= 1.0f) + t0 += hcal::reconstruction::compute_time_slew_delay(1.0, tzeroTimeSlew, slopeTimeSlew, tmaxTimeSlew); + else + t0 += hcal::reconstruction::compute_time_slew_delay(amplitude, tzeroTimeSlew, slopeTimeSlew, tmaxTimeSlew); + } + auto const t0m = -deltaT + t0; + auto const t0p = deltaT + t0; + +#ifdef HCAL_MAHI_GPUDEBUG + if (sample == 0 && ipulse == 0) { + printf("time values: %f %f %f\n", t0, t0m, t0p); + } + + if (sample == 0 && ipulse == 0) { + for (int i = 0; i < hcal::constants::maxSamples; i++) { + auto const value = hcal::reconstruction::compute_pulse_shape_value(t0, + i, + 0, + acc25nsVec, + diff25nsItvlVec, + accVarLenIdxMinusOneVec, + diffVarItvlIdxMinusOneVec, + accVarLenIdxZeroVec, + diffVarItvlIdxZeroVec); + printf("pulse(%d) = %f\n", i, value); + } + printf("\n"); + for (int i = 0; i < hcal::constants::maxSamples; i++) { + auto const value = hcal::reconstruction::compute_pulse_shape_value(t0p, + i, + 0, + acc25nsVec, + diff25nsItvlVec, + accVarLenIdxMinusOneVec, + diffVarItvlIdxMinusOneVec, + accVarLenIdxZeroVec, + diffVarItvlIdxZeroVec); + printf("pulseP(%d) = %f\n", i, value); + } + printf("\n"); + for (int i = 0; i < hcal::constants::maxSamples; i++) { + auto const value = hcal::reconstruction::compute_pulse_shape_value(t0m, + i, + 0, + acc25nsVec, + diff25nsItvlVec, + accVarLenIdxMinusOneVec, + diffVarItvlIdxMinusOneVec, + accVarLenIdxZeroVec, + diffVarItvlIdxZeroVec); + printf("pulseM(%d) = %f\n", i, value); + } + } +#endif + + // FIXME: shift should be treated properly, + // here assume 8 time slices and 8 samples + auto const shift = 4 - soi; // as in cpu version! + + // auto const offset = ipulse - soi; + // auto const idx = sample - offset; + int32_t const idx = sample - pulseOffset; + auto const value = idx >= 0 && idx < nsamples + ? hcal::reconstruction::compute_pulse_shape_value(t0, + idx, + shift, + acc25nsVec, + diff25nsItvlVec, + accVarLenIdxMinusOneVec, + diffVarItvlIdxMinusOneVec, + accVarLenIdxZeroVec, + diffVarItvlIdxZeroVec) + : 0; + auto const value_t0m = idx >= 0 && idx < nsamples + ? hcal::reconstruction::compute_pulse_shape_value(t0m, + idx, + shift, + acc25nsVec, + diff25nsItvlVec, + accVarLenIdxMinusOneVec, + diffVarItvlIdxMinusOneVec, + accVarLenIdxZeroVec, + diffVarItvlIdxZeroVec) + : 0; + auto const value_t0p = idx >= 0 && idx < nsamples + ? hcal::reconstruction::compute_pulse_shape_value(t0p, + idx, + shift, + acc25nsVec, + diff25nsItvlVec, + accVarLenIdxMinusOneVec, + diffVarItvlIdxMinusOneVec, + accVarLenIdxZeroVec, + diffVarItvlIdxZeroVec) + : 0; + + // store to global + if (amplitude > 0.f) { + pulseMatrix[ipulse * nsamples + sample] = value; + pulseMatrixM[ipulse * nsamples + sample] = value_t0m; + pulseMatrixP[ipulse * nsamples + sample] = value_t0p; + } else { + pulseMatrix[ipulse * nsamples + sample] = 0.f; + pulseMatrixM[ipulse * nsamples + sample] = 0.f; + pulseMatrixP[ipulse * nsamples + sample] = 0.f; + } + } + + template + __forceinline__ __device__ void update_covariance( + calo::multifit::ColumnVector const& resultAmplitudesVector, + calo::multifit::MapSymM& covarianceMatrix, + Eigen::Map> const& pulseMatrix, + Eigen::Map> const& pulseMatrixM, + Eigen::Map> const& pulseMatrixP) { + CMS_UNROLL_LOOP + for (int ipulse = 0; ipulse < NPULSES; ipulse++) { + auto const resultAmplitude = resultAmplitudesVector(ipulse); + if (resultAmplitude == 0) + continue; + +#ifdef HCAL_MAHI_GPUDEBUG + printf("pulse cov array for ibx = %d\n", ipulse); +#endif + + // preload a column + float pmcol[NSAMPLES], pmpcol[NSAMPLES], pmmcol[NSAMPLES]; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) { + pmcol[counter] = __ldg(&pulseMatrix.coeffRef(counter, ipulse)); + pmpcol[counter] = __ldg(&pulseMatrixP.coeffRef(counter, ipulse)); + pmmcol[counter] = __ldg(&pulseMatrixM.coeffRef(counter, ipulse)); + } + + auto const ampl2 = resultAmplitude * resultAmplitude; + CMS_UNROLL_LOOP + for (int col = 0; col < NSAMPLES; col++) { + auto const valueP_col = pmpcol[col]; + auto const valueM_col = pmmcol[col]; + auto const value_col = pmcol[col]; + auto const tmppcol = valueP_col - value_col; + auto const tmpmcol = valueM_col - value_col; + + // diagonal + auto tmp_value = 0.5 * (tmppcol * tmppcol + tmpmcol * tmpmcol); + covarianceMatrix(col, col) += ampl2 * tmp_value; + + // FIXME: understand if this actually gets unrolled + CMS_UNROLL_LOOP + for (int row = col + 1; row < NSAMPLES; row++) { + float const valueP_row = pmpcol[row]; //pulseMatrixP(j, ipulseReal); + float const value_row = pmcol[row]; //pulseMatrix(j, ipulseReal); + float const valueM_row = pmmcol[row]; //pulseMatrixM(j, ipulseReal); + + float tmpprow = valueP_row - value_row; + float tmpmrow = valueM_row - value_row; + + auto const covValue = 0.5 * (tmppcol * tmpprow + tmpmcol * tmpmrow); + + covarianceMatrix(row, col) += ampl2 * covValue; + } + } + } + } + + template + __global__ void kernel_minimize(float* outputEnergy, + float* outputChi2, + float const* __restrict__ inputAmplitudes, + float const* __restrict__ pulseMatrices, + float const* __restrict__ pulseMatricesM, + float const* __restrict__ pulseMatricesP, + int const* __restrict__ pulseOffsetValues, + float const* __restrict__ noiseTerms, + int8_t const* __restrict__ soiSamples, + float const* __restrict__ pedestalWidths, + float const* __restrict__ effectivePedestalWidths, + bool const useEffectivePedestals, + uint32_t const* __restrict__ idsf01HE, + uint32_t const* __restrict__ idsf5HB, + uint32_t const* __restrict__ idsf3HB, + float const* __restrict__ gainValues, + float const* __restrict__ respCorrectionValues, + uint32_t const nchannelsf01HE, + uint32_t const nchannelsf5HB, + uint32_t const nchannelsTotal, + uint32_t const offsetForHashes, + int const maxDepthHB, + int const maxDepthHE, + int const maxPhiHE, + int const firstHBRing, + int const lastHBRing, + int const firstHERing, + int const lastHERing, + int const nEtaHB, + int const nEtaHE) { + // can be relaxed if needed - minor updates are needed in that case! + static_assert(NPULSES == NSAMPLES); + + // indices + auto const gch = threadIdx.x + blockIdx.x * blockDim.x; + auto const nchannelsf015 = nchannelsf01HE + nchannelsf5HB; + if (gch >= nchannelsTotal) + return; + + // if chi2 is set to -9999 do not run minimization + if (outputChi2[gch] == -9999.f) + return; + + // configure shared mem + extern __shared__ char shrmem[]; + float* shrMatrixLFnnlsStorage = + reinterpret_cast(shrmem) + calo::multifit::MapSymM::total * threadIdx.x; + float* shrAtAStorage = reinterpret_cast(shrmem) + + calo::multifit::MapSymM::total * (threadIdx.x + blockDim.x); + + // conditions for pedestal widths + auto const id = gch < nchannelsf01HE + ? idsf01HE[gch] + : (gch < nchannelsf015 ? idsf5HB[gch - nchannelsf01HE] : idsf3HB[gch - nchannelsf015]); + auto const did = DetId{id}; + auto const hashedId = + did.subdetId() == HcalBarrel + ? hcal::reconstruction::did2linearIndexHB(id, maxDepthHB, firstHBRing, lastHBRing, nEtaHB) + : hcal::reconstruction::did2linearIndexHE(id, maxDepthHE, maxPhiHE, firstHERing, lastHERing, nEtaHE) + + offsetForHashes; + + auto const* pedestalWidthsForChannel = useEffectivePedestals && (gch < nchannelsf01HE || gch >= nchannelsf015) + ? effectivePedestalWidths + hashedId * 4 + : pedestalWidths + hashedId * 4; + auto const averagePedestalWidth2 = 0.25 * (pedestalWidthsForChannel[0] * pedestalWidthsForChannel[0] + + pedestalWidthsForChannel[1] * pedestalWidthsForChannel[1] + + pedestalWidthsForChannel[2] * pedestalWidthsForChannel[2] + + pedestalWidthsForChannel[3] * pedestalWidthsForChannel[3]); + auto const* gains = gainValues + hashedId * 4; + // FIXME on cpu ts 0 capid was used - does it make any difference + auto const gain = gains[0]; + auto const respCorrection = respCorrectionValues[hashedId]; + +#ifdef HCAL_MAHI_GPUDEBUG +#ifdef HCAL_MAHI_GPUDEBUG_FILTERDETID + if (id != DETID_TO_DEBUG) + return; +#endif +#endif + + /* + // TODO: provide this properly + int const soi = soiSamples[gch]; + */ + calo::multifit::ColumnVector pulseOffsets; + CMS_UNROLL_LOOP + for (int i = 0; i < NPULSES; ++i) + pulseOffsets(i) = i; + // pulseOffsets(i) = pulseOffsetValues[i] - pulseOffsetValues[0]; + + // output amplitudes/weights + calo::multifit::ColumnVector resultAmplitudesVector = calo::multifit::ColumnVector::Zero(); + + // map views + Eigen::Map> inputAmplitudesView{inputAmplitudes + gch * NSAMPLES}; + Eigen::Map> noiseTermsView{noiseTerms + gch * NSAMPLES}; + Eigen::Map> glbPulseMatrixMView{pulseMatricesM + + gch * NSAMPLES * NPULSES}; + Eigen::Map> glbPulseMatrixPView{pulseMatricesP + + gch * NSAMPLES * NPULSES}; + Eigen::Map> glbPulseMatrixView{pulseMatrices + + gch * NSAMPLES * NPULSES}; + +#ifdef HCAL_MAHI_GPUDEBUG + for (int i = 0; i < NSAMPLES; i++) + printf("inputValues(%d) = %f noiseTerms(%d) = %f\n", i, inputAmplitudesView(i), i, noiseTermsView(i)); + for (int i = 0; i < NSAMPLES; i++) { + for (int j = 0; j < NPULSES; j++) + printf("%f ", glbPulseMatrixView(i, j)); + printf("\n"); + } + printf("\n"); + for (int i = 0; i < NSAMPLES; i++) { + for (int j = 0; j < NPULSES; j++) + printf("%f ", glbPulseMatrixMView(i, j)); + printf("\n"); + } + printf("\n"); + for (int i = 0; i < NSAMPLES; i++) { + for (int j = 0; j < NPULSES; j++) + printf("%f ", glbPulseMatrixPView(i, j)); + printf("\n"); + } +#endif + + int npassive = 0; + float chi2 = 0, previous_chi2 = 0.f, chi2_2itersback = 0.f; + for (int iter = 1; iter < nMaxItersMin; iter++) { + //float covarianceMatrixStorage[MapSymM::total]; + // NOTE: only works when NSAMPLES == NPULSES + // if does not hold -> slightly rearrange shared mem to still reuse + // shared memory + float* covarianceMatrixStorage = shrMatrixLFnnlsStorage; + calo::multifit::MapSymM covarianceMatrix{covarianceMatrixStorage}; + CMS_UNROLL_LOOP + for (int counter = 0; counter < calo::multifit::MapSymM::total; counter++) + covarianceMatrixStorage[counter] = averagePedestalWidth2; + CMS_UNROLL_LOOP + for (int counter = 0; counter < calo::multifit::MapSymM::stride; counter++) + covarianceMatrix(counter, counter) += __ldg(&noiseTermsView.coeffRef(counter)); + + // update covariance matrix + update_covariance( + resultAmplitudesVector, covarianceMatrix, glbPulseMatrixView, glbPulseMatrixMView, glbPulseMatrixPView); + +#ifdef HCAL_MAHI_GPUDEBUG + printf("covariance matrix\n"); + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) + printf("%f ", covarianceMatrix(i, j)); + printf("\n"); + } +#endif + + // compute Cholesky Decomposition L matrix + //matrixDecomposition.compute(covarianceMatrix); + //auto const& matrixL = matrixDecomposition.matrixL(); + float matrixLStorage[calo::multifit::MapSymM::total]; + calo::multifit::MapSymM matrixL{matrixLStorage}; + calo::multifit::compute_decomposition_unrolled(matrixL, covarianceMatrix); + + // + // replace eigen + // + //auto const& A = matrixDecomposition + // .matrixL() + // .solve(pulseMatrixView); + calo::multifit::ColMajorMatrix A; + calo::multifit::solve_forward_subst_matrix(A, glbPulseMatrixView, matrixL); + + // + // remove eigen + // + //auto const& b = matrixL + // .solve(inputAmplitudesView); + // + float reg_b[NSAMPLES]; + calo::multifit::solve_forward_subst_vector(reg_b, inputAmplitudesView, matrixL); + + // TODO: we do not really need to change these matrcies + // will be fixed in the optimized version + //ColMajorMatrix AtA = A.transpose() * A; + //ColumnVector Atb = A.transpose() * b; + //ColMajorMatrix AtA; + //float AtAStorage[MapSymM::total]; + calo::multifit::MapSymM AtA{shrAtAStorage}; + calo::multifit::ColumnVector Atb; + CMS_UNROLL_LOOP + for (int icol = 0; icol < NPULSES; icol++) { + float reg_ai[NSAMPLES]; + + // load column icol + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + reg_ai[counter] = A(counter, icol); + + // compute diagonal + float sum = 0.f; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + sum += reg_ai[counter] * reg_ai[counter]; + + // store + AtA(icol, icol) = sum; + + // go thru the other columns + CMS_UNROLL_LOOP + for (int j = icol + 1; j < NPULSES; j++) { + // load column j + float reg_aj[NSAMPLES]; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + reg_aj[counter] = A(counter, j); + + // accum + float sum = 0.f; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + sum += reg_aj[counter] * reg_ai[counter]; + + // store + //AtA(icol, j) = sum; + AtA(j, icol) = sum; + } + + // Atb accum + float sum_atb = 0; + CMS_UNROLL_LOOP + for (int counter = 0; counter < NSAMPLES; counter++) + sum_atb += reg_ai[counter] * reg_b[counter]; + + // store atb + Atb(icol) = sum_atb; + } + +#ifdef HCAL_MAHI_GPUDEBUG + printf("AtA\n"); + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) + printf("%f ", AtA(i, j)); + printf("\n"); + } + printf("Atb\n"); + for (int i = 0; i < 8; i++) + printf("%f ", Atb(i)); + printf("\n"); + printf("result Amplitudes before nnls\n"); + for (int i = 0; i < 8; i++) + printf("%f ", resultAmplitudesVector(i)); + printf("\n"); +#endif + + // for fnnls + calo::multifit::MapSymM matrixLForFnnls{shrMatrixLFnnlsStorage}; + + // run fast nnls + calo::multifit::fnnls( + AtA, Atb, resultAmplitudesVector, npassive, pulseOffsets, matrixLForFnnls, nnlsThresh, nMaxItersNNLS, 10, 10); + +#ifdef HCAL_MAHI_GPUDEBUG + printf("result Amplitudes\n"); + for (int i = 0; i < 8; i++) + printf("resultAmplitudes(%d) = %f\n", i, resultAmplitudesVector(i)); +#endif + + calo::multifit::calculateChiSq(matrixL, glbPulseMatrixView, resultAmplitudesVector, inputAmplitudesView, chi2); + + auto const deltaChi2 = std::abs(chi2 - previous_chi2); + if (chi2 == chi2_2itersback && chi2 < previous_chi2) + break; + + // update + chi2_2itersback = previous_chi2; + previous_chi2 = chi2; + + // exit condition + if (deltaChi2 < deltaChi2Threashold) + break; + } + +#ifdef HCAL_MAHI_GPUDEBUG + for (int i = 0; i < NPULSES; i++) + printf("pulseOffsets(%d) = %d outputAmplitudes(%d) = %f\n", i, pulseOffsets(i), i, resultAmplitudesVector(i)); + printf("chi2 = %f\n", chi2); +#endif + + outputChi2[gch] = chi2; + auto const idx_for_energy = std::abs(pulseOffsetValues[0]); + outputEnergy[gch] = (gain * resultAmplitudesVector(idx_for_energy)) * respCorrection; + /* + CMS_UNROLL_LOOP + for (int i=0; i(inputGPU.f01HEDigis.stride); + auto const f5nsamples = compute_nsamples(inputGPU.f5HBDigis.stride); + auto const f3nsamples = compute_nsamples(inputGPU.f3HBDigis.stride); + int constexpr windowSize = 8; + int const startingSample = f01nsamples - windowSize; + assert(startingSample == 0 || startingSample == 2); + if (inputGPU.f01HEDigis.stride > 0 && inputGPU.f5HBDigis.stride > 0) + assert(f01nsamples == f5nsamples); + if (inputGPU.f01HEDigis.stride > 0 && inputGPU.f3HBDigis.stride > 0) + assert(f01nsamples == f3nsamples); + + dim3 threadsPerBlock{windowSize, configParameters.kprep1dChannelsPerBlock}; + int blocks = static_cast(threadsPerBlock.y) > totalChannels + ? 1 + : (totalChannels + threadsPerBlock.y - 1) / threadsPerBlock.y; + int nbytesShared = + ((2 * windowSize + 2) * sizeof(float) + sizeof(uint64_t)) * configParameters.kprep1dChannelsPerBlock; + hcal::mahi::kernel_prep1d_sameNumberOfSamples<<>>( + scratch.amplitudes.get(), + scratch.noiseTerms.get(), + outputGPU.recHits.energy.get(), + outputGPU.recHits.chi2.get(), + inputGPU.f01HEDigis.data.get(), + inputGPU.f5HBDigis.data.get(), + inputGPU.f3HBDigis.data.get(), + inputGPU.f01HEDigis.ids.get(), + inputGPU.f5HBDigis.ids.get(), + inputGPU.f3HBDigis.ids.get(), + inputGPU.f01HEDigis.stride, + inputGPU.f5HBDigis.stride, + inputGPU.f3HBDigis.stride, + inputGPU.f01HEDigis.size, + inputGPU.f5HBDigis.size, + inputGPU.f5HBDigis.npresamples.get(), + scratch.soiSamples.get(), + outputGPU.recHits.energyM0.get(), + outputGPU.recHits.timeM0.get(), + outputGPU.recHits.did.get(), + totalChannels, + conditions.recoParams.param1, + conditions.recoParams.param2, + conditions.qieCoders.offsets, + conditions.qieCoders.slopes, + conditions.qieTypes.values, + conditions.pedestalWidths.values, + conditions.effectivePedestalWidths.values, + conditions.pedestals.values, + conditions.convertedEffectivePedestals ? conditions.convertedEffectivePedestals->values + : conditions.pedestals.values, + configParameters.useEffectivePedestals, + conditions.sipmParameters.type, + conditions.sipmParameters.fcByPE, + conditions.sipmCharacteristics.parLin1, + conditions.sipmCharacteristics.parLin2, + conditions.sipmCharacteristics.parLin3, + conditions.gains.values, + conditions.respCorrs.values, + conditions.topology->maxDepthHB(), + conditions.topology->maxDepthHE(), + conditions.recConstants->getNPhi(1) > hcal::reconstruction::IPHI_MAX ? conditions.recConstants->getNPhi(1) + : hcal::reconstruction::IPHI_MAX, + conditions.topology->firstHBRing(), + conditions.topology->lastHBRing(), + conditions.topology->firstHERing(), + conditions.topology->lastHERing(), + conditions.recConstants->getEtaRange(0).second - conditions.recConstants->getEtaRange(0).first + 1, + conditions.topology->firstHERing() > conditions.topology->lastHERing() + ? 0 + : (conditions.topology->lastHERing() - conditions.topology->firstHERing() + 1), + configParameters.sipmQTSShift, + configParameters.sipmQNTStoSum, + configParameters.firstSampleShift, + conditions.offsetForHashes, + configParameters.ts4Thresh, + startingSample); + cudaCheck(cudaGetLastError()); + + // 1024 is the max threads per block for gtx1080 + // FIXME: take this from cuda service or something like that + uint32_t const channelsPerBlock = 1024 / (windowSize * conditions.pulseOffsetsHost.size()); + dim3 threadsPerBlock2{windowSize, static_cast(conditions.pulseOffsetsHost.size()), channelsPerBlock}; + int blocks2 = + threadsPerBlock2.z > totalChannels ? 1 : (totalChannels + threadsPerBlock2.z - 1) / threadsPerBlock2.z; + +#ifdef HCAL_MAHI_CPUDEBUG + std::cout << "threads: " << threadsPerBlock2.x << " " << threadsPerBlock2.y << " " << threadsPerBlock2.z + << std::endl; + std::cout << "blocks: " << blocks2 << std::endl; +#endif + + hcal::mahi::kernel_prep_pulseMatrices_sameNumberOfSamples<<>>( + scratch.pulseMatrices.get(), + scratch.pulseMatricesM.get(), + scratch.pulseMatricesP.get(), + conditions.pulseOffsets.values, + scratch.amplitudes.get(), + inputGPU.f01HEDigis.ids.get(), + inputGPU.f5HBDigis.ids.get(), + inputGPU.f3HBDigis.ids.get(), + inputGPU.f01HEDigis.size, + inputGPU.f5HBDigis.size, + totalChannels, + scratch.soiSamples.get(), + conditions.recoParams.ids, + conditions.recoParams.acc25nsVec, + conditions.recoParams.diff25nsItvlVec, + conditions.recoParams.accVarLenIdxMinusOneVec, + conditions.recoParams.diffVarItvlIdxMinusOneVec, + conditions.recoParams.accVarLenIdxZEROVec, + conditions.recoParams.diffVarItvlIdxZEROVec, + configParameters.meanTime, + configParameters.timeSigmaSiPM, + configParameters.timeSigmaHPD, + conditions.topology->maxDepthHB(), + conditions.topology->maxDepthHE(), + conditions.recConstants->getNPhi(1) > hcal::reconstruction::IPHI_MAX ? conditions.recConstants->getNPhi(1) + : hcal::reconstruction::IPHI_MAX, + conditions.topology->firstHBRing(), + conditions.topology->lastHBRing(), + conditions.topology->firstHERing(), + conditions.topology->lastHERing(), + conditions.recConstants->getEtaRange(0).second - conditions.recConstants->getEtaRange(0).first + 1, + conditions.topology->firstHERing() > conditions.topology->lastHERing() + ? 0 + : (conditions.topology->lastHERing() - conditions.topology->firstHERing() + 1), + conditions.offsetForHashes, + configParameters.applyTimeSlew, + configParameters.tzeroTimeSlew, + configParameters.slopeTimeSlew, + configParameters.tmaxTimeSlew); + cudaCheck(cudaGetLastError()); + + // number of samples is checked in above assert + if (conditions.pulseOffsetsHost.size() == 8u) { + // FIXME: provide constants from configuration + uint32_t threadsPerBlock = configParameters.kernelMinimizeThreads[0]; + uint32_t blocks = threadsPerBlock > totalChannels ? 1 : (totalChannels + threadsPerBlock - 1) / threadsPerBlock; + auto const nbytesShared = 2 * threadsPerBlock * calo::multifit::MapSymM::total * sizeof(float); + hcal::mahi::kernel_minimize<8, 8><<>>( + outputGPU.recHits.energy.get(), + outputGPU.recHits.chi2.get(), + scratch.amplitudes.get(), + scratch.pulseMatrices.get(), + scratch.pulseMatricesM.get(), + scratch.pulseMatricesP.get(), + conditions.pulseOffsets.values, + scratch.noiseTerms.get(), + scratch.soiSamples.get(), + conditions.pedestalWidths.values, + conditions.effectivePedestalWidths.values, + configParameters.useEffectivePedestals, + inputGPU.f01HEDigis.ids.get(), + inputGPU.f5HBDigis.ids.get(), + inputGPU.f3HBDigis.ids.get(), + conditions.gains.values, + conditions.respCorrs.values, + inputGPU.f01HEDigis.size, + inputGPU.f5HBDigis.size, + totalChannels, + conditions.offsetForHashes, + conditions.topology->maxDepthHB(), + conditions.topology->maxDepthHE(), + conditions.recConstants->getNPhi(1) > hcal::reconstruction::IPHI_MAX ? conditions.recConstants->getNPhi(1) + : hcal::reconstruction::IPHI_MAX, + conditions.topology->firstHBRing(), + conditions.topology->lastHBRing(), + conditions.topology->firstHERing(), + conditions.topology->lastHERing(), + conditions.recConstants->getEtaRange(0).second - conditions.recConstants->getEtaRange(0).first + 1, + conditions.topology->firstHERing() > conditions.topology->lastHERing() + ? 0 + : (conditions.topology->lastHERing() - conditions.topology->firstHERing() + 1)); + } else { + throw cms::Exception("Invalid MahiGPU configuration") + << "Currently support only 8 pulses and 8 time samples and provided: " << f01nsamples << " samples and " + << conditions.pulseOffsetsHost.size() << " pulses" << std::endl; + } + } + + } // namespace reconstruction +} // namespace hcal diff --git a/RecoLocalCalo/HcalRecProducers/src/SimpleAlgoGPU.h b/RecoLocalCalo/HcalRecProducers/src/SimpleAlgoGPU.h new file mode 100644 index 0000000000000..c0bb499b517a7 --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/src/SimpleAlgoGPU.h @@ -0,0 +1,19 @@ +#ifndef RecoLocalCalo_HcalRecProducers_src_SimpleAlgoGPU_h +#define RecoLocalCalo_HcalRecProducers_src_SimpleAlgoGPU_h + +#include "DeclsForKernels.h" + +namespace hcal { + namespace reconstruction { + + void entryPoint(InputDataGPU const&, + OutputDataGPU&, + ConditionsProducts const&, + ScratchDataGPU&, + ConfigParameters const&, + cudaStream_t); + + } +} // namespace hcal + +#endif // RecoLocalCalo_HcalRecProducers_src_SimpleAlgoGPU_h diff --git a/RecoLocalCalo/HcalRecProducers/test/make_GPUvsCPU_HCAL_plots.py b/RecoLocalCalo/HcalRecProducers/test/make_GPUvsCPU_HCAL_plots.py new file mode 100644 index 0000000000000..2b97efc2f2d8c --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/test/make_GPUvsCPU_HCAL_plots.py @@ -0,0 +1,28 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process("PLOT") + +process.load("FWCore.MessageService.MessageLogger_cfi") +process.options = cms.untracked.PSet( + wantSummary = cms.untracked.bool(False) +) + +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load("Configuration.StandardSequences.FrontierConditions_GlobalTag_cff") +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:run2_hlt_relval', '') + +process.maxEvents = cms.untracked.PSet( input = cms.untracked.int32(-1) ) +process.MessageLogger.cerr.FwkReport.reportEvery = 500 + +process.source = cms.Source("PoolSource", + fileNames = cms.untracked.vstring('file:GPUvsCPU_HCAL_rechits.root') +) + +process.comparisonPlots = cms.EDAnalyzer('HCALGPUAnalyzer') + +process.TFileService = cms.Service('TFileService', + fileName = cms.string('GPUvsCPU_HCAL_plots.root') +) + +process.path = cms.Path(process.comparisonPlots) diff --git a/RecoLocalCalo/HcalRecProducers/test/make_GPUvsCPU_HCAL_rechits.py b/RecoLocalCalo/HcalRecProducers/test/make_GPUvsCPU_HCAL_rechits.py new file mode 100644 index 0000000000000..32d4104a842ef --- /dev/null +++ b/RecoLocalCalo/HcalRecProducers/test/make_GPUvsCPU_HCAL_rechits.py @@ -0,0 +1,152 @@ +import FWCore.ParameterSet.Config as cms + +from Configuration.StandardSequences.Eras import eras +#from Configuration.ProcessModifiers.gpu_cff import gpu + +process = cms.Process('RECOgpu', eras.Run2_2018) + +# import of standard configurations +process.load('Configuration.StandardSequences.Services_cff') +process.load('FWCore.MessageService.MessageLogger_cfi') +process.load('HeterogeneousCore.CUDAServices.CUDAService_cfi') + +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:run2_hlt_relval', '') + +process.maxEvents = cms.untracked.PSet( + input = cms.untracked.int32(1000) +) + +#----------------------------------------- +# INPUT +#----------------------------------------- + +process.source = cms.Source("PoolSource", + fileNames = cms.untracked.vstring('/store/data/Run2018D/EphemeralHLTPhysics1/RAW/v1/000/323/775/00000/A27DFA33-8FCB-BE42-A2D2-1A396EEE2B6E.root') +) + +process.hltGetRaw = cms.EDAnalyzer( "HLTGetRaw", + RawDataCollection = cms.InputTag( "rawDataCollector" ) +) + +process.input = cms.Path( process.hltGetRaw ) + +#----------------------------------------- +# CMSSW/Hcal non-DQM Related Module import +#----------------------------------------- + +process.load('Configuration.StandardSequences.GeometryRecoDB_cff') +process.load("RecoLocalCalo.Configuration.hcalLocalReco_cff") +process.load("EventFilter.HcalRawToDigi.HcalRawToDigi_cfi") +process.load("RecoLuminosity.LumiProducer.bunchSpacingProducer_cfi") + +process.hcalDigis.InputLabel = cms.InputTag("rawDataCollector") + +#----------------------------------------- +# CMSSW/Hcal GPU related files +#----------------------------------------- + +process.load("RecoLocalCalo.HcalRecProducers.hbheRecHitProducerGPUTask_cff") +process.load("RecoLocalCalo.HcalRecProducers.hcalCPURecHitsProducer_cfi") +process.hcalCPURecHitsProducer.recHitsM0LabelIn = cms.InputTag("hbheRecHitProducerGPU","") +process.hcalCPURecHitsProducer.recHitsM0LabelOut = cms.string("") + +#----------------------------------------- +# Temporary customization (things not implemented on the GPU) +#----------------------------------------- + +## the one below is taken directly from the DB, regard M0 +#process.hbheprereco.algorithm.correctForPhaseContainment = cms.bool(False) + +## do always 8 pulse +process.hbheprereco.algorithm.chiSqSwitch = cms.double(-1) + +## to match hard coded setting (will be fixed on CPU) +process.hbheprereco.algorithm.nMaxItersMin = cms.int32(50) + +#----------------------------------------- +# Final Custmization for Run3 +#----------------------------------------- + +# we will not run arrival Time at HLT +process.hbheprereco.algorithm.calculateArrivalTime = cms.bool(False) + +## we do not need this +process.hbheprereco.algorithm.applyLegacyHBMCorrection = cms.bool(False) + +# we only run Mahi at HLT +process.hbheprereco.algorithm.useM3 = cms.bool(False) + +# we will not have the HPD noise flags in Run3, as will be all siPM +process.hbheprereco.setLegacyFlagsQIE8 = cms.bool(False) +process.hbheprereco.setNegativeFlagsQIE8 = cms.bool(False) +process.hbheprereco.setNoiseFlagsQIE8 = cms.bool(False) +process.hbheprereco.setPulseShapeFlagsQIE8 = cms.bool(False) + +# for testing M0 only +##process.hbheprereco.algorithm.useMahi = cms.bool(False) + +#----------------------------------------- +# OUTPUT +#----------------------------------------- + +#process.out = cms.OutputModule("AsciiOutputModule", +# outputCommands = cms.untracked.vstring( +# 'keep *_*_*_*', +# ), +# verbosity = cms.untracked.uint32(0) +#) + +process.out = cms.OutputModule("PoolOutputModule", + fileName = cms.untracked.string("GPUvsCPU_HCAL_rechits.root") +) + +#--------------- + +process.finalize = cms.EndPath(process.out) + +process.bunchSpacing = cms.Path( + process.bunchSpacingProducer +) + +#----------------------------------------- +# gpu test +#----------------------------------------- + +process.digiPathCPU = cms.Path( + process.hcalDigis +) + +process.recoPathCPU = cms.Path( + process.hbheprereco +) + +#--------------- + +## hcalCPUDigisProducer <-- this convert the GPU digi on cpu (for dqm) +process.recoPathGPU = cms.Path( + process.hbheRecHitProducerGPUSequence + * process.hcalCPURecHitsProducer +) + +#--------------- + +process.schedule = cms.Schedule( + process.input, + process.digiPathCPU, + process.recoPathCPU, + process.recoPathGPU, + process.finalize +) + +process.options = cms.untracked.PSet( + numberOfThreads = cms.untracked.uint32(8), + numberOfStreams = cms.untracked.uint32(8), + SkipEvent = cms.untracked.vstring('ProductNotFound'), + wantSummary = cms.untracked.bool(True) +) + +# report CUDAService messages +process.MessageLogger.cerr.FwkReport.reportEvery = 100 +process.MessageLogger.categories.append("CUDAService") diff --git a/RecoLocalTracker/Configuration/python/RecoLocalTracker_cff.py b/RecoLocalTracker/Configuration/python/RecoLocalTracker_cff.py index 3cae176059b3b..35a72f0edb08f 100644 --- a/RecoLocalTracker/Configuration/python/RecoLocalTracker_cff.py +++ b/RecoLocalTracker/Configuration/python/RecoLocalTracker_cff.py @@ -9,11 +9,11 @@ from RecoLocalTracker.SiStripRecHitConverter.StripCPEfromTrackAngle_cfi import * from RecoLocalTracker.SiStripZeroSuppression.SiStripZeroSuppression_cfi import * from RecoLocalTracker.SiStripClusterizer.SiStripClusterizer_cfi import * -from RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizerPreSplitting_cfi import * +from RecoLocalTracker.SiPixelClusterizer.siPixelClustersPreSplitting_cff import * from RecoLocalTracker.SiPixelRecHits.SiPixelRecHits_cfi import * from RecoLocalTracker.SubCollectionProducers.clustersummaryproducer_cfi import * -pixeltrackerlocalrecoTask = cms.Task(siPixelClustersPreSplitting,siPixelRecHitsPreSplitting) +pixeltrackerlocalrecoTask = cms.Task(siPixelClustersPreSplittingTask,siPixelRecHitsPreSplittingTask) striptrackerlocalrecoTask = cms.Task(siStripZeroSuppression,siStripClusters,siStripMatchedRecHits) trackerlocalrecoTask = cms.Task(pixeltrackerlocalrecoTask,striptrackerlocalrecoTask,clusterSummaryProducer) diff --git a/RecoLocalTracker/SiPixelClusterizer/BuildFile.xml b/RecoLocalTracker/SiPixelClusterizer/BuildFile.xml new file mode 100644 index 0000000000000..7e71c635c95b8 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/BuildFile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/BuildFile.xml b/RecoLocalTracker/SiPixelClusterizer/plugins/BuildFile.xml index c7b16a6ef4ee2..a4851e4b322be 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/BuildFile.xml +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/BuildFile.xml @@ -1,8 +1,15 @@ - - - + + + - + + + + + + + + diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/PixelClusterizerBase.h b/RecoLocalTracker/SiPixelClusterizer/plugins/PixelClusterizerBase.h index 9e3aad606851c..eb622cccb051e 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/PixelClusterizerBase.h +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/PixelClusterizerBase.h @@ -1,13 +1,14 @@ #ifndef RecoLocalTracker_SiPixelClusterizer_PixelClusterizerBase_H #define RecoLocalTracker_SiPixelClusterizer_PixelClusterizerBase_H +#include + +#include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationServiceBase.h" #include "DataFormats/Common/interface/DetSetVector.h" #include "DataFormats/Common/interface/DetSetVectorNew.h" #include "DataFormats/SiPixelCluster/interface/SiPixelCluster.h" #include "DataFormats/SiPixelDigi/interface/PixelDigi.h" #include "DataFormats/TrackerCommon/interface/TrackerTopology.h" -#include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationServiceBase.h" -#include class PixelGeomDetUnit; @@ -20,29 +21,38 @@ class PixelClusterizerBase { typedef edmNew::DetSet::const_iterator ClusterIterator; struct AccretionCluster { - typedef unsigned short UShort; - static constexpr UShort MAXSIZE = 256; - UShort adc[MAXSIZE]; - UShort x[MAXSIZE]; - UShort y[MAXSIZE]; - UShort xmin = 16000; - UShort ymin = 16000; + static constexpr uint16_t MAXSIZE = 256; + uint16_t adc[MAXSIZE]; + uint16_t x[MAXSIZE]; + uint16_t y[MAXSIZE]; + uint16_t xmin = 16000; + uint16_t ymin = 16000; unsigned int isize = 0; - unsigned int curr = 0; + int charge = 0; // stack interface (unsafe ok for use below) - UShort top() const { return curr; } + unsigned int curr = 0; + uint16_t top() const { return curr; } void pop() { ++curr; } bool empty() { return curr == isize; } - bool add(SiPixelCluster::PixelPos const& p, UShort const iadc) { + void clear() { + xmin = 16000; + ymin = 16000; + isize = 0; + charge = 0; + curr = 0; + } + + bool add(SiPixelCluster::PixelPos const& p, uint16_t const iadc) { if (isize == MAXSIZE) return false; - xmin = std::min(xmin, (unsigned short)(p.row())); - ymin = std::min(ymin, (unsigned short)(p.col())); + xmin = std::min(xmin, p.row()); + ymin = std::min(ymin, p.col()); adc[isize] = iadc; x[isize] = p.row(); y[isize++] = p.col(); + charge += iadc; return true; } }; diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelClusterProducer.cc b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelClusterProducer.cc index 15a6536ca644b..2bd902af01b1e 100644 --- a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelClusterProducer.cc +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelClusterProducer.cc @@ -122,6 +122,14 @@ void SiPixelClusterProducer::produce(edm::Event& e, const edm::EventSetup& es) { // Step D: write output to file output->shrink_to_fit(); + + // set sequential identifier + for (auto& clusters : *output) { + uint16_t id = 0; + for (auto& cluster : clusters) { + cluster.setOriginalId(id++); + } + } e.put(tPutPixelClusters, std::move(output)); } @@ -153,15 +161,14 @@ void SiPixelClusterProducer::run(const T& input, int numberOfClusters = 0; // Iterate on detector units - typename T::const_iterator DSViter = input.begin(); - for (; DSViter != input.end(); DSViter++) { + for (auto const& dsv : input) { ++numberOfDetUnits; // LogDebug takes very long time, get rid off. - //LogDebug("SiStripClusterizer") << "[SiPixelClusterProducer::run] DetID" << DSViter->id; + //LogDebug("SiStripClusterizer") << "[SiPixelClusterProducer::run] DetID" << dsv.id; std::vector badChannels; - DetId detIdObject(DSViter->detId()); + DetId detIdObject(dsv.detId()); // Comment: At the moment the clusterizer depends on geometry // to access information as the pixel topology (number of columns @@ -177,8 +184,8 @@ void SiPixelClusterProducer::run(const T& input, { // Produce clusters for this DetUnit and store them in // a DetSet - edmNew::DetSetVector::FastFiller spc(output, DSViter->detId()); - clusterizer_->clusterizeDetUnit(*DSViter, pixDet, tTopo_, badChannels, spc); + edmNew::DetSetVector::FastFiller spc(output, dsv.detId()); + clusterizer_->clusterizeDetUnit(dsv, pixDet, tTopo_, badChannels, spc); if (spc.empty()) { spc.abort(); } else { diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelDigisClustersFromSoA.cc b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelDigisClustersFromSoA.cc new file mode 100644 index 0000000000000..0078bae38306a --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelDigisClustersFromSoA.cc @@ -0,0 +1,150 @@ +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "DataFormats/Common/interface/DetSetVector.h" +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/DetId/interface/DetId.h" +#include "DataFormats/SiPixelCluster/interface/SiPixelCluster.h" +#include "DataFormats/SiPixelDigi/interface/PixelDigi.h" +#include "DataFormats/SiPixelDigi/interface/SiPixelDigisSoA.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/PixelClusterizerBase.h" + +class SiPixelDigisClustersFromSoA : public edm::global::EDProducer<> { +public: + explicit SiPixelDigisClustersFromSoA(const edm::ParameterSet& iConfig); + ~SiPixelDigisClustersFromSoA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + + const edm::ESGetToken topoToken_; + + edm::EDGetTokenT digiGetToken_; + + edm::EDPutTokenT> digiPutToken_; + edm::EDPutTokenT clusterPutToken_; +}; + +SiPixelDigisClustersFromSoA::SiPixelDigisClustersFromSoA(const edm::ParameterSet& iConfig) + : topoToken_(esConsumes()), + digiGetToken_(consumes(iConfig.getParameter("src"))), + digiPutToken_(produces>()), + clusterPutToken_(produces()) {} + +void SiPixelDigisClustersFromSoA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("src", edm::InputTag("siPixelDigisSoA")); + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelDigisClustersFromSoA::produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const { + const auto& digis = iEvent.get(digiGetToken_); + const uint32_t nDigis = digis.size(); + const auto& ttopo = iSetup.getData(topoToken_); + + auto collection = std::make_unique>(); + auto outputClusters = std::make_unique(); + outputClusters->reserve(gpuClustering::maxNumModules, nDigis / 4); + + edm::DetSet* detDigis = nullptr; + for (uint32_t i = 0; i < nDigis; i++) { + if (digis.pdigi(i) == 0) + continue; + detDigis = &collection->find_or_insert(digis.rawIdArr(i)); + if ((*detDigis).empty()) + (*detDigis).data.reserve(64); // avoid the first relocations + break; + } + + int32_t nclus = -1; + std::vector aclusters(gpuClustering::maxNumClustersPerModules); +#ifdef EDM_ML_DEBUG + auto totClustersFilled = 0; +#endif + + auto fillClusters = [&](uint32_t detId) { + if (nclus < 0) + return; // this in reality should never happen + edmNew::DetSetVector::FastFiller spc(*outputClusters, detId); + auto layer = (DetId(detId).subdetId() == 1) ? ttopo.pxbLayer(detId) : 0; + auto clusterThreshold = (layer == 1) ? 2000 : 4000; + for (int32_t ic = 0; ic < nclus + 1; ++ic) { + auto const& acluster = aclusters[ic]; + // in any case we cannot go out of sync with gpu... + if (acluster.charge < clusterThreshold) + edm::LogWarning("SiPixelDigisClustersFromSoA") << "cluster below charge Threshold " + << "Layer/DetId/clusId " << layer << '/' << detId << '/' << ic + << " size/charge " << acluster.isize << '/' << acluster.charge; + SiPixelCluster cluster(acluster.isize, acluster.adc, acluster.x, acluster.y, acluster.xmin, acluster.ymin, ic); +#ifdef EDM_ML_DEBUG + ++totClustersFilled; +#endif + LogDebug("SiPixelDigisClustersFromSoA") + << "putting in this cluster " << ic << " " << cluster.charge() << " " << cluster.pixelADC().size(); + // sort by row (x) + spc.push_back(std::move(cluster)); + std::push_heap(spc.begin(), spc.end(), [](SiPixelCluster const& cl1, SiPixelCluster const& cl2) { + return cl1.minPixelRow() < cl2.minPixelRow(); + }); + } + for (int32_t ic = 0; ic < nclus + 1; ++ic) + aclusters[ic].clear(); + nclus = -1; + // sort by row (x) + std::sort_heap(spc.begin(), spc.end(), [](SiPixelCluster const& cl1, SiPixelCluster const& cl2) { + return cl1.minPixelRow() < cl2.minPixelRow(); + }); + if (spc.empty()) + spc.abort(); + }; + + for (uint32_t i = 0; i < nDigis; i++) { + if (digis.pdigi(i) == 0) + continue; + if (digis.clus(i) > 9000) + continue; // not in cluster; TODO add an assert for the size + assert(digis.rawIdArr(i) > 109999); + if ((*detDigis).detId() != digis.rawIdArr(i)) { + fillClusters((*detDigis).detId()); + assert(nclus == -1); + detDigis = &collection->find_or_insert(digis.rawIdArr(i)); + if ((*detDigis).empty()) + (*detDigis).data.reserve(64); // avoid the first relocations + else { + edm::LogWarning("SiPixelDigisClustersFromSoA") << "Problem det present twice in input! " << (*detDigis).detId(); + } + } + (*detDigis).data.emplace_back(digis.pdigi(i)); + auto const& dig = (*detDigis).data.back(); + // fill clusters + assert(digis.clus(i) >= 0); + assert(digis.clus(i) < gpuClustering::maxNumClustersPerModules); + nclus = std::max(digis.clus(i), nclus); + auto row = dig.row(); + auto col = dig.column(); + SiPixelCluster::PixelPos pix(row, col); + aclusters[digis.clus(i)].add(pix, digis.adc(i)); + } + + // fill final clusters + if (detDigis) + fillClusters((*detDigis).detId()); +#ifdef EDM_ML_DEBUG + LogDebug("SiPixelDigisClustersFromSoA") << "filled " << totClustersFilled << " clusters"; +#endif + + iEvent.put(digiPutToken_, std::move(collection)); + iEvent.put(clusterPutToken_, std::move(outputClusters)); +} + +DEFINE_FWK_MODULE(SiPixelDigisClustersFromSoA); diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterCUDA.cc b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterCUDA.cc new file mode 100644 index 0000000000000..93b92e145ec5c --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterCUDA.cc @@ -0,0 +1,259 @@ +// C++ includes +#include +#include +#include + +// CMSSW includes +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CalibTracker/Records/interface/SiPixelGainCalibrationForHLTGPURcd.h" +#include "CalibTracker/SiPixelESProducers/interface/SiPixelROCsStatusAndMappingWrapper.h" +#include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationForHLTGPU.h" +#include "CondFormats/DataRecord/interface/SiPixelFedCablingMapRcd.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelFedCablingMap.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelFedCablingTree.h" +#include "DataFormats/FEDRawData/interface/FEDNumbering.h" +#include "DataFormats/FEDRawData/interface/FEDRawData.h" +#include "DataFormats/FEDRawData/interface/FEDRawDataCollection.h" +#include "EventFilter/SiPixelRawToDigi/interface/PixelDataFormatter.h" +#include "EventFilter/SiPixelRawToDigi/interface/PixelUnpackingRegions.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/ESTransientHandle.h" +#include "FWCore/Framework/interface/ESWatcher.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "RecoTracker/Record/interface/CkfComponentsRecord.h" + +// local includes +#include "SiPixelRawToClusterGPUKernel.h" + +class SiPixelRawToClusterCUDA : public edm::stream::EDProducer { +public: + explicit SiPixelRawToClusterCUDA(const edm::ParameterSet& iConfig); + ~SiPixelRawToClusterCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void acquire(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) override; + void produce(edm::Event& iEvent, const edm::EventSetup& iSetup) override; + + edm::EDGetTokenT rawGetToken_; + + edm::EDPutTokenT> digiPutToken_; + edm::EDPutTokenT> digiErrorPutToken_; + edm::EDPutTokenT> clusterPutToken_; + + cms::cuda::ContextState ctxState_; + + edm::ESWatcher recordWatcher_; + edm::ESGetToken gpuMapToken_; + edm::ESGetToken gainsToken_; + edm::ESGetToken cablingMapToken_; + + std::unique_ptr cabling_; + std::vector fedIds_; + const SiPixelFedCablingMap* cablingMap_ = nullptr; + std::unique_ptr regions_; + + pixelgpudetails::SiPixelRawToClusterGPUKernel gpuAlgo_; + std::unique_ptr wordFedAppender_; + PixelDataFormatter::Errors errors_; + + const bool isRun2_; + const bool includeErrors_; + const bool useQuality_; +}; + +SiPixelRawToClusterCUDA::SiPixelRawToClusterCUDA(const edm::ParameterSet& iConfig) + : rawGetToken_(consumes(iConfig.getParameter("InputLabel"))), + digiPutToken_(produces>()), + clusterPutToken_(produces>()), + gpuMapToken_(esConsumes()), + gainsToken_(esConsumes()), + cablingMapToken_(esConsumes( + edm::ESInputTag("", iConfig.getParameter("CablingMapLabel")))), + isRun2_(iConfig.getParameter("isRun2")), + includeErrors_(iConfig.getParameter("IncludeErrors")), + useQuality_(iConfig.getParameter("UseQualityInfo")) { + if (includeErrors_) { + digiErrorPutToken_ = produces>(); + } + + // regions + if (!iConfig.getParameter("Regions").getParameterNames().empty()) { + regions_ = std::make_unique(iConfig, consumesCollector()); + } + + edm::Service cs; + if (cs->enabled()) { + wordFedAppender_ = std::make_unique(); + } +} + +void SiPixelRawToClusterCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("isRun2", true); + desc.add("IncludeErrors", true); + desc.add("UseQualityInfo", false); + desc.add("InputLabel", edm::InputTag("rawDataCollector")); + { + edm::ParameterSetDescription psd0; + psd0.addOptional>("inputs"); + psd0.addOptional>("deltaPhi"); + psd0.addOptional>("maxZ"); + psd0.addOptional("beamSpot"); + desc.add("Regions", psd0) + ->setComment("## Empty Regions PSet means complete unpacking"); + } + desc.add("CablingMapLabel", "")->setComment("CablingMap label"); //Tav + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelRawToClusterCUDA::acquire(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) { + cms::cuda::ScopedContextAcquire ctx{iEvent.streamID(), std::move(waitingTaskHolder), ctxState_}; + + auto hgpuMap = iSetup.getHandle(gpuMapToken_); + if (hgpuMap->hasQuality() != useQuality_) { + throw cms::Exception("LogicError") + << "UseQuality of the module (" << useQuality_ + << ") differs the one from SiPixelROCsStatusAndMappingWrapper. Please fix your configuration."; + } + // get the GPU product already here so that the async transfer can begin + const auto* gpuMap = hgpuMap->getGPUProductAsync(ctx.stream()); + + auto hgains = iSetup.getHandle(gainsToken_); + // get the GPU product already here so that the async transfer can begin + const auto* gpuGains = hgains->getGPUProductAsync(ctx.stream()); + + cms::cuda::device::unique_ptr modulesToUnpackRegional; + const unsigned char* gpuModulesToUnpack; + + if (regions_) { + regions_->run(iEvent, iSetup); + LogDebug("SiPixelRawToCluster") << "region2unpack #feds: " << regions_->nFEDs(); + LogDebug("SiPixelRawToCluster") << "region2unpack #modules (BPIX,EPIX,total): " << regions_->nBarrelModules() << " " + << regions_->nForwardModules() << " " << regions_->nModules(); + modulesToUnpackRegional = hgpuMap->getModToUnpRegionalAsync(*(regions_->modulesToUnpack()), ctx.stream()); + gpuModulesToUnpack = modulesToUnpackRegional.get(); + } else { + gpuModulesToUnpack = hgpuMap->getModToUnpAllAsync(ctx.stream()); + } + + // initialize cabling map or update if necessary + if (recordWatcher_.check(iSetup)) { + // cabling map, which maps online address (fed->link->ROC->local pixel) to offline (DetId->global pixel) + auto cablingMap = iSetup.getTransientHandle(cablingMapToken_); + cablingMap_ = cablingMap.product(); + fedIds_ = cablingMap->fedIds(); + cabling_ = cablingMap->cablingTree(); + LogDebug("map version:") << cabling_->version(); + } + + const auto& buffers = iEvent.get(rawGetToken_); + + errors_.clear(); + + // GPU specific: Data extraction for RawToDigi GPU + unsigned int wordCounterGPU = 0; + unsigned int fedCounter = 0; + bool errorsInEvent = false; + + // In CPU algorithm this loop is part of PixelDataFormatter::interpretRawData() + ErrorChecker errorcheck; + for (int fedId : fedIds_) { + if (regions_ && !regions_->mayUnpackFED(fedId)) + continue; + + // for GPU + // first 150 index stores the fedId and next 150 will store the + // start index of word in that fed + assert(fedId >= FEDNumbering::MINSiPixeluTCAFEDID); + fedCounter++; + + // get event data for this fed + const FEDRawData& rawData = buffers.FEDData(fedId); + + // GPU specific + int nWords = rawData.size() / sizeof(cms_uint64_t); + if (nWords == 0) { + continue; + } + + // check CRC bit + const cms_uint64_t* trailer = reinterpret_cast(rawData.data()) + (nWords - 1); + if (not errorcheck.checkCRC(errorsInEvent, fedId, trailer, errors_)) { + continue; + } + + // check headers + const cms_uint64_t* header = reinterpret_cast(rawData.data()); + header--; + bool moreHeaders = true; + while (moreHeaders) { + header++; + bool headerStatus = errorcheck.checkHeader(errorsInEvent, fedId, header, errors_); + moreHeaders = headerStatus; + } + + // check trailers + bool moreTrailers = true; + trailer++; + while (moreTrailers) { + trailer--; + bool trailerStatus = errorcheck.checkTrailer(errorsInEvent, fedId, nWords, trailer, errors_); + moreTrailers = trailerStatus; + } + + const cms_uint32_t* bw = (const cms_uint32_t*)(header + 1); + const cms_uint32_t* ew = (const cms_uint32_t*)(trailer); + + assert(0 == (ew - bw) % 2); + wordFedAppender_->initializeWordFed(fedId, wordCounterGPU, bw, (ew - bw)); + wordCounterGPU += (ew - bw); + + } // end of for loop + + gpuAlgo_.makeClustersAsync(isRun2_, + gpuMap, + gpuModulesToUnpack, + gpuGains, + *wordFedAppender_, + std::move(errors_), + wordCounterGPU, + fedCounter, + useQuality_, + includeErrors_, + edm::MessageDrop::instance()->debugEnabled, + ctx.stream()); +} + +void SiPixelRawToClusterCUDA::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + cms::cuda::ScopedContextProduce ctx{ctxState_}; + + auto tmp = gpuAlgo_.getResults(); + ctx.emplace(iEvent, digiPutToken_, std::move(tmp.first)); + ctx.emplace(iEvent, clusterPutToken_, std::move(tmp.second)); + if (includeErrors_) { + ctx.emplace(iEvent, digiErrorPutToken_, gpuAlgo_.getErrors()); + } +} + +// define as framework plugin +DEFINE_FWK_MODULE(SiPixelRawToClusterCUDA); diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.cu b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.cu new file mode 100644 index 0000000000000..25e5c925990f8 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.cu @@ -0,0 +1,669 @@ +/* Sushil Dubey, Shashi Dugad, TIFR, July 2017 + * + * File Name: RawToClusterGPU.cu + * Description: It converts Raw data into Digi Format on GPU + * Finaly the Output of RawToDigi data is given to pixelClusterizer +**/ + +// C++ includes +#include +#include +#include +#include +#include +#include +#include + +// CUDA includes +#include + +// CMSSW includes +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelROCsStatusAndMapping.h" +#include "DataFormats/FEDRawData/interface/FEDNumbering.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/gpuCalibPixel.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/gpuClusterChargeCut.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h" + +// local includes +#include "SiPixelRawToClusterGPUKernel.h" + +namespace pixelgpudetails { + + // number of words for all the FEDs + constexpr uint32_t MAX_FED_WORDS = pixelgpudetails::MAX_FED * pixelgpudetails::MAX_WORD; + + SiPixelRawToClusterGPUKernel::WordFedAppender::WordFedAppender() { + word_ = cms::cuda::make_host_noncached_unique(MAX_FED_WORDS, cudaHostAllocWriteCombined); + fedId_ = cms::cuda::make_host_noncached_unique(MAX_FED_WORDS, cudaHostAllocWriteCombined); + } + + void SiPixelRawToClusterGPUKernel::WordFedAppender::initializeWordFed(int fedId, + unsigned int wordCounterGPU, + const cms_uint32_t *src, + unsigned int length) { + std::memcpy(word_.get() + wordCounterGPU, src, sizeof(cms_uint32_t) * length); + std::memset(fedId_.get() + wordCounterGPU / 2, fedId - FEDNumbering::MINSiPixeluTCAFEDID, length / 2); + } + + //////////////////// + + __device__ uint32_t getLink(uint32_t ww) { + return ((ww >> pixelgpudetails::LINK_shift) & pixelgpudetails::LINK_mask); + } + + __device__ uint32_t getRoc(uint32_t ww) { return ((ww >> pixelgpudetails::ROC_shift) & pixelgpudetails::ROC_mask); } + + __device__ uint32_t getADC(uint32_t ww) { return ((ww >> pixelgpudetails::ADC_shift) & pixelgpudetails::ADC_mask); } + + __device__ bool isBarrel(uint32_t rawId) { + return (PixelSubdetector::PixelBarrel == ((rawId >> DetId::kSubdetOffset) & DetId::kSubdetMask)); + } + + __device__ pixelgpudetails::DetIdGPU getRawId(const SiPixelROCsStatusAndMapping *cablingMap, + uint8_t fed, + uint32_t link, + uint32_t roc) { + uint32_t index = fed * MAX_LINK * MAX_ROC + (link - 1) * MAX_ROC + roc; + pixelgpudetails::DetIdGPU detId = { + cablingMap->rawId[index], cablingMap->rocInDet[index], cablingMap->moduleId[index]}; + return detId; + } + + //reference http://cmsdoxygen.web.cern.ch/cmsdoxygen/CMSSW_9_2_0/doc/html/dd/d31/FrameConversion_8cc_source.html + //http://cmslxr.fnal.gov/source/CondFormats/SiPixelObjects/src/PixelROC.cc?v=CMSSW_9_2_0#0071 + // Convert local pixel to pixelgpudetails::global pixel + __device__ pixelgpudetails::Pixel frameConversion( + bool bpix, int side, uint32_t layer, uint32_t rocIdInDetUnit, pixelgpudetails::Pixel local) { + int slopeRow = 0, slopeCol = 0; + int rowOffset = 0, colOffset = 0; + + if (bpix) { + if (side == -1 && layer != 1) { // -Z side: 4 non-flipped modules oriented like 'dddd', except Layer 1 + if (rocIdInDetUnit < 8) { + slopeRow = 1; + slopeCol = -1; + rowOffset = 0; + colOffset = (8 - rocIdInDetUnit) * pixelgpudetails::numColsInRoc - 1; + } else { + slopeRow = -1; + slopeCol = 1; + rowOffset = 2 * pixelgpudetails::numRowsInRoc - 1; + colOffset = (rocIdInDetUnit - 8) * pixelgpudetails::numColsInRoc; + } // if roc + } else { // +Z side: 4 non-flipped modules oriented like 'pppp', but all 8 in layer1 + if (rocIdInDetUnit < 8) { + slopeRow = -1; + slopeCol = 1; + rowOffset = 2 * pixelgpudetails::numRowsInRoc - 1; + colOffset = rocIdInDetUnit * pixelgpudetails::numColsInRoc; + } else { + slopeRow = 1; + slopeCol = -1; + rowOffset = 0; + colOffset = (16 - rocIdInDetUnit) * pixelgpudetails::numColsInRoc - 1; + } + } + + } else { // fpix + if (side == -1) { // pannel 1 + if (rocIdInDetUnit < 8) { + slopeRow = 1; + slopeCol = -1; + rowOffset = 0; + colOffset = (8 - rocIdInDetUnit) * pixelgpudetails::numColsInRoc - 1; + } else { + slopeRow = -1; + slopeCol = 1; + rowOffset = 2 * pixelgpudetails::numRowsInRoc - 1; + colOffset = (rocIdInDetUnit - 8) * pixelgpudetails::numColsInRoc; + } + } else { // pannel 2 + if (rocIdInDetUnit < 8) { + slopeRow = 1; + slopeCol = -1; + rowOffset = 0; + colOffset = (8 - rocIdInDetUnit) * pixelgpudetails::numColsInRoc - 1; + } else { + slopeRow = -1; + slopeCol = 1; + rowOffset = 2 * pixelgpudetails::numRowsInRoc - 1; + colOffset = (rocIdInDetUnit - 8) * pixelgpudetails::numColsInRoc; + } + + } // side + } + + uint32_t gRow = rowOffset + slopeRow * local.row; + uint32_t gCol = colOffset + slopeCol * local.col; + // inside frameConversion row: gRow, column: gCol + pixelgpudetails::Pixel global = {gRow, gCol}; + return global; + } + + // error decoding and handling copied from EventFilter/SiPixelRawToDigi/src/ErrorChecker.cc + __device__ uint8_t conversionError(uint8_t fedId, uint8_t status, bool debug = false) { + uint8_t errorType = 0; + + switch (status) { + case (1): { + if (debug) + printf("Error in Fed: %i, invalid channel Id (errorType = 35\n)", fedId); + errorType = 35; + break; + } + case (2): { + if (debug) + printf("Error in Fed: %i, invalid ROC Id (errorType = 36)\n", fedId); + errorType = 36; + break; + } + case (3): { + if (debug) + printf("Error in Fed: %i, invalid dcol/pixel value (errorType = 37)\n", fedId); + errorType = 37; + break; + } + case (4): { + if (debug) + printf("Error in Fed: %i, dcol/pixel read out of order (errorType = 38)\n", fedId); + errorType = 38; + break; + } + default: + if (debug) + printf("Cabling check returned unexpected result, status = %i\n", status); + }; + + return errorType; + } + + __device__ bool rocRowColIsValid(uint32_t rocRow, uint32_t rocCol) { + /// row and column in ROC representation + return ((rocRow < pixelgpudetails::numRowsInRoc) & (rocCol < pixelgpudetails::numColsInRoc)); + } + + __device__ bool dcolIsValid(uint32_t dcol, uint32_t pxid) { return ((dcol < 26) & (2 <= pxid) & (pxid < 162)); } + + // error decoding and handling copied from EventFilter/SiPixelRawToDigi/src/ErrorChecker.cc + __device__ uint8_t checkROC(uint32_t errorWord, + uint8_t fedId, + uint32_t link, + const SiPixelROCsStatusAndMapping *cablingMap, + bool debug = false) { + uint8_t errorType = (errorWord >> pixelgpudetails::ROC_shift) & pixelgpudetails::ERROR_mask; + if (errorType < 25) + return 0; + bool errorFound = false; + + switch (errorType) { + case (25): { + errorFound = true; + uint32_t index = fedId * MAX_LINK * MAX_ROC + (link - 1) * MAX_ROC + 1; + if (index > 1 && index <= cablingMap->size) { + if (!(link == cablingMap->link[index] && 1 == cablingMap->roc[index])) + errorFound = false; + } + if (debug and errorFound) + printf("Invalid ROC = 25 found (errorType = 25)\n"); + break; + } + case (26): { + if (debug) + printf("Gap word found (errorType = 26)\n"); + errorFound = true; + break; + } + case (27): { + if (debug) + printf("Dummy word found (errorType = 27)\n"); + errorFound = true; + break; + } + case (28): { + if (debug) + printf("Error fifo nearly full (errorType = 28)\n"); + errorFound = true; + break; + } + case (29): { + if (debug) + printf("Timeout on a channel (errorType = 29)\n"); + if ((errorWord >> pixelgpudetails::OMIT_ERR_shift) & pixelgpudetails::OMIT_ERR_mask) { + if (debug) + printf("...first errorType=29 error, this gets masked out\n"); + } + errorFound = true; + break; + } + case (30): { + if (debug) + printf("TBM error trailer (errorType = 30)\n"); + int stateMatch_bits = 4; + int stateMatch_shift = 8; + uint32_t stateMatch_mask = ~(~uint32_t(0) << stateMatch_bits); + int stateMatch = (errorWord >> stateMatch_shift) & stateMatch_mask; + if (stateMatch != 1 && stateMatch != 8) { + if (debug) + printf("FED error 30 with unexpected State Bits (errorType = 30)\n"); + } + if (stateMatch == 1) + errorType = 40; // 1=Overflow -> 40, 8=number of ROCs -> 30 + errorFound = true; + break; + } + case (31): { + if (debug) + printf("Event number error (errorType = 31)\n"); + errorFound = true; + break; + } + default: + errorFound = false; + }; + + return errorFound ? errorType : 0; + } + + // error decoding and handling copied from EventFilter/SiPixelRawToDigi/src/ErrorChecker.cc + __device__ uint32_t getErrRawID(uint8_t fedId, + uint32_t errWord, + uint32_t errorType, + const SiPixelROCsStatusAndMapping *cablingMap, + bool debug = false) { + uint32_t rID = 0xffffffff; + + switch (errorType) { + case 25: + case 30: + case 31: + case 36: + case 40: { + uint32_t roc = 1; + uint32_t link = (errWord >> pixelgpudetails::LINK_shift) & pixelgpudetails::LINK_mask; + uint32_t rID_temp = getRawId(cablingMap, fedId, link, roc).rawId; + if (rID_temp != gpuClustering::invalidModuleId) + rID = rID_temp; + break; + } + case 29: { + int chanNmbr = 0; + const int DB0_shift = 0; + const int DB1_shift = DB0_shift + 1; + const int DB2_shift = DB1_shift + 1; + const int DB3_shift = DB2_shift + 1; + const int DB4_shift = DB3_shift + 1; + const uint32_t DataBit_mask = ~(~uint32_t(0) << 1); + + int CH1 = (errWord >> DB0_shift) & DataBit_mask; + int CH2 = (errWord >> DB1_shift) & DataBit_mask; + int CH3 = (errWord >> DB2_shift) & DataBit_mask; + int CH4 = (errWord >> DB3_shift) & DataBit_mask; + int CH5 = (errWord >> DB4_shift) & DataBit_mask; + int BLOCK_bits = 3; + int BLOCK_shift = 8; + uint32_t BLOCK_mask = ~(~uint32_t(0) << BLOCK_bits); + int BLOCK = (errWord >> BLOCK_shift) & BLOCK_mask; + int localCH = 1 * CH1 + 2 * CH2 + 3 * CH3 + 4 * CH4 + 5 * CH5; + if (BLOCK % 2 == 0) + chanNmbr = (BLOCK / 2) * 9 + localCH; + else + chanNmbr = ((BLOCK - 1) / 2) * 9 + 4 + localCH; + if ((chanNmbr < 1) || (chanNmbr > 36)) + break; // signifies unexpected result + + uint32_t roc = 1; + uint32_t link = chanNmbr; + uint32_t rID_temp = getRawId(cablingMap, fedId, link, roc).rawId; + if (rID_temp != gpuClustering::invalidModuleId) + rID = rID_temp; + break; + } + case 37: + case 38: { + uint32_t roc = (errWord >> pixelgpudetails::ROC_shift) & pixelgpudetails::ROC_mask; + uint32_t link = (errWord >> pixelgpudetails::LINK_shift) & pixelgpudetails::LINK_mask; + uint32_t rID_temp = getRawId(cablingMap, fedId, link, roc).rawId; + if (rID_temp != gpuClustering::invalidModuleId) + rID = rID_temp; + break; + } + default: + break; + }; + + return rID; + } + + // Kernel to perform Raw to Digi conversion + __global__ void RawToDigi_kernel(const SiPixelROCsStatusAndMapping *cablingMap, + const unsigned char *modToUnp, + const uint32_t wordCounter, + const uint32_t *word, + const uint8_t *fedIds, + uint16_t *xx, + uint16_t *yy, + uint16_t *adc, + uint32_t *pdigi, + uint32_t *rawIdArr, + uint16_t *moduleId, + cms::cuda::SimpleVector *err, + bool useQualityInfo, + bool includeErrors, + bool debug) { + //if (threadIdx.x==0) printf("Event: %u blockIdx.x: %u start: %u end: %u\n", eventno, blockIdx.x, begin, end); + + int32_t first = threadIdx.x + blockIdx.x * blockDim.x; + for (int32_t iloop = first, nend = wordCounter; iloop < nend; iloop += blockDim.x * gridDim.x) { + auto gIndex = iloop; + xx[gIndex] = 0; + yy[gIndex] = 0; + adc[gIndex] = 0; + bool skipROC = false; + + uint8_t fedId = fedIds[gIndex / 2]; // +1200; + + // initialize (too many coninue below) + pdigi[gIndex] = 0; + rawIdArr[gIndex] = 0; + moduleId[gIndex] = gpuClustering::invalidModuleId; + + uint32_t ww = word[gIndex]; // Array containing 32 bit raw data + if (ww == 0) { + // 0 is an indicator of a noise/dead channel, skip these pixels during clusterization + continue; + } + + uint32_t link = getLink(ww); // Extract link + uint32_t roc = getRoc(ww); // Extract Roc in link + pixelgpudetails::DetIdGPU detId = getRawId(cablingMap, fedId, link, roc); + + uint8_t errorType = checkROC(ww, fedId, link, cablingMap, debug); + skipROC = (roc < pixelgpudetails::maxROCIndex) ? false : (errorType != 0); + if (includeErrors and skipROC) { + uint32_t rID = getErrRawID(fedId, ww, errorType, cablingMap, debug); + err->push_back(SiPixelErrorCompact{rID, ww, errorType, fedId}); + continue; + } + + uint32_t rawId = detId.rawId; + uint32_t rocIdInDetUnit = detId.rocInDet; + bool barrel = isBarrel(rawId); + + uint32_t index = fedId * MAX_LINK * MAX_ROC + (link - 1) * MAX_ROC + roc; + if (useQualityInfo) { + skipROC = cablingMap->badRocs[index]; + if (skipROC) + continue; + } + skipROC = modToUnp[index]; + if (skipROC) + continue; + + uint32_t layer = 0; + int side = 0, panel = 0, module = 0; + + if (barrel) { + layer = (rawId >> pixelgpudetails::layerStartBit) & pixelgpudetails::layerMask; + module = (rawId >> pixelgpudetails::moduleStartBit) & pixelgpudetails::moduleMask; + side = (module < 5) ? -1 : 1; + } else { + // endcap ids + layer = 0; + panel = (rawId >> pixelgpudetails::panelStartBit) & pixelgpudetails::panelMask; + side = (panel == 1) ? -1 : 1; + } + + // ***special case of layer to 1 be handled here + pixelgpudetails::Pixel localPix; + if (layer == 1) { + uint32_t col = (ww >> pixelgpudetails::COL_shift) & pixelgpudetails::COL_mask; + uint32_t row = (ww >> pixelgpudetails::ROW_shift) & pixelgpudetails::ROW_mask; + localPix.row = row; + localPix.col = col; + if (includeErrors) { + if (not rocRowColIsValid(row, col)) { + uint8_t error = conversionError(fedId, 3, debug); //use the device function and fill the arrays + err->push_back(SiPixelErrorCompact{rawId, ww, error, fedId}); + if (debug) + printf("BPIX1 Error status: %i\n", error); + continue; + } + } + } else { + // ***conversion rules for dcol and pxid + uint32_t dcol = (ww >> pixelgpudetails::DCOL_shift) & pixelgpudetails::DCOL_mask; + uint32_t pxid = (ww >> pixelgpudetails::PXID_shift) & pixelgpudetails::PXID_mask; + uint32_t row = pixelgpudetails::numRowsInRoc - pxid / 2; + uint32_t col = dcol * 2 + pxid % 2; + localPix.row = row; + localPix.col = col; + if (includeErrors and not dcolIsValid(dcol, pxid)) { + uint8_t error = conversionError(fedId, 3, debug); + err->push_back(SiPixelErrorCompact{rawId, ww, error, fedId}); + if (debug) + printf("Error status: %i %d %d %d %d\n", error, dcol, pxid, fedId, roc); + continue; + } + } + + pixelgpudetails::Pixel globalPix = frameConversion(barrel, side, layer, rocIdInDetUnit, localPix); + xx[gIndex] = globalPix.row; // origin shifting by 1 0-159 + yy[gIndex] = globalPix.col; // origin shifting by 1 0-415 + adc[gIndex] = getADC(ww); + pdigi[gIndex] = pixelgpudetails::pack(globalPix.row, globalPix.col, adc[gIndex]); + moduleId[gIndex] = detId.moduleId; + rawIdArr[gIndex] = rawId; + } // end of loop (gIndex < end) + + } // end of Raw to Digi kernel + + __global__ void fillHitsModuleStart(uint32_t const *__restrict__ cluStart, uint32_t *__restrict__ moduleStart) { + assert(gpuClustering::maxNumModules < 2048); // easy to extend at least till 32*1024 + assert(1 == gridDim.x); + assert(0 == blockIdx.x); + + int first = threadIdx.x; + + // limit to maxHitsInModule() + for (int i = first, iend = gpuClustering::maxNumModules; i < iend; i += blockDim.x) { + moduleStart[i + 1] = std::min(gpuClustering::maxHitsInModule(), cluStart[i]); + } + + __shared__ uint32_t ws[32]; + cms::cuda::blockPrefixScan(moduleStart + 1, moduleStart + 1, 1024, ws); + cms::cuda::blockPrefixScan(moduleStart + 1025, moduleStart + 1025, gpuClustering::maxNumModules - 1024, ws); + + for (int i = first + 1025, iend = gpuClustering::maxNumModules + 1; i < iend; i += blockDim.x) { + moduleStart[i] += moduleStart[1024]; + } + __syncthreads(); + +#ifdef GPU_DEBUG + assert(0 == moduleStart[0]); + auto c0 = std::min(gpuClustering::maxHitsInModule(), cluStart[0]); + assert(c0 == moduleStart[1]); + assert(moduleStart[1024] >= moduleStart[1023]); + assert(moduleStart[1025] >= moduleStart[1024]); + assert(moduleStart[gpuClustering::maxNumModules] >= moduleStart[1025]); + + for (int i = first, iend = gpuClustering::maxNumModules + 1; i < iend; i += blockDim.x) { + if (0 != i) + assert(moduleStart[i] >= moduleStart[i - i]); + // [BPX1, BPX2, BPX3, BPX4, FP1, FP2, FP3, FN1, FN2, FN3, LAST_VALID] + // [ 0, 96, 320, 672, 1184, 1296, 1408, 1520, 1632, 1744, 1856] + if (i == 96 || i == 1184 || i == 1744 || i == gpuClustering::maxNumModules) + printf("moduleStart %d %d\n", i, moduleStart[i]); + } +#endif + + // avoid overflow + auto constexpr maxNumClusters = gpuClustering::maxNumClusters; + for (int i = first, iend = gpuClustering::maxNumModules + 1; i < iend; i += blockDim.x) { + moduleStart[i] = std::clamp(moduleStart[i], 0U, maxNumClusters); + } + } + + // Interface to outside + void SiPixelRawToClusterGPUKernel::makeClustersAsync(bool isRun2, + const SiPixelROCsStatusAndMapping *cablingMap, + const unsigned char *modToUnp, + const SiPixelGainForHLTonGPU *gains, + const WordFedAppender &wordFed, + SiPixelFormatterErrors &&errors, + const uint32_t wordCounter, + const uint32_t fedCounter, + bool useQualityInfo, + bool includeErrors, + bool debug, + cudaStream_t stream) { + nDigis = wordCounter; + +#ifdef GPU_DEBUG + std::cout << "decoding " << wordCounter << " digis. Max is " << pixelgpudetails::MAX_FED_WORDS << std::endl; +#endif + + digis_d = SiPixelDigisCUDA(pixelgpudetails::MAX_FED_WORDS, stream); + if (includeErrors) { + digiErrors_d = SiPixelDigiErrorsCUDA(pixelgpudetails::MAX_FED_WORDS, std::move(errors), stream); + } + clusters_d = SiPixelClustersCUDA(gpuClustering::maxNumModules, stream); + + nModules_Clusters_h = cms::cuda::make_host_unique(2, stream); + + if (wordCounter) // protect in case of empty event.... + { + const int threadsPerBlock = 512; + const int blocks = (wordCounter + threadsPerBlock - 1) / threadsPerBlock; // fill it all + + assert(0 == wordCounter % 2); + // wordCounter is the total no of words in each event to be trasfered on device + auto word_d = cms::cuda::make_device_unique(wordCounter, stream); + auto fedId_d = cms::cuda::make_device_unique(wordCounter, stream); + + cudaCheck( + cudaMemcpyAsync(word_d.get(), wordFed.word(), wordCounter * sizeof(uint32_t), cudaMemcpyDefault, stream)); + cudaCheck(cudaMemcpyAsync( + fedId_d.get(), wordFed.fedId(), wordCounter * sizeof(uint8_t) / 2, cudaMemcpyDefault, stream)); + + // Launch rawToDigi kernel + RawToDigi_kernel<<>>( + cablingMap, + modToUnp, + wordCounter, + word_d.get(), + fedId_d.get(), + digis_d.xx(), + digis_d.yy(), + digis_d.adc(), + digis_d.pdigi(), + digis_d.rawIdArr(), + digis_d.moduleInd(), + digiErrors_d.error(), // returns nullptr if default-constructed + useQualityInfo, + includeErrors, + debug); + cudaCheck(cudaGetLastError()); +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + if (includeErrors) { + digiErrors_d.copyErrorToHostAsync(stream); + } + } + // End of Raw2Digi and passing data for clustering + + { + // clusterizer ... + using namespace gpuClustering; + int threadsPerBlock = 256; + int blocks = + (std::max(int(wordCounter), int(gpuClustering::maxNumModules)) + threadsPerBlock - 1) / threadsPerBlock; + + gpuCalibPixel::calibDigis<<>>(isRun2, + digis_d.moduleInd(), + digis_d.xx(), + digis_d.yy(), + digis_d.adc(), + gains, + wordCounter, + clusters_d.moduleStart(), + clusters_d.clusInModule(), + clusters_d.clusModuleStart()); + cudaCheck(cudaGetLastError()); +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + +#ifdef GPU_DEBUG + std::cout << "CUDA countModules kernel launch with " << blocks << " blocks of " << threadsPerBlock + << " threads\n"; +#endif + + countModules<<>>( + digis_d.moduleInd(), clusters_d.moduleStart(), digis_d.clus(), wordCounter); + cudaCheck(cudaGetLastError()); + + // read the number of modules into a data member, used by getProduct()) + cudaCheck(cudaMemcpyAsync( + &(nModules_Clusters_h[0]), clusters_d.moduleStart(), sizeof(uint32_t), cudaMemcpyDefault, stream)); + + threadsPerBlock = 256; + blocks = maxNumModules; +#ifdef GPU_DEBUG + std::cout << "CUDA findClus kernel launch with " << blocks << " blocks of " << threadsPerBlock << " threads\n"; +#endif + findClus<<>>(digis_d.moduleInd(), + digis_d.xx(), + digis_d.yy(), + clusters_d.moduleStart(), + clusters_d.clusInModule(), + clusters_d.moduleId(), + digis_d.clus(), + wordCounter); + cudaCheck(cudaGetLastError()); +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + // apply charge cut + clusterChargeCut<<>>(digis_d.moduleInd(), + digis_d.adc(), + clusters_d.moduleStart(), + clusters_d.clusInModule(), + clusters_d.moduleId(), + digis_d.clus(), + wordCounter); + cudaCheck(cudaGetLastError()); + + // count the module start indices already here (instead of + // rechits) so that the number of clusters/hits can be made + // available in the rechit producer without additional points of + // synchronization/ExternalWork + + // MUST be ONE block + fillHitsModuleStart<<<1, 1024, 0, stream>>>(clusters_d.clusInModule(), clusters_d.clusModuleStart()); + + // last element holds the number of all clusters + cudaCheck(cudaMemcpyAsync(&(nModules_Clusters_h[1]), + clusters_d.clusModuleStart() + gpuClustering::maxNumModules, + sizeof(uint32_t), + cudaMemcpyDefault, + stream)); + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + } // end clusterizer scope + } +} // namespace pixelgpudetails diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.h b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.h new file mode 100644 index 0000000000000..75eeab2606dd5 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.h @@ -0,0 +1,212 @@ +#ifndef RecoLocalTracker_SiPixelClusterizer_plugins_SiPixelRawToClusterGPUKernel_h +#define RecoLocalTracker_SiPixelClusterizer_plugins_SiPixelRawToClusterGPUKernel_h + +#include +#include + +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigiErrorsCUDA.h" +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "FWCore/Utilities/interface/typedefs.h" +#include "HeterogeneousCore/CUDAUtilities/interface/SimpleVector.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/host_noncached_unique_ptr.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelErrorCompact.h" +#include "DataFormats/SiPixelRawData/interface/SiPixelFormatterErrors.h" + +struct SiPixelROCsStatusAndMapping; +class SiPixelGainForHLTonGPU; + +namespace pixelgpudetails { + + // Phase 1 geometry constants + const uint32_t layerStartBit = 20; + const uint32_t ladderStartBit = 12; + const uint32_t moduleStartBit = 2; + + const uint32_t panelStartBit = 10; + const uint32_t diskStartBit = 18; + const uint32_t bladeStartBit = 12; + + const uint32_t layerMask = 0xF; + const uint32_t ladderMask = 0xFF; + const uint32_t moduleMask = 0x3FF; + const uint32_t panelMask = 0x3; + const uint32_t diskMask = 0xF; + const uint32_t bladeMask = 0x3F; + + const uint32_t LINK_bits = 6; + const uint32_t ROC_bits = 5; + const uint32_t DCOL_bits = 5; + const uint32_t PXID_bits = 8; + const uint32_t ADC_bits = 8; + + // special for layer 1 + const uint32_t LINK_bits_l1 = 6; + const uint32_t ROC_bits_l1 = 5; + const uint32_t COL_bits_l1 = 6; + const uint32_t ROW_bits_l1 = 7; + const uint32_t OMIT_ERR_bits = 1; + + const uint32_t maxROCIndex = 8; + const uint32_t numRowsInRoc = 80; + const uint32_t numColsInRoc = 52; + + const uint32_t MAX_WORD = 2000; + + const uint32_t ADC_shift = 0; + const uint32_t PXID_shift = ADC_shift + ADC_bits; + const uint32_t DCOL_shift = PXID_shift + PXID_bits; + const uint32_t ROC_shift = DCOL_shift + DCOL_bits; + const uint32_t LINK_shift = ROC_shift + ROC_bits_l1; + // special for layer 1 ROC + const uint32_t ROW_shift = ADC_shift + ADC_bits; + const uint32_t COL_shift = ROW_shift + ROW_bits_l1; + const uint32_t OMIT_ERR_shift = 20; + + const uint32_t LINK_mask = ~(~uint32_t(0) << LINK_bits_l1); + const uint32_t ROC_mask = ~(~uint32_t(0) << ROC_bits_l1); + const uint32_t COL_mask = ~(~uint32_t(0) << COL_bits_l1); + const uint32_t ROW_mask = ~(~uint32_t(0) << ROW_bits_l1); + const uint32_t DCOL_mask = ~(~uint32_t(0) << DCOL_bits); + const uint32_t PXID_mask = ~(~uint32_t(0) << PXID_bits); + const uint32_t ADC_mask = ~(~uint32_t(0) << ADC_bits); + const uint32_t ERROR_mask = ~(~uint32_t(0) << ROC_bits_l1); + const uint32_t OMIT_ERR_mask = ~(~uint32_t(0) << OMIT_ERR_bits); + + struct DetIdGPU { + uint32_t rawId; + uint32_t rocInDet; + uint32_t moduleId; + }; + + struct Pixel { + uint32_t row; + uint32_t col; + }; + + class Packing { + public: + using PackedDigiType = uint32_t; + + // Constructor: pre-computes masks and shifts from field widths + __host__ __device__ inline constexpr Packing(unsigned int row_w, + unsigned int column_w, + unsigned int time_w, + unsigned int adc_w) + : row_width(row_w), + column_width(column_w), + adc_width(adc_w), + row_shift(0), + column_shift(row_shift + row_w), + time_shift(column_shift + column_w), + adc_shift(time_shift + time_w), + row_mask(~(~0U << row_w)), + column_mask(~(~0U << column_w)), + time_mask(~(~0U << time_w)), + adc_mask(~(~0U << adc_w)), + rowcol_mask(~(~0U << (column_w + row_w))), + max_row(row_mask), + max_column(column_mask), + max_adc(adc_mask) {} + + uint32_t row_width; + uint32_t column_width; + uint32_t adc_width; + + uint32_t row_shift; + uint32_t column_shift; + uint32_t time_shift; + uint32_t adc_shift; + + PackedDigiType row_mask; + PackedDigiType column_mask; + PackedDigiType time_mask; + PackedDigiType adc_mask; + PackedDigiType rowcol_mask; + + uint32_t max_row; + uint32_t max_column; + uint32_t max_adc; + }; + + __host__ __device__ inline constexpr Packing packing() { return Packing(11, 11, 0, 10); } + + __host__ __device__ inline uint32_t pack(uint32_t row, uint32_t col, uint32_t adc) { + constexpr Packing thePacking = packing(); + adc = std::min(adc, thePacking.max_adc); + + return (row << thePacking.row_shift) | (col << thePacking.column_shift) | (adc << thePacking.adc_shift); + } + + constexpr uint32_t pixelToChannel(int row, int col) { + constexpr Packing thePacking = packing(); + return (row << thePacking.column_width) | col; + } + + class SiPixelRawToClusterGPUKernel { + public: + class WordFedAppender { + public: + WordFedAppender(); + ~WordFedAppender() = default; + + void initializeWordFed(int fedId, unsigned int wordCounterGPU, const cms_uint32_t* src, unsigned int length); + + const unsigned int* word() const { return word_.get(); } + const unsigned char* fedId() const { return fedId_.get(); } + + private: + cms::cuda::host::noncached::unique_ptr word_; + cms::cuda::host::noncached::unique_ptr fedId_; + }; + + SiPixelRawToClusterGPUKernel() = default; + ~SiPixelRawToClusterGPUKernel() = default; + + SiPixelRawToClusterGPUKernel(const SiPixelRawToClusterGPUKernel&) = delete; + SiPixelRawToClusterGPUKernel(SiPixelRawToClusterGPUKernel&&) = delete; + SiPixelRawToClusterGPUKernel& operator=(const SiPixelRawToClusterGPUKernel&) = delete; + SiPixelRawToClusterGPUKernel& operator=(SiPixelRawToClusterGPUKernel&&) = delete; + + void makeClustersAsync(bool isRun2, + const SiPixelROCsStatusAndMapping* cablingMap, + const unsigned char* modToUnp, + const SiPixelGainForHLTonGPU* gains, + const WordFedAppender& wordFed, + SiPixelFormatterErrors&& errors, + const uint32_t wordCounter, + const uint32_t fedCounter, + bool useQualityInfo, + bool includeErrors, + bool debug, + cudaStream_t stream); + + std::pair getResults() { + digis_d.setNModulesDigis(nModules_Clusters_h[0], nDigis); + clusters_d.setNClusters(nModules_Clusters_h[1]); + // need to explicitly deallocate while the associated CUDA + // stream is still alive + // + // technically the statement above is not true anymore now that + // the CUDA streams are cached within the cms::cuda::StreamCache, but it is + // still better to release as early as possible + nModules_Clusters_h.reset(); + return std::make_pair(std::move(digis_d), std::move(clusters_d)); + } + + SiPixelDigiErrorsCUDA&& getErrors() { return std::move(digiErrors_d); } + + private: + uint32_t nDigis = 0; + + // Data to be put in the event + cms::cuda::host::unique_ptr nModules_Clusters_h; + SiPixelDigisCUDA digis_d; + SiPixelClustersCUDA clusters_d; + SiPixelDigiErrorsCUDA digiErrors_d; + }; + +} // namespace pixelgpudetails + +#endif // RecoLocalTracker_SiPixelClusterizer_plugins_SiPixelRawToClusterGPUKernel_h diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuCalibPixel.h b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuCalibPixel.h new file mode 100644 index 0000000000000..c21c792f39c30 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuCalibPixel.h @@ -0,0 +1,68 @@ +#ifndef RecoLocalTracker_SiPixelClusterizer_plugins_gpuCalibPixel_h +#define RecoLocalTracker_SiPixelClusterizer_plugins_gpuCalibPixel_h + +#include +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelGainForHLTonGPU.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +namespace gpuCalibPixel { + + using gpuClustering::invalidModuleId; + + // valid for run2 + constexpr float VCaltoElectronGain = 47; // L2-4: 47 +- 4.7 + constexpr float VCaltoElectronGain_L1 = 50; // L1: 49.6 +- 2.6 + constexpr float VCaltoElectronOffset = -60; // L2-4: -60 +- 130 + constexpr float VCaltoElectronOffset_L1 = -670; // L1: -670 +- 220 + + __global__ void calibDigis(bool isRun2, + uint16_t* id, + uint16_t const* __restrict__ x, + uint16_t const* __restrict__ y, + uint16_t* adc, + SiPixelGainForHLTonGPU const* __restrict__ ped, + int numElements, + uint32_t* __restrict__ moduleStart, // just to zero first + uint32_t* __restrict__ nClustersInModule, // just to zero them + uint32_t* __restrict__ clusModuleStart // just to zero first + ) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + + // zero for next kernels... + if (0 == first) + clusModuleStart[0] = moduleStart[0] = 0; + for (int i = first; i < gpuClustering::maxNumModules; i += gridDim.x * blockDim.x) { + nClustersInModule[i] = 0; + } + + for (int i = first; i < numElements; i += gridDim.x * blockDim.x) { + if (invalidModuleId == id[i]) + continue; + + float conversionFactor = (isRun2) ? (id[i] < 96 ? VCaltoElectronGain_L1 : VCaltoElectronGain) : 1.f; + float offset = (isRun2) ? (id[i] < 96 ? VCaltoElectronOffset_L1 : VCaltoElectronOffset) : 0; + + bool isDeadColumn = false, isNoisyColumn = false; + + int row = x[i]; + int col = y[i]; + auto ret = ped->getPedAndGain(id[i], col, row, isDeadColumn, isNoisyColumn); + float pedestal = ret.first; + float gain = ret.second; + // float pedestal = 0; float gain = 1.; + if (isDeadColumn | isNoisyColumn) { + id[i] = invalidModuleId; + adc[i] = 0; + printf("bad pixel at %d in %d\n", i, id[i]); + } else { + float vcal = adc[i] * gain - pedestal * gain; + adc[i] = std::max(100, int(vcal * conversionFactor + offset)); + } + } + } +} // namespace gpuCalibPixel + +#endif // RecoLocalTracker_SiPixelClusterizer_plugins_gpuCalibPixel_h diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClusterChargeCut.h b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClusterChargeCut.h new file mode 100644 index 0000000000000..d9520da80b695 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClusterChargeCut.h @@ -0,0 +1,125 @@ +#ifndef RecoLocalTracker_SiPixelClusterizer_plugins_gpuClusterChargeCut_h +#define RecoLocalTracker_SiPixelClusterizer_plugins_gpuClusterChargeCut_h + +#include +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "HeterogeneousCore/CUDAUtilities/interface/prefixScan.h" + +namespace gpuClustering { + + __global__ void clusterChargeCut( + uint16_t* __restrict__ id, // module id of each pixel (modified if bad cluster) + uint16_t const* __restrict__ adc, // charge of each pixel + uint32_t const* __restrict__ moduleStart, // index of the first pixel of each module + uint32_t* __restrict__ nClustersInModule, // modified: number of clusters found in each module + uint32_t const* __restrict__ moduleId, // module id of each module + int32_t* __restrict__ clusterId, // modified: cluster id of each pixel + uint32_t numElements) { + __shared__ int32_t charge[maxNumClustersPerModules]; + __shared__ uint8_t ok[maxNumClustersPerModules]; + __shared__ uint16_t newclusId[maxNumClustersPerModules]; + + auto firstModule = blockIdx.x; + auto endModule = moduleStart[0]; + for (auto module = firstModule; module < endModule; module += gridDim.x) { + auto firstPixel = moduleStart[1 + module]; + auto thisModuleId = id[firstPixel]; + assert(thisModuleId < maxNumModules); + assert(thisModuleId == moduleId[module]); + + auto nclus = nClustersInModule[thisModuleId]; + if (nclus == 0) + continue; + + if (threadIdx.x == 0 && nclus > maxNumClustersPerModules) + printf("Warning too many clusters in module %d in block %d: %d > %d\n", + thisModuleId, + blockIdx.x, + nclus, + maxNumClustersPerModules); + + auto first = firstPixel + threadIdx.x; + + if (nclus > maxNumClustersPerModules) { + // remove excess FIXME find a way to cut charge first.... + for (auto i = first; i < numElements; i += blockDim.x) { + if (id[i] == invalidModuleId) + continue; // not valid + if (id[i] != thisModuleId) + break; // end of module + if (clusterId[i] >= maxNumClustersPerModules) { + id[i] = invalidModuleId; + clusterId[i] = invalidModuleId; + } + } + nclus = maxNumClustersPerModules; + } + +#ifdef GPU_DEBUG + if (thisModuleId % 100 == 1) + if (threadIdx.x == 0) + printf("start cluster charge cut for module %d in block %d\n", thisModuleId, blockIdx.x); +#endif + + assert(nclus <= maxNumClustersPerModules); + for (auto i = threadIdx.x; i < nclus; i += blockDim.x) { + charge[i] = 0; + } + __syncthreads(); + + for (auto i = first; i < numElements; i += blockDim.x) { + if (id[i] == invalidModuleId) + continue; // not valid + if (id[i] != thisModuleId) + break; // end of module + atomicAdd(&charge[clusterId[i]], adc[i]); + } + __syncthreads(); + + auto chargeCut = thisModuleId < 96 ? 2000 : 4000; // move in constants (calib?) + for (auto i = threadIdx.x; i < nclus; i += blockDim.x) { + newclusId[i] = ok[i] = charge[i] > chargeCut ? 1 : 0; + } + + __syncthreads(); + + // renumber + __shared__ uint16_t ws[32]; + cms::cuda::blockPrefixScan(newclusId, nclus, ws); + + assert(nclus >= newclusId[nclus - 1]); + + if (nclus == newclusId[nclus - 1]) + continue; + + nClustersInModule[thisModuleId] = newclusId[nclus - 1]; + __syncthreads(); + + // mark bad cluster again + for (auto i = threadIdx.x; i < nclus; i += blockDim.x) { + if (0 == ok[i]) + newclusId[i] = invalidModuleId + 1; + } + __syncthreads(); + + // reassign id + for (auto i = first; i < numElements; i += blockDim.x) { + if (id[i] == invalidModuleId) + continue; // not valid + if (id[i] != thisModuleId) + break; // end of module + clusterId[i] = newclusId[clusterId[i]] - 1; + if (clusterId[i] == invalidModuleId) + id[i] = invalidModuleId; + } + + //done + } // loop on modules + } + +} // namespace gpuClustering + +#endif // RecoLocalTracker_SiPixelClusterizer_plugins_gpuClusterChargeCut_h diff --git a/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h new file mode 100644 index 0000000000000..9f295981ca732 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h @@ -0,0 +1,305 @@ +#ifndef RecoLocalTracker_SiPixelClusterizer_plugins_gpuClustering_h +#define RecoLocalTracker_SiPixelClusterizer_plugins_gpuClustering_h + +#include +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +namespace gpuClustering { + +#ifdef GPU_DEBUG + __device__ uint32_t gMaxHit = 0; +#endif + + __global__ void countModules(uint16_t const* __restrict__ id, + uint32_t* __restrict__ moduleStart, + int32_t* __restrict__ clusterId, + int numElements) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int i = first; i < numElements; i += gridDim.x * blockDim.x) { + clusterId[i] = i; + if (invalidModuleId == id[i]) + continue; + auto j = i - 1; + while (j >= 0 and id[j] == invalidModuleId) + --j; + if (j < 0 or id[j] != id[i]) { + // boundary... + auto loc = atomicInc(moduleStart, maxNumModules); + moduleStart[loc + 1] = i; + } + } + } + + __global__ + void + findClus(uint16_t const* __restrict__ id, // module id of each pixel + uint16_t const* __restrict__ x, // local coordinates of each pixel + uint16_t const* __restrict__ y, // + uint32_t const* __restrict__ moduleStart, // index of the first pixel of each module + uint32_t* __restrict__ nClustersInModule, // output: number of clusters found in each module + uint32_t* __restrict__ moduleId, // output: module id of each module + int32_t* __restrict__ clusterId, // output: cluster id of each pixel + int numElements) { + __shared__ int msize; + + auto firstModule = blockIdx.x; + auto endModule = moduleStart[0]; + for (auto module = firstModule; module < endModule; module += gridDim.x) { + auto firstPixel = moduleStart[1 + module]; + auto thisModuleId = id[firstPixel]; + assert(thisModuleId < maxNumModules); + +#ifdef GPU_DEBUG + if (thisModuleId % 100 == 1) + if (threadIdx.x == 0) + printf("start clusterizer for module %d in block %d\n", thisModuleId, blockIdx.x); +#endif + + auto first = firstPixel + threadIdx.x; + + // find the index of the first pixel not belonging to this module (or invalid) + msize = numElements; + __syncthreads(); + + // skip threads not associated to an existing pixel + for (int i = first; i < numElements; i += blockDim.x) { + if (id[i] == invalidModuleId) // skip invalid pixels + continue; + if (id[i] != thisModuleId) { // find the first pixel in a different module + atomicMin(&msize, i); + break; + } + } + + //init hist (ymax=416 < 512 : 9bits) + constexpr uint32_t maxPixInModule = 4000; + constexpr auto nbins = phase1PixelTopology::numColsInModule + 2; //2+2; + using Hist = cms::cuda::HistoContainer; + __shared__ Hist hist; + __shared__ typename Hist::Counter ws[32]; + for (auto j = threadIdx.x; j < Hist::totbins(); j += blockDim.x) { + hist.off[j] = 0; + } + __syncthreads(); + + assert((msize == numElements) or ((msize < numElements) and (id[msize] != thisModuleId))); + + // limit to maxPixInModule (FIXME if recurrent (and not limited to simulation with low threshold) one will need to implement something cleverer) + if (0 == threadIdx.x) { + if (msize - firstPixel > maxPixInModule) { + printf("too many pixels in module %d: %d > %d\n", thisModuleId, msize - firstPixel, maxPixInModule); + msize = maxPixInModule + firstPixel; + } + } + + __syncthreads(); + assert(msize - firstPixel <= maxPixInModule); + +#ifdef GPU_DEBUG + __shared__ uint32_t totGood; + totGood = 0; + __syncthreads(); +#endif + + // fill histo + for (int i = first; i < msize; i += blockDim.x) { + if (id[i] == invalidModuleId) // skip invalid pixels + continue; + hist.count(y[i]); +#ifdef GPU_DEBUG + atomicAdd(&totGood, 1); +#endif + } + __syncthreads(); + if (threadIdx.x < 32) + ws[threadIdx.x] = 0; // used by prefix scan... + __syncthreads(); + hist.finalize(ws); + __syncthreads(); +#ifdef GPU_DEBUG + assert(hist.size() == totGood); + if (thisModuleId % 100 == 1) + if (threadIdx.x == 0) + printf("histo size %d\n", hist.size()); +#endif + for (int i = first; i < msize; i += blockDim.x) { + if (id[i] == invalidModuleId) // skip invalid pixels + continue; + hist.fill(y[i], i - firstPixel); + } + +#ifdef __CUDA_ARCH__ + // assume that we can cover the whole module with up to 16 blockDim.x-wide iterations + constexpr int maxiter = 16; +#else + auto maxiter = hist.size(); +#endif + // allocate space for duplicate pixels: a pixel can appear more than once with different charge in the same event + constexpr int maxNeighbours = 10; + assert((hist.size() / blockDim.x) <= maxiter); + // nearest neighbour + uint16_t nn[maxiter][maxNeighbours]; + uint8_t nnn[maxiter]; // number of nn + for (uint32_t k = 0; k < maxiter; ++k) + nnn[k] = 0; + + __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; j < Hist::nbins(); j += blockDim.x) { + if (hist.size(j) > 60) + 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 + + // fill NN + for (auto j = threadIdx.x, k = 0U; j < hist.size(); j += blockDim.x, ++k) { + assert(k < maxiter); + auto p = hist.begin() + j; + auto i = *p + firstPixel; + assert(id[i] != invalidModuleId); + assert(id[i] == thisModuleId); // same module + int be = Hist::bin(y[i] + 1); + auto e = hist.end(be); + ++p; + assert(0 == nnn[k]); + for (; p < e; ++p) { + auto m = (*p) + firstPixel; + assert(m != i); + assert(int(y[m]) - int(y[i]) >= 0); + assert(int(y[m]) - int(y[i]) <= 1); + if (std::abs(int(x[m]) - int(x[i])) > 1) + continue; + auto l = nnn[k]++; + assert(l < maxNeighbours); + nn[k][l] = *p; + } + } + + // 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 + // pixel in the cluster ( clus[i] == i ). + bool more = true; + int nloops = 0; + while (__syncthreads_or(more)) { + if (1 == nloops % 2) { + for (auto j = threadIdx.x, k = 0U; j < hist.size(); j += blockDim.x, ++k) { + auto p = hist.begin() + j; + auto i = *p + firstPixel; + auto m = clusterId[i]; + while (m != clusterId[m]) + m = clusterId[m]; + clusterId[i] = m; + } + } else { + more = false; + for (auto j = threadIdx.x, k = 0U; j < hist.size(); j += blockDim.x, ++k) { + auto p = hist.begin() + j; + auto i = *p + firstPixel; + for (int kk = 0; kk < nnn[k]; ++kk) { + auto l = nn[k][kk]; + auto m = l + firstPixel; + assert(m != i); + auto old = atomicMin(&clusterId[m], clusterId[i]); + if (old != clusterId[i]) { + // end the loop only if no changes were applied + more = true; + } + atomicMin(&clusterId[i], old); + } // nnloop + } // pixel loop + } + ++nloops; + } // end while + +#ifdef GPU_DEBUG + { + __shared__ int n0; + if (threadIdx.x == 0) + n0 = nloops; + __syncthreads(); + auto ok = n0 == nloops; + assert(__syncthreads_and(ok)); + if (thisModuleId % 100 == 1) + if (threadIdx.x == 0) + printf("# loops %d\n", nloops); + } +#endif + + __shared__ unsigned int foundClusters; + foundClusters = 0; + __syncthreads(); + + // find the number of different clusters, identified by a pixels with clus[i] == i; + // mark these pixels with a negative id. + for (int i = first; i < msize; i += blockDim.x) { + if (id[i] == invalidModuleId) // skip invalid pixels + continue; + if (clusterId[i] == i) { + auto old = atomicInc(&foundClusters, 0xffffffff); + clusterId[i] = -(old + 1); + } + } + __syncthreads(); + + // propagate the negative id to all the pixels in the cluster. + for (int i = first; i < msize; i += blockDim.x) { + if (id[i] == invalidModuleId) // skip invalid pixels + continue; + if (clusterId[i] >= 0) { + // mark each pixel in a cluster with the same id as the first one + clusterId[i] = clusterId[clusterId[i]]; + } + } + __syncthreads(); + + // adjust the cluster id to be a positive value starting from 0 + for (int i = first; i < msize; i += blockDim.x) { + if (id[i] == invalidModuleId) { // skip invalid pixels + clusterId[i] = -9999; + continue; + } + clusterId[i] = -clusterId[i] - 1; + } + __syncthreads(); + + if (threadIdx.x == 0) { + nClustersInModule[thisModuleId] = foundClusters; + moduleId[module] = thisModuleId; +#ifdef GPU_DEBUG + if (foundClusters > gMaxHit) { + gMaxHit = foundClusters; + if (foundClusters > 8) + printf("max hit %d in %d\n", foundClusters, thisModuleId); + } +#endif +#ifdef GPU_DEBUG + if (thisModuleId % 100 == 1) + printf("%d clusters in module %d\n", foundClusters, thisModuleId); +#endif + } + } // module loop + } +} // namespace gpuClustering + +#endif // RecoLocalTracker_SiPixelClusterizer_plugins_gpuClustering_h diff --git a/RecoLocalTracker/SiPixelClusterizer/python/SiPixelClusterizerPreSplitting_cfi.py b/RecoLocalTracker/SiPixelClusterizer/python/SiPixelClusterizerPreSplitting_cfi.py index ba8d492c5f610..b9c6862b015bf 100644 --- a/RecoLocalTracker/SiPixelClusterizer/python/SiPixelClusterizerPreSplitting_cfi.py +++ b/RecoLocalTracker/SiPixelClusterizer/python/SiPixelClusterizerPreSplitting_cfi.py @@ -1,7 +1,17 @@ - import FWCore.ParameterSet.Config as cms -# from CondTools.SiPixel.SiPixelGainCalibrationService_cfi import * from RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizer_cfi import siPixelClusters as _siPixelClusters -siPixelClustersPreSplitting = _siPixelClusters.clone() +from HeterogeneousCore.CUDACore.SwitchProducerCUDA import SwitchProducerCUDA +siPixelClustersPreSplitting = SwitchProducerCUDA( + cpu = _siPixelClusters.clone() +) + +from Configuration.ProcessModifiers.gpu_cff import gpu +gpu.toModify(siPixelClustersPreSplitting, + cuda = cms.EDAlias( + siPixelDigisClustersPreSplitting = cms.VPSet( + cms.PSet(type = cms.string("SiPixelClusteredmNewDetSetVector")) + ) + ) +) diff --git a/RecoLocalTracker/SiPixelClusterizer/python/siPixelClustersPreSplitting_cff.py b/RecoLocalTracker/SiPixelClusterizer/python/siPixelClustersPreSplitting_cff.py new file mode 100644 index 0000000000000..8bbf47e9ebf90 --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/python/siPixelClustersPreSplitting_cff.py @@ -0,0 +1,26 @@ +import FWCore.ParameterSet.Config as cms + +from RecoLocalTracker.SiPixelClusterizer.SiPixelClusterizerPreSplitting_cfi import siPixelClustersPreSplitting +from RecoLocalTracker.SiPixelClusterizer.siPixelRawToClusterCUDA_cfi import siPixelRawToClusterCUDA as _siPixelRawToClusterCUDA +from RecoLocalTracker.SiPixelClusterizer.siPixelDigisClustersFromSoA_cfi import siPixelDigisClustersFromSoA as _siPixelDigisClustersFromSoA +from CalibTracker.SiPixelESProducers.siPixelROCsStatusAndMappingWrapperESProducer_cfi import * +from CalibTracker.SiPixelESProducers.siPixelGainCalibrationForHLTGPU_cfi import * + +siPixelClustersPreSplittingTask = cms.Task(siPixelClustersPreSplitting) + +siPixelClustersPreSplittingCUDA = _siPixelRawToClusterCUDA.clone() +from Configuration.Eras.Modifier_run3_common_cff import run3_common +run3_common.toModify(siPixelClustersPreSplittingCUDA, + isRun2=False +) + +siPixelDigisClustersPreSplitting = _siPixelDigisClustersFromSoA.clone() +siPixelClustersPreSplittingTaskCUDA = cms.Task( + siPixelClustersPreSplittingCUDA, + siPixelDigisClustersPreSplitting, +) + +from Configuration.ProcessModifiers.gpu_cff import gpu +_siPixelClustersPreSplittingTask_gpu = siPixelClustersPreSplittingTask.copy() +_siPixelClustersPreSplittingTask_gpu.add(siPixelClustersPreSplittingTaskCUDA) +gpu.toReplaceWith(siPixelClustersPreSplittingTask, _siPixelClustersPreSplittingTask_gpu) diff --git a/RecoLocalTracker/SiPixelClusterizer/test/BuildFile.xml b/RecoLocalTracker/SiPixelClusterizer/test/BuildFile.xml index e4a31cc26cd56..4420adb507027 100644 --- a/RecoLocalTracker/SiPixelClusterizer/test/BuildFile.xml +++ b/RecoLocalTracker/SiPixelClusterizer/test/BuildFile.xml @@ -16,6 +16,7 @@ + @@ -31,3 +32,26 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RecoLocalTracker/SiPixelClusterizer/test/cpuClustering_t.cpp b/RecoLocalTracker/SiPixelClusterizer/test/cpuClustering_t.cpp new file mode 100644 index 0000000000000..19a3b8d014c9c --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/test/cpuClustering_t.cpp @@ -0,0 +1 @@ +#include "gpuClustering_t.h" diff --git a/RecoLocalTracker/SiPixelClusterizer/test/gpuClustering_t.cu b/RecoLocalTracker/SiPixelClusterizer/test/gpuClustering_t.cu new file mode 100644 index 0000000000000..19a3b8d014c9c --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/test/gpuClustering_t.cu @@ -0,0 +1 @@ +#include "gpuClustering_t.h" diff --git a/RecoLocalTracker/SiPixelClusterizer/test/gpuClustering_t.h b/RecoLocalTracker/SiPixelClusterizer/test/gpuClustering_t.h new file mode 100644 index 0000000000000..02611ab1cac1d --- /dev/null +++ b/RecoLocalTracker/SiPixelClusterizer/test/gpuClustering_t.h @@ -0,0 +1,382 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CUDACC__ +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "HeterogeneousCore/CUDAUtilities/interface/launch.h" +#include "HeterogeneousCore/CUDAUtilities/interface/requireDevices.h" +#endif // __CUDACC__ + +#include "RecoLocalTracker/SiPixelClusterizer/plugins/gpuClustering.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/gpuClusterChargeCut.h" + +int main(void) { +#ifdef __CUDACC__ + cms::cudatest::requireDevices(); +#endif // __CUDACC__ + + using namespace gpuClustering; + + constexpr int numElements = 256 * maxNumModules; + + // these in reality are already on GPU + auto h_id = std::make_unique(numElements); + auto h_x = std::make_unique(numElements); + auto h_y = std::make_unique(numElements); + auto h_adc = std::make_unique(numElements); + auto h_clus = std::make_unique(numElements); + +#ifdef __CUDACC__ + auto d_id = cms::cuda::make_device_unique(numElements, nullptr); + auto d_x = cms::cuda::make_device_unique(numElements, nullptr); + auto d_y = cms::cuda::make_device_unique(numElements, nullptr); + auto d_adc = cms::cuda::make_device_unique(numElements, nullptr); + auto d_clus = cms::cuda::make_device_unique(numElements, nullptr); + auto d_moduleStart = cms::cuda::make_device_unique(maxNumModules + 1, nullptr); + auto d_clusInModule = cms::cuda::make_device_unique(maxNumModules, nullptr); + auto d_moduleId = cms::cuda::make_device_unique(maxNumModules, nullptr); +#else // __CUDACC__ + auto h_moduleStart = std::make_unique(maxNumModules + 1); + auto h_clusInModule = std::make_unique(maxNumModules); + auto h_moduleId = std::make_unique(maxNumModules); +#endif // __CUDACC__ + + // later random number + int n = 0; + int ncl = 0; + int y[10] = {5, 7, 9, 1, 3, 0, 4, 8, 2, 6}; + + auto generateClusters = [&](int kn) { + auto addBigNoise = 1 == kn % 2; + if (addBigNoise) { + constexpr int MaxPixels = 1000; + int id = 666; + for (int x = 0; x < 140; x += 3) { + for (int yy = 0; yy < 400; yy += 3) { + h_id[n] = id; + h_x[n] = x; + h_y[n] = yy; + h_adc[n] = 1000; + ++n; + ++ncl; + if (MaxPixels <= ncl) + break; + } + if (MaxPixels <= ncl) + break; + } + } + + { + // isolated + int id = 42; + int x = 10; + ++ncl; + h_id[n] = id; + h_x[n] = x; + h_y[n] = x; + h_adc[n] = kn == 0 ? 100 : 5000; + ++n; + + // first column + ++ncl; + h_id[n] = id; + h_x[n] = x; + h_y[n] = 0; + h_adc[n] = 5000; + ++n; + // first columns + ++ncl; + h_id[n] = id; + h_x[n] = x + 80; + h_y[n] = 2; + h_adc[n] = 5000; + ++n; + h_id[n] = id; + h_x[n] = x + 80; + h_y[n] = 1; + h_adc[n] = 5000; + ++n; + + // last column + ++ncl; + h_id[n] = id; + h_x[n] = x; + h_y[n] = 415; + h_adc[n] = 5000; + ++n; + // last columns + ++ncl; + h_id[n] = id; + h_x[n] = x + 80; + h_y[n] = 415; + h_adc[n] = 2500; + ++n; + h_id[n] = id; + h_x[n] = x + 80; + h_y[n] = 414; + h_adc[n] = 2500; + ++n; + + // diagonal + ++ncl; + for (int x = 20; x < 25; ++x) { + h_id[n] = id; + h_x[n] = x; + h_y[n] = x; + h_adc[n] = 1000; + ++n; + } + ++ncl; + // reversed + for (int x = 45; x > 40; --x) { + h_id[n] = id; + h_x[n] = x; + h_y[n] = x; + h_adc[n] = 1000; + ++n; + } + ++ncl; + h_id[n++] = invalidModuleId; // error + // messy + int xx[5] = {21, 25, 23, 24, 22}; + for (int k = 0; k < 5; ++k) { + h_id[n] = id; + h_x[n] = xx[k]; + h_y[n] = 20 + xx[k]; + h_adc[n] = 1000; + ++n; + } + // holes + ++ncl; + for (int k = 0; k < 5; ++k) { + h_id[n] = id; + h_x[n] = xx[k]; + h_y[n] = 100; + h_adc[n] = kn == 2 ? 100 : 1000; + ++n; + if (xx[k] % 2 == 0) { + h_id[n] = id; + h_x[n] = xx[k]; + h_y[n] = 101; + h_adc[n] = 1000; + ++n; + } + } + } + { + // id == 0 (make sure it works! + int id = 0; + int x = 10; + ++ncl; + h_id[n] = id; + h_x[n] = x; + h_y[n] = x; + h_adc[n] = 5000; + ++n; + } + // all odd id + for (int id = 11; id <= 1800; id += 2) { + if ((id / 20) % 2) + h_id[n++] = invalidModuleId; // error + for (int x = 0; x < 40; x += 4) { + ++ncl; + if ((id / 10) % 2) { + for (int k = 0; k < 10; ++k) { + h_id[n] = id; + h_x[n] = x; + h_y[n] = x + y[k]; + h_adc[n] = 100; + ++n; + h_id[n] = id; + h_x[n] = x + 1; + h_y[n] = x + y[k] + 2; + h_adc[n] = 1000; + ++n; + } + } else { + for (int k = 0; k < 10; ++k) { + h_id[n] = id; + h_x[n] = x; + h_y[n] = x + y[9 - k]; + h_adc[n] = kn == 2 ? 10 : 1000; + ++n; + if (y[k] == 3) + continue; // hole + if (id == 51) { + h_id[n++] = invalidModuleId; + h_id[n++] = invalidModuleId; + } // error + h_id[n] = id; + h_x[n] = x + 1; + h_y[n] = x + y[k] + 2; + h_adc[n] = kn == 2 ? 10 : 1000; + ++n; + } + } + } + } + }; // end lambda + for (auto kkk = 0; kkk < 5; ++kkk) { + n = 0; + ncl = 0; + generateClusters(kkk); + + std::cout << "created " << n << " digis in " << ncl << " clusters" << std::endl; + assert(n <= numElements); + + uint32_t nModules = 0; +#ifdef __CUDACC__ + size_t size32 = n * sizeof(unsigned int); + size_t size16 = n * sizeof(unsigned short); + // size_t size8 = n * sizeof(uint8_t); + + cudaCheck(cudaMemcpy(d_moduleStart.get(), &nModules, sizeof(uint32_t), cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(d_id.get(), h_id.get(), size16, cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(d_x.get(), h_x.get(), size16, cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(d_y.get(), h_y.get(), size16, cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(d_adc.get(), h_adc.get(), size16, cudaMemcpyHostToDevice)); + + // Launch CUDA Kernels + int threadsPerBlock = (kkk == 5) ? 512 : ((kkk == 3) ? 128 : 256); + int blocksPerGrid = (numElements + threadsPerBlock - 1) / threadsPerBlock; + std::cout << "CUDA countModules kernel launch with " << blocksPerGrid << " blocks of " << threadsPerBlock + << " threads\n"; + + cms::cuda::launch(countModules, {blocksPerGrid, threadsPerBlock}, d_id.get(), d_moduleStart.get(), d_clus.get(), n); + + blocksPerGrid = maxNumModules; //nModules; + + std::cout << "CUDA findModules kernel launch with " << blocksPerGrid << " blocks of " << threadsPerBlock + << " threads\n"; + cudaCheck(cudaMemset(d_clusInModule.get(), 0, maxNumModules * sizeof(uint32_t))); + + cms::cuda::launch(findClus, + {blocksPerGrid, threadsPerBlock}, + d_id.get(), + d_x.get(), + d_y.get(), + d_moduleStart.get(), + d_clusInModule.get(), + d_moduleId.get(), + d_clus.get(), + n); + cudaDeviceSynchronize(); + cudaCheck(cudaMemcpy(&nModules, d_moduleStart.get(), sizeof(uint32_t), cudaMemcpyDeviceToHost)); + + uint32_t nclus[maxNumModules], moduleId[nModules]; + cudaCheck(cudaMemcpy(&nclus, d_clusInModule.get(), maxNumModules * sizeof(uint32_t), cudaMemcpyDeviceToHost)); + + std::cout << "before charge cut found " << std::accumulate(nclus, nclus + maxNumModules, 0) << " clusters" + << std::endl; + for (auto i = maxNumModules; i > 0; i--) + if (nclus[i - 1] > 0) { + std::cout << "last module is " << i - 1 << ' ' << nclus[i - 1] << std::endl; + break; + } + if (ncl != std::accumulate(nclus, nclus + maxNumModules, 0)) + std::cout << "ERROR!!!!! wrong number of cluster found" << std::endl; + + cms::cuda::launch(clusterChargeCut, + {blocksPerGrid, threadsPerBlock}, + d_id.get(), + d_adc.get(), + d_moduleStart.get(), + d_clusInModule.get(), + d_moduleId.get(), + d_clus.get(), + n); + + cudaDeviceSynchronize(); +#else // __CUDACC__ + h_moduleStart[0] = nModules; + countModules(h_id.get(), h_moduleStart.get(), h_clus.get(), n); + memset(h_clusInModule.get(), 0, maxNumModules * sizeof(uint32_t)); + findClus( + h_id.get(), h_x.get(), h_y.get(), h_moduleStart.get(), h_clusInModule.get(), h_moduleId.get(), h_clus.get(), n); + + nModules = h_moduleStart[0]; + auto nclus = h_clusInModule.get(); + + std::cout << "before charge cut found " << std::accumulate(nclus, nclus + maxNumModules, 0) << " clusters" + << std::endl; + for (auto i = maxNumModules; i > 0; i--) + if (nclus[i - 1] > 0) { + std::cout << "last module is " << i - 1 << ' ' << nclus[i - 1] << std::endl; + break; + } + if (ncl != std::accumulate(nclus, nclus + maxNumModules, 0)) + std::cout << "ERROR!!!!! wrong number of cluster found" << std::endl; + + clusterChargeCut( + h_id.get(), h_adc.get(), h_moduleStart.get(), h_clusInModule.get(), h_moduleId.get(), h_clus.get(), n); +#endif // __CUDACC__ + + std::cout << "found " << nModules << " Modules active" << std::endl; + +#ifdef __CUDACC__ + cudaCheck(cudaMemcpy(h_id.get(), d_id.get(), size16, cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(h_clus.get(), d_clus.get(), size32, cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(&nclus, d_clusInModule.get(), maxNumModules * sizeof(uint32_t), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(&moduleId, d_moduleId.get(), nModules * sizeof(uint32_t), cudaMemcpyDeviceToHost)); +#endif // __CUDACC__ + + std::set clids; + for (int i = 0; i < n; ++i) { + assert(h_id[i] != 666); // only noise + if (h_id[i] == invalidModuleId) + continue; + assert(h_clus[i] >= 0); + assert(h_clus[i] < int(nclus[h_id[i]])); + clids.insert(h_id[i] * 1000 + h_clus[i]); + // clids.insert(h_clus[i]); + } + + // verify no hole in numbering + auto p = clids.begin(); + auto cmid = (*p) / 1000; + assert(0 == (*p) % 1000); + auto c = p; + ++c; + std::cout << "first clusters " << *p << ' ' << *c << ' ' << nclus[cmid] << ' ' << nclus[(*c) / 1000] << std::endl; + std::cout << "last cluster " << *clids.rbegin() << ' ' << nclus[(*clids.rbegin()) / 1000] << std::endl; + for (; c != clids.end(); ++c) { + auto cc = *c; + auto pp = *p; + auto mid = cc / 1000; + auto pnc = pp % 1000; + auto nc = cc % 1000; + if (mid != cmid) { + assert(0 == cc % 1000); + assert(nclus[cmid] - 1 == pp % 1000); + // if (nclus[cmid]-1 != pp%1000) std::cout << "error size " << mid << ": " << nclus[mid] << ' ' << pp << std::endl; + cmid = mid; + p = c; + continue; + } + p = c; + // assert(nc==pnc+1); + if (nc != pnc + 1) + std::cout << "error " << mid << ": " << nc << ' ' << pnc << std::endl; + } + + std::cout << "found " << std::accumulate(nclus, nclus + maxNumModules, 0) << ' ' << clids.size() << " clusters" + << std::endl; + for (auto i = maxNumModules; i > 0; i--) + if (nclus[i - 1] > 0) { + std::cout << "last module is " << i - 1 << ' ' << nclus[i - 1] << std::endl; + break; + } + // << " and " << seeds.size() << " seeds" << std::endl; + } /// end loop kkk + return 0; +} diff --git a/RecoLocalTracker/SiPixelRecHits/BuildFile.xml b/RecoLocalTracker/SiPixelRecHits/BuildFile.xml index d0f5f096dbb19..d9376d88f7bbd 100644 --- a/RecoLocalTracker/SiPixelRecHits/BuildFile.xml +++ b/RecoLocalTracker/SiPixelRecHits/BuildFile.xml @@ -1,12 +1,14 @@ - - + + + + + - + + - - diff --git a/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h b/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h index 4a7ba119b0a5b..05e59585ba6ba 100644 --- a/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h +++ b/RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h @@ -1,5 +1,5 @@ -#ifndef RecoLocalTracker_SiPixelRecHits_PixelCPEBase_H -#define RecoLocalTracker_SiPixelRecHits_PixelCPEBase_H 1 +#ifndef RecoLocalTracker_SiPixelRecHits_interface_PixelCPEBase_h +#define RecoLocalTracker_SiPixelRecHits_interface_PixelCPEBase_h 1 //----------------------------------------------------------------------------- // \class PixelCPEBase @@ -11,43 +11,32 @@ // Change to use Generic error & Template calibration from DB - D.Fehling 11/08 //----------------------------------------------------------------------------- +#ifdef EDM_ML_DEBUG +#include +#endif +#include #include #include -#include "TMath.h" -#include "RecoLocalTracker/ClusterParameterEstimator/interface/PixelClusterParameterEstimator.h" -#include "DataFormats/TrackerRecHit2D/interface/SiPixelRecHitQuality.h" +#include +#include "CondFormats/SiPixelObjects/interface/SiPixelGenErrorDBObject.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelLorentzAngle.h" +#include "CondFormats/SiPixelObjects/interface/SiPixelTemplateDBObject.h" +#include "DataFormats/GeometryCommonDetAlgo/interface/MeasurementError.h" +#include "DataFormats/GeometryCommonDetAlgo/interface/MeasurementPoint.h" +#include "DataFormats/GeometrySurface/interface/GloballyPositioned.h" #include "DataFormats/TrackerCommon/interface/TrackerTopology.h" -#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "DataFormats/TrackerRecHit2D/interface/SiPixelRecHitQuality.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" #include "Geometry/CommonDetUnit/interface/GeomDetType.h" #include "Geometry/CommonDetUnit/interface/PixelGeomDetUnit.h" #include "Geometry/CommonTopologies/interface/PixelTopology.h" #include "Geometry/CommonTopologies/interface/Topology.h" - -//--- For the configuration: -#include "FWCore/ParameterSet/interface/ParameterSet.h" -#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" - -#include "DataFormats/GeometryCommonDetAlgo/interface/MeasurementPoint.h" -#include "DataFormats/GeometryCommonDetAlgo/interface/MeasurementError.h" -#include "DataFormats/GeometrySurface/interface/GloballyPositioned.h" - -#include "CondFormats/SiPixelObjects/interface/SiPixelLorentzAngle.h" - -// new errors -#include "CondFormats/SiPixelObjects/interface/SiPixelGenErrorDBObject.h" -// old errors -//#include "CondFormats/SiPixelObjects/interface/SiPixelCPEGenericErrorParm.h" - -#include "CondFormats/SiPixelObjects/interface/SiPixelTemplateDBObject.h" - -#include - -#include -#ifdef EDM_ML_DEBUG -#include -#endif +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "RecoLocalTracker/ClusterParameterEstimator/interface/PixelClusterParameterEstimator.h" class RectangularPixelTopology; class MagneticField; @@ -78,11 +67,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; @@ -143,7 +133,7 @@ class PixelCPEBase : public PixelClusterParameterEstimator { inline ReturnType getParameters(const SiPixelCluster& cl, const GeomDetUnit& det) const override { #ifdef EDM_ML_DEBUG nRecHitsTotal_++; - //std::cout<<" in PixelCPEBase:localParameters(all) - "< + +#include "CalibTracker/SiPixelESProducers/interface/SiPixelCPEGenericDBErrorParametrization.h" +#include "CondFormats/SiPixelTransient/interface/SiPixelGenError.h" +#include "CondFormats/SiPixelTransient/interface/SiPixelTemplate.h" +#include "HeterogeneousCore/CUDACore/interface/ESProduct.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HostAllocator.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" + +class MagneticField; +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 + // sums are made. Morris + int pixmx; + + // These are errors predicted by PIXELAV + float sigmay; // CPE Generic y-error for multi-pixel cluster + float sigmax; // CPE Generic x-error for multi-pixel cluster + float sy1; // CPE Generic y-error for single single-pixel + float sy2; // CPE Generic y-error for single double-pixel cluster + float sx1; // CPE Generic x-error for single single-pixel cluster + float sx2; // CPE Generic x-error for single double-pixel cluster + }; + + PixelCPEFast(edm::ParameterSet const &conf, + const MagneticField *, + const TrackerGeometry &, + const TrackerTopology &, + const SiPixelLorentzAngle *, + const SiPixelGenErrorDBObject *, + const SiPixelLorentzAngle *); + + ~PixelCPEFast() override = default; + + static void fillPSetDescription(edm::ParameterSetDescription &desc); + + // The return value can only be used safely in kernels launched on + // the same cudaStream, or after cudaStreamSynchronize. + const pixelCPEforGPU::ParamsOnGPU *getGPUProductAsync(cudaStream_t cudaStream) const; + + pixelCPEforGPU::ParamsOnGPU const &getCPUProduct() const { return cpuData_; } + +private: + std::unique_ptr createClusterParam(const SiPixelCluster &cl) const override; + + 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 + int &Q_f_X, //!< output, Q first in X + int &Q_l_X, //!< output, Q last in X + int &Q_f_Y, //!< output, Q first in Y + int &Q_l_Y, //!< output, Q last in Y + bool truncate); + + const float edgeClusterErrorX_; + const float edgeClusterErrorY_; + const bool useErrorsFromTemplates_; + const bool truncatePixelCharge_; + + std::vector xerr_barrel_l1_, yerr_barrel_l1_, xerr_barrel_ln_; + std::vector yerr_barrel_ln_, xerr_endcap_, yerr_endcap_; + float xerr_barrel_l1_def_, yerr_barrel_l1_def_, xerr_barrel_ln_def_; + float yerr_barrel_ln_def_, xerr_endcap_def_, yerr_endcap_def_; + + //--- DB Error Parametrization object, new light templates + std::vector thePixelGenError_; + + // allocate this with posix malloc to be compatible with the cpu workflow + std::vector m_detParamsGPU; + pixelCPEforGPU::CommonParams m_commonParamsGPU; + pixelCPEforGPU::LayerGeometry m_layerGeometry; + pixelCPEforGPU::AverageGeometry m_averageGeometry; + pixelCPEforGPU::ParamsOnGPU cpuData_; + + struct GPUData { + ~GPUData(); + // not needed if not used on CPU... + pixelCPEforGPU::ParamsOnGPU paramsOnGPU_h; + pixelCPEforGPU::ParamsOnGPU *paramsOnGPU_d = nullptr; // copy of the above on the Device + }; + cms::cuda::ESProduct gpuData_; + + void fillParamsForGpu(); +}; + +#endif // RecoLocalTracker_SiPixelRecHits_PixelCPEFast_h diff --git a/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h new file mode 100644 index 0000000000000..f655329d02013 --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h @@ -0,0 +1,343 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_pixelCPEforGPU_h +#define RecoLocalTracker_SiPixelRecHits_pixelCPEforGPU_h + +#include +#include +#include +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "DataFormats/GeometrySurface/interface/SOARotation.h" +#include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCompat.h" + +namespace pixelCPEforGPU { + + using Frame = SOAFrame; + using Rotation = SOARotation; + + // all modules are identical! + struct CommonParams { + float theThicknessB; + float theThicknessE; + float thePitchX; + float thePitchY; + }; + + struct DetParams { + bool isBarrel; + bool isPosZ; + uint16_t layer; + uint16_t index; + uint32_t rawId; + + float shiftX; + float shiftY; + float chargeWidthX; + float chargeWidthY; + uint16_t pixmx; // max pix charge + + float x0, y0, z0; // the vertex in the local coord of the detector + + float sx[3], sy[3]; // the errors... + + Frame frame; + }; + + using phase1PixelTopology::AverageGeometry; + + struct LayerGeometry { + uint32_t layerStart[phase1PixelTopology::numberOfLayers + 1]; + uint8_t layer[phase1PixelTopology::layerIndexSize]; + }; + + struct ParamsOnGPU { + CommonParams const* m_commonParams; + DetParams const* m_detParams; + LayerGeometry const* m_layerGeometry; + AverageGeometry const* m_averageGeometry; + + constexpr CommonParams const& __restrict__ commonParams() const { + CommonParams const* __restrict__ l = m_commonParams; + return *l; + } + constexpr DetParams const& __restrict__ detParams(int i) const { + DetParams const* __restrict__ l = m_detParams; + return l[i]; + } + constexpr LayerGeometry const& __restrict__ layerGeometry() const { return *m_layerGeometry; } + constexpr AverageGeometry const& __restrict__ averageGeometry() const { return *m_averageGeometry; } + + __device__ uint8_t layer(uint16_t id) const { + return __ldg(m_layerGeometry->layer + id / phase1PixelTopology::maxModuleStride); + }; + }; + + // SOA (on device) + template + struct ClusParamsT { + uint32_t minRow[N]; + uint32_t maxRow[N]; + uint32_t minCol[N]; + uint32_t maxCol[N]; + + int32_t Q_f_X[N]; + int32_t Q_l_X[N]; + int32_t Q_f_Y[N]; + int32_t Q_l_Y[N]; + + int32_t charge[N]; + + float xpos[N]; + float ypos[N]; + + float xerr[N]; + float yerr[N]; + + int16_t xsize[N]; // clipped at 127 if negative is edge.... + int16_t ysize[N]; + }; + + constexpr int32_t MaxHitsInIter = gpuClustering::maxHitsInIter(); + using ClusParams = ClusParamsT; + + constexpr inline void computeAnglesFromDet( + DetParams const& __restrict__ detParams, float const x, float const y, float& cotalpha, float& cotbeta) { + // x,y local position on det + auto gvx = x - detParams.x0; + auto gvy = y - detParams.y0; + auto gvz = -1.f / detParams.z0; + // normalization not required as only ratio used... + // calculate angles + cotalpha = gvx * gvz; + cotbeta = gvy * gvz; + } + + constexpr inline float correction(int sizeM1, + int Q_f, //!< Charge in the first pixel. + int Q_l, //!< Charge in the last pixel. + uint16_t upper_edge_first_pix, //!< As the name says. + uint16_t lower_edge_last_pix, //!< As the name says. + float lorentz_shift, //!< L-shift at half thickness + float theThickness, //detector thickness + float cot_angle, //!< cot of alpha_ or beta_ + float pitch, //!< thePitchX or thePitchY + bool first_is_big, //!< true if the first is big + bool last_is_big) //!< true if the last is big + { + if (0 == sizeM1) // size 1 + return 0; + + float w_eff = 0; + bool simple = true; + if (1 == sizeM1) { // size 2 + //--- Width of the clusters minus the edge (first and last) pixels. + //--- In the note, they are denoted x_F and x_L (and y_F and y_L) + // assert(lower_edge_last_pix >= upper_edge_first_pix); + auto W_inner = pitch * float(lower_edge_last_pix - upper_edge_first_pix); // in cm + + //--- Predicted charge width from geometry + auto W_pred = theThickness * cot_angle // geometric correction (in cm) + - lorentz_shift; // (in cm) &&& check fpix! + + w_eff = std::abs(W_pred) - W_inner; + + //--- If the observed charge width is inconsistent with the expectations + //--- based on the track, do *not* use W_pred-W_inner. Instead, replace + //--- it with an *average* effective charge width, which is the average + //--- length of the edge pixels. + + // this can produce "large" regressions for very small numeric differences + simple = (w_eff < 0.0f) | (w_eff > pitch); + } + + if (simple) { + //--- Total length of the two edge pixels (first+last) + float sum_of_edge = 2.0f; + if (first_is_big) + sum_of_edge += 1.0f; + if (last_is_big) + sum_of_edge += 1.0f; + w_eff = pitch * 0.5f * sum_of_edge; // ave. length of edge pixels (first+last) (cm) + } + + //--- Finally, compute the position in this projection + float Qdiff = Q_l - Q_f; + float Qsum = Q_l + Q_f; + + //--- Temporary fix for clusters with both first and last pixel with charge = 0 + if (Qsum == 0) + Qsum = 1.0f; + + return 0.5f * (Qdiff / Qsum) * w_eff; + } + + constexpr inline void position(CommonParams const& __restrict__ comParams, + DetParams const& __restrict__ detParams, + ClusParams& cp, + uint32_t ic) { + //--- Upper Right corner of Lower Left pixel -- in measurement frame + uint16_t llx = cp.minRow[ic] + 1; + uint16_t lly = cp.minCol[ic] + 1; + + //--- Lower Left corner of Upper Right pixel -- in measurement frame + uint16_t urx = cp.maxRow[ic]; + uint16_t ury = cp.maxCol[ic]; + + auto llxl = phase1PixelTopology::localX(llx); + auto llyl = phase1PixelTopology::localY(lly); + auto urxl = phase1PixelTopology::localX(urx); + auto uryl = phase1PixelTopology::localY(ury); + + auto mx = llxl + urxl; + auto my = llyl + uryl; + + auto xsize = int(urxl) + 2 - int(llxl); + auto ysize = int(uryl) + 2 - int(llyl); + assert(xsize >= 0); // 0 if bixpix... + assert(ysize >= 0); + + if (phase1PixelTopology::isBigPixX(cp.minRow[ic])) + ++xsize; + if (phase1PixelTopology::isBigPixX(cp.maxRow[ic])) + ++xsize; + if (phase1PixelTopology::isBigPixY(cp.minCol[ic])) + ++ysize; + if (phase1PixelTopology::isBigPixY(cp.maxCol[ic])) + ++ysize; + + int unbalanceX = 8. * std::abs(float(cp.Q_f_X[ic] - cp.Q_l_X[ic])) / float(cp.Q_f_X[ic] + cp.Q_l_X[ic]); + int unbalanceY = 8. * std::abs(float(cp.Q_f_Y[ic] - cp.Q_l_Y[ic])) / float(cp.Q_f_Y[ic] + cp.Q_l_Y[ic]); + xsize = 8 * xsize - unbalanceX; + ysize = 8 * ysize - unbalanceY; + + cp.xsize[ic] = std::min(xsize, 1023); + cp.ysize[ic] = std::min(ysize, 1023); + + if (cp.minRow[ic] == 0 || cp.maxRow[ic] == phase1PixelTopology::lastRowInModule) + cp.xsize[ic] = -cp.xsize[ic]; + if (cp.minCol[ic] == 0 || cp.maxCol[ic] == phase1PixelTopology::lastColInModule) + cp.ysize[ic] = -cp.ysize[ic]; + + // apply the lorentz offset correction + auto xPos = detParams.shiftX + comParams.thePitchX * (0.5f * float(mx) + float(phase1PixelTopology::xOffset)); + auto yPos = detParams.shiftY + comParams.thePitchY * (0.5f * float(my) + float(phase1PixelTopology::yOffset)); + + float cotalpha = 0, cotbeta = 0; + + computeAnglesFromDet(detParams, xPos, yPos, cotalpha, cotbeta); + + auto thickness = detParams.isBarrel ? comParams.theThicknessB : comParams.theThicknessE; + + auto xcorr = correction(cp.maxRow[ic] - cp.minRow[ic], + cp.Q_f_X[ic], + cp.Q_l_X[ic], + llxl, + urxl, + detParams.chargeWidthX, // lorentz shift in cm + thickness, + cotalpha, + comParams.thePitchX, + phase1PixelTopology::isBigPixX(cp.minRow[ic]), + phase1PixelTopology::isBigPixX(cp.maxRow[ic])); + + auto ycorr = correction(cp.maxCol[ic] - cp.minCol[ic], + cp.Q_f_Y[ic], + cp.Q_l_Y[ic], + llyl, + uryl, + detParams.chargeWidthY, // lorentz shift in cm + thickness, + cotbeta, + comParams.thePitchY, + phase1PixelTopology::isBigPixY(cp.minCol[ic]), + phase1PixelTopology::isBigPixY(cp.maxCol[ic])); + + cp.xpos[ic] = xPos + xcorr; + cp.ypos[ic] = yPos + ycorr; + } + + constexpr inline 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; + + // FIXME these are errors form Run1 + constexpr float xerr_barrel_l1[] = {0.00115, 0.00120, 0.00088}; + 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.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}; + constexpr float xerr_endcap_def = 0.0020; + 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 (!isEdgeX && !isBig1X) { + if (not detParams.isBarrel) { + cp.xerr[ic] = sx < std::size(xerr_endcap) ? xerr_endcap[sx] : xerr_endcap_def; + } else if (detParams.layer == 1) { + cp.xerr[ic] = sx < std::size(xerr_barrel_l1) ? xerr_barrel_l1[sx] : xerr_barrel_l1_def; + } else { + cp.xerr[ic] = sx < std::size(xerr_barrel_ln) ? xerr_barrel_ln[sx] : xerr_barrel_ln_def; + } + } + + if (!isEdgeY && !isBig1Y) { + if (not detParams.isBarrel) { + cp.yerr[ic] = sy < std::size(yerr_endcap) ? yerr_endcap[sy] : yerr_endcap_def; + } else if (detParams.layer == 1) { + cp.yerr[ic] = sy < std::size(yerr_barrel_l1) ? yerr_barrel_l1[sy] : yerr_barrel_l1_def; + } else { + cp.yerr[ic] = sy < std::size(yerr_barrel_ln) ? yerr_barrel_ln[sy] : yerr_barrel_ln_def; + } + } + } + + constexpr inline 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; + + 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? + uint32_t ix = (0 == sx); + uint32_t iy = (0 == sy); + ix += (0 == sx) && phase1PixelTopology::isBigPixX(cp.minRow[ic]); + iy += (0 == sy) && phase1PixelTopology::isBigPixY(cp.minCol[ic]); + + if (not isEdgeX) + cp.xerr[ic] = detParams.sx[ix]; + if (not isEdgeY) + cp.yerr[ic] = detParams.sy[iy]; + } + +} // namespace pixelCPEforGPU + +#endif // RecoLocalTracker_SiPixelRecHits_pixelCPEforGPU_h diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/BuildFile.xml b/RecoLocalTracker/SiPixelRecHits/plugins/BuildFile.xml index e02a0b722c1ae..4457b02203e66 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/BuildFile.xml +++ b/RecoLocalTracker/SiPixelRecHits/plugins/BuildFile.xml @@ -1,7 +1,12 @@ + + + + + - + diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelCPEFastESProducer.cc b/RecoLocalTracker/SiPixelRecHits/plugins/PixelCPEFastESProducer.cc new file mode 100644 index 0000000000000..332baabe8842a --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelCPEFastESProducer.cc @@ -0,0 +1,98 @@ +#include +#include + +#include "CondFormats/DataRecord/interface/SiPixelGenErrorDBObjectRcd.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/ESProducer.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/ModuleFactory.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "MagneticField/Engine/interface/MagneticField.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" +#include "RecoLocalTracker/ClusterParameterEstimator/interface/PixelClusterParameterEstimator.h" +#include "RecoLocalTracker/Records/interface/TkPixelCPERecord.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h" + +class PixelCPEFastESProducer : public edm::ESProducer { +public: + PixelCPEFastESProducer(const edm::ParameterSet& p); + std::unique_ptr produce(const TkPixelCPERecord&); + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + edm::ESGetToken magfieldToken_; + edm::ESGetToken pDDToken_; + edm::ESGetToken hTTToken_; + edm::ESGetToken lorentzAngleToken_; + edm::ESGetToken lorentzAngleWidthToken_; + edm::ESGetToken genErrorDBObjectToken_; + + edm::ParameterSet pset_; + bool useErrorsFromTemplates_; +}; + +using namespace edm; + +PixelCPEFastESProducer::PixelCPEFastESProducer(const edm::ParameterSet& p) : pset_(p) { + auto const& myname = p.getParameter("ComponentName"); + auto const& magname = p.getParameter("MagneticFieldRecord"); + useErrorsFromTemplates_ = p.getParameter("UseErrorsFromTemplates"); + + auto cc = setWhatProduced(this, myname); + magfieldToken_ = cc.consumes(magname); + pDDToken_ = cc.consumes(); + hTTToken_ = cc.consumes(); + lorentzAngleToken_ = cc.consumes(edm::ESInputTag("")); + lorentzAngleWidthToken_ = cc.consumes(edm::ESInputTag("", "forWidth")); + if (useErrorsFromTemplates_) { + genErrorDBObjectToken_ = cc.consumes(); + } +} + +std::unique_ptr PixelCPEFastESProducer::produce(const TkPixelCPERecord& iRecord) { + // add the new la width object + const SiPixelLorentzAngle* lorentzAngleWidthProduct = nullptr; + lorentzAngleWidthProduct = &iRecord.get(lorentzAngleWidthToken_); + + const SiPixelGenErrorDBObject* genErrorDBObjectProduct = nullptr; + + // Errors take only from new GenError + if (useErrorsFromTemplates_) { // do only when generrors are needed + genErrorDBObjectProduct = &iRecord.get(genErrorDBObjectToken_); + //} else { + //std::cout<<" pass an empty GenError pointer"<(pset_, + &iRecord.get(magfieldToken_), + iRecord.get(pDDToken_), + iRecord.get(hTTToken_), + &iRecord.get(lorentzAngleToken_), + genErrorDBObjectProduct, + lorentzAngleWidthProduct); +} + +void PixelCPEFastESProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + // from PixelCPEBase + PixelCPEBase::fillPSetDescription(desc); + + // used by PixelCPEFast + desc.add("EdgeClusterErrorX", 50.0); + desc.add("EdgeClusterErrorY", 85.0); + desc.add("UseErrorsFromTemplates", true); + desc.add("TruncatePixelCharge", true); + + // specific to PixelCPEFastESProducer + desc.add("ComponentName", "PixelCPEFast"); + desc.add("MagneticFieldRecord", edm::ESInputTag()); + desc.add("useLAAlignmentOffsets", false); + desc.add("DoLorentz", false); + + descriptions.add("PixelCPEFastESProducer", desc); +} + +DEFINE_FWK_EVENTSETUP_MODULE(PixelCPEFastESProducer); diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHitGPUKernel.cu b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHitGPUKernel.cu new file mode 100644 index 0000000000000..f75d5e3b3bef7 --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHitGPUKernel.cu @@ -0,0 +1,78 @@ +// C++ headers +#include +#include + +// CUDA runtime +#include + +// CMSSW headers +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.h" + +#include "PixelRecHitGPUKernel.h" +#include "gpuPixelRecHits.h" + +namespace { + __global__ void setHitsLayerStart(uint32_t const* __restrict__ hitsModuleStart, + pixelCPEforGPU::ParamsOnGPU const* cpeParams, + uint32_t* hitsLayerStart) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + + assert(0 == hitsModuleStart[0]); + + if (i < 11) { + hitsLayerStart[i] = hitsModuleStart[cpeParams->layerGeometry().layerStart[i]]; +#ifdef GPU_DEBUG + printf("LayerStart %d %d: %d\n", i, cpeParams->layerGeometry().layerStart[i], hitsLayerStart[i]); +#endif + } + } +} // namespace + +namespace pixelgpudetails { + + TrackingRecHit2DCUDA PixelRecHitGPUKernel::makeHitsAsync(SiPixelDigisCUDA const& digis_d, + SiPixelClustersCUDA const& clusters_d, + BeamSpotCUDA const& bs_d, + pixelCPEforGPU::ParamsOnGPU const* cpeParams, + cudaStream_t stream) const { + auto nHits = clusters_d.nClusters(); + TrackingRecHit2DCUDA hits_d(nHits, cpeParams, clusters_d.clusModuleStart(), stream); + + int threadsPerBlock = 128; + int blocks = digis_d.nModules(); // active modules (with digis) + +#ifdef GPU_DEBUG + std::cout << "launching getHits kernel for " << blocks << " blocks" << std::endl; +#endif + if (blocks) // protect from empty events + gpuPixelRecHits::getHits<<>>( + cpeParams, bs_d.data(), digis_d.view(), digis_d.nDigis(), clusters_d.view(), hits_d.view()); + cudaCheck(cudaGetLastError()); +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + // assuming full warp of threads is better than a smaller number... + if (nHits) { + setHitsLayerStart<<<1, 32, 0, stream>>>(clusters_d.clusModuleStart(), cpeParams, hits_d.hitsLayerStart()); + cudaCheck(cudaGetLastError()); + } + + if (nHits) { + cms::cuda::fillManyFromVector(hits_d.phiBinner(), 10, hits_d.iphi(), hits_d.hitsLayerStart(), nHits, 256, stream); + cudaCheck(cudaGetLastError()); + } + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + return hits_d; + } + +} // namespace pixelgpudetails diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHitGPUKernel.h b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHitGPUKernel.h new file mode 100644 index 0000000000000..61bc8b58bb7d6 --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/PixelRecHitGPUKernel.h @@ -0,0 +1,33 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_plugins_PixelRecHitGPUKernel_h +#define RecoLocalTracker_SiPixelRecHits_plugins_PixelRecHitGPUKernel_h + +#include + +#include + +#include "CUDADataFormats/BeamSpot/interface/BeamSpotCUDA.h" +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" + +namespace pixelgpudetails { + + class PixelRecHitGPUKernel { + public: + PixelRecHitGPUKernel() = default; + ~PixelRecHitGPUKernel() = default; + + PixelRecHitGPUKernel(const PixelRecHitGPUKernel&) = delete; + PixelRecHitGPUKernel(PixelRecHitGPUKernel&&) = delete; + PixelRecHitGPUKernel& operator=(const PixelRecHitGPUKernel&) = delete; + PixelRecHitGPUKernel& operator=(PixelRecHitGPUKernel&&) = delete; + + TrackingRecHit2DCUDA makeHitsAsync(SiPixelDigisCUDA const& digis_d, + SiPixelClustersCUDA const& clusters_d, + BeamSpotCUDA const& bs_d, + pixelCPEforGPU::ParamsOnGPU const* cpeParams, + cudaStream_t stream) const; + }; +} // namespace pixelgpudetails + +#endif // RecoLocalTracker_SiPixelRecHits_plugins_PixelRecHitGPUKernel_h diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitCUDA.cc b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitCUDA.cc new file mode 100644 index 0000000000000..09b90526bf7db --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitCUDA.cc @@ -0,0 +1,92 @@ +#include + +#include "CUDADataFormats/BeamSpot/interface/BeamSpotCUDA.h" +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "RecoLocalTracker/Records/interface/TkPixelCPERecord.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h" + +#include "PixelRecHitGPUKernel.h" + +class SiPixelRecHitCUDA : public edm::global::EDProducer<> { +public: + explicit SiPixelRecHitCUDA(const edm::ParameterSet& iConfig); + ~SiPixelRecHitCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + + const edm::ESGetToken cpeToken_; + const edm::EDGetTokenT> tBeamSpot; + const edm::EDGetTokenT> token_; + const edm::EDGetTokenT> tokenDigi_; + const edm::EDPutTokenT> tokenHit_; + + const pixelgpudetails::PixelRecHitGPUKernel gpuAlgo_; +}; + +SiPixelRecHitCUDA::SiPixelRecHitCUDA(const edm::ParameterSet& iConfig) + : cpeToken_(esConsumes(edm::ESInputTag("", iConfig.getParameter("CPE")))), + tBeamSpot(consumes>(iConfig.getParameter("beamSpot"))), + token_(consumes>(iConfig.getParameter("src"))), + tokenDigi_(consumes>(iConfig.getParameter("src"))), + tokenHit_(produces>()) {} + +void SiPixelRecHitCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + desc.add("beamSpot", edm::InputTag("offlineBeamSpotCUDA")); + desc.add("src", edm::InputTag("siPixelClustersPreSplittingCUDA")); + desc.add("CPE", "PixelCPEFast"); + descriptions.add("siPixelRecHitCUDA", desc); +} + +void SiPixelRecHitCUDA::produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& es) const { + PixelCPEFast const* fcpe = dynamic_cast(&es.getData(cpeToken_)); + if (not fcpe) { + throw cms::Exception("Configuration") << "SiPixelRecHitSoAFromLegacy can only use a CPE of type PixelCPEFast"; + } + + edm::Handle> hclusters; + iEvent.getByToken(token_, hclusters); + + cms::cuda::ScopedContextProduce ctx{*hclusters}; + auto const& clusters = ctx.get(*hclusters); + + edm::Handle> hdigis; + iEvent.getByToken(tokenDigi_, hdigis); + auto const& digis = ctx.get(*hdigis); + + edm::Handle> hbs; + iEvent.getByToken(tBeamSpot, hbs); + auto const& bs = ctx.get(*hbs); + + auto nHits = clusters.nClusters(); + if (nHits >= TrackingRecHit2DSOAView::maxHits()) { + edm::LogWarning("PixelRecHitGPUKernel") + << "Clusters/Hits Overflow " << nHits << " >= " << TrackingRecHit2DSOAView::maxHits(); + } + + ctx.emplace(iEvent, + tokenHit_, + gpuAlgo_.makeHitsAsync(digis, clusters, bs, fcpe->getGPUProductAsync(ctx.stream()), ctx.stream())); +} + +DEFINE_FWK_MODULE(SiPixelRecHitCUDA); diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitConverter.cc b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitConverter.cc index 42efbd12c2e2d..8c16be54e5774 100644 --- a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitConverter.cc +++ b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitConverter.cc @@ -82,6 +82,10 @@ #include "RecoLocalTracker/Records/interface/TkPixelCPERecord.h" +// Make heterogeneous framework happy +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "CUDADataFormats/Common/interface/HostProduct.h" + using namespace std; namespace cms { @@ -104,22 +108,21 @@ namespace cms { void produce(edm::Event& e, const edm::EventSetup& c) override; //--- Execute the position estimator algorithm(s). - //--- New interface with DetSetVector - void run(const edmNew::DetSetVector& input, - SiPixelRecHitCollectionNew& output, - TrackerGeometry const& geom); - - void run(edm::Handle> inputhandle, + void run(edm::Event& e, + edm::Handle> inputhandle, SiPixelRecHitCollectionNew& output, TrackerGeometry const& geom); private: + using HMSstorage = HostProduct; + // TO DO: maybe allow a map of pointers? /// const PixelClusterParameterEstimator * cpe_; // what we got (for now, one ptr to base class) PixelCPEBase const* cpe_ = nullptr; // What we got (for now, one ptr to base class) edm::InputTag const src_; edm::EDGetTokenT> const tPixelCluster_; edm::EDPutTokenT const tPut_; + edm::EDPutTokenT const tHost_; edm::ESGetToken const tTrackerGeom_; edm::ESGetToken const tCPE_; bool m_newCont; // save also in emdNew::DetSetVector @@ -132,6 +135,7 @@ namespace cms { : src_(conf.getParameter("src")), tPixelCluster_(consumes>(src_)), tPut_(produces()), + tHost_(produces()), tTrackerGeom_(esConsumes()), tCPE_(esConsumes( edm::ESInputTag("", conf.getParameter("CPE")))) {} @@ -159,7 +163,7 @@ namespace cms { // Step C: Iterate over DetIds and invoke the strip CPE algorithm // on each DetUnit - run(input, output, geom); + run(e, input, output, geom); output.shrink_to_fit(); e.emplace(tPut_, std::move(output)); @@ -170,7 +174,8 @@ namespace cms { //! and make a RecHit to store the result. //! New interface reading DetSetVector by V.Chiochia (May 30th, 2006) //--------------------------------------------------------------------------- - void SiPixelRecHitConverter::run(edm::Handle> inputhandle, + void SiPixelRecHitConverter::run(edm::Event& iEvent, + edm::Handle> inputhandle, SiPixelRecHitCollectionNew& output, TrackerGeometry const& geom) { if (!cpe_) { @@ -185,18 +190,45 @@ namespace cms { const edmNew::DetSetVector& input = *inputhandle; - edmNew::DetSetVector::const_iterator DSViter = input.begin(); + // allocate a buffer for the indices of the clusters + auto hmsp = std::make_unique(gpuClustering::maxNumModules + 1); + // hitsModuleStart is a non-owning pointer to the buffer + auto hitsModuleStart = hmsp.get(); + // fill cluster arrays + std::array clusInModule{}; + for (auto const& dsv : input) { + unsigned int detid = dsv.detId(); + DetId detIdObject(detid); + const GeomDetUnit* genericDet = geom.idToDetUnit(detIdObject); + auto gind = genericDet->index(); + // FIXME to be changed to support Phase2 + if (gind >= int(gpuClustering::maxNumModules)) + continue; + auto const nclus = dsv.size(); + assert(nclus > 0); + clusInModule[gind] = nclus; + numberOfClusters += nclus; + } + hitsModuleStart[0] = 0; + assert(clusInModule.size() > gpuClustering::maxNumModules); + for (int i = 1, n = clusInModule.size(); i < n; ++i) + hitsModuleStart[i] = hitsModuleStart[i - 1] + clusInModule[i - 1]; + assert(numberOfClusters == int(hitsModuleStart[gpuClustering::maxNumModules])); + + // wrap the buffer in a HostProduct, and move it to the Event, without reallocating the buffer or affecting hitsModuleStart + iEvent.emplace(tHost_, std::move(hmsp)); - for (; DSViter != input.end(); DSViter++) { + numberOfClusters = 0; + for (auto const& dsv : input) { numberOfDetUnits++; - unsigned int detid = DSViter->detId(); + unsigned int detid = dsv.detId(); DetId detIdObject(detid); const GeomDetUnit* genericDet = geom.idToDetUnit(detIdObject); const PixelGeomDetUnit* pixDet = dynamic_cast(genericDet); assert(pixDet); SiPixelRecHitCollectionNew::FastFiller recHitsOnDetUnit(output, detid); - edmNew::DetSet::const_iterator clustIt = DSViter->begin(), clustEnd = DSViter->end(); + edmNew::DetSet::const_iterator clustIt = dsv.begin(), clustEnd = dsv.end(); for (; clustIt != clustEnd; clustIt++) { numberOfClusters++; diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitFromCUDA.cc b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitFromCUDA.cc new file mode 100644 index 0000000000000..790b0da51ecfb --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitFromCUDA.cc @@ -0,0 +1,187 @@ +#include + +#include + +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "DataFormats/Common/interface/DetSetVectorNew.h" +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/SiPixelCluster/interface/SiPixelCluster.h" +#include "DataFormats/TrackerRecHit2D/interface/SiPixelRecHitCollection.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "Geometry/CommonDetUnit/interface/PixelGeomDetUnit.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" + +class SiPixelRecHitFromCUDA : public edm::stream::EDProducer { +public: + explicit SiPixelRecHitFromCUDA(const edm::ParameterSet& iConfig); + ~SiPixelRecHitFromCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + using HMSstorage = HostProduct; + +private: + void acquire(edm::Event const& iEvent, + edm::EventSetup const& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) override; + void produce(edm::Event& iEvent, edm::EventSetup const& iSetup) override; + + const edm::ESGetToken geomToken_; + const edm::EDGetTokenT> hitsToken_; // CUDA hits + const edm::EDGetTokenT clusterToken_; // legacy clusters + const edm::EDPutTokenT rechitsPutToken_; // legacy rechits + const edm::EDPutTokenT hostPutToken_; + + uint32_t nHits_; + cms::cuda::host::unique_ptr store32_; + cms::cuda::host::unique_ptr hitsModuleStart_; +}; + +SiPixelRecHitFromCUDA::SiPixelRecHitFromCUDA(const edm::ParameterSet& iConfig) + : geomToken_(esConsumes()), + hitsToken_( + consumes>(iConfig.getParameter("pixelRecHitSrc"))), + clusterToken_(consumes(iConfig.getParameter("src"))), + rechitsPutToken_(produces()), + hostPutToken_(produces()) {} + +void SiPixelRecHitFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("pixelRecHitSrc", edm::InputTag("siPixelRecHitsPreSplittingCUDA")); + desc.add("src", edm::InputTag("siPixelClustersPreSplitting")); + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelRecHitFromCUDA::acquire(edm::Event const& iEvent, + edm::EventSetup const& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) { + cms::cuda::Product const& inputDataWrapped = iEvent.get(hitsToken_); + cms::cuda::ScopedContextAcquire ctx{inputDataWrapped, std::move(waitingTaskHolder)}; + auto const& inputData = ctx.get(inputDataWrapped); + + nHits_ = inputData.nHits(); + + LogDebug("SiPixelRecHitFromCUDA") << "converting " << nHits_ << " Hits"; + + if (0 == nHits_) + return; + store32_ = inputData.localCoordToHostAsync(ctx.stream()); + hitsModuleStart_ = inputData.hitsModuleStartToHostAsync(ctx.stream()); +} + +void SiPixelRecHitFromCUDA::produce(edm::Event& iEvent, edm::EventSetup const& es) { + // allocate a buffer for the indices of the clusters + auto hmsp = std::make_unique(gpuClustering::maxNumModules + 1); + std::copy(hitsModuleStart_.get(), hitsModuleStart_.get() + gpuClustering::maxNumModules + 1, hmsp.get()); + // wrap the buffer in a HostProduct, and move it to the Event, without reallocating the buffer or affecting hitsModuleStart + iEvent.emplace(hostPutToken_, std::move(hmsp)); + + SiPixelRecHitCollection output; + if (0 == nHits_) { + iEvent.emplace(rechitsPutToken_, std::move(output)); + return; + } + + auto xl = store32_.get(); + auto yl = xl + nHits_; + auto xe = yl + nHits_; + auto ye = xe + nHits_; + + const TrackerGeometry* geom = &es.getData(geomToken_); + + edm::Handle hclusters = iEvent.getHandle(clusterToken_); + auto const& input = *hclusters; + + constexpr uint32_t maxHitsInModule = gpuClustering::maxHitsInModule(); + + int numberOfDetUnits = 0; + int numberOfClusters = 0; + for (auto const& dsv : input) { + numberOfDetUnits++; + unsigned int detid = dsv.detId(); + DetId detIdObject(detid); + const GeomDetUnit* genericDet = geom->idToDetUnit(detIdObject); + auto gind = genericDet->index(); + const PixelGeomDetUnit* pixDet = dynamic_cast(genericDet); + assert(pixDet); + SiPixelRecHitCollection::FastFiller recHitsOnDetUnit(output, detid); + auto fc = hitsModuleStart_[gind]; + auto lc = hitsModuleStart_[gind + 1]; + auto nhits = lc - fc; + + assert(lc > fc); + LogDebug("SiPixelRecHitFromCUDA") << "in det " << gind << ": conv " << nhits << " hits from " << dsv.size() + << " legacy clusters" << ' ' << fc << ',' << lc; + if (nhits > maxHitsInModule) + edm::LogWarning("SiPixelRecHitFromCUDA") << fmt::sprintf( + "Too many clusters %d in module %d. Only the first %d hits will be converted", nhits, gind, maxHitsInModule); + nhits = std::min(nhits, maxHitsInModule); + + LogDebug("SiPixelRecHitFromCUDA") << "in det " << gind << "conv " << nhits << " hits from " << dsv.size() + << " legacy clusters" << ' ' << lc << ',' << fc; + + if (0 == nhits) + continue; + auto jnd = [&](int k) { return fc + k; }; + assert(nhits <= dsv.size()); + if (nhits != dsv.size()) { + edm::LogWarning("GPUHits2CPU") << "nhits!= nclus " << nhits << ' ' << dsv.size(); + } + for (auto const& clust : dsv) { + assert(clust.originalId() >= 0); + assert(clust.originalId() < dsv.size()); + if (clust.originalId() >= nhits) + continue; + auto ij = jnd(clust.originalId()); + if (ij >= TrackingRecHit2DSOAView::maxHits()) + continue; // overflow... + LocalPoint lp(xl[ij], yl[ij]); + LocalError le(xe[ij], 0, ye[ij]); + SiPixelRecHitQuality::QualWordType rqw = 0; + + numberOfClusters++; + + /* cpu version.... (for reference) + std::tuple tuple = cpe_->getParameters( clust, *genericDet ); + LocalPoint lp( std::get<0>(tuple) ); + LocalError le( std::get<1>(tuple) ); + SiPixelRecHitQuality::QualWordType rqw( std::get<2>(tuple) ); + */ + + // Create a persistent edm::Ref to the cluster + edm::Ref, SiPixelCluster> cluster = edmNew::makeRefTo(hclusters, &clust); + // Make a RecHit and add it to the DetSet + SiPixelRecHit hit(lp, le, rqw, *genericDet, cluster); + // + // Now save it ================= + recHitsOnDetUnit.push_back(hit); + // ============================= + + LogDebug("SiPixelRecHitFromCUDA") << "cluster " << numberOfClusters << " at " << lp << ' ' << le; + + } // <-- End loop on Clusters + + // LogDebug("SiPixelRecHitGPU") + LogDebug("SiPixelRecHitFromCUDA") << "found " << recHitsOnDetUnit.size() << " RecHits on " << detid; + + } // <-- End loop on DetUnits + + LogDebug("SiPixelRecHitFromCUDA") << "found " << numberOfDetUnits << " dets, " << numberOfClusters << " clusters"; + + iEvent.emplace(rechitsPutToken_, std::move(output)); +} + +DEFINE_FWK_MODULE(SiPixelRecHitFromCUDA); diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitSoAFromLegacy.cc b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitSoAFromLegacy.cc new file mode 100644 index 0000000000000..2397434027fa1 --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/SiPixelRecHitSoAFromLegacy.cc @@ -0,0 +1,252 @@ +#include + +#include "CUDADataFormats/BeamSpot/interface/BeamSpotCUDA.h" +#include "CUDADataFormats/SiPixelCluster/interface/SiPixelClustersCUDA.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "DataFormats/BeamSpot/interface/BeamSpot.h" +#include "DataFormats/Common/interface/DetSetVectorNew.h" +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/SiPixelCluster/interface/SiPixelCluster.h" +#include "DataFormats/TrackerRecHit2D/interface/SiPixelRecHitCollection.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "RecoLocalTracker/Records/interface/TkPixelCPERecord.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEBase.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h" + +#include "gpuPixelRecHits.h" + +class SiPixelRecHitSoAFromLegacy : public edm::global::EDProducer<> { +public: + explicit SiPixelRecHitSoAFromLegacy(const edm::ParameterSet& iConfig); + ~SiPixelRecHitSoAFromLegacy() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + using HitModuleStart = std::array; + using HMSstorage = HostProduct; + +private: + void produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + + const edm::ESGetToken geomToken_; + const edm::ESGetToken cpeToken_; + const edm::EDGetTokenT bsGetToken_; + const edm::EDGetTokenT clusterToken_; // Legacy Clusters + const edm::EDPutTokenT tokenHit_; + const edm::EDPutTokenT tokenModuleStart_; + const bool convert2Legacy_; +}; + +SiPixelRecHitSoAFromLegacy::SiPixelRecHitSoAFromLegacy(const edm::ParameterSet& iConfig) + : geomToken_(esConsumes()), + cpeToken_(esConsumes(edm::ESInputTag("", iConfig.getParameter("CPE")))), + bsGetToken_{consumes(iConfig.getParameter("beamSpot"))}, + clusterToken_{consumes(iConfig.getParameter("src"))}, + tokenHit_{produces()}, + tokenModuleStart_{produces()}, + convert2Legacy_(iConfig.getParameter("convertToLegacy")) { + if (convert2Legacy_) + produces(); +} + +void SiPixelRecHitSoAFromLegacy::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + desc.add("beamSpot", edm::InputTag("offlineBeamSpot")); + desc.add("src", edm::InputTag("siPixelClustersPreSplitting")); + desc.add("CPE", "PixelCPEFast"); + desc.add("convertToLegacy", false); + descriptions.addWithDefaultLabel(desc); +} + +void SiPixelRecHitSoAFromLegacy::produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& es) const { + const TrackerGeometry* geom_ = &es.getData(geomToken_); + PixelCPEFast const* fcpe = dynamic_cast(&es.getData(cpeToken_)); + if (not fcpe) { + throw cms::Exception("Configuration") << "SiPixelRecHitSoAFromLegacy can only use a CPE of type PixelCPEFast"; + } + auto const& cpeView = fcpe->getCPUProduct(); + + const reco::BeamSpot& bs = iEvent.get(bsGetToken_); + + BeamSpotPOD bsHost; + bsHost.x = bs.x0(); + bsHost.y = bs.y0(); + bsHost.z = bs.z0(); + + edm::Handle hclusters; + iEvent.getByToken(clusterToken_, hclusters); + auto const& input = *hclusters; + + // allocate a buffer for the indices of the clusters + auto hmsp = std::make_unique(gpuClustering::maxNumModules + 1); + // hitsModuleStart is a non-owning pointer to the buffer + auto hitsModuleStart = hmsp.get(); + // wrap the buffer in a HostProduct + auto hms = std::make_unique(std::move(hmsp)); + // move the HostProduct to the Event, without reallocating the buffer or affecting hitsModuleStart + iEvent.put(tokenModuleStart_, std::move(hms)); + + // legacy output + auto legacyOutput = std::make_unique(); + + // storage + std::vector xx; + std::vector yy; + std::vector adc; + std::vector moduleInd; + std::vector clus; + + std::vector, SiPixelCluster>> clusterRef; + + constexpr uint32_t maxHitsInModule = gpuClustering::maxHitsInModule(); + + HitModuleStart moduleStart_; // index of the first pixel of each module + HitModuleStart clusInModule_; + memset(&clusInModule_, 0, sizeof(HitModuleStart)); // needed?? + assert(gpuClustering::maxNumModules + 1 == clusInModule_.size()); + assert(0 == clusInModule_[gpuClustering::maxNumModules]); + uint32_t moduleId_; + moduleStart_[1] = 0; // we run sequentially.... + + SiPixelClustersCUDA::DeviceConstView clusterView{ + moduleStart_.data(), clusInModule_.data(), &moduleId_, hitsModuleStart}; + + // fill cluster arrays + int numberOfClusters = 0; + for (auto const& dsv : input) { + unsigned int detid = dsv.detId(); + DetId detIdObject(detid); + const GeomDetUnit* genericDet = geom_->idToDetUnit(detIdObject); + auto gind = genericDet->index(); + assert(gind < gpuClustering::maxNumModules); + auto const nclus = dsv.size(); + clusInModule_[gind] = nclus; + numberOfClusters += nclus; + } + hitsModuleStart[0] = 0; + for (int i = 1, n = clusInModule_.size(); i < n; ++i) + hitsModuleStart[i] = hitsModuleStart[i - 1] + clusInModule_[i - 1]; + assert(numberOfClusters == int(hitsModuleStart[gpuClustering::maxNumModules])); + + // output SoA + auto output = std::make_unique(numberOfClusters, &cpeView, hitsModuleStart, nullptr); + + if (0 == numberOfClusters) { + iEvent.put(std::move(output)); + if (convert2Legacy_) + iEvent.put(std::move(legacyOutput)); + return; + } + + if (convert2Legacy_) + legacyOutput->reserve(gpuClustering::maxNumModules, numberOfClusters); + + int numberOfDetUnits = 0; + int numberOfHits = 0; + for (auto const& dsv : input) { + numberOfDetUnits++; + unsigned int detid = dsv.detId(); + DetId detIdObject(detid); + const GeomDetUnit* genericDet = geom_->idToDetUnit(detIdObject); + auto const gind = genericDet->index(); + assert(gind < gpuClustering::maxNumModules); + const PixelGeomDetUnit* pixDet = dynamic_cast(genericDet); + assert(pixDet); + auto const nclus = dsv.size(); + assert(clusInModule_[gind] == nclus); + if (0 == nclus) + continue; // is this really possible? + + auto const fc = hitsModuleStart[gind]; + auto const lc = hitsModuleStart[gind + 1]; + assert(lc > fc); + LogDebug("SiPixelRecHitSoAFromLegacy") << "in det " << gind << ": conv " << nclus << " hits from " + << dsv.size() << " legacy clusters" << ' ' << fc << ',' << lc; + assert((lc - fc) == nclus); + if (nclus > maxHitsInModule) + printf( + "WARNING: too many clusters %d in Module %d. Only first %d Hits converted\n", nclus, gind, maxHitsInModule); + + // fill digis + xx.clear(); + yy.clear(); + adc.clear(); + moduleInd.clear(); + clus.clear(); + clusterRef.clear(); + moduleId_ = gind; + uint32_t ic = 0; + uint32_t ndigi = 0; + for (auto const& clust : dsv) { + assert(clust.size() > 0); + for (int i = 0, nd = clust.size(); i < nd; ++i) { + auto px = clust.pixel(i); + xx.push_back(px.x); + yy.push_back(px.y); + adc.push_back(px.adc); + moduleInd.push_back(gind); + clus.push_back(ic); + ++ndigi; + } + assert(clust.originalId() == ic); // make sure hits and clus are in sync + if (convert2Legacy_) + clusterRef.emplace_back(edmNew::makeRefTo(hclusters, &clust)); + ic++; + } + assert(nclus == ic); + assert(clus.size() == ndigi); + numberOfHits += nclus; + // filled creates view + SiPixelDigisCUDA::DeviceConstView digiView{xx.data(), yy.data(), adc.data(), moduleInd.data(), clus.data()}; + assert(digiView.adc(0) != 0); + // we run on blockId.x==0 + gpuPixelRecHits::getHits(&cpeView, &bsHost, &digiView, ndigi, &clusterView, output->view()); + for (auto h = fc; h < lc; ++h) + if (h - fc < maxHitsInModule) + assert(gind == output->view()->detectorIndex(h)); + else + assert(gpuClustering::invalidModuleId == output->view()->detectorIndex(h)); + if (convert2Legacy_) { + SiPixelRecHitCollectionNew::FastFiller recHitsOnDetUnit(*legacyOutput, detid); + for (auto h = fc; h < lc; ++h) { + auto ih = h - fc; + if (ih >= maxHitsInModule) + break; + assert(ih < clusterRef.size()); + LocalPoint lp(output->view()->xLocal(h), output->view()->yLocal(h)); + LocalError le(output->view()->xerrLocal(h), 0, output->view()->yerrLocal(h)); + SiPixelRecHitQuality::QualWordType rqw = 0; + SiPixelRecHit hit(lp, le, rqw, *genericDet, clusterRef[ih]); + recHitsOnDetUnit.push_back(hit); + } + } + } + assert(numberOfHits == numberOfClusters); + + // fill data structure to support CA + for (auto i = 0; i < 11; ++i) { + output->hitsLayerStart()[i] = hitsModuleStart[cpeView.layerGeometry().layerStart[i]]; + } + cms::cuda::fillManyFromVector( + output->phiBinner(), 10, output->iphi(), output->hitsLayerStart(), numberOfHits, 256, nullptr); + + LogDebug("SiPixelRecHitSoAFromLegacy") << "created HitSoa for " << numberOfClusters << " clusters in " + << numberOfDetUnits << " Dets"; + iEvent.put(std::move(output)); + if (convert2Legacy_) + iEvent.put(std::move(legacyOutput)); +} + +DEFINE_FWK_MODULE(SiPixelRecHitSoAFromLegacy); diff --git a/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h b/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h new file mode 100644 index 0000000000000..89a40c8723ae3 --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/plugins/gpuPixelRecHits.h @@ -0,0 +1,212 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelRecHits_h +#define RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelRecHits_h + +#include +#include +#include + +#include "CUDADataFormats/BeamSpot/interface/BeamSpotCUDA.h" +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "DataFormats/Math/interface/approx_atan2.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" + +namespace gpuPixelRecHits { + + __global__ void getHits(pixelCPEforGPU::ParamsOnGPU const* __restrict__ cpeParams, + BeamSpotPOD const* __restrict__ bs, + SiPixelDigisCUDA::DeviceConstView const* __restrict__ pdigis, + int numElements, + SiPixelClustersCUDA::DeviceConstView const* __restrict__ pclusters, + TrackingRecHit2DSOAView* phits) { + // FIXME + // the compiler seems NOT to optimize loads from views (even in a simple test case) + // The whole gimnastic here of copying or not is a pure heuristic exercise that seems to produce the fastest code with the above signature + // not using views (passing a gazzilion of array pointers) seems to produce the fastest code (but it is harder to mantain) + + assert(phits); + assert(cpeParams); + + auto& hits = *phits; + + auto const digis = *pdigis; // the copy is intentional! + auto const& clusters = *pclusters; + + // copy average geometry corrected by beamspot . FIXME (move it somewhere else???) + if (0 == blockIdx.x) { + auto& agc = hits.averageGeometry(); + auto const& ag = cpeParams->averageGeometry(); + for (int il = threadIdx.x, nl = TrackingRecHit2DSOAView::AverageGeometry::numberOfLaddersInBarrel; il < nl; + il += blockDim.x) { + agc.ladderZ[il] = ag.ladderZ[il] - bs->z; + agc.ladderX[il] = ag.ladderX[il] - bs->x; + agc.ladderY[il] = ag.ladderY[il] - bs->y; + agc.ladderR[il] = sqrt(agc.ladderX[il] * agc.ladderX[il] + agc.ladderY[il] * agc.ladderY[il]); + agc.ladderMinZ[il] = ag.ladderMinZ[il] - bs->z; + agc.ladderMaxZ[il] = ag.ladderMaxZ[il] - bs->z; + } + if (0 == threadIdx.x) { + agc.endCapZ[0] = ag.endCapZ[0] - bs->z; + agc.endCapZ[1] = ag.endCapZ[1] - bs->z; + // printf("endcapZ %f %f\n",agc.endCapZ[0],agc.endCapZ[1]); + } + } + + // to be moved in common namespace... + using gpuClustering::invalidModuleId; + constexpr int32_t MaxHitsInIter = pixelCPEforGPU::MaxHitsInIter; + + using ClusParams = pixelCPEforGPU::ClusParams; + + // as usual one block per module + __shared__ ClusParams clusParams; + + auto me = clusters.moduleId(blockIdx.x); + int nclus = clusters.clusInModule(me); + + if (0 == nclus) + return; + +#ifdef GPU_DEBUG + if (threadIdx.x == 0) { + auto k = clusters.moduleStart(1 + blockIdx.x); + while (digis.moduleInd(k) == invalidModuleId) + ++k; + assert(digis.moduleInd(k) == me); + } +#endif + +#ifdef GPU_DEBUG + if (me % 100 == 1) + if (threadIdx.x == 0) + printf("hitbuilder: %d clusters in module %d. will write at %d\n", nclus, me, clusters.clusModuleStart(me)); +#endif + + for (int startClus = 0, endClus = nclus; startClus < endClus; startClus += MaxHitsInIter) { + int nClusInIter = std::min(MaxHitsInIter, endClus - startClus); + int lastClus = startClus + nClusInIter; + assert(nClusInIter <= nclus); + assert(nClusInIter > 0); + assert(lastClus <= nclus); + + assert(nclus > MaxHitsInIter || (0 == startClus && nClusInIter == nclus && lastClus == nclus)); + + // init + for (int ic = threadIdx.x; ic < nClusInIter; ic += blockDim.x) { + clusParams.minRow[ic] = std::numeric_limits::max(); + clusParams.maxRow[ic] = 0; + clusParams.minCol[ic] = std::numeric_limits::max(); + clusParams.maxCol[ic] = 0; + clusParams.charge[ic] = 0; + clusParams.Q_f_X[ic] = 0; + clusParams.Q_l_X[ic] = 0; + clusParams.Q_f_Y[ic] = 0; + clusParams.Q_l_Y[ic] = 0; + } + + __syncthreads(); + + // one thread per "digi" + auto first = clusters.moduleStart(1 + blockIdx.x) + threadIdx.x; + for (int i = first; i < numElements; i += blockDim.x) { + auto id = digis.moduleInd(i); + if (id == invalidModuleId) + continue; // not valid + if (id != me) + break; // end of module + auto cl = digis.clus(i); + if (cl < startClus || cl >= lastClus) + continue; + cl -= startClus; + assert(cl >= 0); + assert(cl < MaxHitsInIter); + auto x = digis.xx(i); + auto y = digis.yy(i); + atomicMin(&clusParams.minRow[cl], x); + atomicMax(&clusParams.maxRow[cl], x); + atomicMin(&clusParams.minCol[cl], y); + atomicMax(&clusParams.maxCol[cl], y); + } + + __syncthreads(); + + auto pixmx = cpeParams->detParams(me).pixmx; + for (int i = first; i < numElements; i += blockDim.x) { + auto id = digis.moduleInd(i); + if (id == invalidModuleId) + continue; // not valid + if (id != me) + break; // end of module + auto cl = digis.clus(i); + if (cl < startClus || cl >= lastClus) + continue; + cl -= startClus; + assert(cl >= 0); + assert(cl < MaxHitsInIter); + auto x = digis.xx(i); + auto y = digis.yy(i); + auto ch = std::min(digis.adc(i), pixmx); + atomicAdd(&clusParams.charge[cl], ch); + if (clusParams.minRow[cl] == x) + atomicAdd(&clusParams.Q_f_X[cl], ch); + if (clusParams.maxRow[cl] == x) + atomicAdd(&clusParams.Q_l_X[cl], ch); + if (clusParams.minCol[cl] == y) + atomicAdd(&clusParams.Q_f_Y[cl], ch); + if (clusParams.maxCol[cl] == y) + atomicAdd(&clusParams.Q_l_Y[cl], ch); + } + + __syncthreads(); + + // next one cluster per thread... + + first = clusters.clusModuleStart(me) + startClus; + for (int ic = threadIdx.x; ic < nClusInIter; ic += blockDim.x) { + auto h = first + ic; // output index in global memory + + assert(h < TrackingRecHit2DSOAView::maxHits()); + assert(h < hits.nHits()); + assert(h < clusters.clusModuleStart(me + 1)); + + pixelCPEforGPU::position(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); + pixelCPEforGPU::errorFromDB(cpeParams->commonParams(), cpeParams->detParams(me), clusParams, ic); + + // store it + hits.charge(h) = clusParams.charge[ic]; + hits.detectorIndex(h) = me; + + float xl, yl; + hits.xLocal(h) = xl = clusParams.xpos[ic]; + hits.yLocal(h) = yl = clusParams.ypos[ic]; + + hits.clusterSizeX(h) = clusParams.xsize[ic]; + hits.clusterSizeY(h) = clusParams.ysize[ic]; + + hits.xerrLocal(h) = clusParams.xerr[ic] * clusParams.xerr[ic]; + hits.yerrLocal(h) = clusParams.yerr[ic] * clusParams.yerr[ic]; + + // keep it local for computations + float xg, yg, zg; + // to global and compute phi... + cpeParams->detParams(me).frame.toGlobal(xl, yl, xg, yg, zg); + // here correct for the beamspot... + xg -= bs->x; + yg -= bs->y; + zg -= bs->z; + + hits.xGlobal(h) = xg; + hits.yGlobal(h) = yg; + hits.zGlobal(h) = zg; + + hits.rGlobal(h) = std::sqrt(xg * xg + yg * yg); + hits.iphi(h) = unsafe_atan2s<7>(yg, xg); + } + __syncthreads(); + } // end loop on batches + } + +} // namespace gpuPixelRecHits + +#endif // RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelRecHits_h diff --git a/RecoLocalTracker/SiPixelRecHits/python/PixelCPEESProducers_cff.py b/RecoLocalTracker/SiPixelRecHits/python/PixelCPEESProducers_cff.py index e349a515c69b3..e3879f4d9d34c 100644 --- a/RecoLocalTracker/SiPixelRecHits/python/PixelCPEESProducers_cff.py +++ b/RecoLocalTracker/SiPixelRecHits/python/PixelCPEESProducers_cff.py @@ -10,6 +10,7 @@ # 2. Pixel Generic CPE # from RecoLocalTracker.SiPixelRecHits.PixelCPEGeneric_cfi import * +from RecoLocalTracker.SiPixelRecHits.PixelCPEFastESProducer_cfi import * # # 3. ESProducer for the Magnetic-field dependent template records # diff --git a/RecoLocalTracker/SiPixelRecHits/python/SiPixelRecHits_cfi.py b/RecoLocalTracker/SiPixelRecHits/python/SiPixelRecHits_cfi.py index 465aa0bb346ce..eb9dbad4934cd 100644 --- a/RecoLocalTracker/SiPixelRecHits/python/SiPixelRecHits_cfi.py +++ b/RecoLocalTracker/SiPixelRecHits/python/SiPixelRecHits_cfi.py @@ -6,6 +6,39 @@ VerboseLevel = cms.untracked.int32(0) ) -siPixelRecHitsPreSplitting = siPixelRecHits.clone( +_siPixelRecHitsPreSplitting = siPixelRecHits.clone( src = 'siPixelClustersPreSplitting' ) + +from HeterogeneousCore.CUDACore.SwitchProducerCUDA import SwitchProducerCUDA +siPixelRecHitsPreSplitting = SwitchProducerCUDA( + cpu = _siPixelRecHitsPreSplitting.clone() +) + + + +from Configuration.ProcessModifiers.gpu_cff import gpu +from RecoLocalTracker.SiPixelRecHits.siPixelRecHitCUDA_cfi import siPixelRecHitCUDA as _siPixelRecHitCUDA +from RecoLocalTracker.SiPixelRecHits.siPixelRecHitFromCUDA_cfi import siPixelRecHitFromCUDA as _siPixelRecHitFromCUDA + +gpu.toModify(siPixelRecHitsPreSplitting, + cuda = _siPixelRecHitFromCUDA.clone() +) + + +siPixelRecHitsPreSplittingTask = cms.Task(siPixelRecHitsPreSplitting) + +siPixelRecHitsPreSplittingCUDA = _siPixelRecHitCUDA.clone( + beamSpot = "offlineBeamSpotToCUDA" +) + +siPixelRecHitsPreSplittingLegacy = _siPixelRecHitFromCUDA.clone() +siPixelRecHitsPreSplittingTaskCUDA = cms.Task( + siPixelRecHitsPreSplittingCUDA, + siPixelRecHitsPreSplittingLegacy, +) + +from Configuration.ProcessModifiers.gpu_cff import gpu +_siPixelRecHitsPreSplittingTask_gpu = siPixelRecHitsPreSplittingTask.copy() +_siPixelRecHitsPreSplittingTask_gpu.add(siPixelRecHitsPreSplittingTaskCUDA) +gpu.toReplaceWith(siPixelRecHitsPreSplittingTask, _siPixelRecHitsPreSplittingTask_gpu) diff --git a/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc new file mode 100644 index 0000000000000..548119cef501b --- /dev/null +++ b/RecoLocalTracker/SiPixelRecHits/src/PixelCPEFast.cc @@ -0,0 +1,561 @@ +#include + +#include "CondFormats/SiPixelTransient/interface/SiPixelTemplate.h" +#include "DataFormats/DetId/interface/DetId.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "Geometry/CommonDetUnit/interface/PixelGeomDetUnit.h" +#include "Geometry/TrackerGeometryBuilder/interface/RectangularPixelTopology.h" +#include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "MagneticField/Engine/interface/MagneticField.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/PixelCPEFast.h" + +// Services +// this is needed to get errors from templates + +namespace { + constexpr float micronsToCm = 1.0e-4; +} + +//----------------------------------------------------------------------------- +//! The constructor. +//----------------------------------------------------------------------------- +PixelCPEFast::PixelCPEFast(edm::ParameterSet const& conf, + const MagneticField* mag, + const TrackerGeometry& geom, + const TrackerTopology& ttopo, + const SiPixelLorentzAngle* lorentzAngle, + const SiPixelGenErrorDBObject* genErrorDBObject, + const SiPixelLorentzAngle* lorentzAngleWidth) + : PixelCPEBase(conf, mag, geom, ttopo, lorentzAngle, genErrorDBObject, nullptr, lorentzAngleWidth, 0), + edgeClusterErrorX_(conf.getParameter("EdgeClusterErrorX")), + edgeClusterErrorY_(conf.getParameter("EdgeClusterErrorY")), + useErrorsFromTemplates_(conf.getParameter("UseErrorsFromTemplates")), + truncatePixelCharge_(conf.getParameter("TruncatePixelCharge")) { + // Use errors from templates or from GenError + if (useErrorsFromTemplates_) { + if (!SiPixelGenError::pushfile(*genErrorDBObject_, thePixelGenError_)) + throw cms::Exception("InvalidCalibrationLoaded") + << "ERROR: GenErrors not filled correctly. Check the sqlite file. Using SiPixelTemplateDBObject version " + << (*genErrorDBObject_).version(); + } + + // Rechit errors in case other, more correct, errors are not vailable + // These are constants. Maybe there is a more efficienct way to store them. + xerr_barrel_l1_ = {0.00115, 0.00120, 0.00088}; + xerr_barrel_l1_def_ = 0.01030; + yerr_barrel_l1_ = {0.00375, 0.00230, 0.00250, 0.00250, 0.00230, 0.00230, 0.00210, 0.00210, 0.00240}; + yerr_barrel_l1_def_ = 0.00210; + xerr_barrel_ln_ = {0.00115, 0.00120, 0.00088}; + xerr_barrel_ln_def_ = 0.01030; + yerr_barrel_ln_ = {0.00375, 0.00230, 0.00250, 0.00250, 0.00230, 0.00230, 0.00210, 0.00210, 0.00240}; + yerr_barrel_ln_def_ = 0.00210; + xerr_endcap_ = {0.0020, 0.0020}; + xerr_endcap_def_ = 0.0020; + yerr_endcap_ = {0.00210}; + yerr_endcap_def_ = 0.00075; + + fillParamsForGpu(); + + cpuData_ = { + &m_commonParamsGPU, + m_detParamsGPU.data(), + &m_layerGeometry, + &m_averageGeometry, + }; +} + +const pixelCPEforGPU::ParamsOnGPU* PixelCPEFast::getGPUProductAsync(cudaStream_t cudaStream) const { + const auto& data = gpuData_.dataForCurrentDeviceAsync(cudaStream, [this](GPUData& data, cudaStream_t stream) { + // and now copy to device... + cudaCheck(cudaMalloc((void**)&data.paramsOnGPU_h.m_commonParams, sizeof(pixelCPEforGPU::CommonParams))); + cudaCheck(cudaMalloc((void**)&data.paramsOnGPU_h.m_detParams, + this->m_detParamsGPU.size() * sizeof(pixelCPEforGPU::DetParams))); + cudaCheck(cudaMalloc((void**)&data.paramsOnGPU_h.m_averageGeometry, sizeof(pixelCPEforGPU::AverageGeometry))); + cudaCheck(cudaMalloc((void**)&data.paramsOnGPU_h.m_layerGeometry, sizeof(pixelCPEforGPU::LayerGeometry))); + cudaCheck(cudaMalloc((void**)&data.paramsOnGPU_d, sizeof(pixelCPEforGPU::ParamsOnGPU))); + + cudaCheck(cudaMemcpyAsync( + data.paramsOnGPU_d, &data.paramsOnGPU_h, sizeof(pixelCPEforGPU::ParamsOnGPU), cudaMemcpyDefault, stream)); + cudaCheck(cudaMemcpyAsync((void*)data.paramsOnGPU_h.m_commonParams, + &this->m_commonParamsGPU, + sizeof(pixelCPEforGPU::CommonParams), + cudaMemcpyDefault, + stream)); + cudaCheck(cudaMemcpyAsync((void*)data.paramsOnGPU_h.m_averageGeometry, + &this->m_averageGeometry, + sizeof(pixelCPEforGPU::AverageGeometry), + cudaMemcpyDefault, + stream)); + cudaCheck(cudaMemcpyAsync((void*)data.paramsOnGPU_h.m_layerGeometry, + &this->m_layerGeometry, + sizeof(pixelCPEforGPU::LayerGeometry), + cudaMemcpyDefault, + stream)); + cudaCheck(cudaMemcpyAsync((void*)data.paramsOnGPU_h.m_detParams, + this->m_detParamsGPU.data(), + this->m_detParamsGPU.size() * sizeof(pixelCPEforGPU::DetParams), + cudaMemcpyDefault, + stream)); + }); + return data.paramsOnGPU_d; +} + +void PixelCPEFast::fillParamsForGpu() { + m_commonParamsGPU.theThicknessB = m_DetParams.front().theThickness; + m_commonParamsGPU.theThicknessE = m_DetParams.back().theThickness; + m_commonParamsGPU.thePitchX = m_DetParams[0].thePitchX; + m_commonParamsGPU.thePitchY = m_DetParams[0].thePitchY; + + LogDebug("PixelCPEFast") << "pitch & thickness " << m_commonParamsGPU.thePitchX << ' ' << m_commonParamsGPU.thePitchY + << " " << m_commonParamsGPU.theThicknessB << ' ' << m_commonParamsGPU.theThicknessE; + + // zero average geometry + memset(&m_averageGeometry, 0, sizeof(pixelCPEforGPU::AverageGeometry)); + + uint32_t oldLayer = 0; + uint32_t oldLadder = 0; + float rl = 0; + float zl = 0; + float miz = 90, mxz = 0; + float pl = 0; + int nl = 0; + m_detParamsGPU.resize(m_DetParams.size()); + for (auto i = 0U; i < m_DetParams.size(); ++i) { + auto& p = m_DetParams[i]; + auto& g = m_detParamsGPU[i]; + + assert(p.theDet->index() == int(i)); + assert(m_commonParamsGPU.thePitchY == p.thePitchY); + assert(m_commonParamsGPU.thePitchX == p.thePitchX); + + g.isBarrel = GeomDetEnumerators::isBarrel(p.thePart); + g.isPosZ = p.theDet->surface().position().z() > 0; + g.layer = ttopo_.layer(p.theDet->geographicalId()); + g.index = i; // better be! + g.rawId = p.theDet->geographicalId(); + assert((g.isBarrel ? m_commonParamsGPU.theThicknessB : m_commonParamsGPU.theThicknessE) == p.theThickness); + + auto ladder = ttopo_.pxbLadder(p.theDet->geographicalId()); + if (oldLayer != g.layer) { + oldLayer = g.layer; + LogDebug("PixelCPEFast") << "new layer at " << i << (g.isBarrel ? " B " : (g.isPosZ ? " E+ " : " E- ")) + << g.layer << " starting at " << g.rawId << '\n' + << "old layer had " << nl << " ladders"; + nl = 0; + } + if (oldLadder != ladder) { + oldLadder = ladder; + LogDebug("PixelCPEFast") << "new ladder at " << i << (g.isBarrel ? " B " : (g.isPosZ ? " E+ " : " E- ")) + << ladder << " starting at " << g.rawId << '\n' + << "old ladder ave z,r,p mz " << zl / 8.f << " " << rl / 8.f << " " << pl / 8.f << ' ' + << miz << ' ' << mxz; + rl = 0; + zl = 0; + pl = 0; + miz = 90; + mxz = 0; + nl++; + } + + g.shiftX = 0.5f * p.lorentzShiftInCmX; + g.shiftY = 0.5f * p.lorentzShiftInCmY; + g.chargeWidthX = p.lorentzShiftInCmX * p.widthLAFractionX; + g.chargeWidthY = p.lorentzShiftInCmY * p.widthLAFractionY; + + g.x0 = p.theOrigin.x(); + g.y0 = p.theOrigin.y(); + g.z0 = p.theOrigin.z(); + + 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); + + zl += vv.z(); + miz = std::min(miz, std::abs(vv.z())); + mxz = std::max(mxz, std::abs(vv.z())); + rl += vv.perp(); + pl += vv.phi(); // (not obvious) + + // errors ..... + ClusterParamGeneric cp; + auto gvx = p.theOrigin.x() + 40.f * m_commonParamsGPU.thePitchX; + auto gvy = p.theOrigin.y(); + auto gvz = 1.f / p.theOrigin.z(); + //--- Note that the normalization is not required as only the ratio used + + // calculate angles + cp.cotalpha = gvx * gvz; + cp.cotbeta = gvy * gvz; + + cp.with_track_angle = false; + + auto lape = p.theDet->localAlignmentError(); + if (lape.invalid()) + lape = LocalError(); // zero.... + +#ifdef EDM_ML_DEBUG + auto m = 10000.f; + for (float qclus = 15000; qclus < 35000; qclus += 15000) { + errorFromTemplates(p, cp, qclus); + LogDebug("PixelCPEFast") << i << ' ' << qclus << ' ' << cp.pixmx << ' ' << m * cp.sigmax << ' ' << m * cp.sx1 + << ' ' << m * cp.sx2 << ' ' << m * cp.sigmay << ' ' << m * cp.sy1 << ' ' << m * cp.sy2; + } + LogDebug("PixelCPEFast") << i << ' ' << m * std::sqrt(lape.xx()) << ' ' << m * std::sqrt(lape.yy()); +#endif // EDM_ML_DEBUG + + errorFromTemplates(p, cp, 20000.f); + g.pixmx = std::max(0, cp.pixmx); + 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; + + 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()); + } + } + + // compute ladder baricenter (only in global z) for the barrel + auto& aveGeom = m_averageGeometry; + int il = 0; + for (int im = 0, nm = phase1PixelTopology::numberOfModulesInBarrel; im < nm; ++im) { + auto const& g = m_detParamsGPU[im]; + il = im / 8; + assert(il < int(phase1PixelTopology::numberOfLaddersInBarrel)); + auto z = g.frame.z(); + aveGeom.ladderZ[il] += 0.125f * z; + aveGeom.ladderMinZ[il] = std::min(aveGeom.ladderMinZ[il], z); + aveGeom.ladderMaxZ[il] = std::max(aveGeom.ladderMaxZ[il], z); + aveGeom.ladderX[il] += 0.125f * g.frame.x(); + aveGeom.ladderY[il] += 0.125f * g.frame.y(); + aveGeom.ladderR[il] += 0.125f * sqrt(g.frame.x() * g.frame.x() + g.frame.y() * g.frame.y()); + } + assert(il + 1 == int(phase1PixelTopology::numberOfLaddersInBarrel)); + // add half_module and tollerance + constexpr float module_length = 6.7f; + constexpr float module_tolerance = 0.2f; + for (int il = 0, nl = phase1PixelTopology::numberOfLaddersInBarrel; il < nl; ++il) { + aveGeom.ladderMinZ[il] -= (0.5f * module_length - module_tolerance); + aveGeom.ladderMaxZ[il] += (0.5f * module_length - module_tolerance); + } + + // compute "max z" for first layer in endcap (should we restrict to the outermost ring?) + for (auto im = phase1PixelTopology::layerStart[4]; im < phase1PixelTopology::layerStart[5]; ++im) { + auto const& g = m_detParamsGPU[im]; + aveGeom.endCapZ[0] = std::max(aveGeom.endCapZ[0], g.frame.z()); + } + for (auto im = phase1PixelTopology::layerStart[7]; im < phase1PixelTopology::layerStart[8]; ++im) { + auto const& g = m_detParamsGPU[im]; + aveGeom.endCapZ[1] = std::min(aveGeom.endCapZ[1], g.frame.z()); + } + // correct for outer ring being closer + aveGeom.endCapZ[0] -= 1.5f; + aveGeom.endCapZ[1] += 1.5f; + +#ifdef EDM_ML_DEBUG + for (int jl = 0, nl = phase1PixelTopology::numberOfLaddersInBarrel; jl < nl; ++jl) { + LogDebug("PixelCPEFast") << jl << ':' << aveGeom.ladderR[jl] << '/' + << std::sqrt(aveGeom.ladderX[jl] * aveGeom.ladderX[jl] + + aveGeom.ladderY[jl] * aveGeom.ladderY[jl]) + << ',' << aveGeom.ladderZ[jl] << ',' << aveGeom.ladderMinZ[jl] << ',' + << aveGeom.ladderMaxZ[jl] << '\n'; + } + LogDebug("PixelCPEFast") << aveGeom.endCapZ[0] << ' ' << aveGeom.endCapZ[1]; +#endif // EDM_ML_DEBUG + + // fill Layer and ladders geometry + memcpy(m_layerGeometry.layerStart, phase1PixelTopology::layerStart, sizeof(phase1PixelTopology::layerStart)); + memcpy(m_layerGeometry.layer, phase1PixelTopology::layer.data(), phase1PixelTopology::layer.size()); +} + +PixelCPEFast::GPUData::~GPUData() { + if (paramsOnGPU_d != nullptr) { + cudaFree((void*)paramsOnGPU_h.m_commonParams); + cudaFree((void*)paramsOnGPU_h.m_detParams); + cudaFree((void*)paramsOnGPU_h.m_averageGeometry); + cudaFree((void*)paramsOnGPU_h.m_layerGeometry); + cudaFree(paramsOnGPU_d); + } +} + +std::unique_ptr PixelCPEFast::createClusterParam(const SiPixelCluster& cl) const { + return std::make_unique(cl); +} + +void PixelCPEFast::errorFromTemplates(DetParam const& theDetParam, + ClusterParamGeneric& theClusterParam, + float qclus) const { + float locBz = theDetParam.bz; + float locBx = theDetParam.bx; + LogDebug("PixelCPEFast") << "PixelCPEFast::localPosition(...) : locBz = " << locBz; + + 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 + theClusterParam.sigmax = -999.9; // CPE Generic x-error for multi-pixel cluster + theClusterParam.sy1 = -999.9; // CPE Generic y-error for single single-pixel + 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, + 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; +} + +//----------------------------------------------------------------------------- +//! 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; + } + + int Q_f_X; //!< Q of the first pixel in X + int Q_l_X; //!< Q of the last pixel in X + int Q_f_Y; //!< Q of the first pixel in Y + int Q_l_Y; //!< Q of the last pixel in Y + collect_edge_charges(theClusterParam, Q_f_X, Q_l_X, Q_f_Y, Q_l_Y, useErrorsFromTemplates_ && truncatePixelCharge_); + + // do GPU like ... + pixelCPEforGPU::ClusParams cp; + + cp.minRow[0] = theClusterParam.theCluster->minPixelRow(); + cp.maxRow[0] = theClusterParam.theCluster->maxPixelRow(); + cp.minCol[0] = theClusterParam.theCluster->minPixelCol(); + cp.maxCol[0] = theClusterParam.theCluster->maxPixelCol(); + + cp.Q_f_X[0] = Q_f_X; + cp.Q_l_X[0] = Q_l_X; + cp.Q_f_Y[0] = Q_f_Y; + cp.Q_l_Y[0] = Q_l_Y; + + auto ind = theDetParam.theDet->index(); + pixelCPEforGPU::position(m_commonParamsGPU, m_detParamsGPU[ind], cp, 0); + auto xPos = cp.xpos[0]; + auto yPos = cp.ypos[0]; + + LogDebug("PixelCPEFast") << " in PixelCPEFast:localPosition - pos = " << xPos << " " << yPos << " size " + << cp.maxRow[0] - cp.minRow[0] << ' ' << cp.maxCol[0] - cp.minCol[0]; + + //--- Now put the two together + LocalPoint pos_in_local(xPos, yPos); + return pos_in_local; +} + +//----------------------------------------------------------------------------- +//! Collect the edge charges in x and y, in a single pass over the pixel vector. +//! Calculate charge in the first and last pixel projected in x and y +//! and the inner cluster charge, projected in x and y. +//----------------------------------------------------------------------------- +void PixelCPEFast::collect_edge_charges(ClusterParam& theClusterParamBase, //!< input, the cluster + int& Q_f_X, //!< output, Q first in X + int& Q_l_X, //!< output, Q last in X + int& Q_f_Y, //!< output, Q first in Y + int& Q_l_Y, //!< output, Q last in Y + bool truncate) { + ClusterParamGeneric& theClusterParam = static_cast(theClusterParamBase); + + // Initialize return variables. + Q_f_X = Q_l_X = 0; + Q_f_Y = Q_l_Y = 0; + + // Obtain boundaries in index units + int xmin = theClusterParam.theCluster->minPixelRow(); + int xmax = theClusterParam.theCluster->maxPixelRow(); + int ymin = theClusterParam.theCluster->minPixelCol(); + int ymax = theClusterParam.theCluster->maxPixelCol(); + + // Iterate over the pixels. + int isize = theClusterParam.theCluster->size(); + for (int i = 0; i != isize; ++i) { + auto const& pixel = theClusterParam.theCluster->pixel(i); + // ggiurgiu@fnal.gov: add pixel charge truncation + int pix_adc = pixel.adc; + if (truncate) + pix_adc = std::min(pix_adc, theClusterParam.pixmx); + + // + // X projection + if (pixel.x == xmin) + Q_f_X += pix_adc; + if (pixel.x == xmax) + Q_l_X += pix_adc; + // + // Y projection + if (pixel.y == ymin) + Q_f_Y += pix_adc; + if (pixel.y == ymax) + Q_l_Y += pix_adc; + } +} + +//============== INFLATED ERROR AND ERRORS FROM DB BELOW ================ + +//------------------------------------------------------------------------- +// Hit error in the local frame +//------------------------------------------------------------------------- +LocalError PixelCPEFast::localError(DetParam const& theDetParam, ClusterParam& theClusterParamBase) const { + ClusterParamGeneric& theClusterParam = static_cast(theClusterParamBase); + + // Default errors are the maximum error used for edge clusters. + // These are determined by looking at residuals for edge clusters + float xerr = edgeClusterErrorX_ * micronsToCm; + float yerr = edgeClusterErrorY_ * micronsToCm; + + // Find if cluster is at the module edge. + int maxPixelCol = theClusterParam.theCluster->maxPixelCol(); + int maxPixelRow = theClusterParam.theCluster->maxPixelRow(); + int minPixelCol = theClusterParam.theCluster->minPixelCol(); + int minPixelRow = theClusterParam.theCluster->minPixelRow(); + + bool edgex = phase1PixelTopology::isEdgeX(minPixelRow) | phase1PixelTopology::isEdgeX(maxPixelRow); + bool edgey = phase1PixelTopology::isEdgeY(minPixelCol) | phase1PixelTopology::isEdgeY(maxPixelCol); + + unsigned int sizex = theClusterParam.theCluster->sizeX(); + unsigned int sizey = theClusterParam.theCluster->sizeY(); + + // Find if cluster contains double (big) pixels. + bool bigInX = theDetParam.theRecTopol->containsBigPixelInX(minPixelRow, maxPixelRow); + bool bigInY = theDetParam.theRecTopol->containsBigPixelInY(minPixelCol, maxPixelCol); + + if (useErrorsFromTemplates_) { + // + // Use template errors + + if (!edgex) { // Only use this for non-edge clusters + if (sizex == 1) { + if (!bigInX) { + xerr = theClusterParam.sx1; + } else { + xerr = theClusterParam.sx2; + } + } else { + xerr = theClusterParam.sigmax; + } + } + + if (!edgey) { // Only use for non-edge clusters + if (sizey == 1) { + if (!bigInY) { + yerr = theClusterParam.sy1; + } else { + yerr = theClusterParam.sy2; + } + } else { + yerr = theClusterParam.sigmay; + } + } + + } else { // simple errors + + // This are the simple errors, hardcoded in the code + LogDebug("PixelCPEFast") << "Track angles are not known.\n" + << "Default angle estimation which assumes track from PV (0,0,0) does not work."; + + if (GeomDetEnumerators::isTrackerPixel(theDetParam.thePart)) { + if (GeomDetEnumerators::isBarrel(theDetParam.thePart)) { + DetId id = (theDetParam.theDet->geographicalId()); + int layer = ttopo_.layer(id); + if (layer == 1) { + if (!edgex) { + if (sizex <= xerr_barrel_l1_.size()) + xerr = xerr_barrel_l1_[sizex - 1]; + else + xerr = xerr_barrel_l1_def_; + } + + if (!edgey) { + if (sizey <= yerr_barrel_l1_.size()) + yerr = yerr_barrel_l1_[sizey - 1]; + else + yerr = yerr_barrel_l1_def_; + } + } else { // layer 2,3 + if (!edgex) { + if (sizex <= xerr_barrel_ln_.size()) + xerr = xerr_barrel_ln_[sizex - 1]; + else + xerr = xerr_barrel_ln_def_; + } + + if (!edgey) { + if (sizey <= yerr_barrel_ln_.size()) + yerr = yerr_barrel_ln_[sizey - 1]; + else + yerr = yerr_barrel_ln_def_; + } + } + + } else { // EndCap + + if (!edgex) { + if (sizex <= xerr_endcap_.size()) + xerr = xerr_endcap_[sizex - 1]; + else + xerr = xerr_endcap_def_; + } + + if (!edgey) { + if (sizey <= yerr_endcap_.size()) + yerr = yerr_endcap_[sizey - 1]; + else + yerr = yerr_endcap_def_; + } + } // end endcap + } + + } // end + + LogDebug("PixelCPEFast") << " errors " << xerr << " " << yerr; + + auto xerr_sq = xerr * xerr; + auto yerr_sq = yerr * yerr; + + return LocalError(xerr_sq, 0, yerr_sq); +} + +void PixelCPEFast::fillPSetDescription(edm::ParameterSetDescription& desc) {} diff --git a/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py b/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py index 34ee6fadb04de..424ac13a43627 100644 --- a/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py +++ b/RecoPixelVertexing/Configuration/python/RecoPixelVertexing_cff.py @@ -4,7 +4,21 @@ # # for STARTUP ONLY use try and use Offline 3D PV from pixelTracks, with adaptive vertex # -#from RecoPixelVertexing.PixelVertexFinding.PixelVertexes_cff import * -from RecoVertex.PrimaryVertexProducer.OfflinePixel3DPrimaryVertices_cfi import * +from RecoPixelVertexing.PixelVertexFinding.PixelVertexes_cff import * +#from RecoVertex.PrimaryVertexProducer.OfflinePixel3DPrimaryVertices_cfi import * recopixelvertexingTask = cms.Task(pixelTracksTask,pixelVertices) recopixelvertexing = cms.Sequence(recopixelvertexingTask) + +from Configuration.ProcessModifiers.gpu_cff import gpu + +from RecoPixelVertexing.PixelVertexFinding.pixelVertexCUDA_cfi import pixelVertexCUDA +from RecoPixelVertexing.PixelVertexFinding.pixelVertexSoA_cfi import pixelVertexSoA +from RecoPixelVertexing.PixelVertexFinding.pixelVertexFromSoA_cfi import pixelVertexFromSoA as _pixelVertexFromSoA + +_pixelVertexingCUDATask = cms.Task(pixelTracksTask,pixelVertexCUDA,pixelVertexSoA,pixelVertices) + +# pixelVertexSoAonCPU = pixelVertexCUDA.clone() +# pixelVertexSoAonCPU.onGPU = False; + +gpu.toReplaceWith(pixelVertices,_pixelVertexFromSoA) +gpu.toReplaceWith(recopixelvertexingTask,_pixelVertexingCUDATask) diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py deleted file mode 100644 index 4713b64e5e48a..0000000000000 --- a/RecoPixelVertexing/Configuration/python/customizePixelTracksForProfiling.py +++ /dev/null @@ -1,15 +0,0 @@ -import FWCore.ParameterSet.Config as cms - -def customizePixelTracksForProfiling(process): - process.out = cms.OutputModule("AsciiOutputModule", - outputCommands = cms.untracked.vstring( - "keep *_pixelTracks_*_*", - ), - verbosity = cms.untracked.uint32(0), - ) - - process.outPath = cms.EndPath(process.out) - - process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.outPath) - - return process diff --git a/RecoPixelVertexing/Configuration/python/customizePixelTracksSoAonCPU.py b/RecoPixelVertexing/Configuration/python/customizePixelTracksSoAonCPU.py new file mode 100644 index 0000000000000..909959f2d81be --- /dev/null +++ b/RecoPixelVertexing/Configuration/python/customizePixelTracksSoAonCPU.py @@ -0,0 +1,61 @@ +import FWCore.ParameterSet.Config as cms + +def customizePixelTracksSoAonCPU(process): + + process.CUDAService = cms.Service('CUDAService', + enabled = cms.untracked.bool(False) + ) + + # ensure the same results when running on GPU (which supports only the 'HLT' payload) and CPU + process.siPixelClustersPreSplitting.cpu.payloadType = cms.string('HLT') + + from RecoLocalTracker.SiPixelRecHits.siPixelRecHitSoAFromLegacy_cfi import siPixelRecHitSoAFromLegacy + process.siPixelRecHitsPreSplitting = siPixelRecHitSoAFromLegacy.clone( + convertToLegacy = True + ) + + from RecoPixelVertexing.PixelTriplets.caHitNtupletCUDA_cfi import caHitNtupletCUDA + process.pixelTrackSoA = caHitNtupletCUDA.clone( + onGPU = False, + pixelRecHitSrc = 'siPixelRecHitsPreSplitting' + ) + + from RecoPixelVertexing.PixelVertexFinding.pixelVertexCUDA_cfi import pixelVertexCUDA + process.pixelVertexSoA = pixelVertexCUDA.clone( + onGPU = False, + pixelTrackSrc = 'pixelTrackSoA' + ) + + from RecoPixelVertexing.PixelTrackFitting.pixelTrackProducerFromSoA_cfi import pixelTrackProducerFromSoA + process.pixelTracks = pixelTrackProducerFromSoA.clone( + pixelRecHitLegacySrc = 'siPixelRecHitsPreSplitting' + ) + + from RecoPixelVertexing.PixelVertexFinding.pixelVertexFromSoA_cfi import pixelVertexFromSoA + process.pixelVertices = pixelVertexFromSoA.clone() + + process.reconstruction_step += process.siPixelRecHitsPreSplitting + process.pixelTrackSoA + process.pixelVertexSoA + + return process + + +def customizePixelTracksForTriplets(process): + + from HLTrigger.Configuration.common import producers_by_type + for producer in producers_by_type(process, 'CAHitNtupletCUDA'): + producer.includeJumpingForwardDoublets = True + producer.minHitsPerNtuplet = 3 + + return process + + +def customizePixelTracksSoAonCPUForProfiling(process): + + process.MessageLogger.cerr.FwkReport.reportEvery = 100 + + process = customizePixelTracksSoAonCPU(process) + process.siPixelRecHitSoAFromLegacy.convertToLegacy = False + + process.TkSoA = cms.Path(process.offlineBeamSpot + process.siPixelDigis + process.siPixelClustersPreSplitting + process.siPixelRecHitSoAFromLegacy + process.pixelTrackSoA + process.pixelVertexSoA) + process.schedule = cms.Schedule(process.TkSoA) + return process diff --git a/RecoPixelVertexing/PixelTrackFitting/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/BuildFile.xml index e6fc938dc25a7..a589aad036996 100644 --- a/RecoPixelVertexing/PixelTrackFitting/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/BuildFile.xml @@ -1,3 +1,5 @@ + + diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h b/RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h new file mode 100644 index 0000000000000..be1b67be89c35 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h @@ -0,0 +1,565 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_BrokenLine_h +#define RecoPixelVertexing_PixelTrackFitting_interface_BrokenLine_h + +#include + +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h" + +namespace BrokenLine { + + //!< Karimäki's parameters: (phi, d, k=1/R) + /*!< covariance matrix: \n + |cov(phi,phi)|cov( d ,phi)|cov( k ,phi)| \n + |cov(phi, d )|cov( d , d )|cov( k , d )| \n + |cov(phi, k )|cov( d , k )|cov( k , k )| + */ + using karimaki_circle_fit = Rfit::circle_fit; + + /*! + \brief data needed for the Broken Line fit procedure. + */ + template + struct PreparedBrokenLineData { + int q; //!< particle charge + Rfit::Matrix2xNd radii; //!< xy data in the system in which the pre-fitted center is the origin + Rfit::VectorNd s; //!< total distance traveled in the transverse plane + // starting from the pre-fitted closest approach + Rfit::VectorNd S; //!< total distance traveled (three-dimensional) + Rfit::VectorNd Z; //!< orthogonal coordinate to the pre-fitted line in the sz plane + Rfit::VectorNd VarBeta; //!< kink angles in the SZ plane + }; + + /*! + \brief Computes the Coulomb multiple scattering variance of the planar angle. + + \param length length of the track in the material. + \param B magnetic field in Gev/cm/c. + \param R radius of curvature (needed to evaluate p). + \param Layer denotes which of the four layers of the detector is the endpoint of the multiple scattered track. For example, if Layer=3, then the particle has just gone through the material between the second and the third layer. + + \todo add another Layer variable to identify also the start point of the track, so if there are missing hits or multiple hits, the part of the detector that the particle has traversed can be exactly identified. + + \warning the formula used here assumes beta=1, and so neglects the dependence of theta_0 on the mass of the particle at fixed momentum. + + \return the variance of the planar angle ((theta_0)^2 /3). + */ + __host__ __device__ inline double MultScatt( + const double& length, const double B, const double R, int Layer, double slope) { + // limit R to 20GeV... + auto pt2 = std::min(20., B * R); + pt2 *= pt2; + constexpr double XXI_0 = 0.06 / 16.; //!< inverse of radiation length of the material in cm + //if(Layer==1) XXI_0=0.06/16.; + // else XXI_0=0.06/16.; + //XX_0*=1; + constexpr double geometry_factor = + 0.7; //!< number between 1/3 (uniform material) and 1 (thin scatterer) to be manually tuned + constexpr double fact = geometry_factor * Rfit::sqr(13.6 / 1000.); + return fact / (pt2 * (1. + Rfit::sqr(slope))) * (std::abs(length) * XXI_0) * + Rfit::sqr(1. + 0.038 * log(std::abs(length) * XXI_0)); + } + + /*! + \brief Computes the 2D rotation matrix that transforms the line y=slope*x into the line y=0. + + \param slope tangent of the angle of rotation. + + \return 2D rotation matrix. + */ + __host__ __device__ inline Rfit::Matrix2d RotationMatrix(double slope) { + Rfit::Matrix2d Rot; + Rot(0, 0) = 1. / sqrt(1. + Rfit::sqr(slope)); + Rot(0, 1) = slope * Rot(0, 0); + Rot(1, 0) = -Rot(0, 1); + Rot(1, 1) = Rot(0, 0); + return Rot; + } + + /*! + \brief Changes the Karimäki parameters (and consequently their covariance matrix) under a translation of the coordinate system, such that the old origin has coordinates (x0,y0) in the new coordinate system. The formulas are taken from Karimäki V., 1990, Effective circle fitting for particle trajectories, Nucl. Instr. and Meth. A305 (1991) 187. + + \param circle circle fit in the old coordinate system. + \param x0 x coordinate of the translation vector. + \param y0 y coordinate of the translation vector. + \param jacobian passed by reference in order to save stack. + */ + __host__ __device__ inline void TranslateKarimaki(karimaki_circle_fit& circle, + double x0, + double y0, + Rfit::Matrix3d& jacobian) { + double A, U, BB, C, DO, DP, uu, xi, v, mu, lambda, zeta; + DP = x0 * cos(circle.par(0)) + y0 * sin(circle.par(0)); + DO = x0 * sin(circle.par(0)) - y0 * cos(circle.par(0)) + circle.par(1); + uu = 1 + circle.par(2) * circle.par(1); + C = -circle.par(2) * y0 + uu * cos(circle.par(0)); + BB = circle.par(2) * x0 + uu * sin(circle.par(0)); + A = 2. * DO + circle.par(2) * (Rfit::sqr(DO) + Rfit::sqr(DP)); + U = sqrt(1. + circle.par(2) * A); + xi = 1. / (Rfit::sqr(BB) + Rfit::sqr(C)); + v = 1. + circle.par(2) * DO; + lambda = (0.5 * A) / (U * Rfit::sqr(1. + U)); + mu = 1. / (U * (1. + U)) + circle.par(2) * lambda; + zeta = Rfit::sqr(DO) + Rfit::sqr(DP); + + jacobian << xi * uu * v, -xi * Rfit::sqr(circle.par(2)) * DP, xi * DP, 2. * mu * uu * DP, 2. * mu * v, + mu * zeta - lambda * A, 0, 0, 1.; + + circle.par(0) = atan2(BB, C); + circle.par(1) = A / (1 + U); + // circle.par(2)=circle.par(2); + + circle.cov = jacobian * circle.cov * jacobian.transpose(); + } + + /*! + \brief Computes the data needed for the Broken Line fit procedure that are mainly common for the circle and the line fit. + + \param hits hits coordinates. + \param hits_cov hits covariance matrix. + \param fast_fit pre-fit result in the form (X0,Y0,R,tan(theta)). + \param B magnetic field in Gev/cm/c. + \param results PreparedBrokenLineData to be filled (see description of PreparedBrokenLineData). + */ + template + __host__ __device__ inline void prepareBrokenLineData(const M3xN& hits, + const V4& fast_fit, + const double B, + PreparedBrokenLineData& results) { + constexpr auto n = N; + u_int i; + Rfit::Vector2d d; + Rfit::Vector2d e; + + d = hits.block(0, 1, 2, 1) - hits.block(0, 0, 2, 1); + e = hits.block(0, n - 1, 2, 1) - hits.block(0, n - 2, 2, 1); + results.q = Rfit::cross2D(d, e) > 0 ? -1 : 1; + + const double slope = -results.q / fast_fit(3); + + Rfit::Matrix2d R = RotationMatrix(slope); + + // calculate radii and s + results.radii = hits.block(0, 0, 2, n) - fast_fit.head(2) * Rfit::MatrixXd::Constant(1, n, 1); + e = -fast_fit(2) * fast_fit.head(2) / fast_fit.head(2).norm(); + for (i = 0; i < n; i++) { + d = results.radii.block(0, i, 2, 1); + results.s(i) = results.q * fast_fit(2) * atan2(Rfit::cross2D(d, e), d.dot(e)); // calculates the arc length + } + Rfit::VectorNd z = hits.block(2, 0, 1, n).transpose(); + + //calculate S and Z + Rfit::Matrix2xNd pointsSZ = Rfit::Matrix2xNd::Zero(); + for (i = 0; i < n; i++) { + pointsSZ(0, i) = results.s(i); + pointsSZ(1, i) = z(i); + pointsSZ.block(0, i, 2, 1) = R * pointsSZ.block(0, i, 2, 1); + } + results.S = pointsSZ.block(0, 0, 1, n).transpose(); + results.Z = pointsSZ.block(1, 0, 1, n).transpose(); + + //calculate VarBeta + results.VarBeta(0) = results.VarBeta(n - 1) = 0; + for (i = 1; i < n - 1; i++) { + results.VarBeta(i) = MultScatt(results.S(i + 1) - results.S(i), B, fast_fit(2), i + 2, slope) + + MultScatt(results.S(i) - results.S(i - 1), B, fast_fit(2), i + 1, slope); + } + } + + /*! + \brief Computes the n-by-n band matrix obtained minimizing the Broken Line's cost function w.r.t u. This is the whole matrix in the case of the line fit and the main n-by-n block in the case of the circle fit. + + \param w weights of the first part of the cost function, the one with the measurements and not the angles (\sum_{i=1}^n w*(y_i-u_i)^2). + \param S total distance traveled by the particle from the pre-fitted closest approach. + \param VarBeta kink angles' variance. + + \return the n-by-n matrix of the linear system + */ + template + __host__ __device__ inline Rfit::MatrixNd MatrixC_u(const Rfit::VectorNd& w, + const Rfit::VectorNd& S, + const Rfit::VectorNd& VarBeta) { + constexpr u_int n = N; + u_int i; + + Rfit::MatrixNd C_U = Rfit::MatrixNd::Zero(); + for (i = 0; i < n; i++) { + C_U(i, i) = w(i); + if (i > 1) + C_U(i, i) += 1. / (VarBeta(i - 1) * Rfit::sqr(S(i) - S(i - 1))); + if (i > 0 && i < n - 1) + C_U(i, i) += (1. / VarBeta(i)) * Rfit::sqr((S(i + 1) - S(i - 1)) / ((S(i + 1) - S(i)) * (S(i) - S(i - 1)))); + if (i < n - 2) + C_U(i, i) += 1. / (VarBeta(i + 1) * Rfit::sqr(S(i + 1) - S(i))); + + if (i > 0 && i < n - 1) + C_U(i, i + 1) = + 1. / (VarBeta(i) * (S(i + 1) - S(i))) * (-(S(i + 1) - S(i - 1)) / ((S(i + 1) - S(i)) * (S(i) - S(i - 1)))); + if (i < n - 2) + C_U(i, i + 1) += 1. / (VarBeta(i + 1) * (S(i + 1) - S(i))) * + (-(S(i + 2) - S(i)) / ((S(i + 2) - S(i + 1)) * (S(i + 1) - S(i)))); + + if (i < n - 2) + C_U(i, i + 2) = 1. / (VarBeta(i + 1) * (S(i + 2) - S(i + 1)) * (S(i + 1) - S(i))); + + C_U(i, i) *= 0.5; + } + return C_U + C_U.transpose(); + } + + /*! + \brief A very fast helix fit. + + \param hits the measured hits. + + \return (X0,Y0,R,tan(theta)). + + \warning sign of theta is (intentionally, for now) mistaken for negative charges. + */ + + template + __host__ __device__ inline void BL_Fast_fit(const M3xN& hits, V4& result) { + constexpr uint32_t N = M3xN::ColsAtCompileTime; + constexpr auto n = N; // get the number of hits + + const Rfit::Vector2d a = hits.block(0, n / 2, 2, 1) - hits.block(0, 0, 2, 1); + const Rfit::Vector2d b = hits.block(0, n - 1, 2, 1) - hits.block(0, n / 2, 2, 1); + const Rfit::Vector2d c = hits.block(0, 0, 2, 1) - hits.block(0, n - 1, 2, 1); + + auto tmp = 0.5 / Rfit::cross2D(c, a); + result(0) = hits(0, 0) - (a(1) * c.squaredNorm() + c(1) * a.squaredNorm()) * tmp; + result(1) = hits(1, 0) + (a(0) * c.squaredNorm() + c(0) * a.squaredNorm()) * tmp; + // check Wikipedia for these formulas + + result(2) = sqrt(a.squaredNorm() * b.squaredNorm() * c.squaredNorm()) / (2. * std::abs(Rfit::cross2D(b, a))); + // Using Math Olympiad's formula R=abc/(4A) + + const Rfit::Vector2d d = hits.block(0, 0, 2, 1) - result.head(2); + const Rfit::Vector2d e = hits.block(0, n - 1, 2, 1) - result.head(2); + + result(3) = result(2) * atan2(Rfit::cross2D(d, e), d.dot(e)) / (hits(2, n - 1) - hits(2, 0)); + // ds/dz slope between last and first point + } + + /*! + \brief Performs the Broken Line fit in the curved track case (that is, the fit parameters are the interceptions u and the curvature correction \Delta\kappa). + + \param hits hits coordinates. + \param hits_cov hits covariance matrix. + \param fast_fit pre-fit result in the form (X0,Y0,R,tan(theta)). + \param B magnetic field in Gev/cm/c. + \param data PreparedBrokenLineData. + \param circle_results struct to be filled with the results in this form: + -par parameter of the line in this form: (phi, d, k); \n + -cov covariance matrix of the fitted parameter; \n + -chi2 value of the cost function in the minimum. + + \details The function implements the steps 2 and 3 of the Broken Line fit with the curvature correction.\n + The step 2 is the least square fit, done by imposing the minimum constraint on the cost function and solving the consequent linear system. It determines the fitted parameters u and \Delta\kappa and their covariance matrix. + The step 3 is the correction of the fast pre-fitted parameters for the innermost part of the track. It is first done in a comfortable coordinate system (the one in which the first hit is the origin) and then the parameters and their covariance matrix are transformed to the original coordinate system. + */ + template + __host__ __device__ inline void BL_Circle_fit(const M3xN& hits, + const M6xN& hits_ge, + const V4& fast_fit, + const double B, + PreparedBrokenLineData& data, + karimaki_circle_fit& circle_results) { + constexpr u_int n = N; + u_int i; + + circle_results.q = data.q; + auto& radii = data.radii; + const auto& s = data.s; + const auto& S = data.S; + auto& Z = data.Z; + auto& VarBeta = data.VarBeta; + const double slope = -circle_results.q / fast_fit(3); + VarBeta *= 1. + Rfit::sqr(slope); // the kink angles are projected! + + for (i = 0; i < n; i++) { + Z(i) = radii.block(0, i, 2, 1).norm() - fast_fit(2); + } + + Rfit::Matrix2d V; // covariance matrix + Rfit::VectorNd w; // weights + Rfit::Matrix2d RR; // rotation matrix point by point + //double Slope; // slope of the circle point by point + for (i = 0; i < n; i++) { + V(0, 0) = hits_ge.col(i)[0]; // x errors + V(0, 1) = V(1, 0) = hits_ge.col(i)[1]; // cov_xy + V(1, 1) = hits_ge.col(i)[2]; // y errors + //Slope=-radii(0,i)/radii(1,i); + RR = RotationMatrix(-radii(0, i) / radii(1, i)); + w(i) = 1. / ((RR * V * RR.transpose())(1, 1)); // compute the orthogonal weight point by point + } + + Rfit::VectorNplusONEd r_u; + r_u(n) = 0; + for (i = 0; i < n; i++) { + r_u(i) = w(i) * Z(i); + } + + Rfit::MatrixNplusONEd C_U; + C_U.block(0, 0, n, n) = MatrixC_u(w, s, VarBeta); + C_U(n, n) = 0; + //add the border to the C_u matrix + for (i = 0; i < n; i++) { + C_U(i, n) = 0; + if (i > 0 && i < n - 1) { + C_U(i, n) += + -(s(i + 1) - s(i - 1)) * (s(i + 1) - s(i - 1)) / (2. * VarBeta(i) * (s(i + 1) - s(i)) * (s(i) - s(i - 1))); + } + if (i > 1) { + C_U(i, n) += (s(i) - s(i - 2)) / (2. * VarBeta(i - 1) * (s(i) - s(i - 1))); + } + if (i < n - 2) { + C_U(i, n) += (s(i + 2) - s(i)) / (2. * VarBeta(i + 1) * (s(i + 1) - s(i))); + } + C_U(n, i) = C_U(i, n); + if (i > 0 && i < n - 1) + C_U(n, n) += Rfit::sqr(s(i + 1) - s(i - 1)) / (4. * VarBeta(i)); + } + +#ifdef CPP_DUMP + std::cout << "CU5\n" << C_U << std::endl; +#endif + Rfit::MatrixNplusONEd I; + math::cholesky::invert(C_U, I); + // Rfit::MatrixNplusONEd I = C_U.inverse(); +#ifdef CPP_DUMP + std::cout << "I5\n" << I << std::endl; +#endif + + Rfit::VectorNplusONEd u = I * r_u; // obtain the fitted parameters by solving the linear system + + // compute (phi, d_ca, k) in the system in which the midpoint of the first two corrected hits is the origin... + + radii.block(0, 0, 2, 1) /= radii.block(0, 0, 2, 1).norm(); + radii.block(0, 1, 2, 1) /= radii.block(0, 1, 2, 1).norm(); + + Rfit::Vector2d d = hits.block(0, 0, 2, 1) + (-Z(0) + u(0)) * radii.block(0, 0, 2, 1); + Rfit::Vector2d e = hits.block(0, 1, 2, 1) + (-Z(1) + u(1)) * radii.block(0, 1, 2, 1); + + circle_results.par << atan2((e - d)(1), (e - d)(0)), + -circle_results.q * (fast_fit(2) - sqrt(Rfit::sqr(fast_fit(2)) - 0.25 * (e - d).squaredNorm())), + circle_results.q * (1. / fast_fit(2) + u(n)); + + assert(circle_results.q * circle_results.par(1) <= 0); + + Rfit::Vector2d eMinusd = e - d; + double tmp1 = eMinusd.squaredNorm(); + + Rfit::Matrix3d jacobian; + jacobian << (radii(1, 0) * eMinusd(0) - eMinusd(1) * radii(0, 0)) / tmp1, + (radii(1, 1) * eMinusd(0) - eMinusd(1) * radii(0, 1)) / tmp1, 0, + (circle_results.q / 2) * (eMinusd(0) * radii(0, 0) + eMinusd(1) * radii(1, 0)) / + sqrt(Rfit::sqr(2 * fast_fit(2)) - tmp1), + (circle_results.q / 2) * (eMinusd(0) * radii(0, 1) + eMinusd(1) * radii(1, 1)) / + sqrt(Rfit::sqr(2 * fast_fit(2)) - tmp1), + 0, 0, 0, circle_results.q; + + circle_results.cov << I(0, 0), I(0, 1), I(0, n), I(1, 0), I(1, 1), I(1, n), I(n, 0), I(n, 1), I(n, n); + + circle_results.cov = jacobian * circle_results.cov * jacobian.transpose(); + + //...Translate in the system in which the first corrected hit is the origin, adding the m.s. correction... + + TranslateKarimaki(circle_results, 0.5 * (e - d)(0), 0.5 * (e - d)(1), jacobian); + circle_results.cov(0, 0) += (1 + Rfit::sqr(slope)) * MultScatt(S(1) - S(0), B, fast_fit(2), 2, slope); + + //...And translate back to the original system + + TranslateKarimaki(circle_results, d(0), d(1), jacobian); + + // compute chi2 + circle_results.chi2 = 0; + for (i = 0; i < n; i++) { + circle_results.chi2 += w(i) * Rfit::sqr(Z(i) - u(i)); + if (i > 0 && i < n - 1) + circle_results.chi2 += Rfit::sqr(u(i - 1) / (s(i) - s(i - 1)) - + u(i) * (s(i + 1) - s(i - 1)) / ((s(i + 1) - s(i)) * (s(i) - s(i - 1))) + + u(i + 1) / (s(i + 1) - s(i)) + (s(i + 1) - s(i - 1)) * u(n) / 2) / + VarBeta(i); + } + + // assert(circle_results.chi2>=0); + } + + /*! + \brief Performs the Broken Line fit in the straight track case (that is, the fit parameters are only the interceptions u). + + \param hits hits coordinates. + \param hits_cov hits covariance matrix. + \param fast_fit pre-fit result in the form (X0,Y0,R,tan(theta)). + \param B magnetic field in Gev/cm/c. + \param data PreparedBrokenLineData. + \param line_results struct to be filled with the results in this form: + -par parameter of the line in this form: (cot(theta), Zip); \n + -cov covariance matrix of the fitted parameter; \n + -chi2 value of the cost function in the minimum. + + \details The function implements the steps 2 and 3 of the Broken Line fit without the curvature correction.\n + The step 2 is the least square fit, done by imposing the minimum constraint on the cost function and solving the consequent linear system. It determines the fitted parameters u and their covariance matrix. + The step 3 is the correction of the fast pre-fitted parameters for the innermost part of the track. It is first done in a comfortable coordinate system (the one in which the first hit is the origin) and then the parameters and their covariance matrix are transformed to the original coordinate system. + */ + template + __host__ __device__ inline void BL_Line_fit(const M6xN& hits_ge, + const V4& fast_fit, + const double B, + const PreparedBrokenLineData& data, + Rfit::line_fit& line_results) { + constexpr u_int n = N; + u_int i; + + const auto& radii = data.radii; + const auto& S = data.S; + const auto& Z = data.Z; + const auto& VarBeta = data.VarBeta; + + const double slope = -data.q / fast_fit(3); + Rfit::Matrix2d R = RotationMatrix(slope); + + Rfit::Matrix3d V = Rfit::Matrix3d::Zero(); // covariance matrix XYZ + Rfit::Matrix2x3d JacobXYZtosZ = Rfit::Matrix2x3d::Zero(); // jacobian for computation of the error on s (xyz -> sz) + Rfit::VectorNd w = Rfit::VectorNd::Zero(); + for (i = 0; i < n; i++) { + V(0, 0) = hits_ge.col(i)[0]; // x errors + V(0, 1) = V(1, 0) = hits_ge.col(i)[1]; // cov_xy + V(0, 2) = V(2, 0) = hits_ge.col(i)[3]; // cov_xz + V(1, 1) = hits_ge.col(i)[2]; // y errors + V(2, 1) = V(1, 2) = hits_ge.col(i)[4]; // cov_yz + V(2, 2) = hits_ge.col(i)[5]; // z errors + auto tmp = 1. / radii.block(0, i, 2, 1).norm(); + JacobXYZtosZ(0, 0) = radii(1, i) * tmp; + JacobXYZtosZ(0, 1) = -radii(0, i) * tmp; + JacobXYZtosZ(1, 2) = 1.; + w(i) = 1. / ((R * JacobXYZtosZ * V * JacobXYZtosZ.transpose() * R.transpose())( + 1, 1)); // compute the orthogonal weight point by point + } + + Rfit::VectorNd r_u; + for (i = 0; i < n; i++) { + r_u(i) = w(i) * Z(i); + } +#ifdef CPP_DUMP + std::cout << "CU4\n" << MatrixC_u(w, S, VarBeta) << std::endl; +#endif + Rfit::MatrixNd I; + math::cholesky::invert(MatrixC_u(w, S, VarBeta), I); + // Rfit::MatrixNd I=MatrixC_u(w,S,VarBeta).inverse(); +#ifdef CPP_DUMP + std::cout << "I4\n" << I << std::endl; +#endif + + Rfit::VectorNd u = I * r_u; // obtain the fitted parameters by solving the linear system + + // line parameters in the system in which the first hit is the origin and with axis along SZ + line_results.par << (u(1) - u(0)) / (S(1) - S(0)), u(0); + auto idiff = 1. / (S(1) - S(0)); + line_results.cov << (I(0, 0) - 2 * I(0, 1) + I(1, 1)) * Rfit::sqr(idiff) + + MultScatt(S(1) - S(0), B, fast_fit(2), 2, slope), + (I(0, 1) - I(0, 0)) * idiff, (I(0, 1) - I(0, 0)) * idiff, I(0, 0); + + // translate to the original SZ system + Rfit::Matrix2d jacobian; + jacobian(0, 0) = 1.; + jacobian(0, 1) = 0; + jacobian(1, 0) = -S(0); + jacobian(1, 1) = 1.; + line_results.par(1) += -line_results.par(0) * S(0); + line_results.cov = jacobian * line_results.cov * jacobian.transpose(); + + // rotate to the original sz system + auto tmp = R(0, 0) - line_results.par(0) * R(0, 1); + jacobian(1, 1) = 1. / tmp; + jacobian(0, 0) = jacobian(1, 1) * jacobian(1, 1); + jacobian(0, 1) = 0; + jacobian(1, 0) = line_results.par(1) * R(0, 1) * jacobian(0, 0); + line_results.par(1) = line_results.par(1) * jacobian(1, 1); + line_results.par(0) = (R(0, 1) + line_results.par(0) * R(0, 0)) * jacobian(1, 1); + line_results.cov = jacobian * line_results.cov * jacobian.transpose(); + + // compute chi2 + line_results.chi2 = 0; + for (i = 0; i < n; i++) { + line_results.chi2 += w(i) * Rfit::sqr(Z(i) - u(i)); + if (i > 0 && i < n - 1) + line_results.chi2 += Rfit::sqr(u(i - 1) / (S(i) - S(i - 1)) - + u(i) * (S(i + 1) - S(i - 1)) / ((S(i + 1) - S(i)) * (S(i) - S(i - 1))) + + u(i + 1) / (S(i + 1) - S(i))) / + VarBeta(i); + } + + // assert(line_results.chi2>=0); + } + + /*! + \brief Helix fit by three step: + -fast pre-fit (see Fast_fit() for further info); \n + -circle fit of the hits projected in the transverse plane by Broken Line algorithm (see BL_Circle_fit() for further info); \n + -line fit of the hits projected on the (pre-fitted) cilinder surface by Broken Line algorithm (see BL_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 + |x1|x2|x3|...|xn| \n + |y1|y2|y3|...|yn| \n + |z1|z2|z3|...|zn| + \param hits_cov Matrix3Nd covariance matrix in this form (()->cov()): \n + |(x1,x1)|(x2,x1)|(x3,x1)|(x4,x1)|.|(y1,x1)|(y2,x1)|(y3,x1)|(y4,x1)|.|(z1,x1)|(z2,x1)|(z3,x1)|(z4,x1)| \n + |(x1,x2)|(x2,x2)|(x3,x2)|(x4,x2)|.|(y1,x2)|(y2,x2)|(y3,x2)|(y4,x2)|.|(z1,x2)|(z2,x2)|(z3,x2)|(z4,x2)| \n + |(x1,x3)|(x2,x3)|(x3,x3)|(x4,x3)|.|(y1,x3)|(y2,x3)|(y3,x3)|(y4,x3)|.|(z1,x3)|(z2,x3)|(z3,x3)|(z4,x3)| \n + |(x1,x4)|(x2,x4)|(x3,x4)|(x4,x4)|.|(y1,x4)|(y2,x4)|(y3,x4)|(y4,x4)|.|(z1,x4)|(z2,x4)|(z3,x4)|(z4,x4)| \n + . . . . . . . . . . . . . . . \n + |(x1,y1)|(x2,y1)|(x3,y1)|(x4,y1)|.|(y1,y1)|(y2,y1)|(y3,x1)|(y4,y1)|.|(z1,y1)|(z2,y1)|(z3,y1)|(z4,y1)| \n + |(x1,y2)|(x2,y2)|(x3,y2)|(x4,y2)|.|(y1,y2)|(y2,y2)|(y3,x2)|(y4,y2)|.|(z1,y2)|(z2,y2)|(z3,y2)|(z4,y2)| \n + |(x1,y3)|(x2,y3)|(x3,y3)|(x4,y3)|.|(y1,y3)|(y2,y3)|(y3,x3)|(y4,y3)|.|(z1,y3)|(z2,y3)|(z3,y3)|(z4,y3)| \n + |(x1,y4)|(x2,y4)|(x3,y4)|(x4,y4)|.|(y1,y4)|(y2,y4)|(y3,x4)|(y4,y4)|.|(z1,y4)|(z2,y4)|(z3,y4)|(z4,y4)| \n + . . . . . . . . . . . . . . . \n + |(x1,z1)|(x2,z1)|(x3,z1)|(x4,z1)|.|(y1,z1)|(y2,z1)|(y3,z1)|(y4,z1)|.|(z1,z1)|(z2,z1)|(z3,z1)|(z4,z1)| \n + |(x1,z2)|(x2,z2)|(x3,z2)|(x4,z2)|.|(y1,z2)|(y2,z2)|(y3,z2)|(y4,z2)|.|(z1,z2)|(z2,z2)|(z3,z2)|(z4,z2)| \n + |(x1,z3)|(x2,z3)|(x3,z3)|(x4,z3)|.|(y1,z3)|(y2,z3)|(y3,z3)|(y4,z3)|.|(z1,z3)|(z2,z3)|(z3,z3)|(z4,z3)| \n + |(x1,z4)|(x2,z4)|(x3,z4)|(x4,z4)|.|(y1,z4)|(y2,z4)|(y3,z4)|(y4,z4)|.|(z1,z4)|(z2,z4)|(z3,z4)|(z4,z4)| + \param B magnetic field in the center of the detector in Gev/cm/c, in order to perform the p_t calculation. + + \warning see BL_Circle_fit(), BL_Line_fit() and Fast_fit() warnings. + + \bug see BL_Circle_fit(), BL_Line_fit() and Fast_fit() bugs. + + \return (phi,Tip,p_t,cot(theta)),Zip), their covariance matrix and the chi2's of the circle and line fits. + */ + template + inline Rfit::helix_fit BL_Helix_fit(const Rfit::Matrix3xNd& hits, + const Eigen::Matrix& hits_ge, + const double B) { + Rfit::helix_fit helix; + Rfit::Vector4d fast_fit; + BL_Fast_fit(hits, fast_fit); + + PreparedBrokenLineData data; + karimaki_circle_fit circle; + Rfit::line_fit line; + Rfit::Matrix3d jacobian; + + prepareBrokenLineData(hits, fast_fit, B, data); + BL_Line_fit(hits_ge, fast_fit, B, data, line); + BL_Circle_fit(hits, hits_ge, fast_fit, B, data, circle); + + // the circle fit gives k, but here we want p_t, so let's change the parameter and the covariance matrix + jacobian << 1., 0, 0, 0, 1., 0, 0, 0, -std::abs(circle.par(2)) * B / (Rfit::sqr(circle.par(2)) * circle.par(2)); + circle.par(2) = B / std::abs(circle.par(2)); + circle.cov = jacobian * circle.cov * jacobian.transpose(); + + helix.par << circle.par, line.par; + helix.cov = Rfit::MatrixXd::Zero(5, 5); + helix.cov.block(0, 0, 3, 3) = circle.cov; + helix.cov.block(3, 3, 2, 2) = line.cov; + helix.q = circle.q; + helix.chi2_circle = circle.chi2; + helix.chi2_line = line.chi2; + + return helix; + } + +} // namespace BrokenLine + +#endif // RecoPixelVertexing_PixelTrackFitting_interface_BrokenLine_h diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h new file mode 100644 index 0000000000000..b97dda4e65919 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h @@ -0,0 +1,65 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_FitResult_h +#define RecoPixelVertexing_PixelTrackFitting_interface_FitResult_h + +#include +#include + +#include +#include +#include + +namespace Rfit { + + using Vector2d = Eigen::Vector2d; + using Vector3d = Eigen::Vector3d; + using Vector4d = Eigen::Vector4d; + using Vector5d = Eigen::Matrix; + using Matrix2d = Eigen::Matrix2d; + using Matrix3d = Eigen::Matrix3d; + using Matrix4d = Eigen::Matrix4d; + using Matrix5d = Eigen::Matrix; + using Matrix6d = Eigen::Matrix; + + template + using Matrix3xNd = Eigen::Matrix; // used for inputs hits + + 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)| + */ + int32_t q; //!< particle charge + float chi2; + }; + + 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; + }; + + struct helix_fit { + Vector5d par; //!<(phi,Tip,pt,cotan(theta)),Zip) + Matrix5d cov; + /*!< ()->cov() \n + |(phi,phi)|(Tip,phi)|(p_t,phi)|(c_t,phi)|(Zip,phi)| \n + |(phi,Tip)|(Tip,Tip)|(p_t,Tip)|(c_t,Tip)|(Zip,Tip)| \n + |(phi,p_t)|(Tip,p_t)|(p_t,p_t)|(c_t,p_t)|(Zip,p_t)| \n + |(phi,c_t)|(Tip,c_t)|(p_t,c_t)|(c_t,c_t)|(Zip,c_t)| \n + |(phi,Zip)|(Tip,Zip)|(p_t,Zip)|(c_t,Zip)|(Zip,Zip)| + */ + float chi2_circle; + float chi2_line; + // Vector4d fast_fit; + int32_t q; //!< particle charge + }; // __attribute__((aligned(16))); + +} // namespace Rfit +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h b/RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h new file mode 100644 index 0000000000000..8710bdcf6c444 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h @@ -0,0 +1,245 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_FitUtils_h +#define RecoPixelVertexing_PixelTrackFitting_interface_FitUtils_h + +#include "DataFormats/Math/interface/choleskyInversion.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" + +namespace Rfit { + + constexpr double d = 1.e-4; //!< used in numerical derivative (J2 in Circle_fit()) + + using VectorXd = Eigen::VectorXd; + using MatrixXd = Eigen::MatrixXd; + template + using MatrixNd = Eigen::Matrix; + template + using MatrixNplusONEd = 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 MatrixNx3d = Eigen::Matrix; + template + using MatrixNx5d = Eigen::Matrix; + template + using VectorNd = Eigen::Matrix; + template + using VectorNplusONEd = Eigen::Matrix; + template + using Vector2Nd = Eigen::Matrix; + template + using Vector3Nd = Eigen::Matrix; + template + using RowVectorNd = Eigen::Matrix; + template + using RowVector2Nd = Eigen::Matrix; + + using Matrix2x3d = Eigen::Matrix; + + using Matrix3f = Eigen::Matrix3f; + using Vector3f = Eigen::Vector3f; + using Vector4f = Eigen::Vector4f; + using Vector6f = Eigen::Matrix; + + using u_int = unsigned int; + + template + __host__ __device__ void printIt(C* m, const char* prefix = "") { +#ifdef RFIT_DEBUG + for (u_int r = 0; r < m->rows(); ++r) { + for (u_int c = 0; c < m->cols(); ++c) { + printf("%s Matrix(%d,%d) = %g\n", prefix, r, c, (*m)(r, c)); + } + } +#endif + } + + /*! + \brief raise to square. + */ + template + constexpr T sqr(const T a) { + return a * 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. + */ + + __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(M6xNf 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 = M6xNf::ColsAtCompileTime; + 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(M6xNf const& ge, M3xNd& 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 = M6xNf::ColsAtCompileTime; + 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 = 5; + j = 2; + l = 2; + 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]; + ge_idx = 3; + j = 2; + 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]; + ge_idx = 4; + j = 2; + l = 1; + 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]; + } + } + + /*! + \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. + \param error flag for errors computation. + */ + __host__ __device__ inline void par_uvrtopak(circle_fit& circle, const double B, const bool error) { + Vector3d par_pak; + const double temp0 = circle.par.head(2).squaredNorm(); + const double temp1 = sqrt(temp0); + par_pak << atan2(circle.q * circle.par(0), -circle.q * circle.par(1)), circle.q * (temp1 - circle.par(2)), + circle.par(2) * B; + if (error) { + 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; + circle.cov = J4 * circle.cov * J4.transpose(); + } + circle.par = par_pak; + } + + /*! + \brief Transform circle parameter from (X0,Y0,R) to (phi,Tip,q/R) and + consequently covariance matrix. + \param circle_uvr parameter (X0,Y0,R), covariance matrix to + be transformed and particle charge. + */ + __host__ __device__ inline void fromCircleToPerigee(circle_fit& circle) { + Vector3d par_pak; + const double temp0 = circle.par.head(2).squaredNorm(); + const double temp1 = sqrt(temp0); + par_pak << atan2(circle.q * circle.par(0), -circle.q * circle.par(1)), circle.q * (temp1 - circle.par(2)), + circle.q / circle.par(2); + + 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., -circle.q / (circle.par(2) * circle.par(2)); + circle.cov = J4 * circle.cov * J4.transpose(); + + circle.par = par_pak; + } + + // transformation between the "perigee" to cmssw localcoord frame + // the plane of the latter is the perigee plane... + // from //!<(phi,Tip,q/pt,cotan(theta)),Zip) + // to q/p,dx/dz,dy/dz,x,z + template + __host__ __device__ inline void transformToPerigeePlane(VI5 const& ip, MI5 const& icov, VO5& op, MO5& ocov) { + auto sinTheta2 = 1. / (1. + ip(3) * ip(3)); + auto sinTheta = std::sqrt(sinTheta2); + auto cosTheta = ip(3) * sinTheta; + + op(0) = sinTheta * ip(2); + op(1) = 0.; + op(2) = -ip(3); + op(3) = ip(1); + op(4) = -ip(4); + + Matrix5d J = Matrix5d::Zero(); + + J(0, 2) = sinTheta; + J(0, 3) = -sinTheta2 * cosTheta * ip(2); + J(1, 0) = 1.; + J(2, 3) = -1.; + J(3, 1) = 1.; + J(4, 4) = -1; + + ocov = J * icov * J.transpose(); + } + +} // namespace Rfit + +#endif // RecoPixelVertexing_PixelTrackFitting_interface_FitUtils_h diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/PixelNtupletsFitter.h b/RecoPixelVertexing/PixelTrackFitting/interface/PixelNtupletsFitter.h new file mode 100644 index 0000000000000..9fb8843589669 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/PixelNtupletsFitter.h @@ -0,0 +1,27 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_PixelNtupletsFitter_h +#define RecoPixelVertexing_PixelTrackFitting_interface_PixelNtupletsFitter_h + +#include + +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackingRecHit/interface/TrackingRecHit.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelFitterBase.h" +#include "RecoTracker/TkTrackingRegions/interface/TrackingRegion.h" + +class PixelNtupletsFitter final : public PixelFitterBase { +public: + explicit PixelNtupletsFitter(float nominalB, const MagneticField* field, bool useRiemannFit); + ~PixelNtupletsFitter() override = default; + std::unique_ptr run(const std::vector& hits, + const TrackingRegion& region, + const edm::EventSetup& setup) const override; + +private: + float nominalB_; + const MagneticField* field_; + bool useRiemannFit_; +}; + +#endif // RecoPixelVertexing_PixelTrackFitting_interface_PixelNtupletsFitter_h diff --git a/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h new file mode 100644 index 0000000000000..4573205f9c11e --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h @@ -0,0 +1,1005 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h +#define RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h + +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h" + +namespace Rfit { + + /*! 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. + */ + + template + __host__ __device__ inline 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.; + u_int n = length_values.rows(); + rad_lengths(0) = length_values(0) * XX_0_inv; + for (u_int j = 1; j < n; ++j) { + rad_lengths(j) = std::abs(length_values(j) - length_values(j - 1)) * XX_0_inv; + } + } + + /*! + \brief Compute the covariance matrix along cartesian S-Z of points due to + multiple Coulomb scattering to be used in the line_fit, for the barrel + and forward cases. + The input covariance matrix is in the variables s-z, original and + unrotated. + The multiple scattering component is computed in the usual linear + approximation, using the 3D path which is computed as the squared root of + the squared sum of the s and z components passed in. + Internally a rotation by theta is performed and the covariance matrix + returned is the one in the direction orthogonal to the rotated S3D axis, + i.e. along the rotated Z axis. + The choice of the rotation is not arbitrary, but derived from the fact that + putting the horizontal axis along the S3D direction allows the usage of the + ordinary least squared fitting techiques with the trivial parametrization y + = mx + q, avoiding the patological case with m = +/- inf, that would + correspond to the case at eta = 0. + */ + + 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) { +#ifdef RFIT_DEBUG + Rfit::printIt(&s_arcs, "Scatter_cov_line - s_arcs: "); +#endif + constexpr 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))); + 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(); + S_values = S_values.array().sqrt(); + computeRadLenUniformMaterial(S_values, rad_lengths_S); + VectorNd sig2_S; + sig2_S = .000225 / p_2 * (1. + 0.038 * rad_lengths_S.array().log()).abs2() * rad_lengths_S.array(); +#ifdef RFIT_DEBUG + Rfit::printIt(cov_sz, "Scatter_cov_line - cov_sz: "); +#endif + 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) { + 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. +#ifdef RFIT_DEBUG + Rfit::printIt(&tmp, "Scatter_cov_line - tmp: "); +#endif + ret = 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). + \details Only the tangential component is computed (the radial one is + negligible). + */ + template + __host__ __device__ inline MatrixNd Scatter_cov_rad(const M2xN& p2D, + const V4& fast_fit, + VectorNd const& rad, + double B) { + constexpr 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; + VectorNd rad_lengths; + 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 = 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) { + 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); + } + scatter_cov_rad(l, k) = scatter_cov_rad(k, l); + } + } +#ifdef RFIT_DEBUG + Rfit::printIt(&scatter_cov_rad, "Scatter_cov_rad - scatter_cov_rad: "); +#endif + return scatter_cov_rad; + } + + /*! + \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. +*/ + + template + __host__ __device__ inline Matrix2Nd cov_radtocart(const M2xN& p2D, + const MatrixNd& cov_rad, + const VectorNd& rad) { +#ifdef RFIT_DEBUG + printf("Address of p2D: %p\n", &p2D); +#endif + printIt(&p2D, "cov_radtocart - p2D:"); + constexpr u_int n = N; + 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) { + for (u_int j = i; j < n; ++j) { + cov_cart(i, j) = cov_rad(i, j) * p2D(1, i) * rad_inv(i) * p2D(1, j) * rad_inv(j); + 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); + cov_cart(j, i + n) = cov_cart(i + n, j); + } + } + return cov_cart; + } + + /*! + \brief Transform covariance matrix from Cartesian coordinates (only + 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. +*/ + template + __host__ __device__ inline VectorNd cov_carttorad(const M2xN& p2D, + const Matrix2Nd& cov_cart, + const VectorNd& rad) { + constexpr u_int n = N; + 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) = cov_cart(i, i); + else { + 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; + } + + /*! + \brief Transform covariance matrix from Cartesian coordinates (only + 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. +*/ + template + __host__ __device__ inline VectorNd cov_carttorad_prefit(const M2xN& p2D, + const Matrix2Nd& cov_cart, + V4& fast_fit, + const VectorNd& rad) { + constexpr u_int 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) = cov_cart(i, i); // TO FIX + else { + Vector2d a = p2D.col(i); + Vector2d b = p2D.col(i) - fast_fit.head(2); + const double x2 = a.dot(b); + const double y2 = cross2D(a, b); + const double tan_c = -y2 / x2; + const double tan_c2 = sqr(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; + } + + /*! + \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. +*/ + + template + __host__ __device__ inline VectorNd Weight_circle(const MatrixNd& cov_rad_inv) { + return cov_rad_inv.colwise().sum().transpose(); + } + + /*! + \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. +*/ + 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; + } + + /*! + \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 + library is used, with the computedDirect method (available only for 2x2 + 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) { +#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); +#ifdef RFIT_DEBUG + printf("min_eigen3D - exit\n"); +#endif + return solver.eigenvectors().col(min_index); + } + + /*! + \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. +*/ + + __host__ __device__ inline Vector3d min_eigen3D_fast(const Matrix3d& A) { + Eigen::SelfAdjointEigenSolver solver(3); + solver.computeDirect(A.cast()); + int min_index; + solver.eigenvalues().minCoeff(&min_index); + return solver.eigenvectors().col(min_index).cast(); + } + + /*! + \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. +*/ + + __host__ __device__ inline Vector2d min_eigen2D(const Matrix2d& A, double& chi2) { + Eigen::SelfAdjointEigenSolver solver(2); + solver.computeDirect(A); + int min_index; + chi2 = solver.eigenvalues().minCoeff(&min_index); + return solver.eigenvectors().col(min_index); + } + + /*! + \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); + - computation of error due to multiple scattering. +*/ + + template + __host__ __device__ inline void Fast_fit(const M3xN& hits, V4& result) { + constexpr uint32_t N = M3xN::ColsAtCompileTime; + constexpr auto n = N; // get the number of hits + printIt(&hits, "Fast_fit - hits: "); + + // CIRCLE FIT + // Make segments between middle-to-first(b) and last-to-first(c) hits + const Vector2d b = hits.block(0, n / 2, 2, 1) - hits.block(0, 0, 2, 1); + const Vector2d c = hits.block(0, n - 1, 2, 1) - hits.block(0, 0, 2, 1); + printIt(&b, "Fast_fit - b: "); + printIt(&c, "Fast_fit - c: "); + // Compute their lengths + 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 + 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: "); + + // LINE FIT + const Vector2d d = hits.block(0, 0, 2, 1) - result.head(2); + const Vector2d e = hits.block(0, n - 1, 2, 1) - result.head(2); + 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) ) + auto dr = result(2) * atan2(cross2D(d, e), d.dot(e)); + // Simple difference in Z between last and first hit + auto dz = hits(2, n - 1) - hits(2, 0); + + result(3) = (dr / dz); + +#ifdef RFIT_DEBUG + printf("Fast_fit: [%f, %f, %f, %f]\n", result(0), result(1), result(2), result(3)); +#endif + } + + /*! + \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)). + (tan(theta) is not used). + \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. +*/ + 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) { +#ifdef RFIT_DEBUG + printf("circle_fit - enter\n"); +#endif + // INITIALIZATION + Matrix2Nd V = hits_cov2D; + constexpr u_int n = N; + printIt(&hits2D, "circle_fit - hits2D:"); + printIt(&hits_cov2D, "circle_fit - hits_cov2D:"); + +#ifdef RFIT_DEBUG + printf("circle_fit - WEIGHT COMPUTATION\n"); +#endif + // WEIGHT COMPUTATION + VectorNd weight; + MatrixNd G; + double renorm; + { + 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:"); +#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:"); + math::cholesky::invert(cov_rad, G); + // G = cov_rad.inverse(); + renorm = G.sum(); + G *= 1. / renorm; + weight = Weight_circle(G); + } + printIt(&weight, "circle_fit - weight:"); + + // SPACE TRANSFORMATION +#ifdef RFIT_DEBUG + printf("circle_fit - SPACE TRANSFORMATION\n"); +#endif + + // center +#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; + p3D.block(0, 0, 2, n) = hits2D.colwise() - h_; + printIt(&p3D, "circle_fit - p3D: a)"); + Vector2Nd mc; // centered hits, used in error computation + mc << p3D.row(0).transpose(), p3D.row(1).transpose(); + printIt(&mc, "circle_fit - mc(centered hits):"); + + // scale + const double q = mc.squaredNorm(); + const double s = sqrt(n * 1. / q); // scaling factor + p3D *= s; + + // project on paraboloid + p3D.row(2) = p3D.block(0, 0, 2, n).colwise().squaredNorm(); + printIt(&p3D, "circle_fit - p3D: b)"); + +#ifdef RFIT_DEBUG + printf("circle_fit - COST FUNCTION\n"); +#endif + // COST FUNCTION + + // compute + Vector3d r0; + r0.noalias() = p3D * weight; // center of gravity + const Matrix3xNd X = p3D.colwise() - r0; + Matrix3d A = X * G * X.transpose(); + printIt(&A, "circle_fit - A:"); + +#ifdef RFIT_DEBUG + printf("circle_fit - MINIMIZE\n"); +#endif + // minimize + double chi2; + Vector3d v = min_eigen3D(A, chi2); +#ifdef RFIT_DEBUG + printf("circle_fit - AFTER MIN_EIGEN\n"); +#endif + printIt(&v, "v BEFORE INVERSION"); + v *= (v(2) > 0) ? 1 : -1; // TO FIX dovrebbe essere N(3)>0 + 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. +#ifdef RFIT_DEBUG + printf("circle_fit - AFTER MIN_EIGEN 1\n"); +#endif + Eigen::Matrix cm; +#ifdef RFIT_DEBUG + printf("circle_fit - AFTER MIN_EIGEN 2\n"); +#endif + cm = -v.transpose() * r0; +#ifdef RFIT_DEBUG + printf("circle_fit - AFTER MIN_EIGEN 3\n"); +#endif + const double c = cm(0, 0); + // const double c = -v.transpose() * r0; + +#ifdef RFIT_DEBUG + printf("circle_fit - COMPUTE CIRCLE PARAMETER\n"); +#endif + // COMPUTE CIRCLE PARAMETER + + // auxiliary quantities + const double h = sqrt(1. - sqr(v(2)) - 4. * c * v(2)); + const double v2x2_inv = 1. / (2. * v(2)); + const double s_inv = 1. / s; + Vector3d par_uvr_; // used in error propagation + par_uvr_ << -v(0) * v2x2_inv, -v(1) * v2x2_inv, h * v2x2_inv; + + circle_fit circle; + circle.par << par_uvr_(0) * s_inv + h_(0), par_uvr_(1) * s_inv + h_(1), par_uvr_(2) * s_inv; + circle.q = Charge(hits2D, circle.par); + 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:"); +#ifdef RFIT_DEBUG + printf("circle_fit - CIRCLE CHARGE: %d\n", circle.q); +#endif + +#ifdef RFIT_DEBUG + printf("circle_fit - ERROR PROPAGATION\n"); +#endif + // ERROR PROPAGATION + if (error) { +#ifdef RFIT_DEBUG + printf("circle_fit - ERROR PRPAGATION ACTIVATED\n"); +#endif + 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; + const double c = cm(0, 0); + 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:"); + C[0][0] = Vcs.block(0, 0, n, n).template selfadjointView(); + Vcs_[0][1] = Vcs.block(0, 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:"); + } + + { + 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); + Vcs_[1][1] = C[1][1]; + C[1][2] = 2. * (Vcs_[1][0] * t0 + Vcs_[1][1] * t1); + 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]:"); + + Matrix3d C0; // cov matrix of center of gravity (r0.x,r0.y,r0.z) + for (u_int i = 0; i < 3; ++i) { + for (u_int j = i; j < 3; ++j) { + 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; + C0(j, i) = C0(i, j); + } + } + printIt(&C0, "circle_fit - C0:"); + + const MatrixNd W = weight * weight.transpose(); + const MatrixNd H = MatrixNd::Identity().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) + { + D_[0][0] = (H * C[0][0] * H.transpose()).cwiseProduct(W); + D_[0][1] = (H * C[0][1] * H.transpose()).cwiseProduct(W); + D_[0][2] = (H * C[0][2] * H.transpose()).cwiseProduct(W); + D_[1][1] = (H * C[1][1] * H.transpose()).cwiseProduct(W); + D_[1][2] = (H * C[1][2] * H.transpose()).cwiseProduct(W); + D_[2][2] = (H * C[2][2] * H.transpose()).cwiseProduct(W); + D_[1][0] = D_[0][1].transpose(); + D_[2][0] = D_[0][2].transpose(); + D_[2][1] = D_[1][2].transpose(); + } + printIt(&D_[0][0], "circle_fit - D_[0][0]:"); + + 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 + for (u_int a = 0; a < 6; ++a) { + const u_int i = nu[a][0], j = nu[a][1]; + 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); + if (l == k) { + t0 = 2. * D_[j][l] * s_v.col(l); + if (i == j) + t1 = t0; + else + t1 = 2. * D_[i][l] * s_v.col(l); + } else { + t0 = D_[j][l] * s_v.col(k) + D_[j][k] * s_v.col(l); + if (i == j) + t1 = t0; + else + t1 = D_[i][l] * s_v.col(k) + D_[i][k] * s_v.col(l); + } + + if (i == j) { + Eigen::Matrix cm; + cm = s_v.col(i).transpose() * (t0 + t1); + const double c = cm(0, 0); + E(a, b) = 0. + c; + } else { + 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); + } + if (b != a) + E(b, a) = E(a, b); + } + } + printIt(&E, "circle_fit - E:"); + + 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]; + Matrix3d Delta = Matrix3d::Zero(); + Delta(i, j) = Delta(j, i) = abs(A(i, j) * d); + J2.col(a) = min_eigen3D_fast(A + Delta); + const int sign = (J2.col(a)(2) > 0) ? 1 : -1; + J2.col(a) = (J2.col(a) * sign - v) / Delta(i, j); + } + printIt(&J2, "circle_fit - J2:"); + + Matrix4d Cvc; // joint cov matrix of (v0,v1,v2,c) + { + Matrix3d t0 = J2 * E * J2.transpose(); + Vector3d t1 = -t0 * r0; + Cvc.block(0, 0, 3, 3) = t0; + Cvc.block(0, 3, 3, 1) = t1; + Cvc.block(3, 0, 1, 3) = t1.transpose(); + Eigen::Matrix cm1; + Eigen::Matrix cm3; + cm1 = (v.transpose() * C0 * v); + // cm2 = (C0.cwiseProduct(t0)).sum(); + cm3 = (r0.transpose() * t0 * r0); + const double c = cm1(0, 0) + (C0.cwiseProduct(t0)).sum() + cm3(0, 0); + Cvc(3, 3) = c; + // (v.transpose() * C0 * v) + (C0.cwiseProduct(t0)).sum() + (r0.transpose() * t0 * r0); + } + printIt(&Cvc, "circle_fit - Cvc:"); + + 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, + 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:"); + + 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) + + (par_uvr_ * par_uvr_.transpose()) * (Jq * V * Jq.transpose()); + + circle.cov = cov_uvr; + } + + printIt(&circle.cov, "Circle cov:"); +#ifdef RFIT_DEBUG + printf("circle_fit - exit\n"); +#endif + return circle; + } + + /*! \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, + const M6xN& hits_ge, + const circle_fit& circle, + const V4& fast_fit, + const double B, + const bool error) { + constexpr uint32_t N = M3xN::ColsAtCompileTime; + constexpr auto n = N; + 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: + // [s1, s2, s3, ..., sn] + // [z1, z2, z3, ..., zn] + // s values will be ordinary x-values + // z values will be ordinary y-values + + Matrix2xNd p2D = Matrix2xNd::Zero(); + Eigen::Matrix Jx; + +#ifdef RFIT_DEBUG + printf("Line_fit - B: %g\n", B); + printIt(&hits, "Line_fit points: "); + 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 + // 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 + Matrix6d Cov = Matrix6d::Zero(); + Matrix2d cov_sz[N]; + 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.block(0, 0, 3, 3) = circle.cov; + 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, cov_with_ms); +#ifdef RFIT_DEBUG + printIt(cov_sz, "line_fit - cov_sz:"); + printIt(&cov_with_ms, "line_fit - cov_with_ms: "); +#endif + + // Rotate Points with the shape [2, n] + Matrix2xNd p2D_rot = rot * p2D; + +#ifdef 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; + A << MatrixXd::Ones(1, n), p2D_rot.row(0); // rotated s values + +#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; + math::cholesky::invert(cov_with_ms, Vy_inv); + // MatrixNd Vy_inv = cov_with_ms.inverse(); + Eigen::Matrix Cov_params = A * Vy_inv * A.transpose(); + // Compute the Covariance Matrix of the fit parameters + math::cholesky::invert(Cov_params, Cov_params); + + // 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(); + +#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; + 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; + + line_fit line; + line.par << m, q; + line.cov << cov_mq; + line.chi2 = chi2; + +#ifdef 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 + -circle fit of hits projected in the transverse plane by Riemann-Chernov + algorithm (see Circle_fit() for further info); \n + -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 + . . . . . . . . . . . \n + |(x0,y0)|(x1,y0)|(x2,y0)|.|(y0,y0)|(y1,y0)|(y2,x0)|.|(z0,y0)|(z1,y0)|(z2,y0)| \n + |(x0,y1)|(x1,y1)|(x2,y1)|.|(y0,y1)|(y1,y1)|(y2,x1)|.|(z0,y1)|(z1,y1)|(z2,y1)| \n + |(x0,y2)|(x1,y2)|(x2,y2)|.|(y0,y2)|(y1,y2)|(y2,x2)|.|(z0,y2)|(z1,y2)|(z2,y2)| \n + . . . . . . . . . . . \n + |(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. +*/ + + template + inline helix_fit Helix_fit(const Matrix3xNd& hits, + const Eigen::Matrix& hits_ge, + const double B, + const bool error) { + constexpr u_int n = N; + 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. + Vector4d fast_fit; + Fast_fit(hits, fast_fit); + Rfit::Matrix2Nd 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, fast_fit, rad, B, error); + line_fit line = Line_fit(hits, hits_ge, circle, fast_fit, B, error); + + par_uvrtopak(circle, B, error); + + helix_fit helix; + helix.par << circle.par, line.par; + if (error) { + helix.cov = MatrixXd::Zero(5, 5); + helix.cov.block(0, 0, 3, 3) = circle.cov; + helix.cov.block(3, 3, 2, 2) = line.cov; + } + helix.q = circle.q; + helix.chi2_circle = circle.chi2; + helix.chi2_line = line.chi2; + + return helix; + } + +} // namespace Rfit + +#endif // RecoPixelVertexing_PixelTrackFitting_interface_RiemannFit_h diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml index be113d7a5a3dc..ecfbd99b667fc 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/BuildFile.xml @@ -1,3 +1,6 @@ + + + diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelNtupletsFitterProducer.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelNtupletsFitterProducer.cc new file mode 100644 index 0000000000000..67ebe16e86840 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelNtupletsFitterProducer.cc @@ -0,0 +1,44 @@ +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "MagneticField/Engine/interface/MagneticField.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelFitter.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelNtupletsFitter.h" +#include "RecoTracker/TkMSParametrization/interface/PixelRecoUtilities.h" + +class PixelNtupletsFitterProducer : public edm::global::EDProducer<> { +public: + explicit PixelNtupletsFitterProducer(const edm::ParameterSet& iConfig) + : useRiemannFit_(iConfig.getParameter("useRiemannFit")) { + produces(); + } + ~PixelNtupletsFitterProducer() override {} + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("useRiemannFit", false)->setComment("true for Riemann, false for BrokenLine"); + descriptions.add("pixelNtupletsFitterDefault", desc); + } + +private: + bool useRiemannFit_; + void produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; +}; + +void PixelNtupletsFitterProducer::produce(edm::StreamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const { + edm::ESHandle fieldESH; + iSetup.get().get(fieldESH); + float bField = 1 / PixelRecoUtilities::fieldInInvGev(iSetup); + auto impl = std::make_unique(bField, fieldESH.product(), useRiemannFit_); + auto prod = std::make_unique(std::move(impl)); + iEvent.put(std::move(prod)); +} + +DEFINE_FWK_MODULE(PixelNtupletsFitterProducer); diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackDumpCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackDumpCUDA.cc new file mode 100644 index 0000000000000..2f0965be50eb8 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackDumpCUDA.cc @@ -0,0 +1,86 @@ +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/Utilities/interface/RunningAverage.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "RecoTracker/TkMSParametrization/interface/PixelRecoUtilities.h" + +class PixelTrackDumpCUDA : public edm::global::EDAnalyzer<> { +public: + explicit PixelTrackDumpCUDA(const edm::ParameterSet& iConfig); + ~PixelTrackDumpCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void analyze(edm::StreamID streamID, edm::Event const& iEvent, const edm::EventSetup& iSetup) const override; + const bool m_onGPU; + edm::EDGetTokenT> tokenGPUTrack_; + edm::EDGetTokenT> tokenGPUVertex_; + edm::EDGetTokenT tokenSoATrack_; + edm::EDGetTokenT tokenSoAVertex_; +}; + +PixelTrackDumpCUDA::PixelTrackDumpCUDA(const edm::ParameterSet& iConfig) + : m_onGPU(iConfig.getParameter("onGPU")) { + if (m_onGPU) { + tokenGPUTrack_ = + consumes>(iConfig.getParameter("pixelTrackSrc")); + tokenGPUVertex_ = + consumes>(iConfig.getParameter("pixelVertexSrc")); + } else { + tokenSoATrack_ = consumes(iConfig.getParameter("pixelTrackSrc")); + tokenSoAVertex_ = consumes(iConfig.getParameter("pixelVertexSrc")); + } +} + +void PixelTrackDumpCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + desc.add("onGPU", true); + desc.add("pixelTrackSrc", edm::InputTag("caHitNtupletCUDA")); + desc.add("pixelVertexSrc", edm::InputTag("pixelVertexCUDA")); + descriptions.add("pixelTrackDumpCUDA", desc); +} + +void PixelTrackDumpCUDA::analyze(edm::StreamID streamID, + edm::Event const& iEvent, + const edm::EventSetup& iSetup) const { + if (m_onGPU) { + auto const& hTracks = iEvent.get(tokenGPUTrack_); + cms::cuda::ScopedContextProduce ctx{hTracks}; + + auto const& tracks = ctx.get(hTracks); + auto const* tsoa = tracks.get(); + assert(tsoa); + + auto const& vertices = ctx.get(iEvent.get(tokenGPUVertex_)); + auto const* vsoa = vertices.get(); + assert(vsoa); + + } else { + auto const* tsoa = iEvent.get(tokenSoATrack_).get(); + assert(tsoa); + + auto const* vsoa = iEvent.get(tokenSoAVertex_).get(); + assert(vsoa); + } +} + +DEFINE_FWK_MODULE(PixelTrackDumpCUDA); diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc index bd390f5f65352..91c3a44cc8643 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.cc @@ -1,23 +1,22 @@ -#include "PixelTrackProducer.h" +#include +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackExtra.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "FWCore/Framework/interface/ESHandle.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 "FWCore/ParameterSet/interface/ParameterSet.h" #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" #include "FWCore/ParameterSet/interface/ParameterSetDescription.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 "DataFormats/TrackerCommon/interface/TrackerTopology.h" #include "Geometry/Records/interface/TrackerTopologyRcd.h" -#include +#include "PixelTrackProducer.h" +#include "storeTracks.h" using namespace pixeltrackfitting; using edm::ParameterSet; @@ -45,62 +44,9 @@ void PixelTrackProducer::produce(edm::Event& ev, const edm::EventSetup& es) { TracksWithTTRHs tracks; theReconstruction.run(tracks, ev, es); - edm::ESHandle httopo; es.get().get(httopo); // store tracks - store(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)); + storeTracks(ev, tracks, *httopo); } diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h index d756a9cf963f5..c38fd44c0d7f5 100644 --- a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducer.h @@ -1,8 +1,7 @@ -#ifndef PixelTrackProducer_H -#define PixelTrackProducer_H +#ifndef RecoPixelVertexing_PixelTrackFitting_plugins_PixelTrackProducer_h +#define RecoPixelVertexing_PixelTrackFitting_plugins_PixelTrackProducer_h #include "FWCore/Framework/interface/stream/EDProducer.h" -#include "RecoPixelVertexing/PixelTrackFitting/interface/TracksWithHits.h" #include "RecoPixelVertexing/PixelTrackFitting/interface/PixelTrackReconstruction.h" namespace edm { @@ -24,7 +23,7 @@ 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); PixelTrackReconstruction theReconstruction; }; -#endif + +#endif // RecoPixelVertexing_PixelTrackFitting_plugins_PixelTrackProducer_h diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromSoA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromSoA.cc new file mode 100644 index 0000000000000..cdea22c3a8a24 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackProducerFromSoA.cc @@ -0,0 +1,209 @@ +#include "DataFormats/BeamSpot/interface/BeamSpot.h" +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackExtra.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "DataFormats/GeometrySurface/interface/Plane.h" +#include "DataFormats/TrackerRecHit2D/interface/SiPixelRecHitCollection.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" + +#include "TrackingTools/AnalyticalJacobians/interface/JacobianLocalToCurvilinear.h" +#include "TrackingTools/TrajectoryParametrization/interface/GlobalTrajectoryParameters.h" +#include "TrackingTools/TrajectoryParametrization/interface/CurvilinearTrajectoryError.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h" + +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" + +#include "storeTracks.h" +#include "CUDADataFormats/Common/interface/HostProduct.h" + +/** + * This class creates "leagcy" reco::Track + * objects from the output of SoA CA. + */ +class PixelTrackProducerFromSoA : public edm::global::EDProducer<> { +public: + using IndToEdm = std::vector; + + explicit PixelTrackProducerFromSoA(const edm::ParameterSet &iConfig); + ~PixelTrackProducerFromSoA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions &descriptions); + + // using HitModuleStart = std::array; + using HMSstorage = HostProduct; + +private: + void produce(edm::StreamID streamID, edm::Event &iEvent, const edm::EventSetup &iSetup) const override; + + edm::EDGetTokenT tBeamSpot_; + edm::EDGetTokenT tokenTrack_; + edm::EDGetTokenT cpuHits_; + edm::EDGetTokenT hmsToken_; + + int32_t const minNumberOfHits_; +}; + +PixelTrackProducerFromSoA::PixelTrackProducerFromSoA(const edm::ParameterSet &iConfig) + : tBeamSpot_(consumes(iConfig.getParameter("beamSpot"))), + tokenTrack_(consumes(iConfig.getParameter("trackSrc"))), + cpuHits_(consumes(iConfig.getParameter("pixelRecHitLegacySrc"))), + hmsToken_(consumes(iConfig.getParameter("pixelRecHitLegacySrc"))), + minNumberOfHits_(iConfig.getParameter("minNumberOfHits")) { + produces(); + produces(); + produces(); + produces(); +} + +void PixelTrackProducerFromSoA::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { + edm::ParameterSetDescription desc; + desc.add("beamSpot", edm::InputTag("offlineBeamSpot")); + desc.add("trackSrc", edm::InputTag("pixelTrackSoA")); + desc.add("pixelRecHitLegacySrc", edm::InputTag("siPixelRecHitsPreSplittingLegacy")); + desc.add("minNumberOfHits", 0); + + descriptions.addWithDefaultLabel(desc); +} + +void PixelTrackProducerFromSoA::produce(edm::StreamID streamID, + edm::Event &iEvent, + const edm::EventSetup &iSetup) const { + // std::cout << "Converting gpu helix in reco tracks" << std::endl; + + auto indToEdmP = std::make_unique(); + auto &indToEdm = *indToEdmP; + + edm::ESHandle fieldESH; + iSetup.get().get(fieldESH); + + pixeltrackfitting::TracksWithRecHits tracks; + edm::ESHandle httopo; + iSetup.get().get(httopo); + + 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()); + + edm::Handle gh; + iEvent.getByToken(cpuHits_, gh); + auto const &rechits = *gh; + std::vector hitmap; + auto const &rcs = rechits.data(); + auto nhits = rcs.size(); + hitmap.resize(nhits, nullptr); + + edm::Handle hhms; + iEvent.getByToken(hmsToken_, hhms); + auto const *hitsModuleStart = (*hhms).get(); + auto fc = hitsModuleStart; + + for (auto const &h : rcs) { + auto const &thit = static_cast(h); + auto detI = thit.det()->index(); + auto const &clus = thit.firstClusterRef(); + assert(clus.isPixel()); + auto i = fc[detI] + clus.pixelCluster().originalId(); + if (i >= hitmap.size()) + hitmap.resize(i + 256, nullptr); // only in case of hit overflow in one module + assert(nullptr == hitmap[i]); + hitmap[i] = &h; + } + + std::vector hits; + hits.reserve(5); + + const auto &tsoa = *iEvent.get(tokenTrack_); + + auto const *quality = tsoa.qualityData(); + auto const &fit = tsoa.stateAtBS; + auto const &hitIndices = tsoa.hitIndices; + auto maxTracks = tsoa.stride(); + + int32_t nt = 0; + + for (int32_t it = 0; it < maxTracks; ++it) { + auto nHits = tsoa.nHits(it); + if (nHits == 0) + break; // this is a guard: maybe we need to move to nTracks... + indToEdm.push_back(-1); + auto q = quality[it]; + if (q != trackQuality::loose) + continue; // FIXME + if (nHits < minNumberOfHits_) + continue; + indToEdm.back() = nt; + ++nt; + + hits.resize(nHits); + auto b = hitIndices.begin(it); + for (int iHit = 0; iHit < nHits; ++iHit) + hits[iHit] = hitmap[*(b + iHit)]; + + // mind: this values are respect the beamspot! + + float chi2 = tsoa.chi2(it); + float phi = tsoa.phi(it); + + Rfit::Vector5d ipar, opar; + Rfit::Matrix5d icov, ocov; + fit.copyToDense(ipar, icov, it); + Rfit::transformToPerigeePlane(ipar, icov, opar, ocov); + + LocalTrajectoryParameters lpar(opar(0), opar(1), opar(2), opar(3), opar(4), 1.); + AlgebraicSymMatrix55 m; + for (int i = 0; i < 5; ++i) + for (int j = i; j < 5; ++j) + m(i, j) = ocov(i, j); + + float sp = std::sin(phi); + float cp = std::cos(phi); + Surface::RotationType rot(sp, -cp, 0, 0, 0, -1.f, cp, sp, 0); + + Plane impPointPlane(bs, rot); + GlobalTrajectoryParameters gp(impPointPlane.toGlobal(lpar.position()), + impPointPlane.toGlobal(lpar.momentum()), + lpar.charge(), + fieldESH.product()); + JacobianLocalToCurvilinear jl2c(impPointPlane, lpar, *fieldESH.product()); + + AlgebraicSymMatrix55 mo = ROOT::Math::Similarity(jl2c.jacobian(), m); + + int ndof = 2 * hits.size() - 5; + chi2 = chi2 * ndof; // FIXME + GlobalPoint vv = gp.position(); + math::XYZPoint pos(vv.x(), vv.y(), vv.z()); + GlobalVector pp = gp.momentum(); + math::XYZVector mom(pp.x(), pp.y(), pp.z()); + + auto track = std::make_unique(chi2, ndof, pos, mom, gp.charge(), CurvilinearTrajectoryError(mo)); + // filter??? + tracks.emplace_back(track.release(), hits); + } + // std::cout << "processed " << nt << " good tuples " << tracks.size() << "out of " << indToEdm.size() << std::endl; + + // store tracks + storeTracks(iEvent, tracks, *httopo); + iEvent.put(std::move(indToEdmP)); +} + +DEFINE_FWK_MODULE(PixelTrackProducerFromSoA); diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackSoAFromCUDA.cc b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackSoAFromCUDA.cc new file mode 100644 index 0000000000000..c8310bc645db3 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/PixelTrackSoAFromCUDA.cc @@ -0,0 +1,82 @@ +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" + +class PixelTrackSoAFromCUDA : public edm::stream::EDProducer { +public: + explicit PixelTrackSoAFromCUDA(const edm::ParameterSet& iConfig); + ~PixelTrackSoAFromCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void acquire(edm::Event const& iEvent, + edm::EventSetup const& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) override; + void produce(edm::Event& iEvent, edm::EventSetup const& iSetup) override; + + edm::EDGetTokenT> tokenCUDA_; + edm::EDPutTokenT tokenSOA_; + + cms::cuda::host::unique_ptr m_soa; +}; + +PixelTrackSoAFromCUDA::PixelTrackSoAFromCUDA(const edm::ParameterSet& iConfig) + : tokenCUDA_(consumes>(iConfig.getParameter("src"))), + tokenSOA_(produces()) {} + +void PixelTrackSoAFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + desc.add("src", edm::InputTag("caHitNtupletCUDA")); + descriptions.add("pixelTrackSoA", desc); +} + +void PixelTrackSoAFromCUDA::acquire(edm::Event const& iEvent, + edm::EventSetup const& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) { + cms::cuda::Product const& inputDataWrapped = iEvent.get(tokenCUDA_); + cms::cuda::ScopedContextAcquire ctx{inputDataWrapped, std::move(waitingTaskHolder)}; + auto const& inputData = ctx.get(inputDataWrapped); + + m_soa = inputData.toHostAsync(ctx.stream()); +} + +void PixelTrackSoAFromCUDA::produce(edm::Event& iEvent, edm::EventSetup const& iSetup) { + /* + auto const & tsoa = *m_soa; + auto maxTracks = tsoa.stride(); + std::cout << "size of SoA" << sizeof(tsoa) << " stride " << maxTracks << std::endl; + + int32_t nt = 0; + for (int32_t it = 0; it < maxTracks; ++it) { + auto nHits = tsoa.nHits(it); + assert(nHits==int(tsoa.hitIndices.size(it))); + if (nHits == 0) break; // this is a guard: maybe we need to move to nTracks... + nt++; + } + std::cout << "found " << nt << " tracks in cpu SoA at " << &tsoa << std::endl; + */ + + // DO NOT make a copy (actually TWO....) + iEvent.emplace(tokenSOA_, PixelTrackHeterogeneous(std::move(m_soa))); + + assert(!m_soa); +} + +DEFINE_FWK_MODULE(PixelTrackSoAFromCUDA); diff --git a/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h new file mode 100644 index 0000000000000..59101b6ba5214 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/plugins/storeTracks.h @@ -0,0 +1,72 @@ +#ifndef RecoPixelVertexingPixelTrackFittingStoreTracks_H +#define RecoPixelVertexingPixelTrackFittingStoreTracks_H + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "DataFormats/TrackReco/interface/TrackExtra.h" +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/TracksWithHits.h" + +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" + +template +void storeTracks(Ev& ev, const TWH& 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[i].first; + const auto& hits = tracksWithHits[i].second; + + for (unsigned int k = 0; k < hits.size(); k++) { + auto* hit = hits[k]->clone(); + + track->appendHitPattern(*hit, ttopo); + recHits->push_back(hit); + } + tracks->push_back(*track); + delete track; + } + + LogDebug("TrackProducer") << "put the collection of TrackingRecHit in the event" + << "\n"; + edm::OrphanHandle ohRH = ev.put(std::move(recHits)); + + edm::RefProd hitCollProd(ohRH); + for (int k = 0; k < nTracks; k++) { + reco::TrackExtra theTrackExtra{}; + + //fill the TrackExtra with TrackingRecHitRef + unsigned int nHits = tracks->at(k).numberOfValidHits(); + theTrackExtra.setHits(hitCollProd, cc, nHits); + cc += nHits; + AlgebraicVector5 v = AlgebraicVector5(0, 0, 0, 0, 0); + reco::TrackExtra::TrajParams trajParams(nHits, LocalTrajectoryParameters(v, 1.)); + reco::TrackExtra::Chi2sFive chi2s(nHits, 0); + theTrackExtra.setTrajParams(std::move(trajParams), std::move(chi2s)); + trackExtras->push_back(theTrackExtra); + } + + LogDebug("TrackProducer") << "put the collection of TrackExtra in the event" + << "\n"; + edm::OrphanHandle ohTE = ev.put(std::move(trackExtras)); + + for (int k = 0; k < nTracks; k++) { + const reco::TrackExtraRef theTrackExtraRef(ohTE, k); + (tracks->at(k)).setExtra(theTrackExtraRef); + } + + ev.put(std::move(tracks)); +} + +#endif diff --git a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py index 4334d724358f3..5ff404cb603d4 100644 --- a/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py +++ b/RecoPixelVertexing/PixelTrackFitting/python/PixelTracks_cff.py @@ -11,6 +11,7 @@ from RecoTracker.TkSeedingLayers.PixelLayerTriplets_cfi import * from RecoTracker.TkSeedingLayers.TTRHBuilderWithoutAngle4PixelTriplets_cfi import * from RecoPixelVertexing.PixelTrackFitting.pixelFitterByHelixProjections_cfi import pixelFitterByHelixProjections +from RecoPixelVertexing.PixelTrackFitting.pixelNtupletsFitter_cfi import pixelNtupletsFitter from RecoPixelVertexing.PixelTrackFitting.pixelTrackFilterByKinematics_cfi import pixelTrackFilterByKinematics from RecoPixelVertexing.PixelTrackFitting.pixelTrackCleanerBySharedHits_cfi import pixelTrackCleanerBySharedHits from RecoPixelVertexing.PixelTrackFitting.pixelTracks_cfi import pixelTracks as _pixelTracks @@ -76,4 +77,26 @@ _pixelTracksTask_lowPU.replace(pixelTracksHitQuadruplets, pixelTracksHitTriplets) trackingLowPU.toReplaceWith(pixelTracksTask, _pixelTracksTask_lowPU) +# Use ntuple fit and substitute previous Fitter producer with the ntuple one +from Configuration.ProcessModifiers.pixelNtupleFit_cff import pixelNtupleFit as ntupleFit +ntupleFit.toModify(pixelTracks, Fitter = "pixelNtupletsFitter") +_pixelTracksTask_ntupleFit = pixelTracksTask.copy() +_pixelTracksTask_ntupleFit.replace(pixelFitterByHelixProjections, pixelNtupletsFitter) +ntupleFit.toReplaceWith(pixelTracksTask, _pixelTracksTask_ntupleFit) + + +from Configuration.ProcessModifiers.gpu_cff import gpu +from RecoPixelVertexing.PixelTriplets.caHitNtupletCUDA_cfi import caHitNtupletCUDA +from RecoPixelVertexing.PixelTrackFitting.pixelTrackSoA_cfi import pixelTrackSoA +from RecoPixelVertexing.PixelTrackFitting.pixelTrackProducerFromSoA_cfi import pixelTrackProducerFromSoA as _pixelTrackFromSoA +_pixelTracksGPUTask = cms.Task( + caHitNtupletCUDA, + pixelTrackSoA, + pixelTracks # FromSoA +) + +gpu.toReplaceWith(pixelTracksTask, _pixelTracksGPUTask) +gpu.toReplaceWith(pixelTracks,_pixelTrackFromSoA) + + pixelTracksSequence = cms.Sequence(pixelTracksTask) diff --git a/RecoPixelVertexing/PixelTrackFitting/python/pixelNtupletsFitter_cfi.py b/RecoPixelVertexing/PixelTrackFitting/python/pixelNtupletsFitter_cfi.py new file mode 100644 index 0000000000000..10e1e3852e9c4 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/python/pixelNtupletsFitter_cfi.py @@ -0,0 +1,6 @@ +import FWCore.ParameterSet.Config as cms + +from RecoPixelVertexing.PixelTrackFitting.pixelNtupletsFitterDefault_cfi import pixelNtupletsFitterDefault + +pixelNtupletsFitter = pixelNtupletsFitterDefault.clone() + diff --git a/RecoPixelVertexing/PixelTrackFitting/src/PixelNtupletsFitter.cc b/RecoPixelVertexing/PixelTrackFitting/src/PixelNtupletsFitter.cc new file mode 100644 index 0000000000000..32a9aeb982094 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/src/PixelNtupletsFitter.cc @@ -0,0 +1,102 @@ +#include "CommonTools/Utils/interface/DynArray.h" +#include "DataFormats/GeometryCommonDetAlgo/interface/GlobalError.h" +#include "DataFormats/GeometryCommonDetAlgo/interface/Measurement1D.h" +#include "DataFormats/GeometryVector/interface/GlobalPoint.h" +#include "DataFormats/GeometryVector/interface/LocalPoint.h" +#include "DataFormats/GeometryVector/interface/Pi.h" +#include "DataFormats/TrackingRecHit/interface/TrackingRecHit.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "Geometry/CommonDetUnit/interface/GeomDet.h" +#include "Geometry/CommonDetUnit/interface/GeomDetType.h" +#include "MagneticField/Engine/interface/MagneticField.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelNtupletsFitter.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelTrackBuilder.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/PixelTrackErrorParam.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +#include "RecoTracker/TkMSParametrization/interface/PixelRecoUtilities.h" + +using namespace std; + +PixelNtupletsFitter::PixelNtupletsFitter(float nominalB, const MagneticField* field, bool useRiemannFit) + : nominalB_(nominalB), field_(field), useRiemannFit_(useRiemannFit) {} + +std::unique_ptr PixelNtupletsFitter::run(const std::vector& hits, + const TrackingRegion& region, + const edm::EventSetup&) const { + using namespace Rfit; + + std::unique_ptr ret; + + unsigned int nhits = hits.size(); + + if (nhits < 2) + return ret; + + declareDynArray(GlobalPoint, nhits, points); + declareDynArray(GlobalError, nhits, errors); + declareDynArray(bool, nhits, isBarrel); + + for (unsigned int i = 0; i != nhits; ++i) { + auto const& recHit = hits[i]; + points[i] = GlobalPoint(recHit->globalPosition().basicVector() - region.origin().basicVector()); + errors[i] = recHit->globalPositionError(); + isBarrel[i] = recHit->detUnit()->type().isBarrel(); + } + + assert(nhits == 4); + Rfit::Matrix3xNd<4> hits_gp; + + Eigen::Matrix hits_ge = Eigen::Matrix::Zero(); + + for (unsigned int i = 0; i < nhits; ++i) { + hits_gp.col(i) << points[i].x(), points[i].y(), points[i].z(); + + hits_ge.col(i) << errors[i].cxx(), errors[i].cyx(), errors[i].cyy(), errors[i].czx(), errors[i].czy(), + errors[i].czz(); + } + + helix_fit fittedTrack = useRiemannFit_ ? Rfit::Helix_fit(hits_gp, hits_ge, nominalB_, true) + : BrokenLine::BL_Helix_fit(hits_gp, hits_ge, nominalB_); + + int iCharge = fittedTrack.q; + + // parameters are: + // 0: phi + // 1: tip + // 2: curvature + // 3: cottheta + // 4: zip + float valPhi = fittedTrack.par(0); + + float valTip = fittedTrack.par(1); + + float valCotTheta = fittedTrack.par(3); + + float valZip = fittedTrack.par(4); + float valPt = fittedTrack.par(2); + // + // PixelTrackErrorParam param(valEta, valPt); + 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; + + PixelTrackBuilder builder; + Measurement1D phi(valPhi, errValPhi); + Measurement1D tip(valTip, errValTip); + + Measurement1D pt(valPt, errValPt); + Measurement1D cotTheta(valCotTheta, errValCotTheta); + Measurement1D zip(valZip, errValZip); + + ret.reset(builder.build(pt, phi, cotTheta, tip, zip, chi2, iCharge, hits, field_, region.origin())); + return ret; +} diff --git a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml index 44820da381dd1..8d02db9a0e638 100644 --- a/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelTrackFitting/test/BuildFile.xml @@ -1,8 +1,80 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc new file mode 100644 index 0000000000000..5395b93629f49 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/PixelTrackRiemannFit.cc @@ -0,0 +1,427 @@ +#define _USE_MATH_DEFINES + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef USE_BL +#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" +#else +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +#endif + +using namespace std; +using namespace Eigen; +using namespace Rfit; +using std::unique_ptr; + +namespace Rfit { + using Vector3i = Eigen::Matrix; + using Vector4i = Eigen::Matrix; + using Vector6d = Eigen::Matrix; + using Vector8d = Eigen::Matrix; +}; // namespace Rfit + +// quadruplets... +struct hits_gen { + Matrix3xNd<4> hits; + Eigen::Matrix hits_ge; + Vector5d true_par; +}; + +struct geometry { + Vector8d barrel; + Vector4i barrel_2; + Vector8d R_err; + Vector8d Rp_err; + Vector8d z_err; + Vector6d hand; + Vector3i hand_2; + Vector6d xy_err; + Vector6d zh_err; + double z_max; + double r_max; +}; + +void test_helix_fit(); + +constexpr int c_speed = 299792458; +constexpr double pi = M_PI; +default_random_engine generator(1); + +void smearing(const Vector5d& err, const bool& isbarrel, double& x, double& y, double& z) { + normal_distribution dist_R(0., err[0]); + normal_distribution dist_Rp(0., err[1]); + normal_distribution dist_z(0., err[2]); + normal_distribution dist_xyh(0., err[3]); + normal_distribution dist_zh(0., err[4]); + if (isbarrel) { + double dev_Rp = dist_Rp(generator); + double dev_R = dist_R(generator); + double R = sqrt(Rfit::sqr(x) + Rfit::sqr(y)); + x += dev_Rp * +y / R + dev_R * -x / R; + y += dev_Rp * -x / R + dev_R * -y / R; + z += dist_z(generator); + } else { + x += dist_xyh(generator); + y += dist_xyh(generator); + z += dist_zh(generator); + } +} + +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.col(i)[0] = (Rfit::sqr(err[1]) * Rfit::sqr(hits(1, i)) + Rfit::sqr(err[0]) * Rfit::sqr(hits(0, i))) / R2; + V.col(i)[2] = (Rfit::sqr(err[1]) * Rfit::sqr(hits(0, i)) + Rfit::sqr(err[0]) * Rfit::sqr(hits(1, i))) / R2; + V.col(i)[1] = (Rfit::sqr(err[0]) - Rfit::sqr(err[1])) * hits(1, i) * hits(0, i) / R2; + V.col(i)[5] = Rfit::sqr(err[2]); + } else { + 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_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, + // 5./10000, 5./10000, 5./10000}; constexpr double Rp_err[8] = {35./10000, 18./10000, + // 15./10000, 34./10000, 35./10000, 18./10000, 15./10000, 34./10000}; constexpr double z_err[8] = + // {72./10000, 38./10000, 25./10000, 56./10000, 72./10000, 38./10000, 25./10000, 56./10000}; + constexpr double R_err[8] = { + 10. / 10000, 10. / 10000, 10. / 10000, 10. / 10000, 10. / 10000, 10. / 10000, 10. / 10000, 10. / 10000}; + constexpr double Rp_err[8] = { + 35. / 10000, 18. / 10000, 15. / 10000, 34. / 10000, 35. / 10000, 18. / 10000, 15. / 10000, 34. / 10000}; + constexpr double z_err[8] = { + 72. / 10000, 38. / 10000, 25. / 10000, 56. / 10000, 72. / 10000, 38. / 10000, 25. / 10000, 56. / 10000}; + const double x2 = gen_par(0) + gen_par(4) * cos(gen_par(3) * pi / 180); + const double y2 = gen_par(1) + gen_par(4) * sin(gen_par(3) * pi / 180); + const double alpha = atan2(y2, x2); + + for (unsigned int i = 0; i < n; ++i) { + const double a = gen_par(4); + const double b = rad[i]; + const double c = sqrt(Rfit::sqr(x2) + Rfit::sqr(y2)); + const double beta = acos((Rfit::sqr(a) - Rfit::sqr(b) - Rfit::sqr(c)) / (-2. * b * c)); + const double gamma = alpha + beta; + gen.hits(0, i) = rad[i] * cos(gamma); + gen.hits(1, i) = rad[i] * sin(gamma); + gen.hits(2, i) = + gen_par(2) + + 1 / tan(gen_par(5) * pi / 180) * 2. * + asin(sqrt(Rfit::sqr((gen_par(0) - gen.hits(0, i))) + Rfit::sqr((gen_par(1) - gen.hits(1, i)))) / + (2. * gen_par(4))) * + gen_par(4); + // isbarrel(i) = ?? + 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_ge, i, n, gen.hits, err, true); + } + + return gen; +} + +Vector5d True_par(const Matrix& gen_par, const int& charge, const double& B_field) { + Vector5d true_par; + const double x0 = gen_par(0) + gen_par(4) * cos(gen_par(3) * pi / 180); + const double y0 = gen_par(1) + gen_par(4) * sin(gen_par(3) * pi / 180); + circle_fit circle; + circle.par << x0, y0, gen_par(4); + circle.q = 1; + Rfit::par_uvrtopak(circle, B_field, false); + true_par.block(0, 0, 3, 1) = circle.par; + true_par(3) = 1 / tan(gen_par(5) * pi / 180); + const int dir = ((gen_par(0) - cos(true_par(0) - pi / 2) * true_par(1)) * (gen_par(1) - y0) - + (gen_par(1) - sin(true_par(0) - pi / 2) * true_par(1)) * (gen_par(0) - x0) > + 0) + ? -1 + : 1; + true_par(4) = gen_par(2) + 1 / tan(gen_par(5) * pi / 180) * dir * 2.f * + asin(sqrt(Rfit::sqr((gen_par(0) - cos(true_par(0) - pi / 2) * true_par(1))) + + Rfit::sqr((gen_par(1) - sin(true_par(0) - pi / 2) * true_par(1)))) / + (2.f * gen_par(4))) * + gen_par(4); + return true_par; +} + +Matrix New_par(const Matrix& gen_par, const int& charge, const double& B_field) { + Matrix new_par; + new_par.block(0, 0, 3, 1) = gen_par.block(0, 0, 3, 1); + new_par(3) = gen_par(3) - charge * 90; + new_par(4) = gen_par(4) / B_field; + // new_par(5) = atan(sinh(gen_par(5))) * 180 / pi; + new_par(5) = 2. * atan(exp(-gen_par(5))) * 180 / pi; + return new_par; +} + +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_; + const double B_field = 3.8 * c_speed / pow(10, 9) / 100; + Matrix gen_par; + Vector5d true_par; + Vector5d err; + generator.seed(1); + std::cout << std::setprecision(6); + cout << "_________________________________________________________________________\n"; + cout << "n x(cm) y(cm) z(cm) phi(grad) R(Gev/c) eta iteration 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 + } + + const int iteration = 5000; + gen_par = New_par(gen_par, 1, B_field); + true_par = True_par(gen_par, 1, B_field); + std::array helixRiemann_fit; + + std::cout << "\nTrue parameters: " + << "phi: " << true_par(0) << " " + << "dxy: " << true_par(1) << " " + << "pt: " << true_par(2) << " " + << "CotT: " << true_par(3) << " " + << "Zip: " << true_par(4) << " " << std::endl; + auto start = std::chrono::high_resolution_clock::now(); + auto delta = start - start; + for (int i = 0; i < 100 * iteration; i++) { + hits_gen gen; + gen = Hits_gen(n_, gen_par); + // gen.hits = MatrixXd::Zero(3, 4); + // gen.hits_cov = MatrixXd::Zero(3 * 4, 3 * 4); + // gen.hits.col(0) << 1.82917642593, 2.0411875248, 7.18495464325; + // gen.hits.col(1) << 4.47041416168, 4.82704305649, 18.6394691467; + // gen.hits.col(2) << 7.25991010666, 7.74653434753, 30.6931324005; + // gen.hits.col(3) << 8.99161434174, 9.54262828827, 38.1338043213; + delta -= std::chrono::high_resolution_clock::now() - start; + helixRiemann_fit[i % iteration] = +#ifdef USE_BL + BrokenLine::BL_Helix_fit(gen.hits, gen.hits_ge, B_field); +#else + Rfit::Helix_fit(gen.hits, gen.hits_ge, B_field, true); +#endif + delta += std::chrono::high_resolution_clock::now() - start; + + if (helixRiemann_fit[i % iteration].par(0) > 10.) + std::cout << "error" << std::endl; + if (0 == i) + cout << std::setprecision(6) << "phi: " << helixRiemann_fit[i].par(0) << " +/- " + << sqrt(helixRiemann_fit[i].cov(0, 0)) << " vs " << true_par(0) << endl + << "Tip: " << helixRiemann_fit[i].par(1) << " +/- " << sqrt(helixRiemann_fit[i].cov(1, 1)) << " vs " + << true_par(1) << endl + << "p_t: " << helixRiemann_fit[i].par(2) << " +/- " << sqrt(helixRiemann_fit[i].cov(2, 2)) << " vs " + << true_par(2) << endl + << "theta:" << helixRiemann_fit[i].par(3) << " +/- " << sqrt(helixRiemann_fit[i].cov(3, 3)) << " vs " + << true_par(3) << endl + << "Zip: " << helixRiemann_fit[i].par(4) << " +/- " << sqrt(helixRiemann_fit[i].cov(4, 4)) << " vs " + << true_par(4) << endl + << "charge:" << helixRiemann_fit[i].q << " vs 1" << endl + << "covariance matrix:" << endl + << helixRiemann_fit[i].cov << endl + << "Initial hits:\n" + << gen.hits << endl + << "Initial Covariance:\n" + << gen.hits_ge << endl; + } + std::cout << "elapsted time " << double(std::chrono::duration_cast(delta).count()) / 1.e6 + << std::endl; + computePull(helixRiemann_fit, "Riemann", n_, iteration, true_par); +} + +int main(int nargs, char**) { + TFile f("TestFitResults.root", "RECREATE"); + test_helix_fit(nargs > 1); + f.Close(); + return 0; +} diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu new file mode 100644 index 0000000000000..f0b641361aee4 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPU.cu @@ -0,0 +1,341 @@ +#include + +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/requireDevices.h" + +#ifdef USE_BL +#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" +#else +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +#endif + +#include "test_common.h" + +using namespace Eigen; + +namespace Rfit { + constexpr uint32_t maxNumberOfTracks() { return 5 * 1024; } + constexpr uint32_t stride() { return maxNumberOfTracks(); } + // hits + template + using Matrix3xNd = Eigen::Matrix; + template + using Map3xNd = Eigen::Map, 0, Eigen::Stride<3 * stride(), stride()>>; + // errors + template + using Matrix6xNf = Eigen::Matrix; + template + using Map6xNf = Eigen::Map, 0, Eigen::Stride<6 * stride(), stride()>>; + // fast fit + using Map4d = Eigen::Map>; + +} // namespace Rfit + +template +__global__ void kernelPrintSizes(double* __restrict__ phits, float* __restrict__ phits_ge) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + Rfit::Map3xNd hits(phits + i, 3, 4); + Rfit::Map6xNf hits_ge(phits_ge + i, 6, 4); + if (i != 0) + return; + printf("GPU sizes %lu %lu %lu %lu %lu\n", + sizeof(hits[i]), + sizeof(hits_ge[i]), + sizeof(Vector4d), + sizeof(Rfit::line_fit), + sizeof(Rfit::circle_fit)); +} + +template +__global__ void kernelFastFit(double* __restrict__ phits, double* __restrict__ presults) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + Rfit::Map3xNd hits(phits + i, 3, N); + Rfit::Map4d result(presults + i, 4); +#ifdef USE_BL + BrokenLine::BL_Fast_fit(hits, result); +#else + Rfit::Fast_fit(hits, result); +#endif +} + +#ifdef USE_BL + +template +__global__ void kernelBrokenLineFit(double* __restrict__ phits, + float* __restrict__ phits_ge, + double* __restrict__ pfast_fit_input, + double B, + Rfit::circle_fit* circle_fit, + Rfit::line_fit* line_fit) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + Rfit::Map3xNd hits(phits + i, 3, N); + Rfit::Map4d fast_fit_input(pfast_fit_input + i, 4); + Rfit::Map6xNf hits_ge(phits_ge + i, 6, N); + + BrokenLine::PreparedBrokenLineData data; + Rfit::Matrix3d Jacob; + + auto& line_fit_results = line_fit[i]; + auto& circle_fit_results = circle_fit[i]; + + BrokenLine::prepareBrokenLineData(hits, fast_fit_input, B, data); + BrokenLine::BL_Line_fit(hits_ge, fast_fit_input, B, data, line_fit_results); + BrokenLine::BL_Circle_fit(hits, hits_ge, fast_fit_input, B, data, circle_fit_results); + Jacob << 1., 0, 0, 0, 1., 0, 0, 0, + -B / std::copysign(Rfit::sqr(circle_fit_results.par(2)), circle_fit_results.par(2)); + circle_fit_results.par(2) = B / std::abs(circle_fit_results.par(2)); + circle_fit_results.cov = Jacob * circle_fit_results.cov * Jacob.transpose(); + +#ifdef TEST_DEBUG + if (0 == i) { + printf("Circle param %f,%f,%f\n", circle_fit[i].par(0), circle_fit[i].par(1), circle_fit[i].par(2)); + } +#endif +} + +#else + +template +__global__ 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; + Rfit::Map3xNd hits(phits + i, 3, N); + Rfit::Map4d fast_fit_input(pfast_fit_input + i, 4); + Rfit::Map6xNf hits_ge(phits_ge + i, 6, N); + + 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)); + printf("hits_cov(0,0): %f\n", (*hits_cov)(0, 0)); + printf("hits_cov(1,1): %f\n", (*hits_cov)(1, 1)); + 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.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 +} + +template +__global__ void kernelLineFit(double* __restrict__ phits, + float* __restrict__ phits_ge, + double B, + Rfit::circle_fit* circle_fit, + double* __restrict__ pfast_fit_input, + Rfit::line_fit* line_fit) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + Rfit::Map3xNd hits(phits + i, 3, N); + Rfit::Map4d fast_fit_input(pfast_fit_input + i, 4); + Rfit::Map6xNf hits_ge(phits_ge + i, 6, N); + line_fit[i] = Rfit::Line_fit(hits, hits_ge, circle_fit[i], fast_fit_input, B, true); +} +#endif + +template +__device__ __host__ void fillHitsAndHitsCov(M3xN& hits, M6xN& hits_ge) { + constexpr uint32_t N = M3xN::ColsAtCompileTime; + + if (N == 5) { + hits << 2.934787, 6.314229, 8.936963, 10.360559, 12.856387, 0.773211, 1.816356, 2.765734, 3.330824, 4.422212, + -10.980247, -23.162731, -32.759060, -38.061260, -47.518867; + hits_ge.col(0) << 1.424715e-07, -4.996975e-07, 1.752614e-06, 3.660689e-11, 1.644638e-09, 7.346080e-05; + hits_ge.col(1) << 6.899177e-08, -1.873414e-07, 5.087101e-07, -2.078806e-10, -2.210498e-11, 4.346079e-06; + hits_ge.col(2) << 1.406273e-06, 4.042467e-07, 6.391180e-07, -3.141497e-07, 6.513821e-08, 1.163863e-07; + hits_ge.col(3) << 1.176358e-06, 2.154100e-07, 5.072816e-07, -8.161219e-08, 1.437878e-07, 5.951832e-08; + hits_ge.col(4) << 2.852843e-05, 7.956492e-06, 3.117701e-06, -1.060541e-06, 8.777413e-09, 1.426417e-07; + return; + } + + if (N > 3) + 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; + else + hits << 1.98645, 4.72598, 7.65632, 2.18002, 4.88864, 7.75845, 2.46338, 6.99838, 11.808; + + hits_ge.col(0)[0] = 7.14652e-06; + hits_ge.col(1)[0] = 2.15789e-06; + hits_ge.col(2)[0] = 1.63328e-06; + if (N > 3) + 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; + if (N > 3) + 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; + if (N > 3) + 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; + if (N > 3) + hits_ge.col(3)[1] = -5.28e-06; +} + +template +__global__ void kernelFillHitsAndHitsCov(double* __restrict__ phits, float* phits_ge) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + Rfit::Map3xNd hits(phits + i, 3, N); + Rfit::Map6xNf hits_ge(phits_ge + i, 6, N); + hits_ge = MatrixXf::Zero(6, N); + fillHitsAndHitsCov(hits, hits_ge); +} + +template +void testFit() { + constexpr double B = 0.0113921; + Rfit::Matrix3xNd hits; + Rfit::Matrix6xNf hits_ge = MatrixXf::Zero(6, N); + 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; + Rfit::line_fit* line_fit_resultsGPUret = new Rfit::line_fit(); + + fillHitsAndHitsCov(hits, hits_ge); + + std::cout << "sizes " << N << ' ' << sizeof(hits) << ' ' << sizeof(hits_ge) << ' ' << sizeof(Vector4d) << ' ' + << sizeof(Rfit::line_fit) << ' ' << sizeof(Rfit::circle_fit) << std::endl; + + std::cout << "Generated hits:\n" << hits << std::endl; + std::cout << "Generated cov:\n" << hits_ge << std::endl; + + // FAST_FIT_CPU +#ifdef USE_BL + Vector4d fast_fit_results; + BrokenLine::BL_Fast_fit(hits, fast_fit_results); +#else + Vector4d fast_fit_results; + Rfit::Fast_fit(hits, fast_fit_results); +#endif + 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(&hitsGPU, Rfit::maxNumberOfTracks() * sizeof(Rfit::Matrix3xNd))); + cudaCheck(cudaMalloc(&hits_geGPU, Rfit::maxNumberOfTracks() * sizeof(Rfit::Matrix6xNf))); + cudaCheck(cudaMalloc(&fast_fit_resultsGPU, Rfit::maxNumberOfTracks() * sizeof(Vector4d))); + cudaCheck(cudaMalloc(&line_fit_resultsGPU, Rfit::maxNumberOfTracks() * sizeof(Rfit::line_fit))); + cudaCheck(cudaMalloc(&circle_fit_resultsGPU, Rfit::maxNumberOfTracks() * sizeof(Rfit::circle_fit))); + + cudaCheck(cudaMemset(fast_fit_resultsGPU, 0, Rfit::maxNumberOfTracks() * sizeof(Vector4d))); + cudaCheck(cudaMemset(line_fit_resultsGPU, 0, Rfit::maxNumberOfTracks() * sizeof(Rfit::line_fit))); + + kernelPrintSizes<<>>(hitsGPU, hits_geGPU); + kernelFillHitsAndHitsCov<<>>(hitsGPU, hits_geGPU); + + // FAST_FIT GPU + kernelFastFit<<>>(hitsGPU, fast_fit_resultsGPU); + cudaDeviceSynchronize(); + + cudaCheck(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)); + +#ifdef USE_BL + // CIRCLE AND LINE FIT CPU + BrokenLine::PreparedBrokenLineData data; + BrokenLine::karimaki_circle_fit circle_fit_results; + Rfit::line_fit line_fit_results; + Rfit::Matrix3d Jacob; + BrokenLine::prepareBrokenLineData(hits, fast_fit_results, B, data); + BrokenLine::BL_Line_fit(hits_ge, fast_fit_results, B, data, line_fit_results); + BrokenLine::BL_Circle_fit(hits, hits_ge, fast_fit_results, B, data, circle_fit_results); + Jacob << 1., 0, 0, 0, 1., 0, 0, 0, + -B / std::copysign(Rfit::sqr(circle_fit_results.par(2)), circle_fit_results.par(2)); + circle_fit_results.par(2) = B / std::abs(circle_fit_results.par(2)); + circle_fit_results.cov = Jacob * circle_fit_results.cov * Jacob.transpose(); + + // fit on GPU + kernelBrokenLineFit + <<>>(hitsGPU, hits_geGPU, fast_fit_resultsGPU, B, circle_fit_resultsGPU, line_fit_resultsGPU); + cudaDeviceSynchronize(); + +#else + // CIRCLE_FIT CPU + Rfit::VectorNd rad = (hits.block(0, 0, 2, N).colwise().norm()); + + Rfit::Matrix2Nd hits_cov = Rfit::Matrix2Nd::Zero(); + Rfit::loadCovariance2D(hits_ge, hits_cov); + Rfit::circle_fit circle_fit_results = + Rfit::Circle_fit(hits.block(0, 0, 2, N), hits_cov, fast_fit_results, rad, B, true); + + // CIRCLE_FIT GPU + kernelCircleFit<<>>(hitsGPU, hits_geGPU, fast_fit_resultsGPU, B, circle_fit_resultsGPU); + cudaDeviceSynchronize(); + + // LINE_FIT CPU + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, B, true); + + kernelLineFit + <<>>(hitsGPU, hits_geGPU, B, circle_fit_resultsGPU, fast_fit_resultsGPU, line_fit_resultsGPU); + cudaDeviceSynchronize(); +#endif + + std::cout << "Fitted values (CircleFit):\n" << circle_fit_results.par << std::endl; + + cudaCheck( + cudaMemcpy(circle_fit_resultsGPUret, circle_fit_resultsGPU, sizeof(Rfit::circle_fit), cudaMemcpyDeviceToHost)); + std::cout << "Fitted values (CircleFit) GPU:\n" << circle_fit_resultsGPUret->par << std::endl; + assert(isEqualFuzzy(circle_fit_results.par, circle_fit_resultsGPUret->par)); + + std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << std::endl; + // LINE_FIT GPU + cudaCheck(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, N == 5 ? 1e-4 : 1e-6)); // requires fma on CPU + + 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; +} + +int main(int argc, char* argv[]) { + cms::cudatest::requireDevices(); + + testFit<4>(); + testFit<3>(); + testFit<5>(); + + std::cout << "TEST FIT, NO ERRORS" << std::endl; + + return 0; +} diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPUNoFit.cu b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPUNoFit.cu new file mode 100644 index 0000000000000..6ac1088943305 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenGPUNoFit.cu @@ -0,0 +1,248 @@ +#include + +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/requireDevices.h" +#include "test_common.h" + +using namespace Eigen; + +using Matrix5d = Matrix; + +__host__ __device__ void eigenValues(Matrix3d *m, Eigen::SelfAdjointEigenSolver::RealVectorType *ret) { +#if TEST_DEBUG + printf("Matrix(0,0): %f\n", (*m)(0, 0)); + printf("Matrix(1,1): %f\n", (*m)(1, 1)); + printf("Matrix(2,2): %f\n", (*m)(2, 2)); +#endif + SelfAdjointEigenSolver es; + es.computeDirect(*m); + (*ret) = es.eigenvalues(); + return; +} + +__global__ void kernel(Matrix3d *m, Eigen::SelfAdjointEigenSolver::RealVectorType *ret) { + eigenValues(m, ret); +} + +__global__ void kernelInverse3x3(Matrix3d *in, Matrix3d *out) { (*out) = in->inverse(); } + +__global__ void kernelInverse4x4(Matrix4d *in, Matrix4d *out) { (*out) = in->inverse(); } + +__global__ void kernelInverse5x5(Matrix5d *in, Matrix5d *out) { (*out) = in->inverse(); } + +template +__global__ void kernelMultiply(M1 *J, M2 *C, M3 *result) { +// Map res(result->data()); +#if TEST_DEBUG + printf("*** GPU IN ***\n"); +#endif + printIt(J); + printIt(C); + // res.noalias() = (*J) * (*C); + // printIt(&res); + (*result) = (*J) * (*C); +#if TEST_DEBUG + printf("*** GPU OUT ***\n"); +#endif + return; +} + +template +void testMultiply() { + std::cout << "TEST MULTIPLY" << std::endl; + std::cout << "Product of type " << row1 << "x" << col1 << " * " << row2 << "x" << col2 << std::endl; + Eigen::Matrix J; + fillMatrix(J); + Eigen::Matrix C; + fillMatrix(C); + Eigen::Matrix multiply_result = J * C; +#if TEST_DEBUG + std::cout << "Input J:" << std::endl; + printIt(&J); + std::cout << "Input C:" << std::endl; + printIt(&C); + std::cout << "Output:" << std::endl; + printIt(&multiply_result); +#endif + // GPU + Eigen::Matrix *JGPU = nullptr; + Eigen::Matrix *CGPU = nullptr; + Eigen::Matrix *multiply_resultGPU = nullptr; + Eigen::Matrix *multiply_resultGPUret = new Eigen::Matrix(); + + cudaCheck(cudaMalloc((void **)&JGPU, sizeof(Eigen::Matrix))); + cudaCheck(cudaMalloc((void **)&CGPU, sizeof(Eigen::Matrix))); + cudaCheck(cudaMalloc((void **)&multiply_resultGPU, sizeof(Eigen::Matrix))); + cudaCheck(cudaMemcpy(JGPU, &J, sizeof(Eigen::Matrix), cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(CGPU, &C, sizeof(Eigen::Matrix), cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy( + multiply_resultGPU, &multiply_result, sizeof(Eigen::Matrix), cudaMemcpyHostToDevice)); + + kernelMultiply<<<1, 1>>>(JGPU, CGPU, multiply_resultGPU); + cudaDeviceSynchronize(); + + cudaCheck(cudaMemcpy( + multiply_resultGPUret, multiply_resultGPU, sizeof(Eigen::Matrix), cudaMemcpyDeviceToHost)); + printIt(multiply_resultGPUret); + assert(isEqualFuzzy(multiply_result, (*multiply_resultGPUret))); +} + +void testInverse3x3() { + std::cout << "TEST INVERSE 3x3" << std::endl; + Matrix3d m; + fillMatrix(m); + m += m.transpose().eval(); + + Matrix3d m_inv = m.inverse(); + Matrix3d *mGPU = nullptr; + Matrix3d *mGPUret = nullptr; + Matrix3d *mCPUret = new Matrix3d(); + +#if TEST_DEBUG + std::cout << "Here is the matrix m:" << std::endl << m << std::endl; + std::cout << "Its inverse is:" << std::endl << m.inverse() << std::endl; +#endif + cudaCheck(cudaMalloc((void **)&mGPU, sizeof(Matrix3d))); + cudaCheck(cudaMalloc((void **)&mGPUret, sizeof(Matrix3d))); + cudaCheck(cudaMemcpy(mGPU, &m, sizeof(Matrix3d), cudaMemcpyHostToDevice)); + + kernelInverse3x3<<<1, 1>>>(mGPU, mGPUret); + cudaDeviceSynchronize(); + + cudaCheck(cudaMemcpy(mCPUret, mGPUret, sizeof(Matrix3d), cudaMemcpyDeviceToHost)); +#if TEST_DEBUG + std::cout << "Its GPU inverse is:" << std::endl << (*mCPUret) << std::endl; +#endif + assert(isEqualFuzzy(m_inv, *mCPUret)); +} + +void testInverse4x4() { + std::cout << "TEST INVERSE 4x4" << std::endl; + Matrix4d m; + fillMatrix(m); + m += m.transpose().eval(); + + Matrix4d m_inv = m.inverse(); + Matrix4d *mGPU = nullptr; + Matrix4d *mGPUret = nullptr; + Matrix4d *mCPUret = new Matrix4d(); + +#if TEST_DEBUG + std::cout << "Here is the matrix m:" << std::endl << m << std::endl; + std::cout << "Its inverse is:" << std::endl << m.inverse() << std::endl; +#endif + cudaCheck(cudaMalloc((void **)&mGPU, sizeof(Matrix4d))); + cudaCheck(cudaMalloc((void **)&mGPUret, sizeof(Matrix4d))); + cudaCheck(cudaMemcpy(mGPU, &m, sizeof(Matrix4d), cudaMemcpyHostToDevice)); + + kernelInverse4x4<<<1, 1>>>(mGPU, mGPUret); + cudaDeviceSynchronize(); + + cudaCheck(cudaMemcpy(mCPUret, mGPUret, sizeof(Matrix4d), cudaMemcpyDeviceToHost)); +#if TEST_DEBUG + std::cout << "Its GPU inverse is:" << std::endl << (*mCPUret) << std::endl; +#endif + assert(isEqualFuzzy(m_inv, *mCPUret)); +} + +void testInverse5x5() { + std::cout << "TEST INVERSE 5x5" << std::endl; + Matrix5d m; + fillMatrix(m); + m += m.transpose().eval(); + + Matrix5d m_inv = m.inverse(); + Matrix5d *mGPU = nullptr; + Matrix5d *mGPUret = nullptr; + Matrix5d *mCPUret = new Matrix5d(); + +#if TEST_DEBUG + std::cout << "Here is the matrix m:" << std::endl << m << std::endl; + std::cout << "Its inverse is:" << std::endl << m.inverse() << std::endl; +#endif + cudaCheck(cudaMalloc((void **)&mGPU, sizeof(Matrix5d))); + cudaCheck(cudaMalloc((void **)&mGPUret, sizeof(Matrix5d))); + cudaCheck(cudaMemcpy(mGPU, &m, sizeof(Matrix5d), cudaMemcpyHostToDevice)); + + kernelInverse5x5<<<1, 1>>>(mGPU, mGPUret); + cudaDeviceSynchronize(); + + cudaCheck(cudaMemcpy(mCPUret, mGPUret, sizeof(Matrix5d), cudaMemcpyDeviceToHost)); +#if TEST_DEBUG + std::cout << "Its GPU inverse is:" << std::endl << (*mCPUret) << std::endl; +#endif + assert(isEqualFuzzy(m_inv, *mCPUret)); +} + +void testEigenvalues() { + std::cout << "TEST EIGENVALUES" << std::endl; + Matrix3d m; + fillMatrix(m); + m += m.transpose().eval(); + + Matrix3d *m_gpu = nullptr; + Matrix3d *mgpudebug = new Matrix3d(); + Eigen::SelfAdjointEigenSolver::RealVectorType *ret = + new Eigen::SelfAdjointEigenSolver::RealVectorType; + Eigen::SelfAdjointEigenSolver::RealVectorType *ret1 = + new Eigen::SelfAdjointEigenSolver::RealVectorType; + Eigen::SelfAdjointEigenSolver::RealVectorType *ret_gpu = nullptr; + eigenValues(&m, ret); +#if TEST_DEBUG + std::cout << "Generated Matrix M 3x3:\n" << m << std::endl; + std::cout << "The eigenvalues of M are:" << std::endl << (*ret) << std::endl; + std::cout << "*************************\n\n" << std::endl; +#endif + cudaCheck(cudaMalloc((void **)&m_gpu, sizeof(Matrix3d))); + cudaCheck(cudaMalloc((void **)&ret_gpu, sizeof(Eigen::SelfAdjointEigenSolver::RealVectorType))); + cudaCheck(cudaMemcpy(m_gpu, &m, sizeof(Matrix3d), cudaMemcpyHostToDevice)); + + kernel<<<1, 1>>>(m_gpu, ret_gpu); + cudaDeviceSynchronize(); + + cudaCheck(cudaMemcpy(mgpudebug, m_gpu, sizeof(Matrix3d), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy( + ret1, ret_gpu, sizeof(Eigen::SelfAdjointEigenSolver::RealVectorType), cudaMemcpyDeviceToHost)); +#if TEST_DEBUG + std::cout << "GPU Generated Matrix M 3x3:\n" << (*mgpudebug) << std::endl; + std::cout << "GPU The eigenvalues of M are:" << std::endl << (*ret1) << std::endl; + std::cout << "*************************\n\n" << std::endl; +#endif + assert(isEqualFuzzy(*ret, *ret1)); +} + +int main(int argc, char *argv[]) { + cms::cudatest::requireDevices(); + + testEigenvalues(); + testInverse3x3(); + testInverse4x4(); + testInverse5x5(); + + testMultiply<1, 2, 2, 1>(); + testMultiply<1, 2, 2, 2>(); + testMultiply<1, 2, 2, 3>(); + testMultiply<1, 2, 2, 4>(); + testMultiply<1, 2, 2, 5>(); + testMultiply<2, 1, 1, 2>(); + testMultiply<2, 1, 1, 3>(); + testMultiply<2, 1, 1, 4>(); + testMultiply<2, 1, 1, 5>(); + testMultiply<2, 2, 2, 2>(); + testMultiply<2, 3, 3, 1>(); + testMultiply<2, 3, 3, 2>(); + testMultiply<2, 3, 3, 4>(); + testMultiply<2, 3, 3, 5>(); + testMultiply<3, 2, 2, 3>(); + testMultiply<2, 3, 3, 3>(); // DOES NOT COMPILE W/O PATCHING EIGEN + testMultiply<3, 3, 3, 3>(); + testMultiply<8, 8, 8, 8>(); + testMultiply<3, 4, 4, 3>(); + testMultiply<2, 4, 4, 2>(); + testMultiply<3, 4, 4, 2>(); // DOES NOT COMPILE W/O PATCHING EIGEN + + return 0; +} diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp b/RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp new file mode 100644 index 0000000000000..709757a803884 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/testEigenJacobian.cpp @@ -0,0 +1,134 @@ +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h" +#include + +using Rfit::Matrix5d; +using Rfit::Vector5d; + +#include "TrackingTools/AnalyticalJacobians/interface/JacobianLocalToCurvilinear.h" + +#include "DataFormats/GeometrySurface/interface/Surface.h" +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "TrackingTools/TrajectoryParametrization/interface/GlobalTrajectoryParameters.h" + +#include "DataFormats/GeometrySurface/interface/Plane.h" + +#include "MagneticField/Engine/interface/MagneticField.h" + +namespace { + + struct M5T : public MagneticField { + M5T() : mf(0., 0., 5.) {} + virtual GlobalVector inTesla(const GlobalPoint&) const { return mf; } + + GlobalVector mf; + }; + +} // namespace + +// old pixeltrack version... +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 loadCov(Vector5d const& e) { + Matrix5d cov; + for (int i = 0; i < 5; ++i) + cov(i, i) = e(i) * e(i); + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < i; ++j) { + double v = 0.3 * std::sqrt(cov(i, i) * cov(j, j)); // this makes the matrix pos defined + cov(i, j) = (i + j) % 2 ? -0.4 * v : 0.1 * v; + cov(j, i) = cov(i, j); + } + } + return cov; +} + +#include +int main() { + M5T const mf; + + for (auto charge = -1; charge < 2; charge += 2) + for (auto szip = -1; szip < 2; szip += 2) + for (auto stip = -1; stip < 2; stip += 2) { + 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; + //!<(phi,Tip,pt,cotan(theta)),Zip) + par0(1) *= stip; + par0(4) *= szip; + + Matrix5d cov0 = loadCov(del0); + + Vector5d par1; + Vector5d par2; + + Matrix5d cov1; + Matrix5d cov2; + + // Matrix5d covf = transfFast(cov0,par0); + + Rfit::transformToPerigeePlane(par0, cov0, par1, cov1); + + std::cout << "cov1\n" << cov1 << std::endl; + + LocalTrajectoryParameters lpar(par1(0), par1(1), par1(2), par1(3), par1(4), 1.); + AlgebraicSymMatrix55 m; + for (int i = 0; i < 5; ++i) + for (int j = i; j < 5; ++j) + m(i, j) = cov1(i, j); + + float phi = par0(0); + float sp = std::sin(phi); + float cp = std::cos(phi); + Surface::RotationType rot(sp, -cp, 0, 0, 0, -1.f, cp, sp, 0); + + Surface::PositionType bs(0., 0., 0.); + Plane plane(bs, rot); + GlobalTrajectoryParameters gp( + plane.toGlobal(lpar.position()), plane.toGlobal(lpar.momentum()), lpar.charge(), &mf); + std::cout << "global par " << gp.position() << ' ' << gp.momentum() << ' ' << gp.charge() << std::endl; + JacobianLocalToCurvilinear jl2c(plane, lpar, mf); + std::cout << "jac l2c" << jl2c.jacobian() << std::endl; + + AlgebraicSymMatrix55 mo = ROOT::Math::Similarity(jl2c.jacobian(), m); + std::cout << "curv error\n" << mo << std::endl; + + /* + + // not accurate as the perigee plane move as well... + Vector5d del1 = par2-par1; + + + // don't ask: guess + std::cout << "charge " << charge << std::endl; + 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 << "cov0\n" << cov0 << std::endl; + std::cout << "cov1\n" << cov1 << std::endl; + std::cout << "cov2\n" << cov2 << std::endl; + */ + + std::cout << std::endl << "----------" << std::endl; + + } // lopp over signs + + return 0; +} diff --git a/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp new file mode 100644 index 0000000000000..370828c4fcef9 --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/testRiemannFit.cpp @@ -0,0 +1,153 @@ +#include + +#include +#include + +#ifdef USE_BL +#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" +#else +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" +#endif + +#include "test_common.h" + +using namespace Eigen; + +namespace Rfit { + constexpr uint32_t maxNumberOfTracks() { return 5 * 1024; } + constexpr uint32_t stride() { return maxNumberOfTracks(); } + // hits + template + using Matrix3xNd = Eigen::Matrix; + template + using Map3xNd = Eigen::Map, 0, Eigen::Stride<3 * stride(), stride()> >; + // errors + template + using Matrix6xNf = Eigen::Matrix; + template + using Map6xNf = Eigen::Map, 0, Eigen::Stride<6 * stride(), stride()> >; + // fast fit + using Map4d = Eigen::Map >; + +} // namespace Rfit + +/* +Hit global: 641,0 2: 2.934787,0.773211,-10.980247 +Error: 641,0 2: 1.424715e-07,-4.996975e-07,1.752614e-06,3.660689e-11,1.644638e-09,7.346080e-05 +Hit global: 641,1 104: 6.314229,1.816356,-23.162731 +Error: 641,1 104: 6.899177e-08,-1.873414e-07,5.087101e-07,-2.078806e-10,-2.210498e-11,4.346079e-06 +Hit global: 641,2 1521: 8.936963,2.765734,-32.759060 +Error: 641,2 1521: 1.406273e-06,4.042467e-07,6.391180e-07,-3.141497e-07,6.513821e-08,1.163863e-07 +Hit global: 641,3 1712: 10.360559,3.330824,-38.061260 +Error: 641,3 1712: 1.176358e-06,2.154100e-07,5.072816e-07,-8.161219e-08,1.437878e-07,5.951832e-08 +Hit global: 641,4 1824: 12.856387,4.422212,-47.518867 +Error: 641,4 1824: 2.852843e-05,7.956492e-06,3.117701e-06,-1.060541e-06,8.777413e-09,1.426417e-07 +*/ + +template +void fillHitsAndHitsCov(M3xN& hits, M6xN& hits_ge) { + constexpr uint32_t N = M3xN::ColsAtCompileTime; + + if (N == 5) { + hits << 2.934787, 6.314229, 8.936963, 10.360559, 12.856387, 0.773211, 1.816356, 2.765734, 3.330824, 4.422212, + -10.980247, -23.162731, -32.759060, -38.061260, -47.518867; + hits_ge.col(0) << 1.424715e-07, -4.996975e-07, 1.752614e-06, 3.660689e-11, 1.644638e-09, 7.346080e-05; + hits_ge.col(1) << 6.899177e-08, -1.873414e-07, 5.087101e-07, -2.078806e-10, -2.210498e-11, 4.346079e-06; + hits_ge.col(2) << 1.406273e-06, 4.042467e-07, 6.391180e-07, -3.141497e-07, 6.513821e-08, 1.163863e-07; + hits_ge.col(3) << 1.176358e-06, 2.154100e-07, 5.072816e-07, -8.161219e-08, 1.437878e-07, 5.951832e-08; + hits_ge.col(4) << 2.852843e-05, 7.956492e-06, 3.117701e-06, -1.060541e-06, 8.777413e-09, 1.426417e-07; + return; + } + + if (N > 3) + 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; + else + hits << 1.98645, 4.72598, 7.65632, 2.18002, 4.88864, 7.75845, 2.46338, 6.99838, 11.808; + + hits_ge.col(0)[0] = 7.14652e-06; + hits_ge.col(1)[0] = 2.15789e-06; + hits_ge.col(2)[0] = 1.63328e-06; + if (N > 3) + 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; + if (N > 3) + 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; + if (N > 3) + 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; + if (N > 3) + hits_ge.col(3)[1] = -5.28e-06; +} + +template +void testFit() { + constexpr double B = 0.0113921; + Rfit::Matrix3xNd hits; + Rfit::Matrix6xNf hits_ge = MatrixXf::Zero(6, N); + + fillHitsAndHitsCov(hits, hits_ge); + + std::cout << "sizes " << N << ' ' << 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 +#ifdef USE_BL + Vector4d fast_fit_results; + BrokenLine::BL_Fast_fit(hits, fast_fit_results); +#else + Vector4d fast_fit_results; + Rfit::Fast_fit(hits, fast_fit_results); +#endif + std::cout << "Fitted values (FastFit, [X0, Y0, R, tan(theta)]):\n" << fast_fit_results << std::endl; + + // CIRCLE_FIT CPU + +#ifdef USE_BL + BrokenLine::PreparedBrokenLineData data; + BrokenLine::karimaki_circle_fit circle_fit_results; + Rfit::Matrix3d Jacob; + + BrokenLine::prepareBrokenLineData(hits, fast_fit_results, B, data); + Rfit::line_fit line_fit_results; + BrokenLine::BL_Line_fit(hits_ge, fast_fit_results, B, data, line_fit_results); + BrokenLine::BL_Circle_fit(hits, hits_ge, fast_fit_results, B, data, circle_fit_results); + Jacob << 1., 0, 0, 0, 1., 0, 0, 0, + -B / std::copysign(Rfit::sqr(circle_fit_results.par(2)), circle_fit_results.par(2)); + circle_fit_results.par(2) = B / std::abs(circle_fit_results.par(2)); + circle_fit_results.cov = Jacob * circle_fit_results.cov * Jacob.transpose(); +#else + Rfit::VectorNd rad = (hits.block(0, 0, 2, N).colwise().norm()); + Rfit::Matrix2Nd hits_cov = Rfit::Matrix2Nd::Zero(); + Rfit::loadCovariance2D(hits_ge, hits_cov); + Rfit::circle_fit circle_fit_results = + Rfit::Circle_fit(hits.block(0, 0, 2, N), hits_cov, fast_fit_results, rad, B, true); + // LINE_FIT CPU + Rfit::line_fit line_fit_results = Rfit::Line_fit(hits, hits_ge, circle_fit_results, fast_fit_results, B, true); + Rfit::par_uvrtopak(circle_fit_results, B, true); + +#endif + + std::cout << "Fitted values (CircleFit):\n" + << circle_fit_results.par << "\nchi2 " << circle_fit_results.chi2 << std::endl; + std::cout << "Fitted values (LineFit):\n" << line_fit_results.par << "\nchi2 " << line_fit_results.chi2 << 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[]) { + testFit<4>(); + testFit<3>(); + testFit<5>(); + + return 0; +} diff --git a/RecoPixelVertexing/PixelTrackFitting/test/test_common.h b/RecoPixelVertexing/PixelTrackFitting/test/test_common.h new file mode 100644 index 0000000000000..6377628b0eeca --- /dev/null +++ b/RecoPixelVertexing/PixelTrackFitting/test/test_common.h @@ -0,0 +1,47 @@ +#ifndef RecoPixelVertexing__PixelTrackFitting__test_common_h +#define RecoPixelVertexing__PixelTrackFitting__test_common_h + +#include +#include +#include + +template +__host__ __device__ void printIt(C* m) { +#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) { + printf("Matrix(%d,%d) = %f\n", r, c, (*m)(r, c)); + } + } +#endif +} + +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)) < std::min(std::abs(a(i, j)), std::abs(b(i, j))) * epsilon); + } + } + return true; +} + +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; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dis(0.0, 2.0); + for (int row = 0; row < t.rows(); ++row) { + for (int col = 0; col < t.cols(); ++col) { + t(row, col) = dis(gen); + } + } + return; +} + +#endif diff --git a/RecoPixelVertexing/PixelTriplets/interface/CAHitQuadrupletGenerator.h b/RecoPixelVertexing/PixelTriplets/interface/CAHitQuadrupletGenerator.h index 9d149533eefbc..deb2beb6099ee 100644 --- a/RecoPixelVertexing/PixelTriplets/interface/CAHitQuadrupletGenerator.h +++ b/RecoPixelVertexing/PixelTriplets/interface/CAHitQuadrupletGenerator.h @@ -42,7 +42,7 @@ class CAHitQuadrupletGenerator { ~CAHitQuadrupletGenerator() = default; static void fillDescriptions(edm::ParameterSetDescription& desc); - static const char* fillDescriptionsLabel() { return "caHitQuadruplet"; } + static const char* fillDescriptionsLabel() { return "caHitQuadrupletDefault"; } void initEvent(const edm::Event& ev, const edm::EventSetup& es); diff --git a/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h b/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h new file mode 100644 index 0000000000000..dfe7da010f99e --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/interface/CircleEq.h @@ -0,0 +1,107 @@ +#ifndef RecoPixelVertexingPixelTripletsCircleEq_H +#define RecoPixelVertexingPixelTripletsCircleEq_H +/** +| 1) circle is parameterized as: | +| C*[(X-Xp)**2+(Y-Yp)**2] - 2*alpha*(X-Xp) - 2*beta*(Y-Yp) = 0 | +| Xp,Yp is a point on the track; | +| C = 1/r0 is the curvature ( sign of C is charge of particle ); | +| alpha & beta are the direction cosines of the radial vector at Xp,Yp | +| i.e. alpha = C*(X0-Xp), | +| beta = C*(Y0-Yp), | +| where center of circle is at X0,Y0. | +| | +| Slope dy/dx of tangent at Xp,Yp is -alpha/beta. | +| 2) the z dimension of the helix is parameterized by gamma = dZ/dSperp | +| this is also the tangent of the pitch angle of the helix. | +| with this parameterization, (alpha,beta,gamma) rotate like a vector. | +| 3) For tracks going inward at (Xp,Yp), C, alpha, beta, and gamma change sign| +| +*/ + +#include + +template +class CircleEq { +public: + CircleEq() {} + + constexpr CircleEq(T x1, T y1, T x2, T y2, T x3, T y3) { compute(x1, y1, x2, y2, x3, y3); } + + constexpr void compute(T x1, T y1, T x2, T y2, T x3, T y3); + + // dca to origin divided by curvature + constexpr T dca0() const { + auto x = m_c * m_xp + m_alpha; + auto y = m_c * m_yp + m_beta; + return std::sqrt(x * x + y * y) - T(1); + } + + // dca to given point (divided by curvature) + constexpr T dca(T x, T y) const { + x = m_c * (m_xp - x) + m_alpha; + y = m_c * (m_yp - y) + m_beta; + return std::sqrt(x * x + y * y) - T(1); + } + + // curvature + constexpr auto curvature() const { return m_c; } + + // alpha and beta + constexpr std::pair cosdir() const { return std::make_pair(m_alpha, m_beta); } + + // alpha and beta af given point + constexpr std::pair cosdir(T x, T y) const { + return std::make_pair(m_alpha - m_c * (x - m_xp), m_beta - m_c * (y - m_yp)); + } + + // center + constexpr std::pair center() const { return std::make_pair(m_xp + m_alpha / m_c, m_yp + m_beta / m_c); } + + constexpr auto radius() const { return T(1) / m_c; } + + T m_xp = 0; + T m_yp = 0; + T m_c = 0; + T m_alpha = 0; + T m_beta = 0; +}; + +template +constexpr void CircleEq::compute(T x1, T y1, T x2, T y2, T x3, T y3) { + bool noflip = std::abs(x3 - x1) < std::abs(y3 - y1); + + auto x1p = noflip ? x1 - x2 : y1 - y2; + auto y1p = noflip ? y1 - y2 : x1 - x2; + auto d12 = x1p * x1p + y1p * y1p; + auto x3p = noflip ? x3 - x2 : y3 - y2; + auto y3p = noflip ? y3 - y2 : x3 - x2; + auto d32 = x3p * x3p + y3p * y3p; + + auto num = x1p * y3p - y1p * x3p; // num also gives correct sign for CT + auto det = d12 * y3p - d32 * y1p; + + /* + auto ct = num/det; + auto sn = det>0 ? T(1.) : T(-1.); + auto st2 = (d12*x3p-d32*x1p)/det; + auto seq = T(1.) +st2*st2; + auto al2 = sn/std::sqrt(seq); + auto be2 = -st2*al2; + ct *= T(2.)*al2; + */ + + auto st2 = (d12 * x3p - d32 * x1p); + auto seq = det * det + st2 * st2; + auto al2 = T(1.) / std::sqrt(seq); + auto be2 = -st2 * al2; + auto ct = T(2.) * num * al2; + al2 *= det; + + m_xp = x2; + m_yp = y2; + m_c = noflip ? ct : -ct; + m_alpha = noflip ? al2 : -be2; + m_beta = noflip ? be2 : -al2; +} + +#endif diff --git a/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.cc new file mode 100644 index 0000000000000..cc5865d97fd95 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.cc @@ -0,0 +1,68 @@ +#include "BrokenLineFitOnGPU.h" + +void HelixFitOnGPU::launchBrokenLineKernelsOnCPU(HitsView const* hv, uint32_t hitsInFit, uint32_t maxNumberOfTuples) { + assert(tuples_d); + + // Fit internals + auto hitsGPU_ = std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd<4>) / sizeof(double)); + auto hits_geGPU_ = std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix6x4f) / sizeof(float)); + auto fast_fit_resultsGPU_ = + std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d) / sizeof(double)); + + for (uint32_t offset = 0; offset < maxNumberOfTuples; offset += maxNumberOfConcurrentFits_) { + // fit triplets + kernelBLFastFit<3>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 3, offset); + + kernelBLFit<3>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 3, + offset); + + // fit quads + kernelBLFastFit<4>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 4, offset); + + kernelBLFit<4>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 4, + offset); + + if (fit5as4_) { + // fit penta (only first 4) + kernelBLFastFit<4>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 5, offset); + + kernelBLFit<4>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 5, + offset); + } else { + // fit penta (all 5) + kernelBLFastFit<5>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 5, offset); + + kernelBLFit<5>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 5, + offset); + } + + } // loop on concurrent fits +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.cu new file mode 100644 index 0000000000000..6fc537237286f --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.cu @@ -0,0 +1,85 @@ +#include "BrokenLineFitOnGPU.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +void HelixFitOnGPU::launchBrokenLineKernels(HitsView const *hv, + uint32_t hitsInFit, + uint32_t maxNumberOfTuples, + cudaStream_t stream) { + assert(tuples_d); + + auto blockSize = 64; + auto numberOfBlocks = (maxNumberOfConcurrentFits_ + blockSize - 1) / blockSize; + + // Fit internals + auto hitsGPU_ = cms::cuda::make_device_unique( + maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd<4>) / sizeof(double), stream); + auto hits_geGPU_ = cms::cuda::make_device_unique( + maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix6x4f) / sizeof(float), stream); + auto fast_fit_resultsGPU_ = cms::cuda::make_device_unique( + maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d) / sizeof(double), stream); + + for (uint32_t offset = 0; offset < maxNumberOfTuples; offset += maxNumberOfConcurrentFits_) { + // fit triplets + kernelBLFastFit<3><<>>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 3, offset); + cudaCheck(cudaGetLastError()); + + kernelBLFit<3><<>>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 3, + offset); + cudaCheck(cudaGetLastError()); + + // fit quads + kernelBLFastFit<4><<>>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 4, offset); + cudaCheck(cudaGetLastError()); + + kernelBLFit<4><<>>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 4, + offset); + cudaCheck(cudaGetLastError()); + + if (fit5as4_) { + // fit penta (only first 4) + kernelBLFastFit<4><<>>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 5, offset); + cudaCheck(cudaGetLastError()); + + kernelBLFit<4><<>>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 5, + offset); + cudaCheck(cudaGetLastError()); + } else { + // fit penta (all 5) + kernelBLFastFit<5><<>>( + tuples_d, tupleMultiplicity_d, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), 5, offset); + cudaCheck(cudaGetLastError()); + + kernelBLFit<5><<>>(tupleMultiplicity_d, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + 5, + offset); + cudaCheck(cudaGetLastError()); + } + + } // loop on concurrent fits +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.h new file mode 100644 index 0000000000000..96a641829d797 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/BrokenLineFitOnGPU.h @@ -0,0 +1,185 @@ +// +// Author: Felice Pantaleo, CERN +// + +// #define BROKENLINE_DEBUG + +#include + +#include + +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/BrokenLine.h" + +#include "HelixFitOnGPU.h" + +using HitsOnGPU = TrackingRecHit2DSOAView; +using Tuples = pixelTrack::HitContainer; +using OutputSoA = pixelTrack::TrackSoA; + +// #define BL_DUMP_HITS + +template +__global__ void kernelBLFastFit(Tuples const *__restrict__ foundNtuplets, + CAConstants::TupleMultiplicity const *__restrict__ tupleMultiplicity, + HitsOnGPU const *__restrict__ hhp, + double *__restrict__ phits, + float *__restrict__ phits_ge, + double *__restrict__ pfast_fit, + uint32_t nHits, + uint32_t offset) { + constexpr uint32_t hitsInFit = N; + + assert(hitsInFit <= nHits); + + assert(hhp); + assert(pfast_fit); + assert(foundNtuplets); + assert(tupleMultiplicity); + + // look in bin for this hit multiplicity + auto local_start = blockIdx.x * blockDim.x + threadIdx.x; + +#ifdef BROKENLINE_DEBUG + if (0 == local_start) { + printf("%d total Ntuple\n", foundNtuplets->nbins()); + printf("%d Ntuple of size %d for %d hits to fit\n", tupleMultiplicity->size(nHits), nHits, hitsInFit); + } +#endif + + for (int local_idx = local_start, nt = Rfit::maxNumberOfConcurrentFits(); local_idx < nt; + local_idx += gridDim.x * blockDim.x) { + auto tuple_idx = local_idx + offset; + if (tuple_idx >= tupleMultiplicity->size(nHits)) + break; + + // get it from the ntuple container (one to one to helix) + auto tkid = *(tupleMultiplicity->begin(nHits) + tuple_idx); + assert(tkid < foundNtuplets->nbins()); + + assert(foundNtuplets->size(tkid) == nHits); + + Rfit::Map3xNd hits(phits + local_idx); + Rfit::Map4d fast_fit(pfast_fit + local_idx); + Rfit::Map6xNf hits_ge(phits_ge + local_idx); + +#ifdef BL_DUMP_HITS + __shared__ int done; + done = 0; + __syncthreads(); + bool dump = (foundNtuplets->size(tkid) == 5 && 0 == atomicAdd(&done, 1)); +#endif + + // Prepare data structure + auto const *hitId = foundNtuplets->begin(tkid); + for (unsigned int i = 0; i < hitsInFit; ++i) { + auto hit = hitId[i]; + float ge[6]; + hhp->cpeParams() + .detParams(hhp->detectorIndex(hit)) + .frame.toGlobal(hhp->xerrLocal(hit), 0, hhp->yerrLocal(hit), ge); +#ifdef BL_DUMP_HITS + if (dump) { + printf("Hit global: %d: %d hits.col(%d) << %f,%f,%f\n", + tkid, + hhp->detectorIndex(hit), + i, + hhp->xGlobal(hit), + hhp->yGlobal(hit), + hhp->zGlobal(hit)); + printf("Error: %d: %d hits_ge.col(%d) << %e,%e,%e,%e,%e,%e\n", + tkid, + hhp->detetectorIndex(hit), + i, + ge[0], + ge[1], + ge[2], + ge[3], + ge[4], + ge[5]); + } +#endif + hits.col(i) << hhp->xGlobal(hit), hhp->yGlobal(hit), hhp->zGlobal(hit); + hits_ge.col(i) << ge[0], ge[1], ge[2], ge[3], ge[4], ge[5]; + } + BrokenLine::BL_Fast_fit(hits, fast_fit); + + // no NaN here.... + 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)); + } +} + +template +__global__ void kernelBLFit(CAConstants::TupleMultiplicity const *__restrict__ tupleMultiplicity, + double B, + OutputSoA *results, + double *__restrict__ phits, + float *__restrict__ phits_ge, + double *__restrict__ pfast_fit, + uint32_t nHits, + uint32_t offset) { + assert(N <= nHits); + + assert(results); + assert(pfast_fit); + + // same as above... + + // look in bin for this hit multiplicity + auto local_start = blockIdx.x * blockDim.x + threadIdx.x; + for (int local_idx = local_start, nt = Rfit::maxNumberOfConcurrentFits(); local_idx < nt; + local_idx += gridDim.x * blockDim.x) { + auto tuple_idx = local_idx + offset; + if (tuple_idx >= tupleMultiplicity->size(nHits)) + break; + + // get it for the ntuple container (one to one to helix) + auto tkid = *(tupleMultiplicity->begin(nHits) + tuple_idx); + + Rfit::Map3xNd hits(phits + local_idx); + Rfit::Map4d fast_fit(pfast_fit + local_idx); + Rfit::Map6xNf hits_ge(phits_ge + local_idx); + + BrokenLine::PreparedBrokenLineData data; + Rfit::Matrix3d Jacob; + + BrokenLine::karimaki_circle_fit circle; + Rfit::line_fit line; + + BrokenLine::prepareBrokenLineData(hits, fast_fit, B, data); + BrokenLine::BL_Line_fit(hits_ge, fast_fit, B, data, line); + BrokenLine::BL_Circle_fit(hits, hits_ge, fast_fit, B, data, circle); + + results->stateAtBS.copyFromCircle(circle.par, circle.cov, line.par, line.cov, 1.f / float(B), tkid); + results->pt(tkid) = float(B) / float(std::abs(circle.par(2))); + results->eta(tkid) = asinhf(line.par(0)); + results->chi2(tkid) = (circle.chi2 + line.chi2) / (2 * N - 5); + +#ifdef BROKENLINE_DEBUG + if (!(circle.chi2 >= 0) || !(line.chi2 >= 0)) + printf("kernelBLFit failed! %f/%f\n", circle.chi2, line.chi2); + printf("kernelBLFit size %d for %d hits circle.par(0,1,2): %d %f,%f,%f\n", + N, + nHits, + tkid, + circle.par(0), + circle.par(1), + circle.par(2)); + printf("kernelBLHits line.par(0,1): %d %f,%f\n", tkid, line.par(0), line.par(1)); + printf("kernelBLHits chi2 cov %f/%f %e,%e,%e,%e,%e\n", + circle.chi2, + line.chi2, + circle.cov(0, 0), + circle.cov(1, 1), + circle.cov(2, 2), + line.cov(0, 0), + line.cov(1, 1)); +#endif + } +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml b/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml index f76451675de59..3a54cd1134bc2 100644 --- a/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml +++ b/RecoPixelVertexing/PixelTriplets/plugins/BuildFile.xml @@ -1,10 +1,14 @@ + + + + + + + - + - - - diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h b/RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h new file mode 100644 index 0000000000000..d9c3ff70e35ed --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAConstants.h @@ -0,0 +1,65 @@ +#ifndef RecoPixelVertexing_PixelTriplets_plugins_CAConstants_h +#define RecoPixelVertexing_PixelTriplets_plugins_CAConstants_h + +#include + +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/SimpleVector.h" +#include "HeterogeneousCore/CUDAUtilities/interface/VecArray.h" +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" + +//#define ONLY_PHICUT + +namespace CAConstants { + + // constants +#ifdef ONLY_PHICUT + constexpr uint32_t maxNumberOfTuples() { return 48 * 1024; } + constexpr uint32_t maxNumberOfDoublets() { return 2 * 1024 * 1024; } + constexpr uint32_t maxCellsPerHit() { return 8 * 128; } +#else +#ifdef GPU_SMALL_EVENTS + // kept for testing and debugging + constexpr uint32_t maxNumberOfTuples() { return 3 * 1024; } + constexpr uint32_t maxNumberOfDoublets() { return 128 * 1024; } + constexpr uint32_t maxCellsPerHit() { return 128 / 2; } +#else + // tested on MC events with 55-75 pileup events + constexpr uint32_t maxNumberOfTuples() { return 24 * 1024; } + constexpr uint32_t maxNumberOfDoublets() { return 512 * 1024; } + constexpr uint32_t maxCellsPerHit() { return 128; } +#endif +#endif // ONLY_PHICUT + constexpr uint32_t maxNumOfActiveDoublets() { return maxNumberOfDoublets() / 8; } + constexpr uint32_t maxNumberOfQuadruplets() { return maxNumberOfTuples(); } + + constexpr uint32_t maxNumberOfLayerPairs() { return 20; } + constexpr uint32_t maxNumberOfLayers() { return 10; } + constexpr uint32_t maxTuples() { return maxNumberOfTuples(); } + + // types + using hindex_type = uint32_t; // FIXME from siPixelRecHitsHeterogeneousProduct + using tindex_type = uint16_t; // for tuples + +#ifdef ONLY_PHICUT + using CellNeighbors = cms::cuda::VecArray; + using CellTracks = cms::cuda::VecArray; +#else + using CellNeighbors = cms::cuda::VecArray; + using CellTracks = cms::cuda::VecArray; +#endif + + using CellNeighborsVector = cms::cuda::SimpleVector; + using CellTracksVector = cms::cuda::SimpleVector; + + using OuterHitOfCell = cms::cuda::VecArray; + using TuplesContainer = cms::cuda::OneToManyAssoc; + using HitToTuple = + cms::cuda::OneToManyAssoc; // 3.5 should be enough + using TupleMultiplicity = cms::cuda::OneToManyAssoc; + +} // namespace CAConstants + +#endif // RecoPixelVertexing_PixelTriplets_plugins_CAConstants_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletCUDA.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletCUDA.cc new file mode 100644 index 0000000000000..3b1ea6fe158b2 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletCUDA.cc @@ -0,0 +1,85 @@ +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/RunningAverage.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "RecoTracker/TkMSParametrization/interface/PixelRecoUtilities.h" + +#include "CAHitNtupletGeneratorOnGPU.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" + +class CAHitNtupletCUDA : public edm::global::EDProducer<> { +public: + explicit CAHitNtupletCUDA(const edm::ParameterSet& iConfig); + ~CAHitNtupletCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + + bool m_OnGPU; + + edm::EDGetTokenT> tokenHitGPU_; + edm::EDPutTokenT> tokenTrackGPU_; + edm::EDGetTokenT tokenHitCPU_; + edm::EDPutTokenT tokenTrackCPU_; + + CAHitNtupletGeneratorOnGPU gpuAlgo_; +}; + +CAHitNtupletCUDA::CAHitNtupletCUDA(const edm::ParameterSet& iConfig) + : m_OnGPU(iConfig.getParameter("onGPU")), gpuAlgo_(iConfig, consumesCollector()) { + if (m_OnGPU) { + tokenHitGPU_ = + consumes>(iConfig.getParameter("pixelRecHitSrc")); + tokenTrackGPU_ = produces>(); + } else { + tokenHitCPU_ = consumes(iConfig.getParameter("pixelRecHitSrc")); + tokenTrackCPU_ = produces(); + } +} + +void CAHitNtupletCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + desc.add("onGPU", true); + desc.add("pixelRecHitSrc", edm::InputTag("siPixelRecHitsPreSplittingCUDA")); + + CAHitNtupletGeneratorOnGPU::fillDescriptions(desc); + auto label = "caHitNtupletCUDA"; + descriptions.add(label, desc); +} + +void CAHitNtupletCUDA::produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& es) const { + auto bf = 1. / PixelRecoUtilities::fieldInInvGev(es); + + if (m_OnGPU) { + edm::Handle> hHits; + iEvent.getByToken(tokenHitGPU_, hHits); + + cms::cuda::ScopedContextProduce ctx{*hHits}; + auto const& hits = ctx.get(*hHits); + + ctx.emplace(iEvent, tokenTrackGPU_, gpuAlgo_.makeTuplesAsync(hits, bf, ctx.stream())); + } else { + auto const& hits = iEvent.get(tokenHitCPU_); + iEvent.emplace(tokenTrackCPU_, gpuAlgo_.makeTuples(hits, bf)); + } +} + +DEFINE_FWK_MODULE(CAHitNtupletCUDA); diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.cc new file mode 100644 index 0000000000000..1646cb503ff81 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.cc @@ -0,0 +1,185 @@ +#include "RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsImpl.h" + +template <> +void CAHitNtupletGeneratorKernelsCPU::printCounters(Counters const *counters) { + kernel_printCounters(counters); +} + +template <> +void CAHitNtupletGeneratorKernelsCPU::fillHitDetIndices(HitsView const *hv, TkSoA *tracks_d, cudaStream_t) { + kernel_fillHitDetIndices(&tracks_d->hitIndices, hv, &tracks_d->detIndices); +} + +template <> +void CAHitNtupletGeneratorKernelsCPU::buildDoublets(HitsOnCPU const &hh, cudaStream_t stream) { + auto nhits = hh.nHits(); + +#ifdef NTUPLE_DEBUG + std::cout << "building Doublets out of " << nhits << " Hits" << std::endl; +#endif + + // in principle we can use "nhits" to heuristically dimension the workspace... + // overkill to use template here (std::make_unique would suffice) + // device_isOuterHitOfCell_ = Traits:: template make_unique(cs, std::max(1U,nhits), stream); + device_isOuterHitOfCell_.reset( + (GPUCACell::OuterHitOfCell *)malloc(std::max(1U, nhits) * sizeof(GPUCACell::OuterHitOfCell))); + assert(device_isOuterHitOfCell_.get()); + + cellStorage_.reset((unsigned char *)malloc(CAConstants::maxNumOfActiveDoublets() * sizeof(GPUCACell::CellNeighbors) + + CAConstants::maxNumOfActiveDoublets() * sizeof(GPUCACell::CellTracks))); + device_theCellNeighborsContainer_ = (GPUCACell::CellNeighbors *)cellStorage_.get(); + device_theCellTracksContainer_ = + (GPUCACell::CellTracks *)(cellStorage_.get() + + CAConstants::maxNumOfActiveDoublets() * sizeof(GPUCACell::CellNeighbors)); + + gpuPixelDoublets::initDoublets(device_isOuterHitOfCell_.get(), + nhits, + device_theCellNeighbors_.get(), + device_theCellNeighborsContainer_, + device_theCellTracks_.get(), + device_theCellTracksContainer_); + + // device_theCells_ = Traits:: template make_unique(cs, m_params.maxNumberOfDoublets_, stream); + device_theCells_.reset((GPUCACell *)malloc(sizeof(GPUCACell) * m_params.maxNumberOfDoublets_)); + if (0 == nhits) + return; // protect against empty events + + // FIXME avoid magic numbers + auto nActualPairs = gpuPixelDoublets::nPairs; + if (!m_params.includeJumpingForwardDoublets_) + nActualPairs = 15; + if (m_params.minHitsPerNtuplet_ > 3) { + nActualPairs = 13; + } + + assert(nActualPairs <= gpuPixelDoublets::nPairs); + gpuPixelDoublets::getDoubletsFromHisto(device_theCells_.get(), + device_nCells_, + device_theCellNeighbors_.get(), + device_theCellTracks_.get(), + hh.view(), + device_isOuterHitOfCell_.get(), + nActualPairs, + m_params.idealConditions_, + m_params.doClusterCut_, + m_params.doZ0Cut_, + m_params.doPtCut_, + m_params.maxNumberOfDoublets_); +} + +template <> +void CAHitNtupletGeneratorKernelsCPU::launchKernels(HitsOnCPU const &hh, TkSoA *tracks_d, cudaStream_t cudaStream) { + auto *tuples_d = &tracks_d->hitIndices; + auto *quality_d = (Quality *)(&tracks_d->m_quality); + + assert(tuples_d && quality_d); + + // zero tuples + cms::cuda::launchZero(tuples_d, cudaStream); + + auto nhits = hh.nHits(); + assert(nhits <= pixelGPUConstants::maxNumberOfHits); + + // std::cout << "N hits " << nhits << std::endl; + // if (nhits<2) std::cout << "too few hits " << nhits << std::endl; + + // + // applying conbinatoric cleaning such as fishbone at this stage is too expensive + // + + kernel_connect(device_hitTuple_apc_, + device_hitToTuple_apc_, // needed only to be reset, ready for next kernel + hh.view(), + device_theCells_.get(), + device_nCells_, + device_theCellNeighbors_.get(), + device_isOuterHitOfCell_.get(), + m_params.hardCurvCut_, + m_params.ptmin_, + m_params.CAThetaCutBarrel_, + m_params.CAThetaCutForward_, + m_params.dcaCutInnerTriplet_, + m_params.dcaCutOuterTriplet_); + + if (nhits > 1 && m_params.earlyFishbone_) { + gpuPixelDoublets::fishbone( + hh.view(), device_theCells_.get(), device_nCells_, device_isOuterHitOfCell_.get(), nhits, false); + } + + kernel_find_ntuplets(hh.view(), + device_theCells_.get(), + device_nCells_, + device_theCellTracks_.get(), + tuples_d, + device_hitTuple_apc_, + quality_d, + m_params.minHitsPerNtuplet_); + if (m_params.doStats_) + kernel_mark_used(hh.view(), device_theCells_.get(), device_nCells_); + + cms::cuda::finalizeBulk(device_hitTuple_apc_, tuples_d); + + // remove duplicates (tracks that share a doublet) + kernel_earlyDuplicateRemover(device_theCells_.get(), device_nCells_, tuples_d, quality_d); + + kernel_countMultiplicity(tuples_d, quality_d, device_tupleMultiplicity_.get()); + cms::cuda::launchFinalize(device_tupleMultiplicity_.get(), cudaStream); + kernel_fillMultiplicity(tuples_d, quality_d, device_tupleMultiplicity_.get()); + + if (nhits > 1 && m_params.lateFishbone_) { + gpuPixelDoublets::fishbone( + hh.view(), device_theCells_.get(), device_nCells_, device_isOuterHitOfCell_.get(), nhits, true); + } + + if (m_params.doStats_) { + kernel_checkOverflows(tuples_d, + device_tupleMultiplicity_.get(), + device_hitToTuple_.get(), + device_hitTuple_apc_, + device_theCells_.get(), + device_nCells_, + device_theCellNeighbors_.get(), + device_theCellTracks_.get(), + device_isOuterHitOfCell_.get(), + nhits, + m_params.maxNumberOfDoublets_, + counters_); + } +} + +template <> +void CAHitNtupletGeneratorKernelsCPU::classifyTuples(HitsOnCPU const &hh, TkSoA *tracks_d, cudaStream_t cudaStream) { + auto const *tuples_d = &tracks_d->hitIndices; + auto *quality_d = (Quality *)(&tracks_d->m_quality); + + // classify tracks based on kinematics + kernel_classifyTracks(tuples_d, tracks_d, m_params.cuts_, quality_d); + + if (m_params.lateFishbone_) { + // apply fishbone cleaning to good tracks + kernel_fishboneCleaner(device_theCells_.get(), device_nCells_, quality_d); + } + + // remove duplicates (tracks that share a doublet) + kernel_fastDuplicateRemover(device_theCells_.get(), device_nCells_, tuples_d, tracks_d); + + // fill hit->track "map" + kernel_countHitInTracks(tuples_d, quality_d, device_hitToTuple_.get()); + cms::cuda::launchFinalize(device_hitToTuple_.get(), cudaStream); + kernel_fillHitInTracks(tuples_d, quality_d, device_hitToTuple_.get()); + + // remove duplicates (tracks that share a hit) + kernel_tripletCleaner(hh.view(), tuples_d, tracks_d, quality_d, device_hitToTuple_.get()); + + if (m_params.doStats_) { + // counters (add flag???) + kernel_doStatsForHitInTracks(device_hitToTuple_.get(), counters_); + kernel_doStatsForTracks(tuples_d, quality_d, counters_); + } + +#ifdef DUMP_GPU_TK_TUPLES + static std::atomic iev(0); + ++iev; + kernel_print_found_ntuplets(hh.view(), tuples_d, tracks_d, quality_d, device_hitToTuple_.get(), 100, iev); +#endif +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.cu new file mode 100644 index 0000000000000..a8dac7992f4fa --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.cu @@ -0,0 +1,309 @@ +#include "RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsImpl.h" + +template <> +void CAHitNtupletGeneratorKernelsGPU::fillHitDetIndices(HitsView const *hv, TkSoA *tracks_d, cudaStream_t cudaStream) { + auto blockSize = 128; + auto numberOfBlocks = (HitContainer::capacity() + blockSize - 1) / blockSize; + + kernel_fillHitDetIndices<<>>( + &tracks_d->hitIndices, hv, &tracks_d->detIndices); + cudaCheck(cudaGetLastError()); +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif +} + +template <> +void CAHitNtupletGeneratorKernelsGPU::launchKernels(HitsOnCPU const &hh, TkSoA *tracks_d, cudaStream_t cudaStream) { + // these are pointer on GPU! + auto *tuples_d = &tracks_d->hitIndices; + auto *quality_d = (Quality *)(&tracks_d->m_quality); + + // zero tuples + cms::cuda::launchZero(tuples_d, cudaStream); + + auto nhits = hh.nHits(); + assert(nhits <= pixelGPUConstants::maxNumberOfHits); + + // std::cout << "N hits " << nhits << std::endl; + // if (nhits<2) std::cout << "too few hits " << nhits << std::endl; + + // + // applying conbinatoric cleaning such as fishbone at this stage is too expensive + // + + auto nthTot = 64; + auto stride = 4; + auto blockSize = nthTot / stride; + auto numberOfBlocks = (3 * m_params.maxNumberOfDoublets_ / 4 + blockSize - 1) / blockSize; + auto rescale = numberOfBlocks / 65536; + blockSize *= (rescale + 1); + numberOfBlocks = (3 * m_params.maxNumberOfDoublets_ / 4 + blockSize - 1) / blockSize; + assert(numberOfBlocks < 65536); + assert(blockSize > 0 && 0 == blockSize % 16); + dim3 blks(1, numberOfBlocks, 1); + dim3 thrs(stride, blockSize, 1); + + kernel_connect<<>>( + device_hitTuple_apc_, + device_hitToTuple_apc_, // needed only to be reset, ready for next kernel + hh.view(), + device_theCells_.get(), + device_nCells_, + device_theCellNeighbors_.get(), + device_isOuterHitOfCell_.get(), + m_params.hardCurvCut_, + m_params.ptmin_, + m_params.CAThetaCutBarrel_, + m_params.CAThetaCutForward_, + m_params.dcaCutInnerTriplet_, + m_params.dcaCutOuterTriplet_); + cudaCheck(cudaGetLastError()); + + if (nhits > 1 && m_params.earlyFishbone_) { + auto nthTot = 128; + auto stride = 16; + auto blockSize = nthTot / stride; + auto numberOfBlocks = (nhits + blockSize - 1) / blockSize; + dim3 blks(1, numberOfBlocks, 1); + dim3 thrs(stride, blockSize, 1); + gpuPixelDoublets::fishbone<<>>( + hh.view(), device_theCells_.get(), device_nCells_, device_isOuterHitOfCell_.get(), nhits, false); + cudaCheck(cudaGetLastError()); + } + + blockSize = 64; + numberOfBlocks = (3 * m_params.maxNumberOfDoublets_ / 4 + blockSize - 1) / blockSize; + kernel_find_ntuplets<<>>(hh.view(), + device_theCells_.get(), + device_nCells_, + device_theCellTracks_.get(), + tuples_d, + device_hitTuple_apc_, + quality_d, + m_params.minHitsPerNtuplet_); + cudaCheck(cudaGetLastError()); + + if (m_params.doStats_) + kernel_mark_used<<>>(hh.view(), device_theCells_.get(), device_nCells_); + cudaCheck(cudaGetLastError()); + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + blockSize = 128; + numberOfBlocks = (HitContainer::totbins() + blockSize - 1) / blockSize; + cms::cuda::finalizeBulk<<>>(device_hitTuple_apc_, tuples_d); + + // remove duplicates (tracks that share a doublet) + numberOfBlocks = (3 * m_params.maxNumberOfDoublets_ / 4 + blockSize - 1) / blockSize; + kernel_earlyDuplicateRemover<<>>( + device_theCells_.get(), device_nCells_, tuples_d, quality_d); + cudaCheck(cudaGetLastError()); + + blockSize = 128; + numberOfBlocks = (3 * CAConstants::maxTuples() / 4 + blockSize - 1) / blockSize; + kernel_countMultiplicity<<>>( + tuples_d, quality_d, device_tupleMultiplicity_.get()); + cms::cuda::launchFinalize(device_tupleMultiplicity_.get(), cudaStream); + kernel_fillMultiplicity<<>>( + tuples_d, quality_d, device_tupleMultiplicity_.get()); + cudaCheck(cudaGetLastError()); + + if (nhits > 1 && m_params.lateFishbone_) { + auto nthTot = 128; + auto stride = 16; + auto blockSize = nthTot / stride; + auto numberOfBlocks = (nhits + blockSize - 1) / blockSize; + dim3 blks(1, numberOfBlocks, 1); + dim3 thrs(stride, blockSize, 1); + gpuPixelDoublets::fishbone<<>>( + hh.view(), device_theCells_.get(), device_nCells_, device_isOuterHitOfCell_.get(), nhits, true); + cudaCheck(cudaGetLastError()); + } + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + // free space asap + // device_isOuterHitOfCell_.reset(); +} + +template <> +void CAHitNtupletGeneratorKernelsGPU::buildDoublets(HitsOnCPU const &hh, cudaStream_t stream) { + auto nhits = hh.nHits(); + +#ifdef NTUPLE_DEBUG + std::cout << "building Doublets out of " << nhits << " Hits" << std::endl; +#endif + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + // in principle we can use "nhits" to heuristically dimension the workspace... + device_isOuterHitOfCell_ = cms::cuda::make_device_unique(std::max(1U, nhits), stream); + assert(device_isOuterHitOfCell_.get()); + + cellStorage_ = cms::cuda::make_device_unique( + CAConstants::maxNumOfActiveDoublets() * sizeof(GPUCACell::CellNeighbors) + + CAConstants::maxNumOfActiveDoublets() * sizeof(GPUCACell::CellTracks), + stream); + device_theCellNeighborsContainer_ = (GPUCACell::CellNeighbors *)cellStorage_.get(); + device_theCellTracksContainer_ = + (GPUCACell::CellTracks *)(cellStorage_.get() + + CAConstants::maxNumOfActiveDoublets() * sizeof(GPUCACell::CellNeighbors)); + + { + int threadsPerBlock = 128; + // at least one block! + int blocks = (std::max(1U, nhits) + threadsPerBlock - 1) / threadsPerBlock; + gpuPixelDoublets::initDoublets<<>>(device_isOuterHitOfCell_.get(), + nhits, + device_theCellNeighbors_.get(), + device_theCellNeighborsContainer_, + device_theCellTracks_.get(), + device_theCellTracksContainer_); + cudaCheck(cudaGetLastError()); + } + + device_theCells_ = cms::cuda::make_device_unique(m_params.maxNumberOfDoublets_, stream); + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + + if (0 == nhits) + return; // protect against empty events + + // FIXME avoid magic numbers + auto nActualPairs = gpuPixelDoublets::nPairs; + if (!m_params.includeJumpingForwardDoublets_) + nActualPairs = 15; + if (m_params.minHitsPerNtuplet_ > 3) { + nActualPairs = 13; + } + + assert(nActualPairs <= gpuPixelDoublets::nPairs); + int stride = 4; + int threadsPerBlock = gpuPixelDoublets::getDoubletsFromHistoMaxBlockSize / stride; + int blocks = (4 * nhits + threadsPerBlock - 1) / threadsPerBlock; + dim3 blks(1, blocks, 1); + dim3 thrs(stride, threadsPerBlock, 1); + gpuPixelDoublets::getDoubletsFromHisto<<>>(device_theCells_.get(), + device_nCells_, + device_theCellNeighbors_.get(), + device_theCellTracks_.get(), + hh.view(), + device_isOuterHitOfCell_.get(), + nActualPairs, + m_params.idealConditions_, + m_params.doClusterCut_, + m_params.doZ0Cut_, + m_params.doPtCut_, + m_params.maxNumberOfDoublets_); + cudaCheck(cudaGetLastError()); + +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif +} + +template <> +void CAHitNtupletGeneratorKernelsGPU::classifyTuples(HitsOnCPU const &hh, TkSoA *tracks_d, cudaStream_t cudaStream) { + // these are pointer on GPU! + auto const *tuples_d = &tracks_d->hitIndices; + auto *quality_d = (Quality *)(&tracks_d->m_quality); + + auto blockSize = 64; + + // classify tracks based on kinematics + auto numberOfBlocks = (3 * CAConstants::maxNumberOfQuadruplets() / 4 + blockSize - 1) / blockSize; + kernel_classifyTracks<<>>(tuples_d, tracks_d, m_params.cuts_, quality_d); + cudaCheck(cudaGetLastError()); + + if (m_params.lateFishbone_) { + // apply fishbone cleaning to good tracks + numberOfBlocks = (3 * m_params.maxNumberOfDoublets_ / 4 + blockSize - 1) / blockSize; + kernel_fishboneCleaner<<>>( + device_theCells_.get(), device_nCells_, quality_d); + cudaCheck(cudaGetLastError()); + } + + // remove duplicates (tracks that share a doublet) + numberOfBlocks = (3 * m_params.maxNumberOfDoublets_ / 4 + blockSize - 1) / blockSize; + kernel_fastDuplicateRemover<<>>( + device_theCells_.get(), device_nCells_, tuples_d, tracks_d); + cudaCheck(cudaGetLastError()); + + if (m_params.minHitsPerNtuplet_ < 4 || m_params.doStats_) { + // fill hit->track "map" + numberOfBlocks = (3 * CAConstants::maxNumberOfQuadruplets() / 4 + blockSize - 1) / blockSize; + kernel_countHitInTracks<<>>( + tuples_d, quality_d, device_hitToTuple_.get()); + cudaCheck(cudaGetLastError()); + cms::cuda::launchFinalize(device_hitToTuple_.get(), cudaStream); + cudaCheck(cudaGetLastError()); + kernel_fillHitInTracks<<>>(tuples_d, quality_d, device_hitToTuple_.get()); + cudaCheck(cudaGetLastError()); + } + if (m_params.minHitsPerNtuplet_ < 4) { + // remove duplicates (tracks that share a hit) + numberOfBlocks = (HitToTuple::capacity() + blockSize - 1) / blockSize; + kernel_tripletCleaner<<>>( + hh.view(), tuples_d, tracks_d, quality_d, device_hitToTuple_.get()); + cudaCheck(cudaGetLastError()); + } + + if (m_params.doStats_) { + auto nhits = hh.nHits(); + numberOfBlocks = (std::max(nhits, m_params.maxNumberOfDoublets_) + blockSize - 1) / blockSize; + kernel_checkOverflows<<>>(tuples_d, + device_tupleMultiplicity_.get(), + device_hitToTuple_.get(), + device_hitTuple_apc_, + device_theCells_.get(), + device_nCells_, + device_theCellNeighbors_.get(), + device_theCellTracks_.get(), + device_isOuterHitOfCell_.get(), + nhits, + m_params.maxNumberOfDoublets_, + counters_); + cudaCheck(cudaGetLastError()); + } + + if (m_params.doStats_) { + // counters (add flag???) + numberOfBlocks = (HitToTuple::capacity() + blockSize - 1) / blockSize; + kernel_doStatsForHitInTracks<<>>(device_hitToTuple_.get(), counters_); + cudaCheck(cudaGetLastError()); + numberOfBlocks = (3 * CAConstants::maxNumberOfQuadruplets() / 4 + blockSize - 1) / blockSize; + kernel_doStatsForTracks<<>>(tuples_d, quality_d, counters_); + cudaCheck(cudaGetLastError()); + } +#ifdef GPU_DEBUG + cudaDeviceSynchronize(); + cudaCheck(cudaGetLastError()); +#endif + +#ifdef DUMP_GPU_TK_TUPLES + static std::atomic iev(0); + ++iev; + kernel_print_found_ntuplets<<<1, 32, 0, cudaStream>>>( + hh.view(), tuples_d, tracks_d, quality_d, device_hitToTuple_.get(), 100, iev); +#endif +} + +template <> +void CAHitNtupletGeneratorKernelsGPU::printCounters(Counters const *counters) { + kernel_printCounters<<<1, 1>>>(counters); +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.h new file mode 100644 index 0000000000000..7ab3ed010927e --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernels.h @@ -0,0 +1,207 @@ +#ifndef RecoPixelVertexing_PixelTriplets_plugins_CAHitNtupletGeneratorKernels_h +#define RecoPixelVertexing_PixelTriplets_plugins_CAHitNtupletGeneratorKernels_h + +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "GPUCACell.h" + +// #define DUMP_GPU_TK_TUPLES + +namespace cAHitNtupletGenerator { + + // counters + struct Counters { + unsigned long long nEvents; + unsigned long long nHits; + unsigned long long nCells; + unsigned long long nTuples; + unsigned long long nFitTracks; + unsigned long long nGoodTracks; + unsigned long long nUsedHits; + unsigned long long nDupHits; + unsigned long long nKilledCells; + unsigned long long nEmptyCells; + unsigned long long nZeroTrackCells; + }; + + using HitsView = TrackingRecHit2DSOAView; + using HitsOnGPU = TrackingRecHit2DSOAView; + + using HitToTuple = CAConstants::HitToTuple; + using TupleMultiplicity = CAConstants::TupleMultiplicity; + + using Quality = pixelTrack::Quality; + using TkSoA = pixelTrack::TrackSoA; + using HitContainer = pixelTrack::HitContainer; + + struct QualityCuts { + // chi2 cut = chi2Scale * (chi2Coeff[0] + pT/GeV * (chi2Coeff[1] + pT/GeV * (chi2Coeff[2] + pT/GeV * chi2Coeff[3]))) + float chi2Coeff[4]; + float chi2MaxPt; // GeV + float chi2Scale; + + struct region { + float maxTip; // cm + float minPt; // GeV + float maxZip; // cm + }; + + region triplet; + region quadruplet; + }; + + // params + struct Params { + Params(bool onGPU, + uint32_t minHitsPerNtuplet, + uint32_t maxNumberOfDoublets, + bool useRiemannFit, + bool fit5as4, + bool includeJumpingForwardDoublets, + bool earlyFishbone, + bool lateFishbone, + bool idealConditions, + bool doStats, + bool doClusterCut, + bool doZ0Cut, + bool doPtCut, + float ptmin, + float CAThetaCutBarrel, + float CAThetaCutForward, + float hardCurvCut, + float dcaCutInnerTriplet, + float dcaCutOuterTriplet, + QualityCuts const& cuts) + : onGPU_(onGPU), + minHitsPerNtuplet_(minHitsPerNtuplet), + maxNumberOfDoublets_(maxNumberOfDoublets), + useRiemannFit_(useRiemannFit), + fit5as4_(fit5as4), + includeJumpingForwardDoublets_(includeJumpingForwardDoublets), + earlyFishbone_(earlyFishbone), + lateFishbone_(lateFishbone), + idealConditions_(idealConditions), + doStats_(doStats), + doClusterCut_(doClusterCut), + doZ0Cut_(doZ0Cut), + doPtCut_(doPtCut), + ptmin_(ptmin), + CAThetaCutBarrel_(CAThetaCutBarrel), + CAThetaCutForward_(CAThetaCutForward), + hardCurvCut_(hardCurvCut), + dcaCutInnerTriplet_(dcaCutInnerTriplet), + dcaCutOuterTriplet_(dcaCutOuterTriplet), + cuts_(cuts) {} + + const bool onGPU_; + const uint32_t minHitsPerNtuplet_; + const uint32_t maxNumberOfDoublets_; + const bool useRiemannFit_; + const bool fit5as4_; + const bool includeJumpingForwardDoublets_; + const bool earlyFishbone_; + const bool lateFishbone_; + const bool idealConditions_; + const bool doStats_; + const bool doClusterCut_; + const bool doZ0Cut_; + const bool doPtCut_; + const float ptmin_; + const float CAThetaCutBarrel_; + const float CAThetaCutForward_; + const float hardCurvCut_; + const float dcaCutInnerTriplet_; + const float dcaCutOuterTriplet_; + + // quality cuts + QualityCuts cuts_{// polynomial coefficients for the pT-dependent chi2 cut + {0.68177776, 0.74609577, -0.08035491, 0.00315399}, + // max pT used to determine the chi2 cut + 10., + // chi2 scale factor: 30 for broken line fit, 45 for Riemann fit + 30., + // regional cuts for triplets + { + 0.3, // |Tip| < 0.3 cm + 0.5, // pT > 0.5 GeV + 12.0 // |Zip| < 12.0 cm + }, + // regional cuts for quadruplets + { + 0.5, // |Tip| < 0.5 cm + 0.3, // pT > 0.3 GeV + 12.0 // |Zip| < 12.0 cm + }}; + + }; // Params + +} // namespace cAHitNtupletGenerator + +template +class CAHitNtupletGeneratorKernels { +public: + using Traits = TTraits; + + using QualityCuts = cAHitNtupletGenerator::QualityCuts; + using Params = cAHitNtupletGenerator::Params; + using Counters = cAHitNtupletGenerator::Counters; + + template + using unique_ptr = typename Traits::template unique_ptr; + + using HitsView = TrackingRecHit2DSOAView; + using HitsOnGPU = TrackingRecHit2DSOAView; + using HitsOnCPU = TrackingRecHit2DHeterogeneous; + + using HitToTuple = CAConstants::HitToTuple; + using TupleMultiplicity = CAConstants::TupleMultiplicity; + + using Quality = pixelTrack::Quality; + using TkSoA = pixelTrack::TrackSoA; + using HitContainer = pixelTrack::HitContainer; + + CAHitNtupletGeneratorKernels(Params const& params) : m_params(params) {} + ~CAHitNtupletGeneratorKernels() = default; + + TupleMultiplicity const* tupleMultiplicity() const { return device_tupleMultiplicity_.get(); } + + void launchKernels(HitsOnCPU const& hh, TkSoA* tuples_d, cudaStream_t cudaStream); + + void classifyTuples(HitsOnCPU const& hh, TkSoA* tuples_d, cudaStream_t cudaStream); + + void fillHitDetIndices(HitsView const* hv, TkSoA* tuples_d, cudaStream_t cudaStream); + + void buildDoublets(HitsOnCPU const& hh, cudaStream_t stream); + void allocateOnGPU(cudaStream_t stream); + void cleanup(cudaStream_t cudaStream); + + static void printCounters(Counters const* counters); + Counters* counters_ = nullptr; + +private: + // workspace + unique_ptr cellStorage_; + unique_ptr device_theCellNeighbors_; + CAConstants::CellNeighbors* device_theCellNeighborsContainer_; + unique_ptr device_theCellTracks_; + CAConstants::CellTracks* device_theCellTracksContainer_; + + unique_ptr device_theCells_; + unique_ptr device_isOuterHitOfCell_; + uint32_t* device_nCells_ = nullptr; + + unique_ptr device_hitToTuple_; + cms::cuda::AtomicPairCounter* device_hitToTuple_apc_ = nullptr; + + cms::cuda::AtomicPairCounter* device_hitTuple_apc_ = nullptr; + + unique_ptr device_tupleMultiplicity_; + + unique_ptr device_storage_; + // params + Params const& m_params; +}; + +using CAHitNtupletGeneratorKernelsGPU = CAHitNtupletGeneratorKernels; +using CAHitNtupletGeneratorKernelsCPU = CAHitNtupletGeneratorKernels; + +#endif // RecoPixelVertexing_PixelTriplets_plugins_CAHitNtupletGeneratorKernels_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.cc new file mode 100644 index 0000000000000..96381673388ca --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.cc @@ -0,0 +1 @@ +#include "CAHitNtupletGeneratorKernelsAlloc.h" diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.cu b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.cu new file mode 100644 index 0000000000000..96381673388ca --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.cu @@ -0,0 +1 @@ +#include "CAHitNtupletGeneratorKernelsAlloc.h" diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.h new file mode 100644 index 0000000000000..1c34275d6bbe2 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsAlloc.h @@ -0,0 +1,35 @@ +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "CAHitNtupletGeneratorKernels.h" + +template <> +#ifdef __CUDACC__ +void CAHitNtupletGeneratorKernelsGPU::allocateOnGPU(cudaStream_t stream) { +#else +void CAHitNtupletGeneratorKernelsCPU::allocateOnGPU(cudaStream_t stream) { +#endif + ////////////////////////////////////////////////////////// + // ALLOCATIONS FOR THE INTERMEDIATE RESULTS (STAYS ON WORKER) + ////////////////////////////////////////////////////////// + + device_theCellNeighbors_ = Traits::template make_unique(stream); + device_theCellTracks_ = Traits::template make_unique(stream); + + device_hitToTuple_ = Traits::template make_unique(stream); + + device_tupleMultiplicity_ = Traits::template make_unique(stream); + + device_storage_ = Traits::template make_unique(3, stream); + + device_hitTuple_apc_ = (cms::cuda::AtomicPairCounter*)device_storage_.get(); + device_hitToTuple_apc_ = (cms::cuda::AtomicPairCounter*)device_storage_.get() + 1; + device_nCells_ = (uint32_t*)(device_storage_.get() + 2); + + if constexpr (std::is_same::value) { + cudaCheck(cudaMemsetAsync(device_nCells_, 0, sizeof(uint32_t), stream)); + } else { + *device_nCells_ = 0; + } + cms::cuda::launchZero(device_tupleMultiplicity_.get(), stream); + cms::cuda::launchZero(device_hitToTuple_.get(), stream); // we may wish to keep it in the edm... +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsImpl.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsImpl.h new file mode 100644 index 0000000000000..3a935efbe2b4b --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorKernelsImpl.h @@ -0,0 +1,606 @@ +// +// Original Author: Felice Pantaleo, CERN +// + +// #define NTUPLE_DEBUG + +#include +#include + +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" + +#include "CAConstants.h" +#include "CAHitNtupletGeneratorKernels.h" +#include "GPUCACell.h" +#include "gpuFishbone.h" +#include "gpuPixelDoublets.h" + +using HitsOnGPU = TrackingRecHit2DSOAView; +using HitsOnCPU = TrackingRecHit2DCUDA; + +using HitToTuple = CAConstants::HitToTuple; +using TupleMultiplicity = CAConstants::TupleMultiplicity; + +using Quality = pixelTrack::Quality; +using TkSoA = pixelTrack::TrackSoA; +using HitContainer = pixelTrack::HitContainer; + +__global__ void kernel_checkOverflows(HitContainer const *foundNtuplets, + CAConstants::TupleMultiplicity const *tupleMultiplicity, + CAHitNtupletGeneratorKernelsGPU::HitToTuple const *hitToTuple, + cms::cuda::AtomicPairCounter *apc, + GPUCACell const *__restrict__ cells, + uint32_t const *__restrict__ nCells, + gpuPixelDoublets::CellNeighborsVector const *cellNeighbors, + gpuPixelDoublets::CellTracksVector const *cellTracks, + GPUCACell::OuterHitOfCell const *__restrict__ isOuterHitOfCell, + uint32_t nHits, + uint32_t maxNumberOfDoublets, + CAHitNtupletGeneratorKernelsGPU::Counters *counters) { + auto first = threadIdx.x + blockIdx.x * blockDim.x; + + auto &c = *counters; + // counters once per event + if (0 == first) { + atomicAdd(&c.nEvents, 1); + atomicAdd(&c.nHits, nHits); + atomicAdd(&c.nCells, *nCells); + atomicAdd(&c.nTuples, apc->get().m); + atomicAdd(&c.nFitTracks, tupleMultiplicity->size()); + } + +#ifdef NTUPLE_DEBUG + if (0 == first) { + printf("number of found cells %d, found tuples %d with total hits %d out of %d\n", + *nCells, + apc->get().m, + apc->get().n, + nHits); + if (apc->get().m < CAConstants::maxNumberOfQuadruplets()) { + assert(foundNtuplets->size(apc->get().m) == 0); + assert(foundNtuplets->size() == apc->get().n); + } + } + + for (int idx = first, nt = foundNtuplets->nbins(); idx < nt; idx += gridDim.x * blockDim.x) { + 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 < nHits); + } +#endif + + if (0 == first) { + if (apc->get().m >= CAConstants::maxNumberOfQuadruplets()) + printf("Tuples overflow\n"); + if (*nCells >= maxNumberOfDoublets) + printf("Cells overflow\n"); + if (cellNeighbors && cellNeighbors->full()) + printf("cellNeighbors overflow\n"); + if (cellTracks && cellTracks->full()) + printf("cellTracks overflow\n"); + } + + for (int idx = first, nt = (*nCells); idx < nt; idx += gridDim.x * blockDim.x) { + auto const &thisCell = cells[idx]; + if (thisCell.outerNeighbors().full()) //++tooManyNeighbors[thisCell.theLayerPairId]; + printf("OuterNeighbors overflow %d in %d\n", idx, thisCell.theLayerPairId); + if (thisCell.tracks().full()) //++tooManyTracks[thisCell.theLayerPairId]; + printf("Tracks overflow %d in %d\n", idx, thisCell.theLayerPairId); + if (thisCell.theDoubletId < 0) + atomicAdd(&c.nKilledCells, 1); + if (0 == thisCell.theUsed) + atomicAdd(&c.nEmptyCells, 1); + if (0 == hitToTuple->size(thisCell.get_inner_hit_id()) && 0 == hitToTuple->size(thisCell.get_outer_hit_id())) + atomicAdd(&c.nZeroTrackCells, 1); + } + + for (int idx = first, nt = nHits; idx < nt; idx += gridDim.x * blockDim.x) { + if (isOuterHitOfCell[idx].full()) // ++tooManyOuterHitOfCell; + printf("OuterHitOfCell overflow %d\n", idx); + } +} + +__global__ void kernel_fishboneCleaner(GPUCACell const *cells, uint32_t const *__restrict__ nCells, Quality *quality) { + constexpr auto bad = trackQuality::bad; + + auto first = threadIdx.x + blockIdx.x * blockDim.x; + for (int idx = first, nt = (*nCells); idx < nt; idx += gridDim.x * blockDim.x) { + auto const &thisCell = cells[idx]; + if (thisCell.theDoubletId >= 0) + continue; + + for (auto it : thisCell.tracks()) + quality[it] = bad; + } +} + +__global__ void kernel_earlyDuplicateRemover(GPUCACell const *cells, + uint32_t const *__restrict__ nCells, + HitContainer *foundNtuplets, + Quality *quality) { + // constexpr auto bad = trackQuality::bad; + constexpr auto dup = trackQuality::dup; + // constexpr auto loose = trackQuality::loose; + + assert(nCells); + auto first = threadIdx.x + blockIdx.x * blockDim.x; + for (int idx = first, nt = (*nCells); idx < nt; idx += gridDim.x * blockDim.x) { + auto const &thisCell = cells[idx]; + + if (thisCell.tracks().size() < 2) + continue; + //if (0==thisCell.theUsed) continue; + // if (thisCell.theDoubletId < 0) continue; + + uint32_t maxNh = 0; + + // find maxNh + for (auto it : thisCell.tracks()) { + auto nh = foundNtuplets->size(it); + maxNh = std::max(nh, maxNh); + } + + for (auto it : thisCell.tracks()) { + if (foundNtuplets->size(it) != maxNh) + quality[it] = dup; //no race: simple assignment of the same constant + } + } +} + +__global__ void kernel_fastDuplicateRemover(GPUCACell const *__restrict__ cells, + uint32_t const *__restrict__ nCells, + HitContainer const *__restrict__ foundNtuplets, + TkSoA *__restrict__ tracks) { + constexpr auto bad = trackQuality::bad; + constexpr auto dup = trackQuality::dup; + constexpr auto loose = trackQuality::loose; + + assert(nCells); + + auto first = threadIdx.x + blockIdx.x * blockDim.x; + for (int idx = first, nt = (*nCells); idx < nt; idx += gridDim.x * blockDim.x) { + auto const &thisCell = cells[idx]; + if (thisCell.tracks().size() < 2) + continue; + // if (thisCell.theDoubletId < 0) continue; + + float mc = 10000.f; + uint16_t im = 60000; + + auto score = [&](auto it) { + return std::abs(tracks->tip(it)); // tip + // return tracks->chi2(it); //chi2 + }; + + // find min socre + for (auto it : thisCell.tracks()) { + if (tracks->quality(it) == loose && score(it) < mc) { + mc = score(it); + im = it; + } + } + // mark all other duplicates + for (auto it : thisCell.tracks()) { + if (tracks->quality(it) != bad && it != im) + tracks->quality(it) = dup; //no race: simple assignment of the same constant + } + } +} + +__global__ void kernel_connect(cms::cuda::AtomicPairCounter *apc1, + cms::cuda::AtomicPairCounter *apc2, // just to zero them, + GPUCACell::Hits const *__restrict__ hhp, + GPUCACell *cells, + uint32_t const *__restrict__ nCells, + gpuPixelDoublets::CellNeighborsVector *cellNeighbors, + GPUCACell::OuterHitOfCell const *__restrict__ isOuterHitOfCell, + float hardCurvCut, + float ptmin, + float CAThetaCutBarrel, + float CAThetaCutForward, + float dcaCutInnerTriplet, + float dcaCutOuterTriplet) { + auto const &hh = *hhp; + + auto firstCellIndex = threadIdx.y + blockIdx.y * blockDim.y; + auto first = threadIdx.x; + auto stride = blockDim.x; + + if (0 == (firstCellIndex + first)) { + (*apc1) = 0; + (*apc2) = 0; + } // ready for next kernel + + for (int idx = firstCellIndex, nt = (*nCells); idx < nt; idx += gridDim.y * blockDim.y) { + auto cellIndex = idx; + auto &thisCell = cells[idx]; + //if (thisCell.theDoubletId < 0 || thisCell.theUsed>1) + // continue; + auto innerHitId = thisCell.get_inner_hit_id(); + int numberOfPossibleNeighbors = isOuterHitOfCell[innerHitId].size(); + auto vi = isOuterHitOfCell[innerHitId].data(); + + constexpr uint32_t last_bpix1_detIndex = 96; + constexpr uint32_t last_barrel_detIndex = 1184; + auto ri = thisCell.get_inner_r(hh); + auto zi = thisCell.get_inner_z(hh); + + auto ro = thisCell.get_outer_r(hh); + auto zo = thisCell.get_outer_z(hh); + auto isBarrel = thisCell.get_inner_detIndex(hh) < last_barrel_detIndex; + + for (int j = first; j < numberOfPossibleNeighbors; j += stride) { + auto otherCell = __ldg(vi + j); + auto &oc = cells[otherCell]; + // if (cells[otherCell].theDoubletId < 0 || + // cells[otherCell].theUsed>1 ) + // continue; + auto r1 = oc.get_inner_r(hh); + auto z1 = oc.get_inner_z(hh); + // auto isBarrel = oc.get_outer_detIndex(hh) < last_barrel_detIndex; + bool aligned = GPUCACell::areAlignedRZ( + r1, + z1, + ri, + zi, + ro, + zo, + ptmin, + isBarrel ? CAThetaCutBarrel : CAThetaCutForward); // 2.f*thetaCut); // FIXME tune cuts + if (aligned && + thisCell.dcaCut(hh, + oc, + oc.get_inner_detIndex(hh) < last_bpix1_detIndex ? dcaCutInnerTriplet : dcaCutOuterTriplet, + hardCurvCut)) { // FIXME tune cuts + oc.addOuterNeighbor(cellIndex, *cellNeighbors); + thisCell.theUsed |= 1; + oc.theUsed |= 1; + } + } // loop on inner cells + } // loop on outer cells +} + +__global__ void kernel_find_ntuplets(GPUCACell::Hits const *__restrict__ hhp, + GPUCACell *__restrict__ cells, + uint32_t const *nCells, + gpuPixelDoublets::CellTracksVector *cellTracks, + HitContainer *foundNtuplets, + cms::cuda::AtomicPairCounter *apc, + Quality *__restrict__ quality, + unsigned int minHitsPerNtuplet) { + // recursive: not obvious to widen + auto const &hh = *hhp; + + auto first = threadIdx.x + blockIdx.x * blockDim.x; + for (int idx = first, nt = (*nCells); idx < nt; idx += gridDim.x * blockDim.x) { + auto const &thisCell = cells[idx]; + if (thisCell.theDoubletId < 0) + continue; // cut by earlyFishbone + + auto pid = thisCell.theLayerPairId; + auto doit = minHitsPerNtuplet > 3 ? pid < 3 : pid < 8 || pid > 12; + if (doit) { + GPUCACell::TmpTuple stack; + stack.reset(); + thisCell.find_ntuplets(hh, cells, *cellTracks, *foundNtuplets, *apc, quality, stack, minHitsPerNtuplet, pid < 3); + assert(stack.empty()); + // printf("in %d found quadruplets: %d\n", cellIndex, apc->get()); + } + } +} + +__global__ void kernel_mark_used(GPUCACell::Hits const *__restrict__ hhp, + GPUCACell *__restrict__ cells, + uint32_t const *nCells) { + // auto const &hh = *hhp; + auto first = threadIdx.x + blockIdx.x * blockDim.x; + for (int idx = first, nt = (*nCells); idx < nt; idx += gridDim.x * blockDim.x) { + auto &thisCell = cells[idx]; + if (!thisCell.tracks().empty()) + thisCell.theUsed |= 2; + } +} + +__global__ void kernel_countMultiplicity(HitContainer const *__restrict__ foundNtuplets, + Quality const *__restrict__ quality, + CAConstants::TupleMultiplicity *tupleMultiplicity) { + auto first = blockIdx.x * blockDim.x + threadIdx.x; + for (int it = first, nt = foundNtuplets->nbins(); it < nt; it += gridDim.x * blockDim.x) { + auto nhits = foundNtuplets->size(it); + if (nhits < 3) + continue; + if (quality[it] == trackQuality::dup) + continue; + assert(quality[it] == trackQuality::bad); + if (nhits > 5) + printf("wrong mult %d %d\n", it, nhits); + assert(nhits < 8); + tupleMultiplicity->countDirect(nhits); + } +} + +__global__ void kernel_fillMultiplicity(HitContainer const *__restrict__ foundNtuplets, + Quality const *__restrict__ quality, + CAConstants::TupleMultiplicity *tupleMultiplicity) { + auto first = blockIdx.x * blockDim.x + threadIdx.x; + for (int it = first, nt = foundNtuplets->nbins(); it < nt; it += gridDim.x * blockDim.x) { + auto nhits = foundNtuplets->size(it); + if (nhits < 3) + continue; + if (quality[it] == trackQuality::dup) + continue; + assert(quality[it] == trackQuality::bad); + if (nhits > 5) + printf("wrong mult %d %d\n", it, nhits); + assert(nhits < 8); + tupleMultiplicity->fillDirect(nhits, it); + } +} + +__global__ void kernel_classifyTracks(HitContainer const *__restrict__ tuples, + TkSoA const *__restrict__ tracks, + CAHitNtupletGeneratorKernelsGPU::QualityCuts cuts, + Quality *__restrict__ quality) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int it = first, nt = tuples->nbins(); it < nt; it += gridDim.x * blockDim.x) { + auto nhits = tuples->size(it); + if (nhits == 0) + break; // guard + + // if duplicate: not even fit + if (quality[it] == trackQuality::dup) + continue; + + assert(quality[it] == trackQuality::bad); + + // mark doublets as bad + if (nhits < 3) + continue; + + // if the fit has any invalid parameters, mark it as bad + bool isNaN = false; + for (int i = 0; i < 5; ++i) { + isNaN |= std::isnan(tracks->stateAtBS.state(it)(i)); + } + if (isNaN) { +#ifdef NTUPLE_DEBUG + printf("NaN in fit %d size %d chi2 %f\n", it, tuples->size(it), tracks->chi2(it)); +#endif + continue; + } + + // compute a pT-dependent chi2 cut + // default parameters: + // - chi2MaxPt = 10 GeV + // - chi2Coeff = { 0.68177776, 0.74609577, -0.08035491, 0.00315399 } + // - chi2Scale = 30 for broken line fit, 45 for Riemann fit + // (see CAHitNtupletGeneratorGPU.cc) + float pt = std::min(tracks->pt(it), cuts.chi2MaxPt); + float chi2Cut = cuts.chi2Scale * + (cuts.chi2Coeff[0] + pt * (cuts.chi2Coeff[1] + pt * (cuts.chi2Coeff[2] + pt * cuts.chi2Coeff[3]))); + // above number were for Quads not normalized so for the time being just multiple by ndof for Quads (triplets to be understood) + if (3.f * tracks->chi2(it) >= chi2Cut) { +#ifdef NTUPLE_DEBUG + printf("Bad fit %d size %d pt %f eta %f chi2 %f\n", + it, + tuples->size(it), + tracks->pt(it), + tracks->eta(it), + 3.f * tracks->chi2(it)); +#endif + continue; + } + + // impose "region cuts" based on the fit results (phi, Tip, pt, cotan(theta)), Zip) + // default cuts: + // - for triplets: |Tip| < 0.3 cm, pT > 0.5 GeV, |Zip| < 12.0 cm + // - for quadruplets: |Tip| < 0.5 cm, pT > 0.3 GeV, |Zip| < 12.0 cm + // (see CAHitNtupletGeneratorGPU.cc) + auto const ®ion = (nhits > 3) ? cuts.quadruplet : cuts.triplet; + bool isOk = (std::abs(tracks->tip(it)) < region.maxTip) and (tracks->pt(it) > region.minPt) and + (std::abs(tracks->zip(it)) < region.maxZip); + + if (isOk) + quality[it] = trackQuality::loose; + } +} + +__global__ void kernel_doStatsForTracks(HitContainer const *__restrict__ tuples, + Quality const *__restrict__ quality, + CAHitNtupletGeneratorKernelsGPU::Counters *counters) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int idx = first, ntot = tuples->nbins(); idx < ntot; idx += gridDim.x * blockDim.x) { + if (tuples->size(idx) == 0) + break; //guard + if (quality[idx] != trackQuality::loose) + continue; + atomicAdd(&(counters->nGoodTracks), 1); + } +} + +__global__ void kernel_countHitInTracks(HitContainer const *__restrict__ tuples, + Quality const *__restrict__ quality, + CAHitNtupletGeneratorKernelsGPU::HitToTuple *hitToTuple) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int idx = first, ntot = tuples->nbins(); idx < ntot; idx += gridDim.x * blockDim.x) { + if (tuples->size(idx) == 0) + break; // guard + if (quality[idx] != trackQuality::loose) + continue; + for (auto h = tuples->begin(idx); h != tuples->end(idx); ++h) + hitToTuple->countDirect(*h); + } +} + +__global__ void kernel_fillHitInTracks(HitContainer const *__restrict__ tuples, + Quality const *__restrict__ quality, + CAHitNtupletGeneratorKernelsGPU::HitToTuple *hitToTuple) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int idx = first, ntot = tuples->nbins(); idx < ntot; idx += gridDim.x * blockDim.x) { + if (tuples->size(idx) == 0) + break; // guard + if (quality[idx] != trackQuality::loose) + continue; + for (auto h = tuples->begin(idx); h != tuples->end(idx); ++h) + hitToTuple->fillDirect(*h, idx); + } +} + +__global__ void kernel_fillHitDetIndices(HitContainer const *__restrict__ tuples, + TrackingRecHit2DSOAView const *__restrict__ hhp, + HitContainer *__restrict__ hitDetIndices) { + int first = blockDim.x * blockIdx.x + threadIdx.x; + // copy offsets + for (int idx = first, ntot = tuples->totbins(); idx < ntot; idx += gridDim.x * blockDim.x) { + hitDetIndices->off[idx] = tuples->off[idx]; + } + // fill hit indices + auto const &hh = *hhp; + auto nhits = hh.nHits(); + for (int idx = first, ntot = tuples->size(); idx < ntot; idx += gridDim.x * blockDim.x) { + assert(tuples->bins[idx] < nhits); + hitDetIndices->bins[idx] = hh.detectorIndex(tuples->bins[idx]); + } +} + +__global__ void kernel_doStatsForHitInTracks(CAHitNtupletGeneratorKernelsGPU::HitToTuple const *__restrict__ hitToTuple, + CAHitNtupletGeneratorKernelsGPU::Counters *counters) { + auto &c = *counters; + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int idx = first, ntot = hitToTuple->nbins(); idx < ntot; idx += gridDim.x * blockDim.x) { + if (hitToTuple->size(idx) == 0) + continue; // SHALL NOT BE break + atomicAdd(&c.nUsedHits, 1); + if (hitToTuple->size(idx) > 1) + atomicAdd(&c.nDupHits, 1); + } +} + +__global__ void kernel_tripletCleaner(TrackingRecHit2DSOAView const *__restrict__ hhp, + HitContainer const *__restrict__ ptuples, + TkSoA const *__restrict__ ptracks, + Quality *__restrict__ quality, + CAHitNtupletGeneratorKernelsGPU::HitToTuple const *__restrict__ phitToTuple) { + constexpr auto bad = trackQuality::bad; + constexpr auto dup = trackQuality::dup; + // constexpr auto loose = trackQuality::loose; + + auto &hitToTuple = *phitToTuple; + auto const &foundNtuplets = *ptuples; + auto const &tracks = *ptracks; + + // auto const & hh = *hhp; + // auto l1end = hh.hitsLayerStart_d[1]; + + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int idx = first, ntot = hitToTuple.nbins(); idx < ntot; idx += gridDim.x * blockDim.x) { + if (hitToTuple.size(idx) < 2) + continue; + + float mc = 10000.f; + uint16_t im = 60000; + uint32_t maxNh = 0; + + // find maxNh + for (auto it = hitToTuple.begin(idx); it != hitToTuple.end(idx); ++it) { + uint32_t nh = foundNtuplets.size(*it); + maxNh = std::max(nh, maxNh); + } + // kill all tracks shorter than maxHn (only triplets???) + for (auto it = hitToTuple.begin(idx); it != hitToTuple.end(idx); ++it) { + uint32_t nh = foundNtuplets.size(*it); + if (maxNh != nh) + quality[*it] = dup; + } + + if (maxNh > 3) + continue; + // if (idx>=l1end) continue; // only for layer 1 + // for triplets choose best tip! + for (auto ip = hitToTuple.begin(idx); ip != hitToTuple.end(idx); ++ip) { + auto const it = *ip; + if (quality[it] != bad && std::abs(tracks.tip(it)) < mc) { + mc = std::abs(tracks.tip(it)); + im = it; + } + } + // mark duplicates + for (auto ip = hitToTuple.begin(idx); ip != hitToTuple.end(idx); ++ip) { + auto const it = *ip; + if (quality[it] != bad && it != im) + quality[it] = dup; //no race: simple assignment of the same constant + } + } // loop over hits +} + +__global__ void kernel_print_found_ntuplets(TrackingRecHit2DSOAView const *__restrict__ hhp, + HitContainer const *__restrict__ ptuples, + TkSoA const *__restrict__ ptracks, + Quality const *__restrict__ quality, + CAHitNtupletGeneratorKernelsGPU::HitToTuple const *__restrict__ phitToTuple, + uint32_t maxPrint, + int iev) { + auto const &foundNtuplets = *ptuples; + auto const &tracks = *ptracks; + int first = blockDim.x * blockIdx.x + threadIdx.x; + for (int i = first, np = std::min(maxPrint, foundNtuplets.nbins()); i < np; i += blockDim.x * gridDim.x) { + auto nh = foundNtuplets.size(i); + if (nh < 3) + continue; + printf("TK: %d %d %d %f %f %f %f %f %f %f %d %d %d %d %d\n", + 10000 * iev + i, + int(quality[i]), + nh, + tracks.charge(i), + tracks.pt(i), + tracks.eta(i), + tracks.phi(i), + tracks.tip(i), + tracks.zip(i), + // asinhf(fit_results[i].par(3)), + tracks.chi2(i), + *foundNtuplets.begin(i), + *(foundNtuplets.begin(i) + 1), + *(foundNtuplets.begin(i) + 2), + nh > 3 ? int(*(foundNtuplets.begin(i) + 3)) : -1, + nh > 4 ? int(*(foundNtuplets.begin(i) + 4)) : -1); + } +} + +__global__ void kernel_printCounters(cAHitNtupletGenerator::Counters const *counters) { + auto const &c = *counters; + printf( + "||Counters | nEvents | nHits | nCells | nTuples | nFitTacks | nGoodTracks | nUsedHits | nDupHits | " + "nKilledCells | " + "nEmptyCells | nZeroTrackCells ||\n"); + printf("Counters Raw %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", + c.nEvents, + c.nHits, + c.nCells, + c.nTuples, + c.nGoodTracks, + c.nFitTracks, + c.nUsedHits, + c.nDupHits, + c.nKilledCells, + c.nEmptyCells, + c.nZeroTrackCells); + printf("Counters Norm %lld || %.1f| %.1f| %.1f| %.1f| %.1f| %.1f| %.1f| %.1f| %.3f| %.3f||\n", + c.nEvents, + c.nHits / double(c.nEvents), + c.nCells / double(c.nEvents), + c.nTuples / double(c.nEvents), + c.nFitTracks / double(c.nEvents), + c.nGoodTracks / double(c.nEvents), + c.nUsedHits / double(c.nEvents), + c.nDupHits / double(c.nEvents), + c.nKilledCells / double(c.nEvents), + c.nEmptyCells / double(c.nCells), + c.nZeroTrackCells / double(c.nCells)); +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorOnGPU.cc new file mode 100644 index 0000000000000..464744594e9a6 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorOnGPU.cc @@ -0,0 +1,229 @@ +// +// Original Author: Felice Pantaleo, CERN +// + +#include +#include +#include +#include + +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "FWCore/Utilities/interface/EDMException.h" +#include "FWCore/Utilities/interface/isFinite.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "TrackingTools/DetLayers/interface/BarrelDetLayer.h" + +#include "CAHitNtupletGeneratorOnGPU.h" + +namespace { + + template + T sqr(T x) { + return x * x; + } + + cAHitNtupletGenerator::QualityCuts makeQualityCuts(edm::ParameterSet const& pset) { + auto coeff = pset.getParameter>("chi2Coeff"); + if (coeff.size() != 4) { + throw edm::Exception(edm::errors::Configuration, + "CAHitNtupletGeneratorOnGPU.trackQualityCuts.chi2Coeff must have 4 elements"); + } + return cAHitNtupletGenerator::QualityCuts{// polynomial coefficients for the pT-dependent chi2 cut + {(float)coeff[0], (float)coeff[1], (float)coeff[2], (float)coeff[3]}, + // max pT used to determine the chi2 cut + (float)pset.getParameter("chi2MaxPt"), + // chi2 scale factor: 30 for broken line fit, 45 for Riemann fit + (float)pset.getParameter("chi2Scale"), + // regional cuts for triplets + {(float)pset.getParameter("tripletMaxTip"), + (float)pset.getParameter("tripletMinPt"), + (float)pset.getParameter("tripletMaxZip")}, + // regional cuts for quadruplets + {(float)pset.getParameter("quadrupletMaxTip"), + (float)pset.getParameter("quadrupletMinPt"), + (float)pset.getParameter("quadrupletMaxZip")}}; + } + +} // namespace + +using namespace std; + +CAHitNtupletGeneratorOnGPU::CAHitNtupletGeneratorOnGPU(const edm::ParameterSet& cfg, edm::ConsumesCollector& iC) + : m_params(cfg.getParameter("onGPU"), + cfg.getParameter("minHitsPerNtuplet"), + cfg.getParameter("maxNumberOfDoublets"), + cfg.getParameter("useRiemannFit"), + cfg.getParameter("fit5as4"), + cfg.getParameter("includeJumpingForwardDoublets"), + cfg.getParameter("earlyFishbone"), + cfg.getParameter("lateFishbone"), + cfg.getParameter("idealConditions"), + cfg.getParameter("fillStatistics"), + cfg.getParameter("doClusterCut"), + cfg.getParameter("doZ0Cut"), + cfg.getParameter("doPtCut"), + cfg.getParameter("ptmin"), + cfg.getParameter("CAThetaCutBarrel"), + cfg.getParameter("CAThetaCutForward"), + cfg.getParameter("hardCurvCut"), + cfg.getParameter("dcaCutInnerTriplet"), + cfg.getParameter("dcaCutOuterTriplet"), + makeQualityCuts(cfg.getParameterSet("trackQualityCuts"))) { +#ifdef DUMP_GPU_TK_TUPLES + printf("TK: %s %s % %s %s %s %s %s %s %s %s %s %s %s %s %s\n", + "tid", + "qual", + "nh", + "charge", + "pt", + "eta", + "phi", + "tip", + "zip", + "chi2", + "h1", + "h2", + "h3", + "h4", + "h5"); +#endif + + if (m_params.onGPU_) { + // allocate pinned host memory only if CUDA is available + edm::Service cs; + if (cs and cs->enabled()) { + cudaCheck(cudaMalloc(&m_counters, sizeof(Counters))); + cudaCheck(cudaMemset(m_counters, 0, sizeof(Counters))); + } + } else { + m_counters = new Counters(); + memset(m_counters, 0, sizeof(Counters)); + } +} + +CAHitNtupletGeneratorOnGPU::~CAHitNtupletGeneratorOnGPU() { + if (m_params.onGPU_) { + // print the gpu statistics and free pinned host memory only if CUDA is available + edm::Service cs; + if (cs and cs->enabled()) { + if (m_params.doStats_) { + // crash on multi-gpu processes + CAHitNtupletGeneratorKernelsGPU::printCounters(m_counters); + } + cudaFree(m_counters); + } + } else { + if (m_params.doStats_) { + CAHitNtupletGeneratorKernelsCPU::printCounters(m_counters); + } + delete m_counters; + } +} + +void CAHitNtupletGeneratorOnGPU::fillDescriptions(edm::ParameterSetDescription& desc) { + // 87 cm/GeV = 1/(3.8T * 0.3) + // take less than radius given by the hardPtCut and reject everything below + // auto hardCurvCut = 1.f/(0.35 * 87.f); + desc.add("ptmin", 0.9f)->setComment("Cut on minimum pt"); + desc.add("CAThetaCutBarrel", 0.002f)->setComment("Cut on RZ alignement for Barrel"); + desc.add("CAThetaCutForward", 0.003f)->setComment("Cut on RZ alignment for Forward"); + desc.add("hardCurvCut", 1.f / (0.35 * 87.f))->setComment("Cut on minimum curvature"); + desc.add("dcaCutInnerTriplet", 0.15f)->setComment("Cut on origin radius when the inner hit is on BPix1"); + desc.add("dcaCutOuterTriplet", 0.25f)->setComment("Cut on origin radius when the outer hit is on BPix1"); + desc.add("earlyFishbone", true); + desc.add("lateFishbone", false); + desc.add("idealConditions", true); + desc.add("fillStatistics", false); + desc.add("minHitsPerNtuplet", 4); + desc.add("maxNumberOfDoublets", CAConstants::maxNumberOfDoublets()); + desc.add("includeJumpingForwardDoublets", false); + desc.add("fit5as4", true); + desc.add("doClusterCut", true); + desc.add("doZ0Cut", true); + desc.add("doPtCut", true); + desc.add("useRiemannFit", false)->setComment("true for Riemann, false for BrokenLine"); + + edm::ParameterSetDescription trackQualityCuts; + trackQualityCuts.add("chi2MaxPt", 10.)->setComment("max pT used to determine the pT-dependent chi2 cut"); + trackQualityCuts.add>("chi2Coeff", {0.68177776, 0.74609577, -0.08035491, 0.00315399}) + ->setComment("Polynomial coefficients to derive the pT-dependent chi2 cut"); + trackQualityCuts.add("chi2Scale", 30.) + ->setComment( + "Factor to multiply the pT-dependent chi2 cut (currently: 30 for the broken line fit, 45 for the Riemann " + "fit)"); + trackQualityCuts.add("tripletMinPt", 0.5)->setComment("Min pT for triplets, in GeV"); + trackQualityCuts.add("tripletMaxTip", 0.3)->setComment("Max |Tip| for triplets, in cm"); + trackQualityCuts.add("tripletMaxZip", 12.)->setComment("Max |Zip| for triplets, in cm"); + trackQualityCuts.add("quadrupletMinPt", 0.3)->setComment("Min pT for quadruplets, in GeV"); + trackQualityCuts.add("quadrupletMaxTip", 0.5)->setComment("Max |Tip| for quadruplets, in cm"); + trackQualityCuts.add("quadrupletMaxZip", 12.)->setComment("Max |Zip| for quadruplets, in cm"); + desc.add("trackQualityCuts", trackQualityCuts) + ->setComment( + "Quality cuts based on the results of the track fit:\n - apply a pT-dependent chi2 cut;\n - apply \"region " + "cuts\" based on the fit results (pT, Tip, Zip)."); +} + +PixelTrackHeterogeneous CAHitNtupletGeneratorOnGPU::makeTuplesAsync(TrackingRecHit2DCUDA const& hits_d, + float bfield, + cudaStream_t stream) const { + PixelTrackHeterogeneous tracks(cms::cuda::make_device_unique(stream)); + + auto* soa = tracks.get(); + + CAHitNtupletGeneratorKernelsGPU kernels(m_params); + kernels.counters_ = m_counters; + + kernels.allocateOnGPU(stream); + + kernels.buildDoublets(hits_d, stream); + kernels.launchKernels(hits_d, soa, stream); + kernels.fillHitDetIndices(hits_d.view(), soa, stream); // in principle needed only if Hits not "available" + + HelixFitOnGPU fitter(bfield, m_params.fit5as4_); + fitter.allocateOnGPU(&(soa->hitIndices), kernels.tupleMultiplicity(), soa); + if (m_params.useRiemannFit_) { + fitter.launchRiemannKernels(hits_d.view(), hits_d.nHits(), CAConstants::maxNumberOfQuadruplets(), stream); + } else { + fitter.launchBrokenLineKernels(hits_d.view(), hits_d.nHits(), CAConstants::maxNumberOfQuadruplets(), stream); + } + kernels.classifyTuples(hits_d, soa, stream); + + return tracks; +} + +PixelTrackHeterogeneous CAHitNtupletGeneratorOnGPU::makeTuples(TrackingRecHit2DCPU const& hits_d, float bfield) const { + PixelTrackHeterogeneous tracks(std::make_unique()); + + auto* soa = tracks.get(); + assert(soa); + + CAHitNtupletGeneratorKernelsCPU kernels(m_params); + kernels.counters_ = m_counters; + kernels.allocateOnGPU(nullptr); + + kernels.buildDoublets(hits_d, nullptr); + kernels.launchKernels(hits_d, soa, nullptr); + kernels.fillHitDetIndices(hits_d.view(), soa, nullptr); // in principle needed only if Hits not "available" + + if (0 == hits_d.nHits()) + return tracks; + + // now fit + HelixFitOnGPU fitter(bfield, m_params.fit5as4_); + fitter.allocateOnGPU(&(soa->hitIndices), kernels.tupleMultiplicity(), soa); + + if (m_params.useRiemannFit_) { + fitter.launchRiemannKernelsOnCPU(hits_d.view(), hits_d.nHits(), CAConstants::maxNumberOfQuadruplets()); + } else { + fitter.launchBrokenLineKernelsOnCPU(hits_d.view(), hits_d.nHits(), CAConstants::maxNumberOfQuadruplets()); + } + + kernels.classifyTuples(hits_d, soa, nullptr); + + return tracks; +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorOnGPU.h new file mode 100644 index 0000000000000..afb591744bf59 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/CAHitNtupletGeneratorOnGPU.h @@ -0,0 +1,66 @@ +#ifndef RecoPixelVertexing_PixelTriplets_plugins_CAHitNtupletGeneratorOnGPU_h +#define RecoPixelVertexing_PixelTriplets_plugins_CAHitNtupletGeneratorOnGPU_h + +#include +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" + +#include "DataFormats/SiPixelDetId/interface/PixelSubdetector.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "HeterogeneousCore/CUDAUtilities/interface/SimpleVector.h" + +#include "CAHitNtupletGeneratorKernels.h" +#include "HelixFitOnGPU.h" + +// FIXME (split header???) +#include "GPUCACell.h" + +namespace edm { + class Event; + class EventSetup; + class ParameterSetDescription; +} // namespace edm + +class CAHitNtupletGeneratorOnGPU { +public: + using HitsOnGPU = TrackingRecHit2DSOAView; + using HitsOnCPU = TrackingRecHit2DCUDA; + using hindex_type = TrackingRecHit2DSOAView::hindex_type; + + using Quality = pixelTrack::Quality; + using OutputSoA = pixelTrack::TrackSoA; + using HitContainer = pixelTrack::HitContainer; + using Tuple = HitContainer; + + using QualityCuts = cAHitNtupletGenerator::QualityCuts; + using Params = cAHitNtupletGenerator::Params; + using Counters = cAHitNtupletGenerator::Counters; + +public: + CAHitNtupletGeneratorOnGPU(const edm::ParameterSet& cfg, edm::ConsumesCollector&& iC) + : CAHitNtupletGeneratorOnGPU(cfg, iC) {} + CAHitNtupletGeneratorOnGPU(const edm::ParameterSet& cfg, edm::ConsumesCollector& iC); + + ~CAHitNtupletGeneratorOnGPU(); + + static void fillDescriptions(edm::ParameterSetDescription& desc); + static const char* fillDescriptionsLabel() { return "caHitNtupletOnGPU"; } + + PixelTrackHeterogeneous makeTuplesAsync(TrackingRecHit2DGPU const& hits_d, float bfield, cudaStream_t stream) const; + + PixelTrackHeterogeneous makeTuples(TrackingRecHit2DCPU const& hits_d, float bfield) const; + +private: + void buildDoublets(HitsOnCPU const& hh, cudaStream_t stream) const; + + void hitNtuplets(HitsOnCPU const& hh, const edm::EventSetup& es, bool useRiemannFit, cudaStream_t cudaStream); + + void launchKernels(HitsOnCPU const& hh, bool useRiemannFit, cudaStream_t cudaStream) const; + + Params m_params; + + Counters* m_counters = nullptr; +}; + +#endif // RecoPixelVertexing_PixelTriplets_plugins_CAHitNtupletGeneratorOnGPU_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h new file mode 100644 index 0000000000000..2a74d6a064e73 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h @@ -0,0 +1,346 @@ +#ifndef RecoPixelVertexing_PixelTriplets_plugins_GPUCACell_h +#define RecoPixelVertexing_PixelTriplets_plugins_GPUCACell_h + +// +// Author: Felice Pantaleo, CERN +// + +// #define ONLY_TRIPLETS_IN_HOLE + +#include + +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "HeterogeneousCore/CUDAUtilities/interface/SimpleVector.h" +#include "HeterogeneousCore/CUDAUtilities/interface/VecArray.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoPixelVertexing/PixelTriplets/interface/CircleEq.h" +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "CAConstants.h" + +class GPUCACell { +public: + using ptrAsInt = unsigned long long; + + static constexpr int maxCellsPerHit = CAConstants::maxCellsPerHit(); + using OuterHitOfCell = CAConstants::OuterHitOfCell; + using CellNeighbors = CAConstants::CellNeighbors; + using CellTracks = CAConstants::CellTracks; + using CellNeighborsVector = CAConstants::CellNeighborsVector; + using CellTracksVector = CAConstants::CellTracksVector; + + using Hits = TrackingRecHit2DSOAView; + using hindex_type = Hits::hindex_type; + + using TmpTuple = cms::cuda::VecArray; + + using HitContainer = pixelTrack::HitContainer; + using Quality = trackQuality::Quality; + static constexpr auto bad = trackQuality::bad; + + GPUCACell() = default; + + __device__ __forceinline__ void init(CellNeighborsVector& cellNeighbors, + CellTracksVector& cellTracks, + Hits const& hh, + int layerPairId, + int doubletId, + hindex_type innerHitId, + hindex_type outerHitId) { + theInnerHitId = innerHitId; + theOuterHitId = outerHitId; + theDoubletId = doubletId; + theLayerPairId = layerPairId; + theUsed = 0; + + // optimization that depends on access pattern + theInnerZ = hh.zGlobal(innerHitId); + theInnerR = hh.rGlobal(innerHitId); + + // link to default empty + theOuterNeighbors = &cellNeighbors[0]; + theTracks = &cellTracks[0]; + assert(outerNeighbors().empty()); + assert(tracks().empty()); + } + + __device__ __forceinline__ int addOuterNeighbor(CellNeighbors::value_t t, CellNeighborsVector& cellNeighbors) { + // use smart cache + if (outerNeighbors().empty()) { + auto i = cellNeighbors.extend(); // maybe waisted.... + if (i > 0) { + cellNeighbors[i].reset(); +#ifdef __CUDACC__ + auto zero = (ptrAsInt)(&cellNeighbors[0]); + atomicCAS((ptrAsInt*)(&theOuterNeighbors), + zero, + (ptrAsInt)(&cellNeighbors[i])); // if fails we cannot give "i" back... +#else + theOuterNeighbors = &cellNeighbors[i]; +#endif + } else + return -1; + } + __threadfence(); + return outerNeighbors().push_back(t); + } + + __device__ __forceinline__ int addTrack(CellTracks::value_t t, CellTracksVector& cellTracks) { + if (tracks().empty()) { + auto i = cellTracks.extend(); // maybe waisted.... + if (i > 0) { + cellTracks[i].reset(); +#ifdef __CUDACC__ + auto zero = (ptrAsInt)(&cellTracks[0]); + atomicCAS((ptrAsInt*)(&theTracks), zero, (ptrAsInt)(&cellTracks[i])); // if fails we cannot give "i" back... +#else + theTracks = &cellTracks[i]; +#endif + } else + return -1; + } + __threadfence(); + return tracks().push_back(t); + } + + __device__ __forceinline__ CellTracks& tracks() { return *theTracks; } + __device__ __forceinline__ CellTracks const& tracks() const { return *theTracks; } + __device__ __forceinline__ CellNeighbors& outerNeighbors() { return *theOuterNeighbors; } + __device__ __forceinline__ CellNeighbors const& outerNeighbors() const { return *theOuterNeighbors; } + __device__ __forceinline__ float get_inner_x(Hits const& hh) const { return hh.xGlobal(theInnerHitId); } + __device__ __forceinline__ float get_outer_x(Hits const& hh) const { return hh.xGlobal(theOuterHitId); } + __device__ __forceinline__ float get_inner_y(Hits const& hh) const { return hh.yGlobal(theInnerHitId); } + __device__ __forceinline__ float get_outer_y(Hits const& hh) const { return hh.yGlobal(theOuterHitId); } + __device__ __forceinline__ float get_inner_z(Hits const& hh) const { return theInnerZ; } + // { return hh.zGlobal(theInnerHitId); } // { return theInnerZ; } + __device__ __forceinline__ float get_outer_z(Hits const& hh) const { return hh.zGlobal(theOuterHitId); } + __device__ __forceinline__ float get_inner_r(Hits const& hh) const { return theInnerR; } + // { return hh.rGlobal(theInnerHitId); } // { return theInnerR; } + __device__ __forceinline__ float get_outer_r(Hits const& hh) const { return hh.rGlobal(theOuterHitId); } + + __device__ __forceinline__ auto get_inner_iphi(Hits const& hh) const { return hh.iphi(theInnerHitId); } + __device__ __forceinline__ auto get_outer_iphi(Hits const& hh) const { return hh.iphi(theOuterHitId); } + + __device__ __forceinline__ float get_inner_detIndex(Hits const& hh) const { return hh.detectorIndex(theInnerHitId); } + __device__ __forceinline__ float get_outer_detIndex(Hits const& hh) const { return hh.detectorIndex(theOuterHitId); } + + constexpr unsigned int get_inner_hit_id() const { return theInnerHitId; } + constexpr unsigned int get_outer_hit_id() const { return theOuterHitId; } + + __device__ void print_cell() const { + printf("printing cell: %d, on layerPair: %d, innerHitId: %d, outerHitId: %d \n", + theDoubletId, + theLayerPairId, + theInnerHitId, + theOuterHitId); + } + + __device__ bool check_alignment(Hits const& hh, + GPUCACell const& otherCell, + const float ptmin, + const float hardCurvCut, + const float CAThetaCutBarrel, + const float CAThetaCutForward, + const float dcaCutInnerTriplet, + const float dcaCutOuterTriplet) const { + // detIndex of the layerStart for the Phase1 Pixel Detector: + // [BPX1, BPX2, BPX3, BPX4, FP1, FP2, FP3, FN1, FN2, FN3, LAST_VALID] + // [ 0, 96, 320, 672, 1184, 1296, 1408, 1520, 1632, 1744, 1856] + constexpr uint32_t last_bpix1_detIndex = 96; + constexpr uint32_t last_barrel_detIndex = 1184; + auto ri = get_inner_r(hh); + auto zi = get_inner_z(hh); + + auto ro = get_outer_r(hh); + auto zo = get_outer_z(hh); + + auto r1 = otherCell.get_inner_r(hh); + auto z1 = otherCell.get_inner_z(hh); + auto isBarrel = otherCell.get_outer_detIndex(hh) < last_barrel_detIndex; + bool aligned = areAlignedRZ(r1, + z1, + ri, + zi, + ro, + zo, + ptmin, + isBarrel ? CAThetaCutBarrel : CAThetaCutForward); // 2.f*thetaCut); // FIXME tune cuts + return (aligned && + dcaCut(hh, + otherCell, + otherCell.get_inner_detIndex(hh) < last_bpix1_detIndex ? dcaCutInnerTriplet : dcaCutOuterTriplet, + hardCurvCut)); // FIXME tune cuts + } + + __device__ __forceinline__ static bool areAlignedRZ( + float r1, float z1, float ri, float zi, float ro, float zo, const float ptmin, const float thetaCut) { + float radius_diff = std::abs(r1 - ro); + float distance_13_squared = radius_diff * radius_diff + (z1 - zo) * (z1 - zo); + + float pMin = ptmin * std::sqrt(distance_13_squared); // this needs to be divided by + // radius_diff later + + float tan_12_13_half_mul_distance_13_squared = fabs(z1 * (ri - ro) + zi * (ro - r1) + zo * (r1 - ri)); + return tan_12_13_half_mul_distance_13_squared * pMin <= thetaCut * distance_13_squared * radius_diff; + } + + __device__ inline bool dcaCut(Hits const& hh, + GPUCACell const& otherCell, + const float region_origin_radius_plus_tolerance, + const float maxCurv) const { + auto x1 = otherCell.get_inner_x(hh); + auto y1 = otherCell.get_inner_y(hh); + + auto x2 = get_inner_x(hh); + auto y2 = get_inner_y(hh); + + auto x3 = get_outer_x(hh); + auto y3 = get_outer_y(hh); + + 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__ __forceinline__ static bool dcaCutH(float x1, + float y1, + float x2, + float y2, + float x3, + float y3, + const float region_origin_radius_plus_tolerance, + const float maxCurv) { + 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__ inline bool hole0(Hits const& hh, GPUCACell const& innerCell) const { + constexpr uint32_t max_ladder_bpx0 = 12; + constexpr uint32_t first_ladder_bpx0 = 0; + constexpr float module_length = 6.7f; + constexpr float module_tolerance = 0.4f; // projection to cylinder is inaccurate on BPIX1 + int p = innerCell.get_inner_iphi(hh); + if (p < 0) + p += std::numeric_limits::max(); + p = (max_ladder_bpx0 * p) / std::numeric_limits::max(); + p %= max_ladder_bpx0; + auto il = first_ladder_bpx0 + p; + auto r0 = hh.averageGeometry().ladderR[il]; + auto ri = innerCell.get_inner_r(hh); + auto zi = innerCell.get_inner_z(hh); + auto ro = get_outer_r(hh); + auto zo = get_outer_z(hh); + auto z0 = zi + (r0 - ri) * (zo - zi) / (ro - ri); + auto z_in_ladder = std::abs(z0 - hh.averageGeometry().ladderZ[il]); + auto z_in_module = z_in_ladder - module_length * int(z_in_ladder / module_length); + auto gap = z_in_module < module_tolerance || z_in_module > (module_length - module_tolerance); + return gap; + } + + __device__ inline bool hole4(Hits const& hh, GPUCACell const& innerCell) const { + constexpr uint32_t max_ladder_bpx4 = 64; + constexpr uint32_t first_ladder_bpx4 = 84; + // constexpr float radius_even_ladder = 15.815f; + // constexpr float radius_odd_ladder = 16.146f; + constexpr float module_length = 6.7f; + constexpr float module_tolerance = 0.2f; + // constexpr float barrel_z_length = 26.f; + // constexpr float forward_z_begin = 32.f; + int p = get_outer_iphi(hh); + if (p < 0) + p += std::numeric_limits::max(); + p = (max_ladder_bpx4 * p) / std::numeric_limits::max(); + p %= max_ladder_bpx4; + auto il = first_ladder_bpx4 + p; + auto r4 = hh.averageGeometry().ladderR[il]; + auto ri = innerCell.get_inner_r(hh); + auto zi = innerCell.get_inner_z(hh); + auto ro = get_outer_r(hh); + auto zo = get_outer_z(hh); + auto z4 = zo + (r4 - ro) * (zo - zi) / (ro - ri); + auto z_in_ladder = std::abs(z4 - hh.averageGeometry().ladderZ[il]); + auto z_in_module = z_in_ladder - module_length * int(z_in_ladder / module_length); + auto gap = z_in_module < module_tolerance || z_in_module > (module_length - module_tolerance); + auto holeP = z4 > hh.averageGeometry().ladderMaxZ[il] && z4 < hh.averageGeometry().endCapZ[0]; + auto holeN = z4 < hh.averageGeometry().ladderMinZ[il] && z4 > hh.averageGeometry().endCapZ[1]; + return gap || holeP || holeN; + } + + // trying to free the track building process from hardcoded layers, leaving + // the visit of the graph based on the neighborhood connections between cells. + __device__ inline void find_ntuplets(Hits const& hh, + GPUCACell* __restrict__ cells, + CellTracksVector& cellTracks, + HitContainer& foundNtuplets, + cms::cuda::AtomicPairCounter& apc, + Quality* __restrict__ quality, + TmpTuple& tmpNtuplet, + const unsigned int minHitsPerNtuplet, + bool startAt0) const { + // the building process for a track ends if: + // it has no right neighbor + // it has no compatible neighbor + // the ntuplets is then saved if the number of hits it contains is greater + // than a threshold + + tmpNtuplet.push_back_unsafe(theDoubletId); + assert(tmpNtuplet.size() <= 4); + + bool last = true; + for (unsigned int otherCell : outerNeighbors()) { + if (cells[otherCell].theDoubletId < 0) + continue; // killed by earlyFishbone + last = false; + cells[otherCell].find_ntuplets( + hh, cells, cellTracks, foundNtuplets, apc, quality, tmpNtuplet, minHitsPerNtuplet, startAt0); + } + if (last) { // if long enough save... + if ((unsigned int)(tmpNtuplet.size()) >= minHitsPerNtuplet - 1) { +#ifdef ONLY_TRIPLETS_IN_HOLE + // triplets accepted only pointing to the hole + if (tmpNtuplet.size() >= 3 || (startAt0 && hole4(hh, cells[tmpNtuplet[0]])) || + ((!startAt0) && hole0(hh, cells[tmpNtuplet[0]]))) +#endif + { + hindex_type hits[6]; + auto nh = 0U; + for (auto c : tmpNtuplet) { + hits[nh++] = cells[c].theInnerHitId; + } + hits[nh] = theOuterHitId; + auto it = foundNtuplets.bulkFill(apc, hits, tmpNtuplet.size() + 1); + if (it >= 0) { // if negative is overflow.... + for (auto c : tmpNtuplet) + cells[c].addTrack(it, cellTracks); + quality[it] = bad; // initialize to bad + } + } + } + } + tmpNtuplet.pop_back(); + assert(tmpNtuplet.size() < 4); + } + +private: + CellNeighbors* theOuterNeighbors; + CellTracks* theTracks; + +public: + int32_t theDoubletId; + int16_t theLayerPairId; + uint16_t theUsed; // tbd + +private: + float theInnerZ; + float theInnerR; + hindex_type theInnerHitId; + hindex_type theOuterHitId; +}; + +#endif // RecoPixelVertexing_PixelTriplets_plugins_GPUCACell_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/HelixFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/HelixFitOnGPU.cc new file mode 100644 index 0000000000000..becbd0a1a8540 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/HelixFitOnGPU.cc @@ -0,0 +1,16 @@ +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HelixFitOnGPU.h" + +void HelixFitOnGPU::allocateOnGPU(Tuples const *tuples, + TupleMultiplicity const *tupleMultiplicity, + OutputSoA *helix_fit_results) { + tuples_d = tuples; + tupleMultiplicity_d = tupleMultiplicity; + outputSoa_d = helix_fit_results; + + assert(tuples_d); + assert(tupleMultiplicity_d); + assert(outputSoa_d); +} + +void HelixFitOnGPU::deallocateOnGPU() {} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/HelixFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/HelixFitOnGPU.h new file mode 100644 index 0000000000000..42f8f0e720b43 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/HelixFitOnGPU.h @@ -0,0 +1,68 @@ +#ifndef RecoPixelVertexing_PixelTrackFitting_plugins_HelixFitOnGPU_h +#define RecoPixelVertexing_PixelTrackFitting_plugins_HelixFitOnGPU_h + +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitResult.h" + +#include "CAConstants.h" + +namespace Rfit { + // in case of memory issue can be made smaller + constexpr uint32_t maxNumberOfConcurrentFits() { return CAConstants::maxNumberOfTuples(); } + constexpr uint32_t stride() { return maxNumberOfConcurrentFits(); } + using Matrix3x4d = Eigen::Matrix; + using Map3x4d = Eigen::Map >; + using Matrix6x4f = Eigen::Matrix; + using Map6x4f = Eigen::Map >; + + // hits + template + using Matrix3xNd = Eigen::Matrix; + template + using Map3xNd = Eigen::Map, 0, Eigen::Stride<3 * stride(), stride()> >; + // errors + template + using Matrix6xNf = Eigen::Matrix; + template + using Map6xNf = Eigen::Map, 0, Eigen::Stride<6 * stride(), stride()> >; + // fast fit + using Map4d = Eigen::Map >; + +} // namespace Rfit + +class HelixFitOnGPU { +public: + using HitsView = TrackingRecHit2DSOAView; + + using Tuples = pixelTrack::HitContainer; + using OutputSoA = pixelTrack::TrackSoA; + + using TupleMultiplicity = CAConstants::TupleMultiplicity; + + explicit HelixFitOnGPU(float bf, bool fit5as4) : bField_(bf), fit5as4_(fit5as4) {} + ~HelixFitOnGPU() { deallocateOnGPU(); } + + void setBField(double bField) { bField_ = bField; } + void launchRiemannKernels(HitsView const *hv, uint32_t nhits, uint32_t maxNumberOfTuples, cudaStream_t cudaStream); + void launchBrokenLineKernels(HitsView const *hv, uint32_t nhits, uint32_t maxNumberOfTuples, cudaStream_t cudaStream); + + void launchRiemannKernelsOnCPU(HitsView const *hv, uint32_t nhits, uint32_t maxNumberOfTuples); + void launchBrokenLineKernelsOnCPU(HitsView const *hv, uint32_t nhits, uint32_t maxNumberOfTuples); + + void allocateOnGPU(Tuples const *tuples, TupleMultiplicity const *tupleMultiplicity, OutputSoA *outputSoA); + void deallocateOnGPU(); + +private: + static constexpr uint32_t maxNumberOfConcurrentFits_ = Rfit::maxNumberOfConcurrentFits(); + + // fowarded + Tuples const *tuples_d = nullptr; + TupleMultiplicity const *tupleMultiplicity_d = nullptr; + OutputSoA *outputSoa_d; + float bField_; + + const bool fit5as4_; +}; + +#endif // RecoPixelVertexing_PixelTrackFitting_plugins_HelixFitOnGPU_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RecHitsMap.h b/RecoPixelVertexing/PixelTriplets/plugins/RecHitsMap.h new file mode 100644 index 0000000000000..3279587d2486e --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RecHitsMap.h @@ -0,0 +1,84 @@ +//FIXME move it to a better place... + +#ifndef RecoPixelVertexing_PixelTriplets_plugins_RecHitsMap_h +#define RecoPixelVertexing_PixelTriplets_plugins_RecHitsMap_h + +#include +#include + +#include "DataFormats/SiPixelCluster/interface/SiPixelCluster.h" +#include "DataFormats/SiStripCluster/interface/SiStripCluster.h" +#include "DataFormats/TrackerRecHit2D/interface/BaseTrackerRecHit.h" +#include "DataFormats/TrackerRecHit2D/interface/OmniClusterRef.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +// store T for each cluster +template +class RecHitsMap { +public: + explicit RecHitsMap(T const& d = T()) : dummy(d) {} + + void clear() { m_map.clear(); } + + void error(const GeomDetUnit& gd) const { edm::LogError("RecHitMap") << "hit not found in det " << gd.index(); } + void error(uint32_t ind) const { edm::LogError("RecHitMap") << "hit not found in det " << ind; } + + // does not work for matched hits... (easy to extend) + void add(TrackingRecHit const& hit, T const& v) { + auto const& thit = static_cast(hit); + auto const& clus = thit.firstClusterRef(); + + if (clus.isPixel()) + add(clus.pixelCluster(), *thit.detUnit(), v); + else + add(clus.stripCluster(), *thit.detUnit(), v); + } + + template + void add(const Cluster& cluster, const GeomDetUnit& gd, T const& v) { + m_map[encode(cluster, gd)] = v; + } + + template + T const& get(const Cluster& cluster, const GeomDetUnit& gd) const { + auto p = m_map.find(encode(cluster, gd)); + if (p != m_map.end()) { + return (*p).second; + } + error(gd); + return dummy; + } + + T const& get(uint32_t ind, uint16_t mr, uint16_t mc) const { + auto p = m_map.find(encode(ind, mr, mc)); + if (p != m_map.end()) { + return (*p).second; + } + error(ind); + return dummy; + } + + static uint64_t encode(uint32_t ind, uint16_t mr, uint16_t mc) { + uint64_t u1 = ind; + uint64_t u2 = mr; + uint64_t u3 = mc; + return (u1 << 32) | (u2 << 16) | u3; + } + + static uint64_t encode(const SiPixelCluster& cluster, const GeomDetUnit& det) { + uint64_t u1 = det.index(); + uint64_t u2 = cluster.minPixelRow(); + uint64_t u3 = cluster.minPixelCol(); + return (u1 << 32) | (u2 << 16) | u3; + } + static uint64_t encode(const SiStripCluster& cluster, const GeomDetUnit& det) { + uint64_t u1 = det.index(); + uint64_t u2 = cluster.firstStrip(); + return (u1 << 32) | u2; + } + + std::unordered_map m_map; + T dummy; +}; + +#endif // RecoPixelVertexing_PixelTriplets_plugins_RecHitsMap_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc new file mode 100644 index 0000000000000..3476362864a79 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cc @@ -0,0 +1,110 @@ +#include "RiemannFitOnGPU.h" + +void HelixFitOnGPU::launchRiemannKernelsOnCPU(HitsView const *hv, uint32_t nhits, uint32_t maxNumberOfTuples) { + assert(tuples_d); + + // Fit internals + auto hitsGPU_ = std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd<4>) / sizeof(double)); + auto hits_geGPU_ = std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix6x4f) / sizeof(float)); + auto fast_fit_resultsGPU_ = + std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d) / sizeof(double)); + auto circle_fit_resultsGPU_holder = std::make_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::circle_fit)); + Rfit::circle_fit *circle_fit_resultsGPU_ = (Rfit::circle_fit *)(circle_fit_resultsGPU_holder.get()); + + for (uint32_t offset = 0; offset < maxNumberOfTuples; offset += maxNumberOfConcurrentFits_) { + // triplets + kernelFastFit<3>( + tuples_d, tupleMultiplicity_d, 3, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + + kernelCircleFit<3>(tupleMultiplicity_d, + 3, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + kernelLineFit<3>(tupleMultiplicity_d, + 3, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + // quads + kernelFastFit<4>( + tuples_d, tupleMultiplicity_d, 4, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + + kernelCircleFit<4>(tupleMultiplicity_d, + 4, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + kernelLineFit<4>(tupleMultiplicity_d, + 4, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + if (fit5as4_) { + // penta + kernelFastFit<4>( + tuples_d, tupleMultiplicity_d, 5, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + + kernelCircleFit<4>(tupleMultiplicity_d, + 5, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + kernelLineFit<4>(tupleMultiplicity_d, + 5, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + } else { + // penta all 5 + kernelFastFit<5>( + tuples_d, tupleMultiplicity_d, 5, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + + kernelCircleFit<5>(tupleMultiplicity_d, + 5, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + + kernelLineFit<5>(tupleMultiplicity_d, + 5, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + } + } +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu new file mode 100644 index 0000000000000..1077bb7736667 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.cu @@ -0,0 +1,131 @@ +#include "RiemannFitOnGPU.h" +#include "HeterogeneousCore/CUDAUtilities/interface/device_unique_ptr.h" + +void HelixFitOnGPU::launchRiemannKernels(HitsView const *hv, + uint32_t nhits, + uint32_t maxNumberOfTuples, + cudaStream_t stream) { + assert(tuples_d); + + auto blockSize = 64; + auto numberOfBlocks = (maxNumberOfConcurrentFits_ + blockSize - 1) / blockSize; + + // Fit internals + auto hitsGPU_ = cms::cuda::make_device_unique( + maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix3xNd<4>) / sizeof(double), stream); + auto hits_geGPU_ = cms::cuda::make_device_unique( + maxNumberOfConcurrentFits_ * sizeof(Rfit::Matrix6x4f) / sizeof(float), stream); + auto fast_fit_resultsGPU_ = cms::cuda::make_device_unique( + maxNumberOfConcurrentFits_ * sizeof(Rfit::Vector4d) / sizeof(double), stream); + auto circle_fit_resultsGPU_holder = + cms::cuda::make_device_unique(maxNumberOfConcurrentFits_ * sizeof(Rfit::circle_fit), stream); + Rfit::circle_fit *circle_fit_resultsGPU_ = (Rfit::circle_fit *)(circle_fit_resultsGPU_holder.get()); + + for (uint32_t offset = 0; offset < maxNumberOfTuples; offset += maxNumberOfConcurrentFits_) { + // triplets + kernelFastFit<3><<>>( + tuples_d, tupleMultiplicity_d, 3, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + cudaCheck(cudaGetLastError()); + + kernelCircleFit<3><<>>(tupleMultiplicity_d, + 3, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + + kernelLineFit<3><<>>(tupleMultiplicity_d, + 3, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + + // quads + kernelFastFit<4><<>>( + tuples_d, tupleMultiplicity_d, 4, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + cudaCheck(cudaGetLastError()); + + kernelCircleFit<4><<>>(tupleMultiplicity_d, + 4, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + + kernelLineFit<4><<>>(tupleMultiplicity_d, + 4, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + + if (fit5as4_) { + // penta + kernelFastFit<4><<>>( + tuples_d, tupleMultiplicity_d, 5, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + cudaCheck(cudaGetLastError()); + + kernelCircleFit<4><<>>(tupleMultiplicity_d, + 5, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + + kernelLineFit<4><<>>(tupleMultiplicity_d, + 5, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + } else { + // penta all 5 + kernelFastFit<5><<>>( + tuples_d, tupleMultiplicity_d, 5, hv, hitsGPU_.get(), hits_geGPU_.get(), fast_fit_resultsGPU_.get(), offset); + cudaCheck(cudaGetLastError()); + + kernelCircleFit<5><<>>(tupleMultiplicity_d, + 5, + bField_, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + + kernelLineFit<5><<>>(tupleMultiplicity_d, + 5, + bField_, + outputSoa_d, + hitsGPU_.get(), + hits_geGPU_.get(), + fast_fit_resultsGPU_.get(), + circle_fit_resultsGPU_, + offset); + cudaCheck(cudaGetLastError()); + } + } +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h new file mode 100644 index 0000000000000..a16374278233a --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/RiemannFitOnGPU.h @@ -0,0 +1,187 @@ +// +// Author: Felice Pantaleo, CERN +// + +#include + +#include + +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/RiemannFit.h" + +#include "HelixFitOnGPU.h" + +using HitsOnGPU = TrackingRecHit2DSOAView; +using Tuples = pixelTrack::HitContainer; +using OutputSoA = pixelTrack::TrackSoA; + +template +__global__ void kernelFastFit(Tuples const *__restrict__ foundNtuplets, + CAConstants::TupleMultiplicity const *__restrict__ tupleMultiplicity, + uint32_t nHits, + HitsOnGPU const *__restrict__ hhp, + double *__restrict__ phits, + float *__restrict__ phits_ge, + double *__restrict__ pfast_fit, + uint32_t offset) { + constexpr uint32_t hitsInFit = N; + + assert(hitsInFit <= nHits); + + assert(pfast_fit); + assert(foundNtuplets); + assert(tupleMultiplicity); + + // look in bin for this hit multiplicity + auto local_start = blockIdx.x * blockDim.x + threadIdx.x; + +#ifdef RIEMANN_DEBUG + if (0 == local_start) + printf("%d Ntuple of size %d for %d hits to fit\n", tupleMultiplicity->size(nHits), nHits, hitsInFit); +#endif + + for (int local_idx = local_start, nt = Rfit::maxNumberOfConcurrentFits(); local_idx < nt; + local_idx += gridDim.x * blockDim.x) { + auto tuple_idx = local_idx + offset; + if (tuple_idx >= tupleMultiplicity->size(nHits)) + break; + + // get it from the ntuple container (one to one to helix) + auto tkid = *(tupleMultiplicity->begin(nHits) + tuple_idx); + assert(tkid < foundNtuplets->nbins()); + + assert(foundNtuplets->size(tkid) == nHits); + + Rfit::Map3xNd hits(phits + local_idx); + Rfit::Map4d fast_fit(pfast_fit + local_idx); + Rfit::Map6xNf hits_ge(phits_ge + local_idx); + + // Prepare data structure + auto const *hitId = foundNtuplets->begin(tkid); + for (unsigned int i = 0; i < hitsInFit; ++i) { + auto hit = hitId[i]; + // printf("Hit global: %f,%f,%f\n", hhp->xg_d[hit],hhp->yg_d[hit],hhp->zg_d[hit]); + float ge[6]; + hhp->cpeParams() + .detParams(hhp->detectorIndex(hit)) + .frame.toGlobal(hhp->xerrLocal(hit), 0, hhp->yerrLocal(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.col(i) << hhp->xGlobal(hit), hhp->yGlobal(hit), hhp->zGlobal(hit); + hits_ge.col(i) << ge[0], ge[1], ge[2], ge[3], ge[4], ge[5]; + } + Rfit::Fast_fit(hits, fast_fit); + + // no NaN here.... + 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)); + } +} + +template +__global__ void kernelCircleFit(CAConstants::TupleMultiplicity const *__restrict__ tupleMultiplicity, + uint32_t nHits, + double B, + double *__restrict__ phits, + float *__restrict__ phits_ge, + double *__restrict__ pfast_fit_input, + Rfit::circle_fit *circle_fit, + uint32_t offset) { + assert(circle_fit); + assert(N <= nHits); + + // same as above... + + // look in bin for this hit multiplicity + auto local_start = blockIdx.x * blockDim.x + threadIdx.x; + for (int local_idx = local_start, nt = Rfit::maxNumberOfConcurrentFits(); local_idx < nt; + local_idx += gridDim.x * blockDim.x) { + auto tuple_idx = local_idx + offset; + if (tuple_idx >= tupleMultiplicity->size(nHits)) + break; + + Rfit::Map3xNd hits(phits + local_idx); + Rfit::Map4d fast_fit(pfast_fit_input + local_idx); + Rfit::Map6xNf hits_ge(phits_ge + local_idx); + + Rfit::VectorNd rad = (hits.block(0, 0, 2, N).colwise().norm()); + + Rfit::Matrix2Nd hits_cov = Rfit::Matrix2Nd::Zero(); + Rfit::loadCovariance2D(hits_ge, hits_cov); + + circle_fit[local_idx] = Rfit::Circle_fit(hits.block(0, 0, 2, N), hits_cov, fast_fit, rad, B, true); + +#ifdef RIEMANN_DEBUG +// auto tkid = *(tupleMultiplicity->begin(nHits) + tuple_idx); +// printf("kernelCircleFit circle.par(0,1,2): %d %f,%f,%f\n", tkid, +// circle_fit[local_idx].par(0), circle_fit[local_idx].par(1), circle_fit[local_idx].par(2)); +#endif + } +} + +template +__global__ void kernelLineFit(CAConstants::TupleMultiplicity const *__restrict__ tupleMultiplicity, + uint32_t nHits, + double B, + OutputSoA *results, + double *__restrict__ phits, + float *__restrict__ phits_ge, + double *__restrict__ pfast_fit_input, + Rfit::circle_fit *__restrict__ circle_fit, + uint32_t offset) { + assert(results); + assert(circle_fit); + assert(N <= nHits); + + // same as above... + + // look in bin for this hit multiplicity + auto local_start = (blockIdx.x * blockDim.x + threadIdx.x); + for (int local_idx = local_start, nt = Rfit::maxNumberOfConcurrentFits(); local_idx < nt; + local_idx += gridDim.x * blockDim.x) { + auto tuple_idx = local_idx + offset; + if (tuple_idx >= tupleMultiplicity->size(nHits)) + break; + + // get it for the ntuple container (one to one to helix) + auto tkid = *(tupleMultiplicity->begin(nHits) + tuple_idx); + + Rfit::Map3xNd hits(phits + local_idx); + Rfit::Map4d fast_fit(pfast_fit_input + local_idx); + Rfit::Map6xNf hits_ge(phits_ge + local_idx); + + auto const &line_fit = Rfit::Line_fit(hits, hits_ge, circle_fit[local_idx], fast_fit, B, true); + + Rfit::fromCircleToPerigee(circle_fit[local_idx]); + + results->stateAtBS.copyFromCircle( + circle_fit[local_idx].par, circle_fit[local_idx].cov, line_fit.par, line_fit.cov, 1.f / float(B), tkid); + results->pt(tkid) = B / std::abs(circle_fit[local_idx].par(2)); + results->eta(tkid) = asinhf(line_fit.par(0)); + results->chi2(tkid) = (circle_fit[local_idx].chi2 + line_fit.chi2) / (2 * N - 5); + +#ifdef RIEMANN_DEBUG + printf("kernelLineFit size %d for %d hits circle.par(0,1,2): %d %f,%f,%f\n", + N, + nHits, + tkid, + circle_fit[local_idx].par(0), + circle_fit[local_idx].par(1), + circle_fit[local_idx].par(2)); + printf("kernelLineFit line.par(0,1): %d %f,%f\n", tkid, line_fit.par(0), line_fit.par(1)); + printf("kernelLineFit chi2 cov %f/%f %e,%e,%e,%e,%e\n", + circle_fit[local_idx].chi2, + line_fit.chi2, + circle_fit[local_idx].cov(0, 0), + circle_fit[local_idx].cov(1, 1), + circle_fit[local_idx].cov(2, 2), + line_fit.cov(0, 0), + line_fit.cov(1, 1)); +#endif + } +} diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h new file mode 100644 index 0000000000000..336dbbc98521f --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuFishbone.h @@ -0,0 +1,93 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_plugins_gpuFishbone_h +#define RecoLocalTracker_SiPixelRecHits_plugins_gpuFishbone_h + +#include +#include +#include +#include +#include + +#include "DataFormats/Math/interface/approx_atan2.h" +#include "Geometry/TrackerGeometryBuilder/interface/phase1PixelTopology.h" +#include "HeterogeneousCore/CUDAUtilities/interface/VecArray.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "GPUCACell.h" + +namespace gpuPixelDoublets { + + // __device__ + // __forceinline__ + __global__ void fishbone(GPUCACell::Hits const* __restrict__ hhp, + GPUCACell* cells, + uint32_t const* __restrict__ nCells, + GPUCACell::OuterHitOfCell const* __restrict__ isOuterHitOfCell, + uint32_t nHits, + bool checkTrack) { + constexpr auto maxCellsPerHit = GPUCACell::maxCellsPerHit; + + auto const& hh = *hhp; + // auto layer = [&](uint16_t id) { return hh.cpeParams().layer(id); }; + + // x run faster... + auto firstY = threadIdx.y + blockIdx.y * blockDim.y; + auto firstX = threadIdx.x; + + float x[maxCellsPerHit], y[maxCellsPerHit], z[maxCellsPerHit], n[maxCellsPerHit]; + uint16_t d[maxCellsPerHit]; // uint8_t l[maxCellsPerHit]; + uint32_t cc[maxCellsPerHit]; + + for (int idy = firstY, nt = nHits; idy < nt; idy += gridDim.y * blockDim.y) { + auto const& vc = isOuterHitOfCell[idy]; + auto s = vc.size(); + if (s < 2) + continue; + // if alligned kill one of the two. + // in principle one could try to relax the cut (only in r-z?) for jumping-doublets + auto const& c0 = cells[vc[0]]; + auto xo = c0.get_outer_x(hh); + auto yo = c0.get_outer_y(hh); + auto zo = c0.get_outer_z(hh); + auto sg = 0; + for (int32_t ic = 0; ic < s; ++ic) { + auto& ci = cells[vc[ic]]; + if (0 == ci.theUsed) + continue; // for triplets equivalent to next + if (checkTrack && ci.tracks().empty()) + continue; + cc[sg] = vc[ic]; + d[sg] = ci.get_inner_detIndex(hh); + // l[sg] = layer(d[sg]); + x[sg] = ci.get_inner_x(hh) - xo; + y[sg] = ci.get_inner_y(hh) - yo; + z[sg] = ci.get_inner_z(hh) - zo; + n[sg] = x[sg] * x[sg] + y[sg] * y[sg] + z[sg] * z[sg]; + ++sg; + } + if (sg < 2) + continue; + // here we parallelize + for (int32_t ic = firstX; ic < sg - 1; ic += blockDim.x) { + auto& ci = cells[cc[ic]]; + for (auto jc = ic + 1; jc < sg; ++jc) { + auto& cj = cells[cc[jc]]; + // must be different detectors (in the same layer) + // if (d[ic]==d[jc]) continue; + // || l[ic]!=l[jc]) continue; + auto cos12 = x[ic] * x[jc] + y[ic] * y[jc] + z[ic] * z[jc]; + 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; + } else { + cj.theDoubletId = -1; + } + } + } //cj + } // ci + } // hits + } +} // namespace gpuPixelDoublets + +#endif // RecoLocalTracker_SiPixelRecHits_plugins_gpuFishbone_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h new file mode 100644 index 0000000000000..5b0d3e8833a52 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoublets.h @@ -0,0 +1,130 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelDoublets_h +#define RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelDoublets_h + +#include "RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoubletsAlgos.h" + +#define CONSTANT_VAR __constant__ + +namespace gpuPixelDoublets { + + constexpr int nPairs = 13 + 2 + 4; + static_assert(nPairs <= CAConstants::maxNumberOfLayerPairs()); + + // start constants + // clang-format off + + CONSTANT_VAR const uint8_t layerPairs[2 * nPairs] = { + 0, 1, 0, 4, 0, 7, // BPIX1 (3) + 1, 2, 1, 4, 1, 7, // BPIX2 (5) + 4, 5, 7, 8, // FPIX1 (8) + 2, 3, 2, 4, 2, 7, 5, 6, 8, 9, // BPIX3 & FPIX2 (13) + 0, 2, 1, 3, // Jumping Barrel (15) + 0, 5, 0, 8, // Jumping Forward (BPIX1,FPIX2) + 4, 6, 7, 9 // Jumping Forward (19) + }; + + constexpr int16_t phi0p05 = 522; // round(521.52189...) = phi2short(0.05); + constexpr int16_t phi0p06 = 626; // round(625.82270...) = phi2short(0.06); + constexpr int16_t phi0p07 = 730; // round(730.12648...) = phi2short(0.07); + + CONSTANT_VAR const int16_t phicuts[nPairs]{phi0p05, + phi0p07, + phi0p07, + phi0p05, + phi0p06, + phi0p06, + phi0p05, + phi0p05, + phi0p06, + phi0p06, + phi0p06, + phi0p05, + phi0p05, + phi0p05, + phi0p05, + phi0p05, + phi0p05, + phi0p05, + phi0p05}; + // phi0p07, phi0p07, phi0p06,phi0p06, phi0p06,phi0p06}; // relaxed cuts + + CONSTANT_VAR float const minz[nPairs] = { + -20., 0., -30., -22., 10., -30., -70., -70., -22., 15., -30, -70., -70., -20., -22., 0, -30., -70., -70.}; + CONSTANT_VAR float const maxz[nPairs] = { + 20., 30., 0., 22., 30., -10., 70., 70., 22., 30., -15., 70., 70., 20., 22., 30., 0., 70., 70.}; + CONSTANT_VAR float const maxr[nPairs] = { + 20., 9., 9., 20., 7., 7., 5., 5., 20., 6., 6., 5., 5., 20., 20., 9., 9., 9., 9.}; + + // end constants + // clang-format on + + using CellNeighbors = CAConstants::CellNeighbors; + using CellTracks = CAConstants::CellTracks; + using CellNeighborsVector = CAConstants::CellNeighborsVector; + using CellTracksVector = CAConstants::CellTracksVector; + + __global__ void initDoublets(GPUCACell::OuterHitOfCell* isOuterHitOfCell, + int nHits, + CellNeighborsVector* cellNeighbors, + CellNeighbors* cellNeighborsContainer, + CellTracksVector* cellTracks, + CellTracks* cellTracksContainer) { + assert(isOuterHitOfCell); + int first = blockIdx.x * blockDim.x + threadIdx.x; + for (int i = first; i < nHits; i += gridDim.x * blockDim.x) + isOuterHitOfCell[i].reset(); + + if (0 == first) { + cellNeighbors->construct(CAConstants::maxNumOfActiveDoublets(), cellNeighborsContainer); + cellTracks->construct(CAConstants::maxNumOfActiveDoublets(), cellTracksContainer); + auto i = cellNeighbors->extend(); + assert(0 == i); + (*cellNeighbors)[0].reset(); + i = cellTracks->extend(); + assert(0 == i); + (*cellTracks)[0].reset(); + } + } + + constexpr auto getDoubletsFromHistoMaxBlockSize = 64; // for both x and y + constexpr auto getDoubletsFromHistoMinBlocksPerMP = 16; + + __global__ +#ifdef __CUDACC__ + __launch_bounds__(getDoubletsFromHistoMaxBlockSize, getDoubletsFromHistoMinBlocksPerMP) +#endif + void getDoubletsFromHisto(GPUCACell* cells, + uint32_t* nCells, + CellNeighborsVector* cellNeighbors, + CellTracksVector* cellTracks, + TrackingRecHit2DSOAView const* __restrict__ hhp, + GPUCACell::OuterHitOfCell* isOuterHitOfCell, + int nActualPairs, + bool ideal_cond, + bool doClusterCut, + bool doZ0Cut, + bool doPtCut, + uint32_t maxNumOfDoublets) { + auto const& __restrict__ hh = *hhp; + doubletsFromHisto(layerPairs, + nActualPairs, + cells, + nCells, + cellNeighbors, + cellTracks, + hh, + isOuterHitOfCell, + phicuts, + minz, + maxz, + maxr, + ideal_cond, + doClusterCut, + doZ0Cut, + doPtCut, + maxNumOfDoublets); + } + +} // namespace gpuPixelDoublets + +#endif // RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelDouplets_h diff --git a/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoubletsAlgos.h b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoubletsAlgos.h new file mode 100644 index 0000000000000..d055c8b7cb867 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/plugins/gpuPixelDoubletsAlgos.h @@ -0,0 +1,244 @@ +#ifndef RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelDoubletsAlgos_h +#define RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelDoubletsAlgos_h + +#include +#include +#include +#include +#include + +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "DataFormats/Math/interface/approx_atan2.h" +#include "HeterogeneousCore/CUDAUtilities/interface/VecArray.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "CAConstants.h" +#include "GPUCACell.h" + +namespace gpuPixelDoublets { + + using CellNeighbors = CAConstants::CellNeighbors; + using CellTracks = CAConstants::CellTracks; + using CellNeighborsVector = CAConstants::CellNeighborsVector; + using CellTracksVector = CAConstants::CellTracksVector; + + __device__ __forceinline__ void doubletsFromHisto(uint8_t const* __restrict__ layerPairs, + uint32_t nPairs, + GPUCACell* cells, + uint32_t* nCells, + CellNeighborsVector* cellNeighbors, + CellTracksVector* cellTracks, + TrackingRecHit2DSOAView const& __restrict__ hh, + GPUCACell::OuterHitOfCell* isOuterHitOfCell, + int16_t const* __restrict__ phicuts, + float const* __restrict__ minz, + float const* __restrict__ maxz, + float const* __restrict__ maxr, + bool ideal_cond, + bool doClusterCut, + bool doZ0Cut, + bool doPtCut, + uint32_t maxNumOfDoublets) { + // ysize cuts (z in the barrel) times 8 + // these are used if doClusterCut is true + constexpr int minYsizeB1 = 36; + constexpr int minYsizeB2 = 28; + constexpr int maxDYsize12 = 28; + constexpr int maxDYsize = 20; + constexpr int maxDYPred = 20; + constexpr float dzdrFact = 8 * 0.0285 / 0.015; // from dz/dr to "DY" + + bool isOuterLadder = ideal_cond; + + using Hist = TrackingRecHit2DSOAView::Hist; + + auto const& __restrict__ hist = hh.phiBinner(); + uint32_t const* __restrict__ offsets = hh.hitsLayerStart(); + assert(offsets); + + auto layerSize = [=](uint8_t li) { return offsets[li + 1] - offsets[li]; }; + + // nPairsMax to be optimized later (originally was 64). + // If it should be much bigger, consider using a block-wide parallel prefix scan, + // e.g. see https://nvlabs.github.io/cub/classcub_1_1_warp_scan.html + const int nPairsMax = CAConstants::maxNumberOfLayerPairs(); + assert(nPairs <= nPairsMax); + __shared__ uint32_t innerLayerCumulativeSize[nPairsMax]; + __shared__ uint32_t ntot; + if (threadIdx.y == 0 && threadIdx.x == 0) { + innerLayerCumulativeSize[0] = layerSize(layerPairs[0]); + for (uint32_t i = 1; i < nPairs; ++i) { + innerLayerCumulativeSize[i] = innerLayerCumulativeSize[i - 1] + layerSize(layerPairs[2 * i]); + } + ntot = innerLayerCumulativeSize[nPairs - 1]; + } + __syncthreads(); + + // x runs faster + auto idy = blockIdx.y * blockDim.y + threadIdx.y; + auto first = threadIdx.x; + auto stride = blockDim.x; + + uint32_t pairLayerId = 0; // cannot go backward + for (auto j = idy; j < ntot; j += blockDim.y * gridDim.y) { + while (j >= innerLayerCumulativeSize[pairLayerId++]) + ; + --pairLayerId; // move to lower_bound ?? + + assert(pairLayerId < nPairs); + assert(j < innerLayerCumulativeSize[pairLayerId]); + assert(0 == pairLayerId || j >= innerLayerCumulativeSize[pairLayerId - 1]); + + uint8_t inner = layerPairs[2 * pairLayerId]; + uint8_t outer = layerPairs[2 * pairLayerId + 1]; + assert(outer > inner); + + auto hoff = Hist::histOff(outer); + + auto i = (0 == pairLayerId) ? j : j - innerLayerCumulativeSize[pairLayerId - 1]; + i += offsets[inner]; + + // printf("Hit in Layer %d %d %d %d\n", i, inner, pairLayerId, j); + + assert(i >= offsets[inner]); + assert(i < offsets[inner + 1]); + + // found hit corresponding to our cuda thread, now do the job + auto mi = hh.detectorIndex(i); + if (mi > gpuClustering::maxNumModules) + continue; // invalid + + /* maybe clever, not effective when zoCut is on + auto bpos = (mi%8)/4; // if barrel is 1 for z>0 + auto fpos = (outer>3) & (outer<7); + if ( ((inner<3) & (outer>3)) && bpos!=fpos) continue; + */ + + auto mez = hh.zGlobal(i); + + if (mez < minz[pairLayerId] || mez > maxz[pairLayerId]) + continue; + + int16_t mes = -1; // make compiler happy + if (doClusterCut) { + // if ideal treat inner ladder as outer + if (inner == 0) + assert(mi < 96); + isOuterLadder = ideal_cond ? true : 0 == (mi / 8) % 2; // only for B1/B2/B3 B4 is opposite, FPIX:noclue... + + // in any case we always test mes>0 ... + mes = inner > 0 || isOuterLadder ? hh.clusterSizeY(i) : -1; + + if (inner == 0 && outer > 3) // B1 and F1 + if (mes > 0 && mes < minYsizeB1) + continue; // only long cluster (5*8) + if (inner == 1 && outer > 3) // B2 and F1 + if (mes > 0 && mes < minYsizeB2) + continue; + } + auto mep = hh.iphi(i); + auto mer = hh.rGlobal(i); + + // all cuts: true if fails + constexpr float z0cut = 12.f; // cm + constexpr float hardPtCut = 0.5f; // GeV + constexpr float minRadius = + hardPtCut * 87.78f; // cm (1 GeV track has 1 GeV/c / (e * 3.8T) ~ 87 cm radius in a 3.8T field) + constexpr float minRadius2T4 = 4.f * minRadius * minRadius; + auto ptcut = [&](int j, int16_t idphi) { + auto r2t4 = minRadius2T4; + auto ri = mer; + auto ro = hh.rGlobal(j); + auto dphi = short2phi(idphi); + return dphi * dphi * (r2t4 - ri * ro) > (ro - ri) * (ro - ri); + }; + auto z0cutoff = [&](int j) { + auto zo = hh.zGlobal(j); + auto ro = hh.rGlobal(j); + auto dr = ro - mer; + return dr > maxr[pairLayerId] || dr < 0 || std::abs((mez * ro - mer * zo)) > z0cut * dr; + }; + + auto zsizeCut = [&](int j) { + auto onlyBarrel = outer < 4; + auto so = hh.clusterSizeY(j); + auto dy = inner == 0 ? maxDYsize12 : maxDYsize; + // in the barrel cut on difference in size + // in the endcap on the prediction on the first layer (actually in the barrel only: happen to be safe for endcap as well) + // FIXME move pred cut to z0cutoff to optmize loading of and computaiton ... + auto zo = hh.zGlobal(j); + auto ro = hh.rGlobal(j); + return onlyBarrel ? mes > 0 && so > 0 && std::abs(so - mes) > dy + : (inner < 4) && mes > 0 && + std::abs(mes - int(std::abs((mez - zo) / (mer - ro)) * dzdrFact + 0.5f)) > maxDYPred; + }; + + auto iphicut = phicuts[pairLayerId]; + + auto kl = Hist::bin(int16_t(mep - iphicut)); + auto kh = Hist::bin(int16_t(mep + iphicut)); + auto incr = [](auto& k) { return k = (k + 1) % Hist::nbins(); }; + // bool piWrap = std::abs(kh-kl) > Hist::nbins()/2; + +#ifdef GPU_DEBUG + int tot = 0; + int nmin = 0; + int tooMany = 0; +#endif + + auto khh = kh; + incr(khh); + for (auto kk = kl; kk != khh; incr(kk)) { +#ifdef GPU_DEBUG + if (kk != kl && kk != kh) + nmin += hist.size(kk + hoff); +#endif + auto const* __restrict__ p = hist.begin(kk + hoff); + auto const* __restrict__ e = hist.end(kk + hoff); + p += first; + for (; p < e; p += stride) { + auto oi = __ldg(p); + assert(oi >= offsets[outer]); + assert(oi < offsets[outer + 1]); + auto mo = hh.detectorIndex(oi); + if (mo > gpuClustering::maxNumModules) + continue; // invalid + + if (doZ0Cut && z0cutoff(oi)) + continue; + + auto mop = hh.iphi(oi); + uint16_t idphi = std::min(std::abs(int16_t(mop - mep)), std::abs(int16_t(mep - mop))); + if (idphi > iphicut) + continue; + + if (doClusterCut && zsizeCut(oi)) + continue; + if (doPtCut && ptcut(oi, idphi)) + continue; + + 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(*cellNeighbors, *cellTracks, hh, pairLayerId, ind, i, oi); + isOuterHitOfCell[oi].push_back(ind); +#ifdef GPU_DEBUG + if (isOuterHitOfCell[oi].full()) + ++tooMany; + ++tot; +#endif + } + } +#ifdef GPU_DEBUG + if (tooMany > 0) + printf("OuterHitOfCell full for %d in layer %d/%d, %d,%d %d\n", i, inner, outer, nmin, tot, tooMany); +#endif + } // loop in block... + } + +} // namespace gpuPixelDoublets + +#endif // RecoLocalTracker_SiPixelRecHits_plugins_gpuPixelDoupletsAlgos_h diff --git a/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py new file mode 100644 index 0000000000000..c72c07ae5a721 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/python/caHitQuadrupletEDProducer_cfi.py @@ -0,0 +1,4 @@ +import FWCore.ParameterSet.Config as cms +from RecoPixelVertexing.PixelTriplets.caHitQuadrupletDefaultEDProducer_cfi import caHitQuadrupletDefaultEDProducer as _caHitQuadrupletDefaultEDProducer + +caHitQuadrupletEDProducer = _caHitQuadrupletDefaultEDProducer.clone() diff --git a/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml b/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml index 6d6f1553b32f3..d480d7408b9e2 100644 --- a/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelTriplets/test/BuildFile.xml @@ -16,3 +16,14 @@ + + + + + + + + + + + diff --git a/RecoPixelVertexing/PixelTriplets/test/CAsizes_t.cpp b/RecoPixelVertexing/PixelTriplets/test/CAsizes_t.cpp new file mode 100644 index 0000000000000..5c57eb7005691 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/test/CAsizes_t.cpp @@ -0,0 +1,25 @@ +#include "RecoPixelVertexing/PixelTriplets/plugins/GPUCACell.h" + +#include +#include + +template +void print() { + std::cout << "size of " << typeid(T).name() << ' ' << sizeof(T) << std::endl; +} + +int main() { + using namespace CAConstants; + + print(); + print(); + print(); + print(); + print(); + print(); + print(); + + print(); + + return 0; +} diff --git a/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp new file mode 100644 index 0000000000000..504f9c144b284 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/test/CircleEq_t.cpp @@ -0,0 +1,77 @@ +#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 < phi + 0.31; phi3 += 0.05) { + float x3 = r3 * cos(phi3); + float y3 = r3 * sin(phi3); + + OriCircle ori(x1, y1, x2, y2, x3, y3); + CircleEq eq(x1, y1, x2, y2, x3, y3); + // std::cout << "r " << ori.radius <<' '<< eq.radius() << std::endl; + assert(equal(ori.radius, std::abs(eq.radius()))); + auto c = eq.center(); + auto dir = eq.cosdir(); + assert(equal(1.f, dir.first * dir.first + dir.second * dir.second)); + assert(equal(ori.x_center, c.first)); + assert(equal(ori.y_center, c.second)); + // std::cout << "dca " << ori.dca0() <<' '<< eq.radius()*eq.dca0() << std::endl; + assert(equal(std::abs(ori.dca0()), std::abs(eq.radius() * eq.dca0()))); + // std::cout << "dca " << ori.dca(1.,1.) <<' '<< eq.radius()*eq.dca(1.,1.) << std::endl; + assert(equal(std::abs(ori.dca(1., 1.)), std::abs(eq.radius() * eq.dca(1., 1.)))); + } + } + + return 0; +} diff --git a/RecoPixelVertexing/PixelTriplets/test/fastDPHI_t.cpp b/RecoPixelVertexing/PixelTriplets/test/fastDPHI_t.cpp new file mode 100644 index 0000000000000..8538970a196ff --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/test/fastDPHI_t.cpp @@ -0,0 +1,165 @@ +// this test documents the derivation of the fast deltaphi used in gpu doublet code.. +// +// +// +#include +#include +#include +#include + +/** +| 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); | +| 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. | +| 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| +| +*/ + +template +class FastCircle { +public: + FastCircle() {} + FastCircle(T x1, T y1, T x2, T y2, T x3, T y3) { compute(x1, y1, x2, y2, x3, y3); } + + void compute(T x1, T y1, T x2, T y2, T x3, T y3); + + T m_xp; + T m_yp; + T m_c; + T m_alpha; + T m_beta; +}; + +template +void FastCircle::compute(T x1, T y1, T x2, T y2, T x3, T y3) { + bool flip = std::abs(x3 - x1) > std::abs(y3 - y1); + + auto x1p = x1 - x2; + auto y1p = y1 - y2; + auto d12 = x1p * x1p + y1p * y1p; + auto x3p = x3 - x2; + auto y3p = y3 - y2; + auto d32 = x3p * x3p + y3p * y3p; + + if (flip) { + std::swap(x1p, y1p); + std::swap(x3p, y3p); + } + + auto num = x1p * y3p - y1p * x3p; // num also gives correct sign for CT + auto det = d12 * y3p - d32 * y1p; + if (std::abs(det) == 0) { + // and why we flip???? + } + 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; + + if (flip) { + std::swap(x1p, y1p); + std::swap(al2, be2); + al2 = -al2; + be2 = -be2; + ct = -ct; + } + + m_xp = x1; + m_yp = y1; + m_c = ct; + m_alpha = al2 - ct * x1p; + m_beta = be2 - ct * y1p; +} + +// compute curvature given two points (and origin) +float fastDPHI(float ri, float ro, float dphi) { + /* + x3=0 y1=0 x1=0; + y3=ro + */ + + // auto x2 = ri*dphi; + // auto y2 = ri*(1.f-0.5f*dphi*dphi); + + /* + auto x1p = x1-x2; + auto y1p = y1-y2; + auto d12 = x1p*x1p + y1p*y1p; + auto x3p = x3-x2; + auto y3p = y3-y2; + auto d32 = x3p*x3p + y3p*y3p; + */ + + /* + auto x1p = -x2; + auto y1p = -y2; + auto d12 = ri*ri; + auto x3p = -x2; + auto y3p = ro-y2; + auto d32 = ri*ri + ro*ro - 2.f*ro*y2; + */ + + // auto rat = (ro -2.f*y2); + // auto det = ro - ri - (ro - 2.f*ri -0.5f*ro)*dphi*dphi; + + //auto det2 = (ro-ri)*(ro-ri) -2.*(ro-ri)*(ro - 2.f*ri -0.5f*ro)*dphi*dphi; + // auto seq = det2 + dphi*dphi*(ro-2.f*ri)*(ro-2.f*ri); // *rat2; + // auto seq = (ro-ri)*(ro-ri) + dphi*dphi*ri*ro; + + // and little by little simplifing and removing higher over terms + // we get + auto r2 = (ro - ri) * (ro - ri) / (dphi * dphi) + ri * ro; + + // d2 = (ro-ri)*(ro-ri)/(4.f*r2 -ri*ro); + // return -2.f*dphi/std::sqrt(seq); + + return -1.f / std::sqrt(r2 / 4.f); +} + +#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 n = 0; +void go(float ri, float ro, float dphi, bool print = false) { + ++n; + float x3 = 0.f, y3 = ro; + float x2 = ri * sin(dphi); + float y2 = ri * cos(dphi); + + FastCircle c(0, 0, x2, y2, x3, y3); + + auto cc = fastDPHI(ri, ro, dphi); + if (print) + std::cout << c.m_c << ' ' << cc << std::endl; + assert(equal(c.m_c, cc)); +} + +int main() { + go(4., 7., 0.1, true); + + for (float r1 = 2; r1 < 15; r1 += 1) + for (float dr = 0.5; dr < 10; dr += 0.5) + for (float dphi = 0.02; dphi < 0.2; dphi += 0.2) + go(r1, r1 + dr, dphi); + + std::cout << "done " << n << std::endl; + return 0; +}; diff --git a/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb new file mode 100644 index 0000000000000..ec33b344a9759 --- /dev/null +++ b/RecoPixelVertexing/PixelTriplets/test/pixHits.ipynb @@ -0,0 +1,6370 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "IPython.OutputArea.prototype._should_scroll = function(lines) {\n", + " return false;\n", + "}" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%javascript\n", + "IPython.OutputArea.prototype._should_scroll = function(lines) {\n", + " return false;\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "import glob\n", + "import math\n", + "import numpy as np\n", + "import pandas as pd\n", + "from bisect import *\n", + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 1 1 2 2 9 10 10\n" + ] + } + ], + "source": [ + "layerStart = [0,96,320,672,1184,1296,1408,1520,1632,1744,1856]\n", + "layerName = [\"BL1\",\"BL2\",\"BL3\",\"BL4\",\"E+1\", \"E+2\", \"E+3\",\"E-1\", \"E-2\", \"E-3\"]\n", + "def layer(x) :\n", + " return bisect_right(layerStart, x)\n", + "\n", + "print layer(0),layer(1),layer(95),layer(96),layer(97),layer(1743),layer(1744),layer(1855)\n", + "\n", + "i2p = math.pi/32769.0" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def curvature(h, ptmin, region_origin_radius,hardPtCut, first, rad=False) :\n", + " region_origin_x = 0\n", + " region_origin_y = 0\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", + " distance_13_squared = (x1 - x3)*(x1 - x3) + (y1 - y3)*(y1 - y3)\n", + " tan_12_13_half_mul_distance_13_squared = abs(y1 * (x2 - x3) + y2 * (x3 - x1) + y3 * (x1 - x2))\n", + " # high pt : just straight\n", + " straight = tan_12_13_half_mul_distance_13_squared * ptmin <= 1.0e-4*distance_13_squared\n", + " def ifStraight() :\n", + " distance_3_beamspot_squared = (x3-region_origin_x) * (x3-region_origin_x) + (y3-region_origin_y) * (y3-region_origin_y)\n", + " dot_bs3_13 = ((x1 - x3)*( region_origin_x - x3) + (y1 - y3) * (region_origin_y-y3))\n", + " proj_bs3_on_13_squared = dot_bs3_13*dot_bs3_13/distance_13_squared\n", + " distance_13_beamspot_squared = distance_3_beamspot_squared - proj_bs3_on_13_squared\n", + " return distance_13_beamspot_squared < (region_origin_radius+phiCut)*(region_origin_radius+phiCut)\n", + " \n", + " def standard() :\n", + " # 87 cm/GeV = 1/(3.8T * 0.3)\n", + " # 165 cm/GeV = 1/(2T * 0.3)\n", + " \n", + " # take less than radius given by the hardPtCut and reject everything below\n", + " minRadius = hardPtCut*87 # // FIXME move out and use real MagField\n", + " \n", + " det = (x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2)\n", + " \n", + " offset = x2 * x2 + y2*y2\n", + " \n", + " bc = (x1 * x1 + y1 * y1 - offset)*0.5\n", + " \n", + " cd = (offset - x3 * x3 - y3 * y3)*0.5\n", + " \n", + " \n", + " \n", + " idet = 1./ det;\n", + " \n", + " x_center = (bc * (y2 - y3) - cd * (y1 - y2)) * idet\n", + " y_center = (cd * (x1 - x2) - bc * (x2 - x3)) * idet\n", + " \n", + " radius = np.sqrt((x2 - x_center)*(x2 - x_center) + (y2 - y_center)*(y2 - y_center))\n", + " if rad: return radius\n", + " def domore() :\n", + " centers_distance_squared = (x_center - region_origin_x)*(x_center - region_origin_x) + (y_center - region_origin_y)*(y_center - region_origin_y)\n", + " #minimumOfIntersectionRange = (radius - region_origin_radius_plus_tolerance)*(radius - region_origin_radius_plus_tolerance)\n", + " #ok = centers_distance_squared >= minimumOfIntersectionRange\n", + " return np.sqrt(centers_distance_squared)-radius # - region_origin_radius\n", + "\n", + "\n", + " # return domore().where(radius > minRadius, radius <= minRadius)\n", + " return domore()\n", + " \n", + " #return ifStraight().where(straight,standard())\n", + " return standard()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "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": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def deltaphi(a,b,ch) :\n", + " ch *=ch\n", + " cb = ch*b\n", + " ca = ch*a\n", + " return np.arcsin(cb) - np.arcsin(ca)\n", + "\n", + "\n", + "def deltaphiA(a,b,ch) :\n", + " ch *=ch\n", + " d = b-a\n", + " cd = ch*d\n", + " ca = ch*a\n", + " return cd*(1.+0.5*ca*(ca+cd)+cd*cd*0.1667)\n", + "\n", + "\n", + "def eta(r,z) :\n", + " t = z/r\n", + " return np.arcsinh(t);\n", + "\n", + "def sag(r,c) :\n", + " return 0.5*c*r*r\n", + "\n", + "def phicutOld(a,b,c) :\n", + " o = np.maximum(a,b)\n", + " i = np.minimum(a,b)\n", + " d = np.minimum(i,o-i)\n", + " m = sag(o,c)\n", + " return d*m/(0.5*o*i)\n", + "\n", + "def phicut(a,b,c) :\n", + " 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", + "\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": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def alignRZ(h, rp, ptmin, first) :\n", + " '''\n", + " float radius_diff = std::abs(r1 - ro);\n", + " float distance_13_squared = radius_diff*radius_diff + (z1 - zo)*(z1 - zo);\n", + " \n", + " float pMin = ptmin*std::sqrt(distance_13_squared); //this needs to be divided by radius_diff later\n", + " \n", + " float tan_12_13_half_mul_distance_13_squared = fabs(z1 * (getInnerR() - ro) + getInnerZ() * (ro - r1) + zo * (r1 - getInnerR())) ;\n", + " return tan_12_13_half_mul_distance_13_squared * pMin <= thetaCut * distance_13_squared * radius_diff;\n", + " '''\n", + " ri = h[rp+'1'] if first else h[rp+'2']\n", + " zi = h['z1'] if first else h['z2']\n", + " rm = h[rp+'2'] if first else h[rp+'3']\n", + " zm = h['z2'] if first else h['z3']\n", + " ro = h[rp+'3'] if first else h[rp+'4']\n", + " zo = h['z3'] if first else h['z4']\n", + " fact = 1. if (rp=='r') else 10.\n", + " radius_diff = fact*abs(ri - ro)\n", + " distance_13_squared = radius_diff*radius_diff + (zi - zo)*(zi - zo)\n", + " \n", + " pMin = ptmin*np.sqrt(distance_13_squared) #this needs to be divided by radius_diff later\n", + " \n", + " tan_12_13_half_mul_distance_13_squared = fact*abs(zi * (rm - ro) + zm * (ro - ri) + zo * (ri - rm)) \n", + " return tan_12_13_half_mul_distance_13_squared * pMin/(distance_13_squared * radius_diff)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def alignRPZ(h, rp, first) :\n", + " ri = h[rp+'1'] if first else h[rp+'2']\n", + " zi = h['z1'] if first else h['z2']\n", + " rm = h[rp+'2'] if first else h[rp+'3']\n", + " zm = h['z2'] if first else h['z3']\n", + " ro = h[rp+'3'] if first else h[rp+'4']\n", + " zo = h['z3'] if first else h['z4']\n", + " \n", + " return (rm-ri)*(zo-zm) - (ro-rm)*(zm-zi)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def doublets(hits,l1,l2,cut=0.2) :\n", + " nd=0\n", + " for h1 in l1.itertuples() :\n", + " phi = h1.phi\n", + " hh = l2['phi'].searchsorted([phi-cut,phi+cut])\n", + " hits.loc[h1.Index,'up0'] = hh[0]\n", + " hits.loc[h1.Index,'up1'] = hh[1]\n", + " nd += hh[1]-hh[0]\n", + " return nd" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def ml(pt1,pz1,pt2,pz2) :\n", + " dp = pt1*pt2+pz1*pz2\n", + " m1 = pt1*pt1+pz1*pz1\n", + " m2 = pt2*pt2+pz2*pz2\n", + " corr = pt1/np.sqrt(m1)\n", + " dp /=np.sqrt(m1*m2)\n", + " dt = np.arccos(dp[dp<1]) \n", + " dtn = dt*np.sqrt(m1)*corr\n", + " return dt,dtn" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "#rawhits 14013319\n" + ] + } + ], + "source": [ + "file = '/Users/innocent/data/ttbarIdealPU50HitsC2.csv'\n", + "# file = '/Users/innocent/data/ttbarPU50Hits.csv'\n", + "# file = '/Users/innocent/data/ttbarPU0Hits.csv'\n", + "rawHits = pd.read_csv(file, delimiter=\" \")\n", + "print '#rawhits', len(rawHits)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "#hits 5148484\n", + " ev ind det charge xg yg zg rg iphi xsize \\\n", + "2233 1 1497 63 203782 -2.0126 -1.8634 23.3378 2.7428 -24977 9 \n", + "2217 1 1481 63 142319 -1.7204 -2.1465 21.7691 2.7508 -23432 13 \n", + "106 1 1258 54 116813 -3.0205 -0.5520 15.8207 3.0705 -30882 15 \n", + "1245 1 1085 46 107050 -2.7395 0.3352 16.0573 2.7600 31497 8 \n", + "2083 1 2179 93 81797 2.7517 -0.2867 11.9489 2.7666 -1084 8 \n", + "\n", + " ... tkId pt z0 r0 n1 phi eta seq trackID \\\n", + "2233 ... 2 1817 25188 19 15 -2.394669 2.837678 1 10000002 \n", + "2217 ... 5 2274 25188 19 13 -2.246446 2.765715 1 10000005 \n", + "106 ... 6 4734 25188 19 8 -2.960836 2.341913 1 10000006 \n", + "1245 ... 7 967 25188 19 10 3.019840 2.461386 1 10000007 \n", + "2083 ... 9 823 25188 19 7 -0.103816 2.169308 1 10000009 \n", + "\n", + " norm \n", + "2233 0.001 \n", + "2217 0.001 \n", + "106 0.001 \n", + "1245 0.001 \n", + "2083 0.001 \n", + "\n", + "[5 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi \\\n", + "14003543 1000 3710 265 65338 0.9296 -6.5121 -17.3848 6.5781 -14904 \n", + "14010785 1000 9896 1568 19832 1.8691 -10.1563 -29.6611 10.3268 -14485 \n", + "14012001 1000 10344 1625 30507 1.9370 -10.3811 -30.4224 10.5603 -14459 \n", + "14010409 1000 11280 1737 29523 2.6579 -12.5591 -37.8870 12.8373 -14208 \n", + "14012416 1000 12263 1849 21723 3.7319 -15.2245 -47.2765 15.6753 -13876 \n", + "\n", + " xsize ... tkId pt z0 r0 n1 phi eta seq \\\n", + "14003543 15 ... 113902 530 42061 12 8 -1.429005 -1.699009 2 \n", + "14010785 13 ... 113902 530 42061 12 3 -1.388799 -1.777253 8 \n", + "14012001 11 ... 113902 530 42061 12 3 -1.386328 -1.780072 8 \n", + "14010409 16 ... 113902 530 42061 12 3 -1.362242 -1.802940 9 \n", + "14012416 8 ... 113902 530 42061 12 2 -1.330411 -1.823490 10 \n", + "\n", + " trackID norm \n", + "14003543 10000113902 0.001 \n", + "14010785 10000113902 0.001 \n", + "14012001 10000113902 0.001 \n", + "14010409 10000113902 0.001 \n", + "14012416 10000113902 0.001 \n", + "\n", + "[5 rows x 21 columns]\n" + ] + } + ], + "source": [ + "rawHits['phi'] = np.arctan2(rawHits['yg'],rawHits['xg']) # rawHits['iphi']*i2p\n", + "rawHits['eta'] = eta(rawHits['rg'],rawHits['zg'])\n", + "rawHits['seq'] = rawHits['det'].apply(layer)\n", + "rawHits['trackID'] = rawHits['tkId']+10000000*rawHits['ev']\n", + "rawHits['norm'] = 1./1000.\n", + "rawHits.sort_values(by=['ev','tkId','det'],inplace=True)\n", + "hits = rawHits[np.logical_and(np.logical_and(rawHits['pt']>400, abs(rawHits['z0'])<150000),rawHits['r0']<1000)]\n", + "print '#hits', len(hits)\n", + "print hits.head()\n", + "print hits.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2573.608 2095.2 1750.034 1479.679 945.452 1024.445 1079.237 951.194\n", + " 1030.265 1084.205]\n", + "All Hits 1.4013E+04\n", + "[5392223.481599452, 3666671.236800405, 2589488.559086526, 2433222.8308156473, 1980911.030399918, 1654573.145368111, 968563.574139946, 1105618.9484649901, 2448000.487951649, 1992941.6687999205, 1664621.840596114, 979981.8864099487, 1117018.4643249928, 0]\n", + "All Doublets 2.7994E+07\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All triplets [9436574428.398449, 5425496428.998095, 5098088475.124949, 3466661654.17527, 2492702962.914902, 1045309646.0640776, 2029334400.538017, 5129050622.356299, 3487715680.4169817, 2522089222.7194767, 1062501261.1551037, 2053258048.4061267, 0]\n", + "4.3249E+10\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": [ + "#print hits[hits['tkId']==3].head(50)\n", + "n,b,p = plt.hist(rawHits['seq'],weights=rawHits['norm'])\n", + "plt.show()\n", + "print n \n", + "print 'All Hits', '{:.4E}'.format(np.sum(n))\n", + "doublets = [n[0]*n[1],n[1]*n[2],n[2]*n[3],\n", + " n[0]*n[4],n[1]*n[4],n[2]*n[4],n[4]*n[5],n[5]*n[6],\n", + " n[0]*n[7],n[1]*n[7],n[2]*n[7],n[7]*n[8],n[8]*n[9],0\n", + " ]\n", + "print doublets\n", + "print 'All Doublets', '{:.4E}'.format(np.sum(doublets))\n", + "xy = np.arange(0.,14.,1.)\n", + "plt.step(xy,doublets,where='post')\n", + "plt.show()\n", + "triplets = [n[0]*n[1]*n[2],n[1]*n[2]*n[3],\n", + " n[0]*n[1]*n[4],n[1]*n[2]*n[4],\n", + " n[0]*n[4]*n[5],n[4]*n[5]*n[6],\n", + " n[1]*n[4]*n[5],\n", + " n[0]*n[1]*n[7],n[1]*n[2]*n[7],\n", + " n[0]*n[7]*n[8],n[7]*n[8]*n[9],\n", + " n[1]*n[7]*n[8],0\n", + " ]\n", + "print 'All triplets', triplets\n", + "print '{:.4E}'.format(np.sum(triplets))\n", + "xy = np.arange(0.,len(triplets),1)\n", + "plt.step(xy,triplets,where='post')\n", + "plt.show()\n", + "\n", + "\n", + "plt.hist(hits['seq'],weights=hits['norm'])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1130010 841967 660647 531391 660653 663638 660178\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAD8CAYAAACcjGjIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAFM1JREFUeJzt3X+s3fV93/HnqxDSJG1iCB4itpmtxcvmRN1CrwxTpi4KHRgS1VRKMmg13BTVqkrWdOuUmESaNyhTUKfQ0rV0Vu3FVBSHkVRYg5S4BJRVmgmGEH45lCsSYlskODGQZrSpnLz3x/mYHPy9l3t9zr0+5977fEhH9/v9fD/fcz5f+d7zOp8f5+tUFZIk9fuJUTdAkjR+DAdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOk4ddQMGdeaZZ9bq1atH3QxJWlAefPDB71TV8pnqLdhwWL16Nfv27Rt1MyRpQUnyzGzqOawkSeowHCRJHYaDJKnDcJAkdRgOkqQOw0GS1GE4SJI6DAdJUofhIEnqWLDfkJakxWD1ljtf3v7GJ987wpa8kuFwgsb1H1KS5pLhIEknWf+HzHFlOEjSSbAQAqHfjBPSSXYkeS7JY1Mc++0kleTMtp8kNyaZTPJIknP76m5K8lR7bOor/9kkj7ZzbkySubq4YazecufLD0laamazWunTwIbjC5OsAi4EvtlXfDGwtj02Aze1umcAW4HzgPXA1iSnt3NuAn6t77zOa0mSTq4Zh5Wq6ktJVk9x6Abgo8AdfWUbgZurqoC9SZYlORt4N7Cnqo4AJNkDbEhyH/DGqtrbym8GLgU+P+gFjYoT1ZIWk4G+55BkI3Coqr563KEVwIG+/YOt7NXKD05RLkkaoROekE7yeuDj9IaUTqokm+kNV3HOOeec7JefNXsRkha6QVYr/SNgDfDVNne8EngoyXrgELCqr+7KVnaI3tBSf/l9rXzlFPWnVFXbgG0AExMTNUDbJWlOLPYPgSccDlX1KPAPju0n+QYwUVXfSbIb+HCSXfQmn1+sqmeT3A38175J6AuBq6vqSJLvJTkfuB+4AviD4S5J0lL3aqsMT+Yb+UJe7ThjOCS5ld6n/jOTHAS2VtX2aarfBVwCTAIvAR8CaCFwLfBAq3fNsclp4DforYh6Hb2J6LGbjB6Xf+DZfFJZ7J9mtDSd6O++hjeb1UqXz3B8dd92AVdNU28HsGOK8n3AO2Zqxzga919Gg0J6dSf6NzKbv/lxf1+YLb8hvUQYFFLPfLx5L5ZA6Gc4zDPflKX5M19vyv7dGg5Lkr/4kmZiOJxE070pD/JmvRi7sdJs+Lt/cvg/wUmSOgwHSVKHw0pjzi60pFGw5yBJ6jAcJEkdhoMkqcM5hzHjHIM0Gv7tvZI9B0lShz0Hvcy7vko6xnAYEbuwksaZ4SDppFiovc6l+kHOcJA0NhZqgCxGhoMG5h+ytHgZDn2WavdRko7nUlZJUseM4ZBkR5LnkjzWV/a7Sb6W5JEkf55kWd+xq5NMJnkyyUV95Rta2WSSLX3la5Lc38o/k+S0ubxASVooVm+58+XHqM2m5/BpYMNxZXuAd1TVzwB/DVwNkGQdcBnw9nbOHyU5JckpwB8CFwPrgMtbXYDrgRuq6q3A88CVQ12RpLExTm92OjEzzjlU1ZeSrD6u7At9u3uB97ftjcCuqvoB8PUkk8D6dmyyqp4GSLIL2JhkP/Ae4JdanZ3AfwZuGuRiNDpOTkuLy1xMSP8q8Jm2vYJeWBxzsJUBHDiu/DzgzcALVXV0ivqSFqDZ9BL8MDH+hpqQTvIJ4Chwy9w0Z8bX25xkX5J9hw8fPhkvKUlL0sA9hyS/ArwPuKCqqhUfAlb1VVvZypim/LvAsiSntt5Df/2OqtoGbAOYmJio6eppbjleLC09A4VDkg3AR4F/VVUv9R3aDfxZkk8BbwHWAl8GAqxNsobem/9lwC9VVSW5l96cxS5gE3DHoBcjaTT8ALH4zGYp663A/wXeluRgkiuB/w78NLAnycNJ/higqh4HbgOeAP4CuKqqfth6BR8G7gb2A7e1ugAfA/5Dm7x+M7B9Tq9QknTCZrNa6fIpiqd9A6+q64Drpii/C7hrivKn+fGKJknSGPAb0pKkDu+tpDk33fizSxYXF+cZFjfDQdKsGQhLh8NKkqQOew6SRsreyHgyHCSNJUNjtAwHTck/TB3j78LSZDgscSfzD9+brUkLhxPSkqQOw0GS1OGwkkbCISZpvNlzkCR1GA6SpA6HlTRyDjFJ48eegySpw3CQJHU4rCSpw29Fy3DQWHH+QRoPDitJkjoMB0lSx4zhkGRHkueSPNZXdkaSPUmeaj9Pb+VJcmOSySSPJDm375xNrf5TSTb1lf9skkfbOTcmyVxfpBam1VvufPkh6eSaTc/h08CG48q2APdU1VrgnrYPcDGwtj02AzdBL0yArcB5wHpg67FAaXV+re+8419L0klgGKvfjOFQVV8CjhxXvBHY2bZ3Apf2ld9cPXuBZUnOBi4C9lTVkap6HtgDbGjH3lhVe6uqgJv7nkuSNCKDrlY6q6qebdvfAs5q2yuAA331DrayVys/OEW5pHniijDNxtBLWauqktRcNGYmSTbTG67inHPOORkvqTFx/FCHb2rS/Bo0HL6d5OyqerYNDT3Xyg8Bq/rqrWxlh4B3H1d+XytfOUX9KVXVNmAbwMTExEkJJGkxc35B0xl0Ketu4NiKo03AHX3lV7RVS+cDL7bhp7uBC5Oc3iaiLwTubse+l+T8tkrpir7nkiSNyIw9hyS30vvUf2aSg/RWHX0SuC3JlcAzwAdb9buAS4BJ4CXgQwBVdSTJtcADrd41VXVskvs36K2Ieh3w+faQJI3QjOFQVZdPc+iCKeoWcNU0z7MD2DFF+T7gHTO1Q+rnpKo0v/yGtCSpwxvvSUuAE886UfYcJEkd9hykRcregoZhOGjBm+5N0IlqaXAOK0mSOuw5SIuIQ0maK/YcJEkdhoMkqcNwkCR1OOegRctbbEiDs+cgSeqw56AlYbH2IlydpPliz0GS1GE4SJI6HFaSFhiHknQyLPlw8A9taVuscxHSsNL7z9sWnomJidq3b9/Qz2M4aCrjEBT+buqYufx9TPJgVU3MVM85B0lSx1DhkOTfJ3k8yWNJbk3yk0nWJLk/yWSSzyQ5rdV9bdufbMdX9z3P1a38ySQXDXdJkqRhDTznkGQF8JvAuqr62yS3AZcBlwA3VNWuJH8MXAnc1H4+X1VvTXIZcD3wb5Ksa+e9HXgL8JdJ/nFV/XCoK5PmwXzMUTjvoXE07LDSqcDrkpwKvB54FngPcHs7vhO4tG1vbPu04xckSSvfVVU/qKqvA5PA+iHbJY2d1VvufPkhjbuBew5VdSjJfwO+Cfwt8AXgQeCFqjraqh0EVrTtFcCBdu7RJC8Cb27le/ueuv8caeTm4818uuc0ODQuhhlWOp3ep/41wAvA/wI2zFG7pnvNzcBmgHPOOWc+X0pLnG/SWuqG+Z7DzwNfr6rDAEk+B7wLWJbk1NZ7WAkcavUPAauAg20Y6k3Ad/vKj+k/5xWqahuwDXpLWYdou3RSGDJaqIaZc/gmcH6S17e5gwuAJ4B7gfe3OpuAO9r27rZPO/7F6n3JYjdwWVvNtAZYC3x5iHZJkoY0zJzD/UluBx4CjgJfofep/k5gV5LfaWXb2ynbgT9NMgkcobdCiap6vK10eqI9z1WuVNJC4CojLWZD3T6jqrYCW48rfpopVhtV1d8BH5jmea4DrhumLZKkueM3pCVJHYaDJKnDcJAkdRgOkqQOw0GS1GE4SJI6DAdJUofhIEnqMBwkSR2GgySpw3CQJHUMdW+lhcrbKEvSq7PnIEnqWJI9B0laSEZxe3h7DpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdQ4VDkmVJbk/ytST7k/yLJGck2ZPkqfbz9FY3SW5MMpnkkSTn9j3Pplb/qSSbhr0oSdJwhu05/D7wF1X1T4B/BuwHtgD3VNVa4J62D3AxsLY9NgM3ASQ5A9gKnAesB7YeCxRJ0mgMHA5J3gT8HLAdoKr+vqpeADYCO1u1ncClbXsjcHP17AWWJTkbuAjYU1VHqup5YA+wYdB2SZKGN0zPYQ1wGPifSb6S5E+SvAE4q6qebXW+BZzVtlcAB/rOP9jKpivvSLI5yb4k+w4fPjxE0yVJr2aYcDgVOBe4qareCfw/fjyEBEBVFVBDvMYrVNW2qpqoqonly5fP1dNKko4zTDgcBA5W1f1t/3Z6YfHtNlxE+/lcO34IWNV3/spWNl25JGlEBg6HqvoWcCDJ21rRBcATwG7g2IqjTcAdbXs3cEVbtXQ+8GIbfrobuDDJ6W0i+sJWJkkakWHvyvrvgFuSnAY8DXyIXuDcluRK4Bngg63uXcAlwCTwUqtLVR1Jci3wQKt3TVUdGbJdkqQhDBUOVfUwMDHFoQumqFvAVdM8zw5gxzBtkSTNHb8hLUnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdhoMkqcNwkCR1GA6SpA7DQZLUYThIkjoMB0lSh+EgSeowHCRJHYaDJKlj6HBIckqSryT5321/TZL7k0wm+UyS01r5a9v+ZDu+uu85rm7lTya5aNg2SZKGMxc9h48A+/v2rwduqKq3As8DV7byK4HnW/kNrR5J1gGXAW8HNgB/lOSUOWiXJGlAQ4VDkpXAe4E/afsB3gPc3qrsBC5t2xvbPu34Ba3+RmBXVf2gqr4OTALrh2mXJGk4w/Ycfg/4KPCjtv9m4IWqOtr2DwIr2vYK4ABAO/5iq/9y+RTnSJJGYOBwSPI+4LmqenAO2zPTa25Osi/JvsOHD5+sl5WkJWeYnsO7gF9I8g1gF73hpN8HliU5tdVZCRxq24eAVQDt+JuA7/aXT3HOK1TVtqqaqKqJ5cuXD9F0SdKrGTgcqurqqlpZVavpTSh/sap+GbgXeH+rtgm4o23vbvu041+sqmrll7XVTGuAtcCXB22XJGl4p85c5YR9DNiV5HeArwDbW/l24E+TTAJH6AUKVfV4ktuAJ4CjwFVV9cN5aJckaZbmJByq6j7gvrb9NFOsNqqqvwM+MM351wHXzUVbJEnD8xvSkqSO+RhWkpac1VvufHn7G5987whbIs0New6SpA57DtIc6+9FSAuVPQdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdhoMkqcNwkCR1GA6SpA7DQZLUYThIkjoGDockq5Lcm+SJJI8n+UgrPyPJniRPtZ+nt/IkuTHJZJJHkpzb91ybWv2nkmwa/rIkScMYpudwFPjtqloHnA9clWQdsAW4p6rWAve0fYCLgbXtsRm4CXphAmwFzgPWA1uPBYokaTQGDoeqeraqHmrbfwPsB1YAG4GdrdpO4NK2vRG4uXr2AsuSnA1cBOypqiNV9TywB9gwaLskScObkzmHJKuBdwL3A2dV1bPt0LeAs9r2CuBA32kHW9l05VO9zuYk+5LsO3z48Fw0XZI0haHDIclPAZ8Ffquqvtd/rKoKqGFfo+/5tlXVRFVNLF++fK6eVpJ0nKHCIclr6AXDLVX1uVb87TZcRPv5XCs/BKzqO31lK5uuXJI0IsOsVgqwHdhfVZ/qO7QbOLbiaBNwR1/5FW3V0vnAi2346W7gwiSnt4noC1uZJGlETh3i3HcB/xZ4NMnDrezjwCeB25JcCTwDfLAduwu4BJgEXgI+BFBVR5JcCzzQ6l1TVUeGaJckaUgDh0NV/RWQaQ5fMEX9Aq6a5rl2ADsGbYskaW75DWlJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdhoMkqcNwkCR1GA6SpA7DQZLUYThIkjoMB0lSh+EgSeowHCRJHWMTDkk2JHkyyWSSLaNujyQtZWMRDklOAf4QuBhYB1yeZN1oWyVJS9dYhAOwHpisqqer6u+BXcDGEbdJkpascQmHFcCBvv2DrUySNAKnjroBJyLJZmBz2/1+kidP8CnOBL4zt60aqcV0PV7LeFpM1wKL4Hpy/cubg17LP5xNpXEJh0PAqr79la3sFapqG7Bt0BdJsq+qJgY9f9wspuvxWsbTYroWWFzXM9/XMi7DSg8Aa5OsSXIacBmwe8RtkqQlayx6DlV1NMmHgbuBU4AdVfX4iJslSUvWWIQDQFXdBdw1zy8z8JDUmFpM1+O1jKfFdC2wuK5nXq8lVTWfzy9JWoDGZc5BkjRGllw4JLk2ySNJHk7yhSRvGXWbBpXkd5N8rV3PnydZNuo2DSPJB5I8nuRHSRbkipLFchuYJDuSPJfksVG3ZVhJViW5N8kT7ffrI6Nu06CS/GSSLyf5aruW/zJvr7XUhpWSvLGqvte2fxNYV1W/PuJmDSTJhcAX24T+9QBV9bERN2tgSf4p8CPgfwD/sar2jbhJJ6TdBuavgX9N74ucDwCXV9UTI23YAJL8HPB94Oaqeseo2zOMJGcDZ1fVQ0l+GngQuHSB/rsEeENVfT/Ja4C/Aj5SVXvn+rWWXM/hWDA0bwAWbDpW1Req6mjb3Uvv+yELVlXtr6oT/WLjOFk0t4Gpqi8BR0bdjrlQVc9W1UNt+2+A/SzQOzBUz/fb7mvaY17ew5ZcOAAkuS7JAeCXgf806vbMkV8FPj/qRixx3gZmzCVZDbwTuH+0LRlcklOSPAw8B+ypqnm5lkUZDkn+MsljUzw2AlTVJ6pqFXAL8OHRtvbVzXQtrc4ngKP0rmeszeZ6pPmQ5KeAzwK/ddwIwoJSVT+sqn9Ob6RgfZJ5GfYbm+85zKWq+vlZVr2F3ncrts5jc4Yy07Uk+RXgfcAFtQAmkE7g32YhmtVtYHTytfH5zwK3VNXnRt2euVBVLyS5F9gAzPnCgUXZc3g1Sdb27W4EvjaqtgwryQbgo8AvVNVLo26PvA3MOGqTuNuB/VX1qVG3ZxhJlh9blZjkdfQWP8zLe9hSXK30WeBt9FbFPAP8elUtyE93SSaB1wLfbUV7F+rKK4Akvwj8AbAceAF4uKouGm2rTkySS4Df48e3gbluxE0aSJJbgXfTu/Pnt4GtVbV9pI0aUJJ/Cfwf4FF6f/cAH293ZVhQkvwMsJPe79dPALdV1TXz8lpLLRwkSTNbcsNKkqSZGQ6SpA7DQZLUYThIkjoMB0lSh+EgSeowHCRJHYaDJKnj/wMMRFFNlfh+0AAAAABJRU5ErkJggg==\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": "iVBORw0KGgoAAAANSUhEUgAAAY0AAAD8CAYAAACLrvgBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAFkVJREFUeJzt3X+w3XWd3/Hnq4kgrFVArhQT2tA1uy0yuyummNbWscSFAI6hHXVg2pK1jJlW3Op2Wzfsziy7KjPYbpeVGWWGldTguiDD2pLRuDFFdm1nChIE+anLFVCSgrlrEHbr+AN994/ziT3e3Nz74Z6be26S52PmzP1+39/P5/v9fMghr/v9cU5SVUiS1ONvjHsAkqTDh6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKnb8nEPYKGdfPLJtWrVqnEPQ5IOK/fcc89fVtXEXO2OuNBYtWoVu3btGvcwJOmwkuQbPe28PCVJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqdsR9InwUqzZ/9ifLT1x94RhHIklLk2cakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKnbnKGRZEuSvUkenGHbryepJCe39SS5NslkkvuTnDXUdmOSR9tr41D9tUkeaH2uTZJWPynJztZ+Z5ITF2bKkqT56jnT+DiwfnoxyWnAucA3h8rnA6vbaxNwXWt7EnAl8DrgbODKoRC4DnjnUL/9x9oM3F5Vq4Hb27okaYzmDI2q+iKwb4ZN1wDvA2qotgG4sQbuBE5IcipwHrCzqvZV1TPATmB92/bSqrqzqgq4EbhoaF9b2/LWobokaUzmdU8jyQZgT1V9ZdqmFcCTQ+u7W222+u4Z6gCnVNVTbflp4JRZxrMpya4ku6ampl7odCRJnV5waCQ5HvhN4LcXfjgza2chNcv266tqTVWtmZiYWKxhSdJRZz5nGj8LnA58JckTwErgy0n+FrAHOG2o7cpWm62+coY6wLfa5Svaz73zGKskaQG94NCoqgeq6hVVtaqqVjG4pHRWVT0NbAMubU9RrQWebZeYdgDnJjmx3QA/F9jRtj2XZG17aupS4LZ2qG3A/qesNg7VJUlj0vPI7U3A/wZ+PsnuJJfN0nw78BgwCfwh8C6AqtoHfAC4u73e32q0Nh9rfb4OfK7VrwZ+OcmjwJvauiRpjOb8avSqumSO7auGlgu4/CDttgBbZqjvAs6cof5tYN1c45MkLR4/ES5J6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuc4ZGki1J9iZ5cKj2n5N8Ncn9Sf5bkhOGtl2RZDLJ15KcN1Rf32qTSTYP1U9PclerfyrJMa1+bFufbNtXLdSkJUnz03Om8XFg/bTaTuDMqvoF4C+AKwCSnAFcDLy69flokmVJlgEfAc4HzgAuaW0BPgRcU1WvAp4BLmv1y4BnWv2a1k6SNEZzhkZVfRHYN632+ap6vq3eCaxsyxuAm6vq+1X1ODAJnN1ek1X1WFX9ALgZ2JAkwDnAra3/VuCioX1tbcu3Autae0nSmCzEPY1/DXyuLa8AnhzatrvVDlZ/OfCdoQDaX/+pfbXtz7b2B0iyKcmuJLumpqZGnpAkaWYjhUaS3wKeBz65MMOZn6q6vqrWVNWaiYmJcQ5Fko5oy+fbMcmvAG8G1lVVtfIe4LShZitbjYPUvw2ckGR5O5sYbr9/X7uTLAde1tpLksZkXmcaSdYD7wPeUlXfHdq0Dbi4Pfl0OrAa+BJwN7C6PSl1DIOb5dta2NwBvLX13wjcNrSvjW35rcAXhsJJkjQGc55pJLkJeCNwcpLdwJUMnpY6FtjZ7k3fWVX/pqoeSnIL8DCDy1aXV9WP2n7eDewAlgFbquqhdojfAG5O8kHgXuCGVr8B+ESSSQY34i9egPlKkkYwZ2hU1SUzlG+Yoba//VXAVTPUtwPbZ6g/xuDpqun17wFvm2t8kqTF4yfCJUndDA1JUjdDQ5LUzdCQJHWb9+c0JC28VZs/+1PrT1x94ZhGIs3MMw1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdTM0JEndDA1JUjdDQ5LUbc7QSLIlyd4kDw7VTkqyM8mj7eeJrZ4k1yaZTHJ/krOG+mxs7R9NsnGo/tokD7Q+1ybJbMeQJI1Pz5nGx4H102qbgdurajVwe1sHOB9Y3V6bgOtgEADAlcDrgLOBK4dC4DrgnUP91s9xDEnSmMwZGlX1RWDftPIGYGtb3gpcNFS/sQbuBE5IcipwHrCzqvZV1TPATmB92/bSqrqzqgq4cdq+ZjqGJGlM5ntP45SqeqotPw2c0pZXAE8OtdvdarPVd89Qn+0YB0iyKcmuJLumpqbmMR1JUo+Rb4S3M4RagLHM+xhVdX1VramqNRMTE4dyKJJ0VJtvaHyrXVqi/dzb6nuA04barWy12eorZ6jPdgxJ0pjMNzS2AfufgNoI3DZUv7Q9RbUWeLZdYtoBnJvkxHYD/FxgR9v2XJK17ampS6fta6ZjSJLGZPlcDZLcBLwRODnJbgZPQV0N3JLkMuAbwNtb8+3ABcAk8F3gHQBVtS/JB4C7W7v3V9X+m+vvYvCE1nHA59qLWY4hSRqTOUOjqi45yKZ1M7Qt4PKD7GcLsGWG+i7gzBnq357pGJKk8fET4ZKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSeo2Umgk+bUkDyV5MMlNSV6c5PQkdyWZTPKpJMe0tse29cm2fdXQfq5o9a8lOW+ovr7VJpNsHmWskqTRzTs0kqwA/h2wpqrOBJYBFwMfAq6pqlcBzwCXtS6XAc+0+jWtHUnOaP1eDawHPppkWZJlwEeA84EzgEtaW0nSmIx6eWo5cFyS5cDxwFPAOcCtbftW4KK2vKGt07avS5JWv7mqvl9VjwOTwNntNVlVj1XVD4CbW1tJ0pjMOzSqag/we8A3GYTFs8A9wHeq6vnWbDewoi2vAJ5sfZ9v7V8+XJ/W52B1SdKYjHJ56kQGv/mfDrwS+BkGl5cWXZJNSXYl2TU1NTWOIUjSUWGUy1NvAh6vqqmq+iHwaeD1wAntchXASmBPW94DnAbQtr8M+PZwfVqfg9UPUFXXV9WaqlozMTExwpQkSbMZJTS+CaxNcny7N7EOeBi4A3hra7MRuK0tb2vrtO1fqKpq9Yvb01WnA6uBLwF3A6vb01jHMLhZvm2E8UqSRrR87iYzq6q7ktwKfBl4HrgXuB74LHBzkg+22g2tyw3AJ5JMAvsYhABV9VCSWxgEzvPA5VX1I4Ak7wZ2MHgya0tVPTTf8UqSRjfv0ACoqiuBK6eVH2Pw5NP0tt8D3naQ/VwFXDVDfTuwfZQxSpIWjp8IlyR1MzQkSd0MDUlSN0NDktTN0JAkdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdRvpX+6TdGit2vzZnyw/cfWFYxyJNOCZhiSp20ihkeSEJLcm+WqSR5L8wyQnJdmZ5NH288TWNkmuTTKZ5P4kZw3tZ2Nr/2iSjUP11yZ5oPW5NklGGa8kaTSjnml8GPjTqvp7wC8CjwCbgdurajVwe1sHOB9Y3V6bgOsAkpwEXAm8DjgbuHJ/0LQ27xzqt37E8UqSRjDv0EjyMuANwA0AVfWDqvoOsAHY2pptBS5qyxuAG2vgTuCEJKcC5wE7q2pfVT0D7ATWt20vrao7q6qAG4f2JUkag1FuhJ8OTAH/NckvAvcA7wFOqaqnWpungVPa8grgyaH+u1tttvruGeqLwhuQknSgUS5PLQfOAq6rqtcA/5f/fykKgHaGUCMco0uSTUl2Jdk1NTV1qA8nSUetUUJjN7C7qu5q67cyCJFvtUtLtJ972/Y9wGlD/Ve22mz1lTPUD1BV11fVmqpaMzExMcKUJEmzmXdoVNXTwJNJfr6V1gEPA9uA/U9AbQRua8vbgEvbU1RrgWfbZawdwLlJTmw3wM8FdrRtzyVZ256aunRoX5KkMRj1w32/CnwyyTHAY8A7GATRLUkuA74BvL213Q5cAEwC321tqap9ST4A3N3avb+q9rXldwEfB44DPtdekqQxGSk0quo+YM0Mm9bN0LaAyw+yny3Alhnqu4AzRxmjJGnh+IlwSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVK3Ub8a/ajgP/0qSQOGhnSY8JcXLQVenpIkdTM0JEndDA1JUjdDQ5LUbeTQSLIsyb1JPtPWT09yV5LJJJ9KckyrH9vWJ9v2VUP7uKLVv5bkvKH6+labTLJ51LFKkkazEGca7wEeGVr/EHBNVb0KeAa4rNUvA55p9WtaO5KcAVwMvBpYD3y0BdEy4CPA+cAZwCWtrSRpTEYKjSQrgQuBj7X1AOcAt7YmW4GL2vKGtk7bvq613wDcXFXfr6rHgUng7PaarKrHquoHwM2trSRpTEY90/gD4H3Aj9v6y4HvVNXzbX03sKItrwCeBGjbn23tf1Kf1udgdUnSmMw7NJK8GdhbVfcs4HjmO5ZNSXYl2TU1NTXu4UjSEWuUM43XA29J8gSDS0fnAB8GTkiy/5PmK4E9bXkPcBpA2/4y4NvD9Wl9DlY/QFVdX1VrqmrNxMTECFOSJM1m3qFRVVdU1cqqWsXgRvYXqupfAHcAb23NNgK3teVtbZ22/QtVVa1+cXu66nRgNfAl4G5gdXsa65h2jG3zHa8kaXSH4runfgO4OckHgXuBG1r9BuATSSaBfQxCgKp6KMktwMPA88DlVfUjgCTvBnYAy4AtVfXQIRivJKnTgoRGVf0Z8Gdt+TEGTz5Nb/M94G0H6X8VcNUM9e3A9oUYoyRpdH4iXJLUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1OxT/RrikQ2zV5s/+ZPmJqy8c40h0tJn3mUaS05LckeThJA8leU+rn5RkZ5JH288TWz1Jrk0ymeT+JGcN7Wtja/9oko1D9dcmeaD1uTZJRpmsJGk0o1yeeh749ao6A1gLXJ7kDGAzcHtVrQZub+sA5wOr22sTcB0MQga4EngdcDZw5f6gaW3eOdRv/QjjlSSNaN6hUVVPVdWX2/JfAY8AK4ANwNbWbCtwUVveANxYA3cCJyQ5FTgP2FlV+6rqGWAnsL5te2lV3VlVBdw4tC9J0hgsyI3wJKuA1wB3AadU1VNt09PAKW15BfDkULfdrTZbffcMdUnSmIwcGkleAvwJ8N6qem54WztDqFGP0TGGTUl2Jdk1NTV1qA8nSUetkUIjyYsYBMYnq+rTrfytdmmJ9nNvq+8BThvqvrLVZquvnKF+gKq6vqrWVNWaiYmJUaYkSZrFKE9PBbgBeKSqfn9o0zZg/xNQG4HbhuqXtqeo1gLPtstYO4Bzk5zYboCfC+xo255LsrYd69KhfUmSxmCUz2m8HvhXwANJ7mu13wSuBm5JchnwDeDtbdt24AJgEvgu8A6AqtqX5APA3a3d+6tqX1t+F/Bx4Djgc+0lSRqTeYdGVf0v4GCfm1g3Q/sCLj/IvrYAW2ao7wLOnO8YJUkLy68RkSR1MzQkSd0MDUlSN7+w8AXyi+IkHc0805AkdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN797SpKWuKX0nXeeaUiSunmmIUlL0PDZxVJiaEjSYWTcl6qWfGgkWQ98GFgGfKyqrh7zkCRpSZh+NrIYIbKk72kkWQZ8BDgfOAO4JMkZ4x2VJB29lnRoAGcDk1X1WFX9ALgZ2DDmMUnSUWuph8YK4Mmh9d2tJkkagyV/T6NHkk3Aprb610m+Nkvzk4G/XJDjfmgh9jIvCzaHMXIOC2TE9+GSmMOInEMz4nvh7/Q0WuqhsQc4bWh9Zav9lKq6Hri+Z4dJdlXVmoUZ3ng4h6XBOSwNzmFxLfXLU3cDq5OcnuQY4GJg25jHJElHrSV9plFVzyd5N7CDwSO3W6rqoTEPS5KOWks6NACqajuwfQF32XUZa4lzDkuDc1ganMMiSlWNewySpMPEUr+nIUlaQo6Y0EiyJcneJA8O1X4nyZ4k97XXBQfp+2tJHkryYJKbkrx48Ub+U+MYZQ7vaeN/KMl7F2/UB4zjgDm0+q8m+Wob3386SN/1Sb6WZDLJ5sUZ8YzjGGUOM/ZdbPOdQ5LTktyR5OHW5j2LN+oDxjLfObw4yZeSfKW1+d3FG/UBY5n3e6m1W5bk3iSfOfSj7VRVR8QLeANwFvDgUO13gP8wR78VwOPAcW39FuBXDrM5nAk8CBzP4D7V/wBetYTm8E/bmI5t66+Yod8y4OvA3wWOAb4CnHE4zeFgfQ+nOQCnAme15b8J/MXh9ucABHhJW34RcBew9nCaw1Dbfw/8MfCZcb6fhl9HzJlGVX0R2DfP7suB45IsZ/AX7/9ZsIG9ACPM4e8Dd1XVd6vqeeDPgX++oIPrdJA5/Fvg6qr6fmuzd4auS+YrY0aYw6jvwwUz3zlU1VNV9eW2/FfAI4zpWxhGmENV1V+31Re111hu3o7yXkqyErgQ+NghHeQLdMSExizeneT+dpp44vSNVbUH+D3gm8BTwLNV9fnFHuQcZp0Dg7OMf5Lk5UmOBy7gpz8UOW4/x2B8dyX58yT/YIY2S/0rY3rmsNS9oDkkWQW8hsFv6ktF1xzaZZ37gL3Azqo67OYA/AHwPuDHize0uR3poXEd8LPALzEIhP8yvUH7S3gDcDrwSuBnkvzLxRzkHOacQ1U9AnwI+Dzwp8B9wI8WcYxzWQ6cBKwF/iNwS5KMd0gv2FE1hyQvAf4EeG9VPbd4Q5xT1xyq6kdV9UsMvkXi7CRnLu4wZzXnHJK8GdhbVfeMYXyzOqJDo6q+1d48Pwb+kMElkOneBDxeVVNV9UPg08A/WsxxzqZzDlTVDVX12qp6A/AMg2vRS8Vu4NPtssGXGPzmdPK0Nl1fGTNGPXNY6rrmkORFDALjk1X16UUe41xe0J9DVX0HuANYv0jj69Ezh9cDb0nyBINLteck+aPFHebMjujQSHLq0Oo/Y3AZZ7pvAmuTHN/Sfh2D67hLQuccSPKK9vNvM7if8ceHfnTd/juDm38k+TkGN7qnfznbUv/KmJ45LHVzzqH9P3AD8EhV/f6ij3BuPXOYSHJCWz4O+GXgq4s8ztnMOYequqKqVlbVKgb/L3yhqpbGFZBx34lfqBdwE4PLNz9kkOSXAZ8AHgDuZ/AX0Kmt7SuB7UN9f5fBm+rB1ufYw3AO/xN4mMFTR+uW2J/DMcAftf++XwbOOcgcLmBwhvR14LcO0zkc0PdwmgPwjxncNL6fwWXO+4ALDrM5/AJwb5vDg8BvH47vpaF9vJEl9PSUnwiXJHU7oi9PSZIWlqEhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkbv8PT2uKFzs5bCQAAAAASUVORK5CYII=\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": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAD8CAYAAACcjGjIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAFm1JREFUeJzt3X+w3XV95/Hna5NFxVUDkrI2CZtsje6AU7f0FtN12lHpQrCO4Q/rwLZLajPNTIu223Wrwc6UHZUdbJ1SmSqdrKSEjkukrC2ZFcUM1To7Iz+CKBCQchcq3CyYqwHcXafY2Pf+cT7g8X7vJck5595zb+7zMXPmfs/7+/me8/kavK/7+Xw/33NSVUiS1O+fjLsDkqTFx3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdhoMkqWPluDswqNNOO63Wr18/7m5I0pJy9913f7uqVh+t3ZINh/Xr17N///5xd0OSlpQk3zyWdk4rSZI6DAdJUofhIEnqMBwkSR1HDYcku5IcSnL/jPp7knwjyYEkf9BXvyzJZJKHkpzfV9/capNJdvTVNyS5o9U/neSkUZ2cJGkwxzJyuA7Y3F9I8mZgC/D6qjoL+GirnwlcBJzVjvlEkhVJVgAfBy4AzgQubm0BPgJcVVWvBp4Ctg17UpKk4Rw1HKrqy8DhGeXfAK6sqmdbm0OtvgXYU1XPVtWjwCRwTntMVtUjVfV9YA+wJUmAtwA3teN3AxcOeU6SpCENes3hNcDPtemgv0nyM62+Bni8r91Uq81VfyXwdFUdmVGfVZLtSfYn2T89PT1g1yVJRzNoOKwETgU2Ab8L3NhGAfOqqnZW1URVTaxefdQb/CRJAxr0Dukp4DNVVcCdSf4ROA04CKzra7e21Zij/h1gVZKVbfTQ335BrN/x2ee3/+7KX1zIt5akRWvQkcNfAW8GSPIa4CTg28Be4KIkL0qyAdgI3AncBWxsK5NOonfRem8Lly8C72ivuxW4edCTkSSNxlFHDkluAN4EnJZkCrgc2AXsastbvw9sbb/oDyS5EXgAOAJcWlU/aK/zbuBWYAWwq6oOtLd4P7AnyYeBe4BrR3h+kqQBHDUcquriOXb9yhztrwCumKV+C3DLLPVH6K1mkiQtEkv2U1nng9cfJKnHj8+QJHUYDpKkDsNBktRhOEiSOrwgPQcvTktazhw5SJI6DAdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOrxD+hh4t7Sk5caRgySp46jhkGRXkkPtK0Fn7ntvkkpyWnueJFcnmUxyb5Kz+9puTfJwe2ztq/90kvvaMVcnyahOTpI0mGMZOVwHbJ5ZTLIOOA94rK98AbCxPbYD17S2p9L77uk30PtK0MuTnNKOuQb49b7jOu8lSVpYRw2HqvoycHiWXVcB7wOqr7YFuL56bgdWJXkVcD6wr6oOV9VTwD5gc9v38qq6vaoKuB64cLhTkiQNa6BrDkm2AAer6uszdq0BHu97PtVqL1SfmqU+1/tuT7I/yf7p6elBui5JOgbHHQ5JTgY+APz+6LvzwqpqZ1VNVNXE6tWrF/rtJWnZGGQp608AG4Cvt2vHa4GvJjkHOAis62u7ttUOAm+aUf9Sq6+dpf2i5bJWScvBcY8cquq+qvqxqlpfVevpTQWdXVVPAnuBS9qqpU3AM1X1BHArcF6SU9qF6POAW9u+7ybZ1FYpXQLcPKJzkyQN6FiWst4AfAV4bZKpJNteoPktwCPAJPBfgd8EqKrDwIeAu9rjg61Ga/PJdsz/Aj432KlIkkblqNNKVXXxUfav79su4NI52u0Cds1S3w+87mj9kCQtHO+QliR1GA6SpA7DQZLU4aeyDsFlrZJOVI4cJEkdhoMkqcNwkCR1GA6SpA7DQZLUYThIkjoMB0lSh+EgSeowHCRJHd4hPSLeLS3pROLIQZLUYThIkjoMB0lSx7F8TeiuJIeS3N9X+8Mk30hyb5K/TLKqb99lSSaTPJTk/L765labTLKjr74hyR2t/ukkJ43yBCVJx+9YRg7XAZtn1PYBr6uqnwT+FrgMIMmZwEXAWe2YTyRZkWQF8HHgAuBM4OLWFuAjwFVV9WrgKeCFvqN6JNbv+OzzD0lS17F8h/SXk6yfUftC39PbgXe07S3Anqp6Fng0ySRwTts3WVWPACTZA2xJ8iDwFuDftTa7gf8MXDPIySwWrlyStNSN4prDrwGfa9trgMf79k212lz1VwJPV9WRGfVZJdmeZH+S/dPT0yPouiRpNkOFQ5LfA44AnxpNd15YVe2sqomqmli9evVCvKUkLUsD3wSX5FeBtwHnVlW18kFgXV+zta3GHPXvAKuSrGyjh/72JwSnmCQtRQONHJJsBt4HvL2qvte3ay9wUZIXJdkAbATuBO4CNraVSSfRu2i9t4XKF/nhNYutwM2DnYokaVSOZSnrDcBXgNcmmUqyDfgT4GXAviRfS/KnAFV1ALgReAD4PHBpVf2gjQreDdwKPAjc2NoCvB/4j+3i9SuBa0d6hpKk43Ysq5UunqU85y/wqroCuGKW+i3ALbPUH+GHK5okSYuAd0hLkjoMB0lShx/ZLTWuLJN+yJGDJKnDkcMC8i9TSUuFIwdJUofhIEnqMBwkSR2GgySpwwvSY+LF6cXNfx8td44cJEkdhoMkqcNpJekonGLScuTIQZLUYThIkjqcVloEnLZYOvr/rfr576YTjSMHSVLHsXxN6K4kh5Lc31c7Ncm+JA+3n6e0epJcnWQyyb1Jzu47Zmtr/3CSrX31n05yXzvm6iQZ9UlKko7PsYwcrgM2z6jtAG6rqo3Abe05wAXAxvbYDlwDvTABLgfeQO8rQS9/LlBam1/vO27me0mSFthRw6GqvgwcnlHeAuxu27uBC/vq11fP7cCqJK8Czgf2VdXhqnoK2AdsbvteXlW3V1UB1/e9liRpTAa95nB6VT3Rtp8ETm/ba4DH+9pNtdoL1admqc8qyfYk+5Psn56eHrDrkqSjGXq1UlVVkhpFZ47hvXYCOwEmJiYW5D0X2szVMK6CWRpccaYTzaAjh2+1KSHaz0OtfhBY19dubau9UH3tLHVJ0hgNGg57gedWHG0Fbu6rX9JWLW0CnmnTT7cC5yU5pV2IPg+4te37bpJNbZXSJX2vJXp/kT73kKSFctRppSQ3AG8CTksyRW/V0ZXAjUm2Ad8E3tma3wK8FZgEvge8C6CqDif5EHBXa/fBqnruIvdv0lsR9RLgc+0hSRqj9BYJLT0TExO1f//+gY49Ef4Kd157NBbyvwX/zbQYJLm7qiaO1s47pCVJHYaDJKnDcJAkdfiprNIC8V4ILSWGwxLlLxpJ88lpJUlShyOHE4CjCEmjZjhIY2Cga7EzHKQxMyi0GHnNQZLUYThIkjqcVjrBOEUhaRQcOUiSOhw5nMAcRUgalCMHSVKH4SBJ6jAcJEkdQ4VDkt9JciDJ/UluSPLiJBuS3JFkMsmnk5zU2r6oPZ9s+9f3vc5lrf5QkvOHOyXNxu+ilnQ8Bg6HJGuA3wImqup1wArgIuAjwFVV9WrgKWBbO2Qb8FSrX9XakeTMdtxZwGbgE0lWDNovSdLwhp1WWgm8JMlK4GTgCeAtwE1t/27gwra9pT2n7T83SVp9T1U9W1WPApPAOUP2S5I0hIHDoaoOAh8FHqMXCs8AdwNPV9WR1mwKWNO21wCPt2OPtPav7K/PcowkaQwGvs8hySn0/urfADwN/AW9aaF5k2Q7sB3gjDPOmM+3OqF5/4OkoxlmWukXgEerarqq/gH4DPBGYFWbZgJYCxxs2weBdQBt/yuA7/TXZznmR1TVzqqaqKqJ1atXD9F1SdILGSYcHgM2JTm5XTs4F3gA+CLwjtZmK3Bz297bntP2/3VVVatf1FYzbQA2AncO0S9J0pAGnlaqqjuS3AR8FTgC3APsBD4L7Eny4Va7th1yLfDnSSaBw/RWKFFVB5LcSC9YjgCXVtUPBu2XJGl4Q322UlVdDlw+o/wIs6w2qqq/B35pjte5ArhimL5oMF5/kDQb75CWJHUYDpKkDj+yW89ziknScxw5SJI6HDloVo4ipOXNkYMkqcORg7SIOGLTYmE46Kj8hSUtP04rSZI6DAdJUofTSjouTjFJy4MjB0lSh+EgSepwWkkDc4pJOnE5cpAkdThy0Eg4ipBOLI4cJEkdQ4VDklVJbkryjSQPJvnZJKcm2Zfk4fbzlNY2Sa5OMpnk3iRn973O1tb+4SRb535HSdJCGHZa6WPA56vqHUlOAk4GPgDcVlVXJtkB7ADeD1wAbGyPNwDXAG9Iciq9rxqdAAq4O8neqnpqyL5pTJxikpa+gUcOSV4B/DxwLUBVfb+qnga2ALtbs93AhW17C3B99dwOrEryKuB8YF9VHW6BsA/YPGi/JEnDG2ZaaQMwDfxZknuSfDLJS4HTq+qJ1uZJ4PS2vQZ4vO/4qVabqy5JGpNhppVWAmcD76mqO5J8jN4U0vOqqpLUMB3sl2Q7sB3gjDPOGNXLah45xSQtTcOMHKaAqaq6oz2/iV5YfKtNF9F+Hmr7DwLr+o5f22pz1TuqamdVTVTVxOrVq4fousZh/Y7PPv+QtLgNHA5V9STweJLXttK5wAPAXuC5FUdbgZvb9l7gkrZqaRPwTJt+uhU4L8kpbWXTea0mSRqTYVcrvQf4VFup9AjwLnqBc2OSbcA3gXe2trcAbwUmge+1tlTV4SQfAu5q7T5YVYeH7JckaQhDhUNVfY3eEtSZzp2lbQGXzvE6u4Bdw/RFkjQ6fnyGxmKu6w5etJYWBz8+Q5LU4chBi4ojih9yGbDGyZGDJKnDkYOWJP+qluaX4SAtAU63aaEZDloSvKtaWliGg7SEvVBoOqrQMAwHLXlef5BGz3CQlhnDVMfCcNAJa7n/Elzu56/hGA46oXjhenb+76Lj5U1wkqQORw7SMub9E5qL4aBlwWkV6fgYDpI6vJgtrzlIkjqGDockK5Lck+R/tOcbktyRZDLJp9tXiJLkRe35ZNu/vu81Lmv1h5KcP2yfJI3O+h2fff6h5WMUI4ffBh7se/4R4KqqejXwFLCt1bcBT7X6Va0dSc4ELgLOAjYDn0iyYgT9kjRiBsXyMVQ4JFkL/CLwyfY8wFuAm1qT3cCFbXtLe07bf25rvwXYU1XPVtWjwCRwzjD9kjT/DIoT27AXpP8YeB/wsvb8lcDTVXWkPZ8C1rTtNcDjAFV1JMkzrf0a4Pa+1+w/RtIS4AXsE8/AI4ckbwMOVdXdI+zP0d5ze5L9SfZPT08v1NtK0rIzzMjhjcDbk7wVeDHwcuBjwKokK9voYS1wsLU/CKwDppKsBF4BfKev/pz+Y35EVe0EdgJMTEzUEH2XNE/mGkV4w93SMnA4VNVlwGUASd4E/Keq+uUkfwG8A9gDbAVubofsbc+/0vb/dVVVkr3Af0vyR8CPAxuBOwftl6TFw+sRS9d83AT3fmBPkg8D9wDXtvq1wJ8nmQQO01uhRFUdSHIj8ABwBLi0qn4wD/2SJB2jkYRDVX0J+FLbfoRZVhtV1d8DvzTH8VcAV4yiL5JOPF7wXnh+fIaksTreqSeDYmH48RmSpA5HDpKWLEcR88dwkLSsGCjHxnCQdEJw2exoGQ6Sli1HEXMzHCRpBkPDcJAkYO5pqeUaFIaDJB2j5RQUhoMkDelE/FBBw0GSBnAsq6OW8kjDcJCkBXAsH2W+mALEcJCkBbYULn772UqSpA5HDpK0CI17FOHIQZLUYThIkjoGDock65J8MckDSQ4k+e1WPzXJviQPt5+ntHqSXJ1kMsm9Sc7ue62trf3DSbYOf1qSpGEMM3I4Ary3qs4ENgGXJjkT2AHcVlUbgdvac4ALgI3tsR24BnphAlwOvIHe14te/lygSJLGY+BwqKonquqrbfv/AA8Ca4AtwO7WbDdwYdveAlxfPbcDq5K8Cjgf2FdVh6vqKWAfsHnQfkmShjeSaw5J1gM/BdwBnF5VT7RdTwKnt+01wON9h0212lx1SdKYDB0OSf4Z8N+B/1BV3+3fV1UF1LDv0fde25PsT7J/enp6VC8rSZphqHBI8k/pBcOnquozrfytNl1E+3mo1Q8C6/oOX9tqc9U7qmpnVU1U1cTq1auH6bok6QUMs1opwLXAg1X1R3279gLPrTjaCtzcV7+krVraBDzTpp9uBc5Lckq7EH1eq0mSxmSYO6TfCPx74L4kX2u1DwBXAjcm2QZ8E3hn23cL8FZgEvge8C6Aqjqc5EPAXa3dB6vq8BD9kqQTyjjulh44HKrqfwKZY/e5s7Qv4NI5XmsXsGvQvkiSRss7pCVJHYaDJKnDcJAkdRgOkqQOw0GS1GE4SJI6DAdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdiyYckmxO8lCSySQ7xt0fSVrOFkU4JFkBfBy4ADgTuDjJmePtlSQtX4siHIBzgMmqeqSqvg/sAbaMuU+StGwtlnBYAzze93yq1SRJY7By3B04Hkm2A9vb0/+b5KFx9mfETgO+Pe5OjJjntDR4TkvDacC385GhX+dfHEujxRIOB4F1fc/XttqPqKqdwM6F6tRCSrK/qibG3Y9R8pyWBs9paVjoc1os00p3ARuTbEhyEnARsHfMfZKkZWtRjByq6kiSdwO3AiuAXVV1YMzdkqRla1GEA0BV3QLcMu5+jNGJOF3mOS0NntPSsKDnlKpayPeTJC0Bi+WagyRpETEcxizJqiQ3JflGkgeT/Oy4+zSsJL+T5ECS+5PckOTF4+7TIJLsSnIoyf19tVOT7EvycPt5yjj7eLzmOKc/bP/93ZvkL5OsGmcfj9ds59S3771JKslp4+jboOY6pyTvaf9WB5L8wXz2wXAYv48Bn6+qfwW8HnhwzP0ZSpI1wG8BE1X1OnoLDC4ab68Gdh2weUZtB3BbVW0EbmvPl5Lr6J7TPuB1VfWTwN8Cly10p4Z0Hd1zIsk64DzgsYXu0Ahcx4xzSvJmep8c8fqqOgv46Hx2wHAYoySvAH4euBagqr5fVU+Pt1cjsRJ4SZKVwMnA/x5zfwZSVV8GDs8obwF2t+3dwIUL2qkhzXZOVfWFqjrSnt5O7z6jJWOOfyeAq4D3AUvuwuoc5/QbwJVV9Wxrc2g++2A4jNcGYBr4syT3JPlkkpeOu1PDqKqD9P6ieQx4Animqr4w3l6N1OlV9UTbfhI4fZydmQe/Bnxu3J0YVpItwMGq+vq4+zJCrwF+LskdSf4myc/M55sZDuO1EjgbuKaqfgr4fyy9aYof0ebgt9ALvh8HXprkV8bbq/lRvaV+S+6v0rkk+T3gCPCpcfdlGElOBj4A/P64+zJiK4FTgU3A7wI3Jsl8vZnhMF5TwFRV3dGe30QvLJayXwAerarpqvoH4DPAvxlzn0bpW0leBdB+zuvQfqEk+VXgbcAv19Jf3/4T9P44+XqSv6M3TfbVJP98rL0a3hTwmeq5E/hHep+3NC8MhzGqqieBx5O8tpXOBR4YY5dG4TFgU5KT218157LEL7LPsBfY2ra3AjePsS8jkWQzvbn5t1fV98bdn2FV1X1V9WNVtb6q1tP7pXp2+//bUvZXwJsBkrwGOIl5/HBBw2H83gN8Ksm9wL8G/suY+zOUNgq6CfgqcB+9/8aW5N2qSW4AvgK8NslUkm3AlcC/TfIwvVHSlePs4/Ga45z+BHgZsC/J15L86Vg7eZzmOKclbY5z2gX8y7a8dQ+wdT5Hed4hLUnqcOQgSeowHCRJHYaDJKnDcJAkdRgOkqQOw0GS1GE4SJI6DAdJUsf/B4TLArTbjmCTAAAAAElFTkSuQmCC\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": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAD8CAYAAACcjGjIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAFqpJREFUeJzt3X+w3XV95/Hna8mi4lbDj5RifmzSGt0Bpm7xVtJ12lXpQrBOwx/WQn+QWqaZaVG7Xbsa7Myyo7KDrSMrU8XJSkroWCLL2pIZUMxSXWdn5EfwBxDQchcUbhZMNIC76wiNvveP8wke7vdecnPOzT33x/Mxc+Z+v+/v53vO+8uP+76fH9/zTVUhSVK/fzLqBCRJ84/FQZLUYXGQJHVYHCRJHRYHSVKHxUGS1GFxkCR1WBwkSR0WB0lSx7JRJzCoU045pdauXTvqNCRpQbnnnnu+W1UrjtRuwRaHtWvXsmfPnlGnIUkLSpJvz6Sdw0qSpA6LgySpw+IgSeqwOEiSOiwOkqQOi4MkqeOIxSHJ9iT7k9w/Kf7OJN9IsjfJn/fFL0synuSbSc7ri29ssfEkW/vi65Lc2eKfTnL8bF2cJGkwM+k5XAds7A8keSOwCXhNVZ0BfLjFTwcuBM5o53w8yXFJjgM+BpwPnA5c1NoCfAi4qqpeCTwJXDLsRUmShnPE4lBVXwIOTgr/IXBlVT3T2uxv8U3Azqp6pqoeAcaB17XXeFU9XFXPAjuBTUkCvAm4qZ2/A7hgyGuSJA1p0DukXwX8cpIrgB8Cf1pVdwMrgTv62k20GMBjk+JnAycDT1XVoSnaz4m1W295bvtbV/7aXH60JM1bgxaHZcBJwAbgF4Ebk/zsrGU1jSRbgC0Aa9asOdYfJ0lL1qCrlSaAz1TPXcCPgVOAfcDqvnarWmy6+PeA5UmWTYpPqaq2VdVYVY2tWHHE742SJA1o0OLwd8AbAZK8Cjge+C6wC7gwyYuSrAPWA3cBdwPr28qk4+lNWu+qqgK+ALy1ve9m4OZBL0aSNDuOOKyU5AbgDcApSSaAy4HtwPa2vPVZYHP7Rb83yY3AA8Ah4NKq+lF7n3cAtwHHAduram/7iPcCO5N8EPgqcO0sXp8kaQBHLA5VddE0h35nmvZXAFdMEb8VuHWK+MP0VjNJkuYJ75CWJHVYHCRJHQv2SXDHQv89D/28/0HSUmPPQZLUYXGQJHVYHCRJHRYHSVKHxUGS1GFxkCR1WBwkSR0WB0lSh8VBktRhcZAkdVgcJEkdFgdJUofFQZLUYXGQJHUcsTgk2Z5kf3sk6ORj705SSU5p+0lydZLxJPcmOauv7eYkD7XX5r74a5Pc1865Oklm6+Jmy9qttzz3kqSlYCY9h+uAjZODSVYD5wKP9oXPB9a31xbgmtb2JHrPnj6b3iNBL09yYjvnGuAP+s7rfJYkaW7N5BnSX0qydopDVwHvAW7ui20Crq+qAu5IsjzJacAbgN1VdRAgyW5gY5IvAi+rqjta/HrgAuCzg17Qsdbfe/AhQJIWq4HmHJJsAvZV1dcnHVoJPNa3P9FiLxSfmCIuSRqho35MaJITgPfRG1KaU0m20BuuYs2aNXP98ZK0ZAzSc/g5YB3w9STfAlYBX0nyM8A+YHVf21Ut9kLxVVPEp1RV26pqrKrGVqxYMUDqkqSZOOriUFX3VdVPV9XaqlpLbyjorKp6AtgFXNxWLW0Anq6qx4HbgHOTnNgmos8FbmvHvp9kQ1uldDHPn8OQJI3ATJay3gB8GXh1kokkl7xA81uBh4Fx4L8AfwTQJqI/ANzdXu8/PDnd2nyynfO/mMeT0ZK0VMxktdJFRzi+tm+7gEunabcd2D5FfA9w5pHykCTNHe+QliR1WBwkSR0WB0lSx1Hf56Cf8G5pSYuVPQdJUofFQZLUYXGQJHVYHCRJHRYHSVKHxUGS1GFxkCR1eJ/DLPGeB0mLiT0HSVKHxUGS1GFxkCR1WBwkSR0WB0lSx0weE7o9yf4k9/fF/iLJN5Lcm+RvkyzvO3ZZkvEk30xyXl98Y4uNJ9naF1+X5M4W/3SS42fzAiVJR28mPYfrgI2TYruBM6vq54F/AC4DSHI6cCFwRjvn40mOS3Ic8DHgfOB04KLWFuBDwFVV9UrgSeCFnlE9K9ZuveW5lySpaybPkP5SkrWTYp/v270DeGvb3gTsrKpngEeSjAOva8fGq+phgCQ7gU1JHgTeBPxWa7MD+I/ANYNczHzhPQ+SFrrZmHP4feCzbXsl8FjfsYkWmy5+MvBUVR2aFJ9Ski1J9iTZc+DAgVlIXZI0laGKQ5I/Aw4Bn5qddF5YVW2rqrGqGluxYsVcfKQkLUkDf31Gkt8D3gKcU1XVwvuA1X3NVrUY08S/ByxPsqz1HvrbS5JGZKDikGQj8B7gX1fVD/oO7QL+JslHgFcA64G7gADrk6yj98v/QuC3qqqSfIHenMVOYDNw86AXMx85/yBpIZrJUtYbgC8Dr04ykeQS4C+BnwJ2J/lakk8AVNVe4EbgAeBzwKVV9aPWK3gHcBvwIHBjawvwXuDftcnrk4FrZ/UKJUlHbSarlS6aIjztL/CqugK4Yor4rcCtU8Qf5icrmiRJ84B3SEuSOiwOkqQOH/YjNS4ekH7CnoMkqcPiIEnqsDhIkjosDpKkDiek55ATnguH/6601NlzkCR1WBwkSR0WB0lSh3MOI+KYtqT5zOIgHYGFXEuRw0qSpA6LgySpw2El6Sg4xKSlYiZPgtueZH+S+/tiJyXZneSh9vPEFk+Sq5OMJ7k3yVl952xu7R9Ksrkv/tok97Vzrk6S2b5I6VhYu/WW517SYjOTnsN19B4Len1fbCtwe1VdmWRr238vcD6950avB84GrgHOTnIScDkwBhRwT5JdVfVka/MHwJ30nhS3Efjs8Je2cLzQLxf/OpU0CkfsOVTVl4CDk8KbgB1tewdwQV/8+uq5A1ie5DTgPGB3VR1sBWE3sLEde1lV3VFVRa8AXYAkaaQGnXM4taoeb9tPAKe27ZXAY33tJlrsheITU8SlBct5CS0GQ09IV1UlqdlI5kiSbAG2AKxZs2YuPlKLnPMF0tQGLQ7fSXJaVT3ehob2t/g+YHVfu1Uttg94w6T4F1t81RTtp1RV24BtAGNjY3NSkKSZsMhosRn0PoddwOEVR5uBm/viF7dVSxuAp9vw023AuUlObCubzgVua8e+n2RDW6V0cd97SZJG5Ig9hyQ30Pur/5QkE/RWHV0J3JjkEuDbwNta81uBNwPjwA+AtwNU1cEkHwDubu3eX1WHJ7n/iN6KqJfQW6W0pFYqaXFz/kEL1RGLQ1VdNM2hc6ZoW8Cl07zPdmD7FPE9wJlHykOSNHf8+gxJUodfnzHPOSwhaRQsDtIcsdBrIbE4LCD+cpE0V5xzkCR12HOQRsBeoOY7i8MC5S8XSceSw0qSpA6LgySpw+IgSepwzkEaMeePNB/Zc5AkdVgcJEkdDistAg5LSJpt9hwkSR32HBYxexSSBmVxWGR8lrGk2TDUsFKSP0myN8n9SW5I8uIk65LcmWQ8yaeTHN/avqjtj7fja/ve57IW/2aS84a7JEnSsAYuDklWAu8CxqrqTOA44ELgQ8BVVfVK4EngknbKJcCTLX5Va0eS09t5ZwAbgY8nOW7QvCRJwxt2QnoZ8JIky4ATgMeBNwE3teM7gAva9qa2Tzt+TpK0+M6qeqaqHgHGgdcNmZcmWbv1ludeknQkAxeHqtoHfBh4lF5ReBq4B3iqqg61ZhPAyra9EnisnXuotT+5Pz7FOZKkERhmWOlEen/1rwNeAbyU3rDQMZNkS5I9SfYcOHDgWH6UJC1pwwwr/SrwSFUdqKp/BD4DvB5Y3oaZAFYB+9r2PmA1QDv+cuB7/fEpznmeqtpWVWNVNbZixYohUpckvZBhisOjwIYkJ7S5g3OAB4AvAG9tbTYDN7ftXW2fdvzvq6pa/MK2mmkdsB64a4i8JElDGvg+h6q6M8lNwFeAQ8BXgW3ALcDOJB9ssWvbKdcCf51kHDhIb4USVbU3yY30Cssh4NKq+tGgeUmShjfUTXBVdTlw+aTww0yx2qiqfgj8xjTvcwVwxTC5SJJmj3dIL0HTLWf1KzYkHeYX70mSOuw56Dl+UZ+kw+w5SJI6LA6SpA6HlaR5xKE9zRf2HCRJHRYHSVKHw0qaksMb0tJmz0GS1GHPQUdkL0Jaeuw5SJI6LA6SpA6HlXRUHGKSlgaLgwZmoZAWL4eVJEkd9hw0K+xFSIvLUD2HJMuT3JTkG0keTPJLSU5KsjvJQ+3nia1tklydZDzJvUnO6nufza39Q0k2T/+JkqS5MGzP4aPA56rqrUmOB04A3gfcXlVXJtkKbAXeC5wPrG+vs4FrgLOTnETvUaNjQAH3JNlVVU8OmZtGxF6EtPAN3HNI8nLgV4BrAarq2ap6CtgE7GjNdgAXtO1NwPXVcwewPMlpwHnA7qo62ArCbmDjoHlJkoY3zLDSOuAA8FdJvprkk0leCpxaVY+3Nk8Ap7btlcBjfedPtNh0cUnSiAwzrLQMOAt4Z1XdmeSj9IaQnlNVlaSGSbBfki3AFoA1a9bM1tvqGHKISVqYhuk5TAATVXVn27+JXrH4Thsuov3c347vA1b3nb+qxaaLd1TVtqoaq6qxFStWDJG6JOmFDFwcquoJ4LEkr26hc4AHgF3A4RVHm4Gb2/Yu4OK2amkD8HQbfroNODfJiW1l07ktpkVm7dZbnntJmt+GXa30TuBTbaXSw8Db6RWcG5NcAnwbeFtreyvwZmAc+EFrS1UdTPIB4O7W7v1VdXDIvCRJQxiqOFTV1+gtQZ3snCnaFnDpNO+zHdg+TC5auJyXkOYfvz5DktTh12doJKabd7AXIc0P9hwkSR0WB0lSh8NKmreW+hDTUr9+jZY9B0lShz0HLQiTJ7CX2l/Sg9w4uNT+GWl22XOQJHVYHCRJHQ4raUGabrLWSdyf8J+FhmFxkJYAC4WOlsVBC57f8np0LBSaCeccJEkd9hy0aPkXsjQ4ew6SpA57DtISZu9K07E4aElw0vrILBTqN3RxSHIcsAfYV1VvSbIO2AmcDNwD/G5VPZvkRcD1wGuB7wG/WVXfau9xGXAJ8CPgXVXlM6SlecJ7Spam2eg5/DHwIPCytv8h4Kqq2pnkE/R+6V/Tfj5ZVa9McmFr95tJTgcuBM4AXgH89ySvqqofzUJukgYwk4cxaXEbakI6ySrg14BPtv0AbwJuak12ABe07U1tn3b8nNZ+E7Czqp6pqkeAceB1w+QlSRrOsKuV/jPwHuDHbf9k4KmqOtT2J4CVbXsl8BhAO/50a/9cfIpznifJliR7kuw5cODAkKlLkqYz8LBSkrcA+6vqniRvmL2UpldV24BtAGNjYzUXnynpyJx/WHyGmXN4PfDrSd4MvJjenMNHgeVJlrXewSpgX2u/D1gNTCRZBryc3sT04fhh/edIWmCmm5ewaCwsAxeHqroMuAyg9Rz+tKp+O8l/Bd5Kb8XSZuDmdsqutv/ldvzvq6qS7AL+JslH6E1IrwfuGjQvSfOTq54WlmNxn8N7gZ1JPgh8Fbi2xa8F/jrJOHCQ3golqmpvkhuBB4BDwKWuVJIWN1c9zX+zUhyq6ovAF9v2w0yx2qiqfgj8xjTnXwFcMRu5SJKG5x3SkuYNh57mD794T5LUYc9B0rzkvMRoWRwkLSiDLJWdybCUQ1fPZ3GQtCjM9Jf70fZIlmrRsDhIWnQckhqexUGSJrG4WBwkaWiLcejJ4iBJM7SUehQWB0maRYvliwctDpI0QvN1SMriIElzYCZDUvOp1+HXZ0iSOiwOkqQOi4MkqcPiIEnqGLg4JFmd5AtJHkiyN8kft/hJSXYneaj9PLHFk+TqJONJ7k1yVt97bW7tH0qyefjLkiQNY5iewyHg3VV1OrABuDTJ6cBW4PaqWg/c3vYBzqf3fOj1wBbgGugVE+By4Gx6T5C7/HBBkSSNxsDFoaoer6qvtO3/AzwIrAQ2ATtasx3ABW17E3B99dwBLE9yGnAesLuqDlbVk8BuYOOgeUmShjcrcw5J1gK/ANwJnFpVj7dDTwCntu2VwGN9p0202HRxSdKIDF0ckvwz4L8B/7aqvt9/rKoKqGE/o++ztiTZk2TPgQMHZuttJUmTDFUckvxTeoXhU1X1mRb+Thsuov3c3+L7gNV9p69qseniHVW1rarGqmpsxYoVw6QuSXoBw6xWCnAt8GBVfaTv0C7g8IqjzcDNffGL26qlDcDTbfjpNuDcJCe2iehzW0ySRO9rNQ6/5sow3630euB3gfuSfK3F3gdcCdyY5BLg28Db2rFbgTcD48APgLcDVNXBJB8A7m7t3l9VB4fIS5I0pIGLQ1X9TyDTHD5nivYFXDrNe20Htg+aiyRpdnmHtCSpw+IgSeqwOEiSOiwOkqQOi4MkqcPiIEnqsDhIkjosDpKkDouDJKnD4iBJ6rA4SJI6LA6SpA6LgySpw+IgSeqwOEiSOiwOkqQOi4MkqWPeFIckG5N8M8l4kq2jzkeSlrJ5URySHAd8DDgfOB24KMnpo81KkpaueVEcgNcB41X1cFU9C+wENo04J0lasuZLcVgJPNa3P9FikqQRWDbqBI5Gki3Alrb7f5N8c5T5zLJTgO+OOolZ5jUtDF7TwnAK8N18aOj3+eczaTRfisM+YHXf/qoWe56q2gZsm6uk5lKSPVU1Nuo8ZpPXtDB4TQvDXF/TfBlWuhtYn2RdkuOBC4FdI85JkpasedFzqKpDSd4B3AYcB2yvqr0jTkuSlqx5URwAqupW4NZR5zFCi3G4zGtaGLymhWFOrylVNZefJ0laAObLnIMkaR6xOIxYkuVJbkryjSQPJvmlUec0rCR/kmRvkvuT3JDkxaPOaRBJtifZn+T+vthJSXYneaj9PHGUOR6taa7pL9p/f/cm+dsky0eZ49Ga6pr6jr07SSU5ZRS5DWq6a0ryzvbvam+SPz+WOVgcRu+jwOeq6l8ArwEeHHE+Q0myEngXMFZVZ9JbYHDhaLMa2HXAxkmxrcDtVbUeuL3tLyTX0b2m3cCZVfXzwD8Al811UkO6ju41kWQ1cC7w6FwnNAuuY9I1JXkjvW+OeE1VnQF8+FgmYHEYoSQvB34FuBagqp6tqqdGm9WsWAa8JMky4ATgf484n4FU1ZeAg5PCm4AdbXsHcMGcJjWkqa6pqj5fVYfa7h307jNaMKb59wRwFfAeYMFNrE5zTX8IXFlVz7Q2+49lDhaH0VoHHAD+KslXk3wyyUtHndQwqmofvb9oHgUeB56uqs+PNqtZdWpVPd62nwBOHWUyx8DvA58ddRLDSrIJ2FdVXx91LrPoVcAvJ7kzyf9I8ovH8sMsDqO1DDgLuKaqfgH4fyy8YYrnaWPwm+gVvlcAL03yO6PN6tio3lK/BfdX6XSS/BlwCPjUqHMZRpITgPcB/2HUucyyZcBJwAbg3wM3Jsmx+jCLw2hNABNVdWfbv4lesVjIfhV4pKoOVNU/Ap8B/tWIc5pN30lyGkD7eUy79nMlye8BbwF+uxb++vafo/fHydeTfIveMNlXkvzMSLMa3gTwmeq5C/gxve9bOiYsDiNUVU8AjyV5dQudAzwwwpRmw6PAhiQntL9qzmGBT7JPsgvY3LY3AzePMJdZkWQjvbH5X6+qH4w6n2FV1X1V9dNVtbaq1tL7pXpW+/9tIfs74I0ASV4FHM8x/HJBi8PovRP4VJJ7gX8J/KcR5zOU1gu6CfgKcB+9/8YW5N2qSW4Avgy8OslEkkuAK4F/k+Qher2kK0eZ49Ga5pr+EvgpYHeSryX5xEiTPErTXNOCNs01bQd+ti1v3QlsPpa9PO+QliR12HOQJHVYHCRJHRYHSVKHxUGS1GFxkCR1WBwkSR0WB0lSh8VBktTx/wHwehTAj3tRwQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "hb1 = hits.query('seq==1')\n", + "hb2 = hits.query('seq==2')\n", + "hb3 = hits.query('seq==3')\n", + "hb4 = hits.query('seq==4')\n", + "hf1 = hits.query('seq==5 or seq==8')\n", + "hf2 = hits.query('seq==6 or seq==9')\n", + "hf3 = hits.query('seq==7 or seq==10')\n", + "\n", + "print len(hb1), len(hb2), len(hb3), len(hb4), len(hf1), len(hf2), len(hf3)\n", + "plt.hist(hb1['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb1['eta'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb1['rg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb2['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb2['eta'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb2['rg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb3['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb3['eta'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb3['rg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb4['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hb4['eta'],log=False, bins=100)\n", + "plt.show()\n", + "data = [hb1['eta'],hb2['eta'],hb3['eta'],hb4['eta'],hf1['eta'],hf2['eta'],hf3['eta']]\n", + "plt.hist(data,log=False, bins=100, histtype='barstacked')\n", + "plt.show()\n", + "plt.hist(data,log=False, bins=100, histtype='step')\n", + "plt.show()\n", + "\n", + "\n", + "plt.hist(hb4['rg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hf1['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hf1['rg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hf2['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hf2['rg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hf3['zg'],log=False, bins=100)\n", + "plt.show()\n", + "plt.hist(hf3['rg'],log=False, bins=100)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAY0AAAD8CAYAAACLrvgBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEaJJREFUeJzt3X3MXnV9x/H3xxaUoI6new1py8pmE4NmQ2wAo1kcRCiwrCxRAtlGZ4hdIiQYl8zqPyiOBJdNHImysNFYFrU2PoxGq7VBjPMPoK0ij2PcYgltgFbKg8SIAb/74/pVL+/dD78+cd33zfuVXLnO+Z7fOb/fySn3x/NwHVNVSJLU4zWjHoAkae4wNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdVs46gEcbieddFItW7Zs1MOQpDllx44dP6uqsZnazbvQWLZsGdu3bx/1MCRpTknyWE87L09Jkrp1hUaSnUnuS3JPku2tdkKSrUkead/Ht3qS3JhkPMm9Sc4Y2s7q1v6RJKuH6m9v2x9v62a6PiRJo3EgZxp/VlWnV9WKNr8WuL2qlgO3t3mAC4Dl7bMGuAkGAQBcA5wFnAlcMxQCNwEfGFpv5Qx9SJJG4FAuT60C1rfp9cDFQ/Vba+BO4LgkJwPnA1ural9VPQNsBVa2ZW+sqjtr8J72Wydsa7I+JEkj0BsaBXwnyY4ka1ptUVU90aafBBa16cXA40Pr7mq16eq7JqlP14ckaQR6n556V1XtTvL7wNYk/zO8sKoqyRH9f3Oaro8WZGsATjnllCM5DEl6Ves606iq3e17D/B1BvcknmqXlmjfe1rz3cDSodWXtNp09SWT1Jmmj4nju7mqVlTVirGxGR8zliQdpBlDI8mxSd6wfxo4D7gf2ATsfwJqNXBbm94EXN6eojobeK5dYtoCnJfk+HYD/DxgS1v2fJKz21NTl0/Y1mR9SJJGoOfy1CLg6+0p2IXAF6vq20m2ARuTXAE8BlzS2m8GLgTGgV8A7weoqn1JPglsa+2urap9bfqDwOeBY4BvtQ/A9VP0IUkagQweWJo/VqxYUUf6F+HL1n6zq93O6y86ouOQpMMlyY6hn1RMyV+ES5K6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkrp1h0aSBUl+lOQbbf7UJHclGU/y5SRHt/pr2/x4W75saBsfbfWHk5w/VF/ZauNJ1g7VJ+1DkjQaB3KmcTXw0ND8p4AbqupNwDPAFa1+BfBMq9/Q2pHkNOBS4C3ASuBzLYgWAJ8FLgBOAy5rbafrQ5I0Al2hkWQJcBHwH20+wDnAV1qT9cDFbXpVm6ctP7e1XwVsqKoXq+qnwDhwZvuMV9WjVfUrYAOwaoY+JEkj0Hum8RngH4Bft/kTgWer6qU2vwtY3KYXA48DtOXPtfa/qU9YZ6r6dH1IkkZgxtBI8ufAnqra8QqM56AkWZNke5Lte/fuHfVwJGne6jnTeCfwF0l2Mrh0dA7wr8BxSRa2NkuA3W16N7AUoC3/PeDp4fqEdaaqPz1NH7+jqm6uqhVVtWJsbKxjlyRJB2PG0Kiqj1bVkqpaxuBG9ner6q+AO4D3tmargdva9KY2T1v+3aqqVr+0PV11KrAcuBvYBixvT0od3frY1NaZqg9J0ggcyu80PgJ8OMk4g/sPt7T6LcCJrf5hYC1AVT0AbAQeBL4NXFlVL7d7FlcBWxg8nbWxtZ2uD0nSCCycuclvVdX3gO+16UcZPPk0sc0vgfdNsf51wHWT1DcDmyepT9qHJGk0/EW4JKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqduMoZHkdUnuTvLjJA8k+USrn5rkriTjSb6c5OhWf22bH2/Llw1t66Ot/nCS84fqK1ttPMnaofqkfUiSRqPnTONF4Jyq+hPgdGBlkrOBTwE3VNWbgGeAK1r7K4BnWv2G1o4kpwGXAm8BVgKfS7IgyQLgs8AFwGnAZa0t0/QhSRqBGUOjBl5os0e1TwHnAF9p9fXAxW16VZunLT83SVp9Q1W9WFU/BcaBM9tnvKoerapfARuAVW2dqfqQJI1A1z2NdkZwD7AH2Ar8BHi2ql5qTXYBi9v0YuBxgLb8OeDE4fqEdaaqnzhNH5KkEegKjap6uapOB5YwODN48xEd1QFKsibJ9iTb9+7dO+rhSNK8dUBPT1XVs8AdwDuA45IsbIuWALvb9G5gKUBb/nvA08P1CetMVX96mj4mjuvmqlpRVSvGxsYOZJckSQeg5+mpsSTHteljgPcADzEIj/e2ZquB29r0pjZPW/7dqqpWv7Q9XXUqsBy4G9gGLG9PSh3N4Gb5prbOVH1IkkZg4cxNOBlY355yeg2wsaq+keRBYEOSfwR+BNzS2t8C/GeScWAfgxCgqh5IshF4EHgJuLKqXgZIchWwBVgArKuqB9q2PjJFH5KkEZgxNKrqXuBtk9QfZXB/Y2L9l8D7ptjWdcB1k9Q3A5t7+5AkjYa/CJckdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd0MDUlSN0NDktTN0JAkdTM0JEndDA1JUjdDQ5LUzdCQJHUzNCRJ3QwNSVI3Q0OS1M3QkCR1MzQkSd1mDI0kS5PckeTBJA8kubrVT0iyNckj7fv4Vk+SG5OMJ7k3yRlD21rd2j+SZPVQ/e1J7mvr3Jgk0/UhSRqNnjONl4C/r6rTgLOBK5OcBqwFbq+q5cDtbR7gAmB5+6wBboJBAADXAGcBZwLXDIXATcAHhtZb2epT9SFJGoEZQ6OqnqiqH7bpnwMPAYuBVcD61mw9cHGbXgXcWgN3AsclORk4H9haVfuq6hlgK7CyLXtjVd1ZVQXcOmFbk/UhSRqBA7qnkWQZ8DbgLmBRVT3RFj0JLGrTi4HHh1bb1WrT1XdNUmeaPiaOa02S7Um2792790B2SZJ0ALpDI8nrga8CH6qq54eXtTOEOsxj+x3T9VFVN1fViqpaMTY2diSHIUmval2hkeQoBoHxhar6Wis/1S4t0b73tPpuYOnQ6ktabbr6kknq0/UhSRqBnqenAtwCPFRVnx5atAnY/wTUauC2ofrl7Smqs4Hn2iWmLcB5SY5vN8DPA7a0Zc8nObv1dfmEbU3WhyRpBBZ2tHkn8DfAfUnuabWPAdcDG5NcATwGXNKWbQYuBMaBXwDvB6iqfUk+CWxr7a6tqn1t+oPA54FjgG+1D9P0IUkagRlDo6p+AGSKxedO0r6AK6fY1jpg3ST17cBbJ6k/PVkfkqTR8BfhkqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqtnDUA5jPlq39Zle7nddfdIRHIkmHh2cakqRuhoYkqZuhIUnqZmhIkroZGpKkbjOGRpJ1SfYkuX+odkKSrUkead/Ht3qS3JhkPMm9Sc4YWmd1a/9IktVD9bcnua+tc2OSTNeHJGl0es40Pg+snFBbC9xeVcuB29s8wAXA8vZZA9wEgwAArgHOAs4ErhkKgZuADwytt3KGPiRJIzJjaFTV94F9E8qrgPVtej1w8VD91hq4EzguycnA+cDWqtpXVc8AW4GVbdkbq+rOqirg1gnbmqwPSdKIHOw9jUVV9USbfhJY1KYXA48PtdvVatPVd01Sn64PSdKIHPKN8HaGUIdhLAfdR5I1SbYn2b53794jORRJelU72NB4ql1aon3vafXdwNKhdktabbr6kknq0/Xx/1TVzVW1oqpWjI2NHeQuSZJmcrChsQnY/wTUauC2ofrl7Smqs4Hn2iWmLcB5SY5vN8DPA7a0Zc8nObs9NXX5hG1N1ockaURmfGFhki8B7wZOSrKLwVNQ1wMbk1wBPAZc0ppvBi4ExoFfAO8HqKp9ST4JbGvtrq2q/TfXP8jgCa1jgG+1D9P0IUkakRlDo6oum2LRuZO0LeDKKbazDlg3SX078NZJ6k9P1ockaXT8RbgkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG6GhiSpm6EhSepmaEiSuhkakqRuhoYkqZuhIUnqZmhIkrotHPUABMvWfrO77c7rLzqCI5Gk6XmmIUnqZmhIkroZGpKkboaGJKmboSFJ6jbrQyPJyiQPJxlPsnbU45GkV7NZ/chtkgXAZ4H3ALuAbUk2VdWDox3Z6PQ+nuujuZKOhNl+pnEmMF5Vj1bVr4ANwKoRj0mSXrVm9ZkGsBh4fGh+F3DWiMYypxzIDwYPJ89wpPlttodGlyRrgDVt9oUkDx/kpk4CfnZ4RjVyI9mXfOqwb9JjMju5L7PPoe7HH/Q0mu2hsRtYOjS/pNV+R1XdDNx8qJ0l2V5VKw51O7PBfNmX+bIf4L7MVvNlX16p/Zjt9zS2AcuTnJrkaOBSYNOIxyRJr1qz+kyjql5KchWwBVgArKuqB0Y8LEl61ZrVoQFQVZuBza9Qd4d8iWsWmS/7Ml/2A9yX2Wq+7Msrsh+pqleiH0nSPDDb72lIkmYRQ6OZL68rSbIzyX1J7kmyfdTjORBJ1iXZk+T+odoJSbYmeaR9Hz/KMfaaYl8+nmR3Ozb3JLlwlGPskWRpkjuSPJjkgSRXt/qcOy7T7MtcPC6vS3J3kh+3fflEq5+a5K72d+zL7QGiw9u3l6d+87qS/2XodSXAZXPxdSVJdgIrqmrOPXee5E+BF4Bbq+qtrfZPwL6qur6F+fFV9ZFRjrPHFPvyceCFqvrnUY7tQCQ5GTi5qn6Y5A3ADuBi4G+ZY8dlmn25hLl3XAIcW1UvJDkK+AFwNfBh4GtVtSHJvwE/rqqbDmffnmkM+LqSWaCqvg/sm1BeBaxv0+sZ/Ec+602xL3NOVT1RVT9s0z8HHmLwpoY5d1ym2Zc5pwZeaLNHtU8B5wBfafUjclwMjYHJXlcyJ/8xMfiH850kO9ov5ee6RVX1RJt+Elg0ysEcBlclubddvpr1l3SGJVkGvA24izl+XCbsC8zB45JkQZJ7gD3AVuAnwLNV9VJrckT+jhka88+7quoM4ALgynaZZF6owbXUuXw99Sbgj4DTgSeAfxntcPoleT3wVeBDVfX88LK5dlwm2Zc5eVyq6uWqOp3BmzLOBN78SvRraAx0va5kLqiq3e17D/B1Bv+Y5rKn2rXo/dek94x4PAetqp5q/6H/Gvh35sixadfMvwp8oaq+1spz8rhMti9z9bjsV1XPAncA7wCOS7L/93dH5O+YoTEwL15XkuTYdoOPJMcC5wH3T7/WrLcJWN2mVwO3jXAsh2T/H9nmL5kDx6bdcL0FeKiqPj20aM4dl6n2ZY4el7Ekx7XpYxg8xPMQg/B4b2t2RI6LT0817TG7z/Db15VcN+IhHbAkf8jg7AIGv/b/4lzajyRfAt7N4G2dTwHXAP8FbAROAR4DLqmqWX+DeYp9eTeDSyAF7AT+bui+wKyU5F3AfwP3Ab9u5Y8xuBcwp47LNPtyGXPvuPwxgxvdCxj8j/+NVXVt+xuwATgB+BHw11X14mHt29CQJPXy8pQkqZuhIUnqZmhIkroZGpKkboaGJKmboSFJ6mZoSJK6GRqSpG7/B7/G4TPm5vzDAAAAAElFTkSuQmCC\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": [ + "plt.hist(hb1['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()\n", + "plt.hist(hb2['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()\n", + "plt.hist(hb3['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()\n", + "plt.hist(hb4['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()\n", + "plt.hist(hf1['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()\n", + "plt.hist(hf2['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()\n", + "plt.hist(hf3['ysize']/8.,log=False, bins=30,range=[0,30])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "min/max\n", + " ev ind det charge xg yg zg rg iphi xsize \\\n", + "min 1 0 0 2019 -3.1587 -3.3743 -26.6751 2.7388 -32766 -1023 \n", + "max 1000 4382 95 7570745 6.6091 3.3743 26.6750 11.0548 32765 1023 \n", + "\n", + " ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min ... 0 401 -147338 0 0 -3.141557 -2.971983 1 \n", + "max ... 142826 292338 149881 999 206 3.141416 2.971979 1 \n", + "\n", + " trackID norm \n", + "min 10000002 0.001 \n", + "max 10000113902 0.001 \n", + "\n", + "[2 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi xsize \\\n", + "min 1 855 96 4021 -6.9806 -7.2495 -26.6751 6.5775 -32767 -1023 \n", + "max 1000 8049 319 5558487 6.9806 7.2495 26.6750 15.8274 32767 1023 \n", + "\n", + " ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min ... 0 401 -147949 0 1 -3.141593 -2.108088 2 \n", + "max ... 142826 292338 149881 999 196 3.141578 2.108084 2 \n", + "\n", + " trackID norm \n", + "min 10000009 0.001 \n", + "max 10000113902 0.001 \n", + "\n", + "[2 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi \\\n", + "min 1 2326 320 4022 -11.0658 -11.3359 -26.6751 10.0193 -32767 \n", + "max 1000 11211 671 6401557 11.0658 11.3359 38.0934 15.9663 32767 \n", + "\n", + " xsize ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min -1023 ... 0 401 -147949 0 1 -3.141593 -1.643978 3 \n", + "max 908 ... 142826 292338 149881 998 179 3.141593 1.643975 3 \n", + "\n", + " trackID norm \n", + "min 10000015 0.001 \n", + "max 10000113896 0.001 \n", + "\n", + "[2 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi \\\n", + "min 1 3277 672 4017 -16.1666 -16.4362 -26.6751 15.8152 -32767 \n", + "max 1000 13712 1183 5935571 16.1666 16.4362 26.6750 16.4362 32767 \n", + "\n", + " xsize ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min -1023 ... 0 401 -147949 0 1 -3.141580 -1.294041 4 \n", + "max 918 ... 142826 292338 149881 998 170 3.141586 1.294038 4 \n", + "\n", + " trackID norm \n", + "min 10000016 0.001 \n", + "max 10000113896 0.001 \n", + "\n", + "[2 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi \\\n", + "min 1 4148 1184 4014 -16.0677 -16.0011 -35.182 4.6140 -32767 \n", + "max 1000 20608 1631 4746542 16.0588 16.0029 35.187 16.0703 32766 \n", + "\n", + " xsize ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min -1023 ... 0 401 -147338 0 1 -3.141583 -2.683823 5 \n", + "max 892 ... 142812 232351 149881 999 229 3.141567 2.683831 8 \n", + "\n", + " trackID norm \n", + "min 10000095 0.001 \n", + "max 10000113902 0.001 \n", + "\n", + "[2 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi \\\n", + "min 1 4625 1296 4018 -16.0667 -16.0011 -42.6813 4.6140 -32767 \n", + "max 1000 22376 1743 6625049 16.0550 16.0020 42.6870 16.0706 32767 \n", + "\n", + " xsize ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min -1023 ... 0 401 -147949 0 1 -3.141581 -2.883513 6 \n", + "max 1013 ... 142812 232351 149881 999 303 3.141593 2.883501 9 \n", + "\n", + " trackID norm \n", + "min 10108662 0.001 \n", + "max 10000113902 0.001 \n", + "\n", + "[2 rows x 21 columns]\n", + " ev ind det charge xg yg zg rg iphi \\\n", + "min 1 5146 1408 4006 -16.0706 -15.9973 -52.1806 4.6140 -32767 \n", + "max 1000 24119 1855 5782400 16.0677 16.0009 52.1861 16.0706 32767 \n", + "\n", + " xsize ... tkId pt z0 r0 n1 phi eta seq \\\n", + "min -1023 ... 0 401 -147338 0 1 -3.141585 -3.090231 7 \n", + "max 821 ... 142812 232351 149881 997 207 3.141585 3.090221 10 \n", + "\n", + " trackID norm \n", + "min 10108661 0.001 \n", + "max 10000113902 0.001 \n", + "\n", + "[2 rows x 21 columns]\n" + ] + } + ], + "source": [ + "print 'min/max'\n", + "print hb1.agg(['min','max'])\n", + "print hb2.agg(['min','max'])\n", + "print hb3.agg(['min','max'])\n", + "print hb4.agg(['min','max'])\n", + "print hf1.agg(['min','max'])\n", + "print hf2.agg(['min','max'])\n", + "print hf3.agg(['min','max'])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "def build(hh,n) :\n", + " return pd.DataFrame({ 'z'+n : hh['zg'],\n", + " 'r'+n : hh['rg'],\n", + " 'phi'+n : hh['phi'],\n", + " 'ys'+n : hh['ysize'],\n", + " 'pt'+n : hh['pt'],\n", + " 'det'+n : hh['det'],\n", + " 'trackID' : hh['trackID'],\n", + " 'tkz'+n : hh['z0']/10000.,\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": 18, + "metadata": {}, + "outputs": [], + "source": [ + "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": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAER9JREFUeJzt3X+snfVdwPH3h5KyyKQD2iVLS9ciFXclG9uOxZjoECW75Vc3JFkbo8gqDTPVaGICCybGGZIuGucI1eXKug41rchwaeUubNkknRFGyxRoaWCl29ILJC0y64+oyPbxj/Mwzu56bp/z47nn3O99v5KTe873ec7zfD/ntJ/7vZ/v8yMyE0lSuc4adQckSc0y0UtS4Uz0klQ4E70kFc5EL0mFM9FLUuFM9JJUOBO9JBXORC9JhTt71B0AWL58ea5Zs2bU3ZCkBeWJJ554OTNXnGm9sUj0a9as4eDBg6PuhiQtKBHx7TrrjbR0ExHXR8TUqVOnRtkNSSraSBN9Zu7LzK3Lli0bZTckqWhOxkpS4Uz0klQ4E70kFc5EL0mFM9FLUuFM9JJUuKGfMBURVwJ/CBwG9mTmI8PehyQtZGvueOj7z7+1/drG91drRB8ROyPiREQcmtU+GRHPRsTRiLijak7gP4E3ATPD7a4kqVd1Sze7gMnOhohYAuwANgATwOaImAC+mpkbgNuBPxheVyVJ/aiV6DNzP/DKrOb1wNHMPJaZrwJ7gI2Z+b1q+XeAc4bWU0lSXwap0a8Ejne8ngGuiIgbgfcDbwHu6fbmiNgKbAVYvXr1AN2QJM1l6JOxmfkg8GCN9aaAKYBWq5XD7ockqW2QwytfAC7qeL2qaqvNq1dKUvMGSfQHgHURsTYilgKbgL29bMCrV0pS8+oeXrkbeBS4NCJmImJLZr4GbAMeBo4A92fm4V527ohekppXq0afmZu7tE8D0/3uPDP3Aftardat/W5DkjQ37zAlSYXzDlOSVDgvaiZJhbN0I0mFs3QjSYWzdCNJhbN0I0mFs3QjSYWzdCNJhbN0I0mFs3QjSYWzdCNJhTPRS1LhTPSSVDgnYyWpcE7GSlLhLN1IUuFM9JJUOBO9JBXORC9JhTPRS1LhPLxSkgrn4ZWSVDhLN5JUOBO9JBXORC9JhTPRS1LhTPSSVDgTvSQVrpFEHxHnRsTBiLiuie1LkuqrlegjYmdEnIiIQ7PaJyPi2Yg4GhF3dCy6Hbh/mB2VJPWn7oh+FzDZ2RARS4AdwAZgAtgcERMRcTXwDHBiiP2UJPXp7DorZeb+iFgzq3k9cDQzjwFExB5gI/Bm4Fzayf+/I2I6M783tB5LknpSK9F3sRI43vF6BrgiM7cBRMSvAS93S/IRsRXYCrB69eoBuiFJmktjR91k5q7M/Ps5lk9lZiszWytWrGiqG5K06A2S6F8ALup4vapqq82rV0pS8wZJ9AeAdRGxNiKWApuAvb1swKtXSlLz6h5euRt4FLg0ImYiYktmvgZsAx4GjgD3Z+bhXnbuiF6Smlf3qJvNXdqngel+d56Z+4B9rVbr1n63IUmam3eYkqTCeYcpSSqcI3pJKpwjekkqnJcplqTCWbqRpMJZupGkwlm6kaTCmeglqXDW6CWpcNboJalwlm4kqXAmekkqnIlekgrnZKwkFc7JWEkqnKUbSSqciV6SCmeil6TCmeglqXAmekkqnIdXSlLhPLxSkgpn6UaSCmeil6TCmeglqXAmekkqnIlekgpnopekwg090UfEOyLiUxHxQER8ZNjblyT15uw6K0XETuA64ERmXtbRPgl8ElgC3JuZ2zPzCHBbRJwF3Af8+fC7LUkLy5o7HhrZvuuO6HcBk50NEbEE2AFsACaAzRExUS27AXgImB5aTyVJfamV6DNzP/DKrOb1wNHMPJaZrwJ7gI3V+nszcwPwy8PsrCSpd7VKN12sBI53vJ4BroiIK4EbgXOYY0QfEVuBrQCrV68eoBuSpLkMkuhPKzMfAR6psd5URLwEXL906dL3DrsfkqS2QY66eQG4qOP1qqqtNi9qJknNGyTRHwDWRcTaiFgKbAL29rIBL1MsSc2rlegjYjfwKHBpRMxExJbMfA3YBjwMHAHuz8zDvezcEb0kNa9WjT4zN3dpn8ZDKCVprA19MrYXEXE9cP0ll1wyym5I0tCN8gSp2bzDlCQVznvGSlLhHNFLUuG8TLEkFc7SjSQVztKNJBXO0o0kFc5EL0mF84QpSRqScTpJqpM1ekkqnKUbSSqciV6SCmeil6TCORkrSQMY1wnYTk7GSlLhLN1IUuFM9JJUOBO9JBVupJOxkrRQLIRJ124c0UtS4Ty8UpK6WMij+E4eXilJhbN0I0mFM9FLUuE86kbSoldKLb4bR/SSVDgTvSQVzkQvSYVrpEYfER8ArgXOAz6dmV9sYj+S1K/S6/Kdaif6iNgJXAecyMzLOtongU8CS4B7M3N7Zn4e+HxEnA/8MWCil9SozsT9re3XnrF9MellRL8LuAe47/WGiFgC7ACuBmaAAxGxNzOfqVb5vWq5JM2bbqP1xTSK71S7Rp+Z+4FXZjWvB45m5rHMfBXYA2yMto8DX8jMr59uexGxNSIORsTBkydP9tt/SdIZDDoZuxI43vF6pmr7TeAXgZsi4rbTvTEzpzKzlZmtFStWDNgNSVI3jUzGZubdwN1nWs+LmklS8wYd0b8AXNTxelXVVosXNZOk5g2a6A8A6yJibUQsBTYBe+u+OSKuj4ipU6dODdgNSVI3tRN9ROwGHgUujYiZiNiSma8B24CHgSPA/Zl5uO42HdFL6tWaOx76/kP11K7RZ+bmLu3TwHQ/O7dGL2kQJvt6Rnr1yszcB+xrtVq3jrIfksaPJzoNj5cpljRUTZyh6sh9MCO9qJmTsZLUPEs3kgYy12jbkfh4sHQjqWcm8IVlpIneo26khWOYyb1OHV/DY+lGkom3cJZupAWsiUMQTe7lMdFLGil/sTTPGr20SJlgF4+RHkfvtW4kqXkjTfSSpOaZ6CWpcE7GSoXz4mByMlYagaaTrxOt6uQJU9IY8ReAmmDpRlpgTNbqlYleqmmuBDvI6Lvbdq2ta1g86kaSCueIXo2az1HpsPbVz3aauKtSryzpqBuPutGCMKoyhslTJfCoGy0q3RL3MH951Km5S/PJ0o20APhLQoMw0euMPPpDWthM9BpbwxrFOhrWYmeiX4BKG2H3Gk9p8UtNM9EvcCa90+t1FO+oXyXzhClJKtzQR/QRcTFwJ7AsM28a9vbVDP8ykMpVK9FHxE7gOuBEZl7W0T4JfBJYAtybmdsz8xiwJSIeaKLDs5mg3jAun4VlEGm81C3d7AImOxsiYgmwA9gATACbI2JiqL2TJA2s1og+M/dHxJpZzeuBo9UInojYA2wEnhlmBxei+Tj7skkLaUS+kPoqjcogk7ErgeMdr2eAlRFxYUR8Cnh3RHy025sjYmtEHIyIgydPnhygG5KkuQx9MjYz/xW4rcZ6U8AUQKvVymH3Q5LUNkiifwG4qOP1qqqttnG7emWpJ+401c9SPy+pNIOUbg4A6yJibUQsBTYBe3vZQGbuy8yty5YtG6AbkqS51D28cjdwJbA8ImaA38/MT0fENuBh2odX7szMw73sfNxG9IuNE5nS4lD3qJvNXdqngel+d+716CWped5hqotRHSLZxO3w6rQPul1J42uk17qxRi9JzfOiZpJUOEs3GgnvqyrNH0s3klQ4SzeSVLhiSzfzfdRMnZLDIEfUWNKQ1C9LN5JUOEs3klQ4E70kFa7YGn1ThlUr77WmL0n9skYvSYWzdCNJhTPRS1LhFnWN3hr48PhZSuPLGr0kFc7SjSQVzkQvSYUz0UtS4Uz0klQ4E70kFW6kiT4iro+IqVOnTo2yG5JUNA+vlKTCWbqRpMKZ6CWpcCZ6SSqciV6SCmeil6TCmeglqXBDv0xxRJwL/BnwKvBIZv71sPchSaqv1og+InZGxImIODSrfTIino2IoxFxR9V8I/BAZt4K3DDk/kqSelS3dLMLmOxsiIglwA5gAzABbI6ICWAVcLxa7bvD6aYkqV+1En1m7gdemdW8Hjiamccy81VgD7ARmKGd7GtvX5LUnEFq9Ct5Y+QO7QR/BXA3cE9EXAvs6/bmiNgKbAVYvXr1AN14Q53b2XnLO0mLzdAnYzPzv4Bbaqw3BUwBtFqtHHY/JEltg5RWXgAu6ni9qmqrzatXSlLzBkn0B4B1EbE2IpYCm4C9vWzAq1dKUvPqHl65G3gUuDQiZiJiS2a+BmwDHgaOAPdn5uFedu6IXpKaV6tGn5mbu7RPA9P97jwz9wH7Wq3Wrf1uQ5I0N+8wJUmF8w5TklQ4T2iSpMJZupGkwkXm6M9VioiTwLf7fPty4OUhdmchMObFwZgXh0FifntmrjjTSmOR6AcREQczszXqfswnY14cjHlxmI+YrdFLUuFM9JJUuBIS/dSoOzACxrw4GPPi0HjMC75GL0maWwkjeknSHMYq0Xe5B23n8rdHxJcj4qmIeCQiVnUs+3hEHKoeH+po3xUR34yIf6kel89XPHU0FHNExF0R8VxEHImI35qveOpoKOavdnzHL0bE5+crnjoaivkXIuLrVcz/GBGXzFc8dTQU81VVzIci4rMRMfR7agyi2/21O5ZHRNxdfSZPRcR7OpbdHBHfqB43d7S/NyKert5zd0REzx3LzLF4AEuA54GLgaXAk8DErHX+Fri5en4V8JfV82uBL9G+SNu5tC+hfF61bBdw06jjm+eYbwHuA86qXr911LE2HfOs938O+NVRxzoP3/NzwDuq578B7Bp1rE3GTHtgehz48Wq9jwFbRh3rrJh+DngPcKjL8muALwAB/DTwtar9AuBY9fP86vn51bLHq3Wjeu+GXvs1TiP6bveg7TQBfKV6/g8dyyeA/Zn5WrbvcPUUs25mPqaaivkjwMcy83sAmXmiwRh61ej3HBHn0U4a4zSibyrmpJ0AAZYBLzbU/340EfOFwKuZ+Vy13peAX2owhp7l6e+v3WkjcF+2PQa8JSLeBrwf+FJmvpKZ36Ed22S17LzMfCzbWf8+4AO99mucEv3p7kG7ctY6TwI3Vs8/CPxoRFxYtU9GxI9ExHLg5/nBu1/dVf2Z9ImIOKeZ7velqZh/DPhQRByMiC9ExLrGIuhdk98ztP8TfDkz/33oPe9fUzH/OjAdETPArwDbG+p/P5qI+WXg7Ih4/eSim/jh73/cdftc5mqfOU17T8Yp0dfxu8D7IuKfgffRvnXhdzPzi7Svi/9PwOs3Sflu9Z6PAj8B/BTtP4tun+9OD6ifmM8B/ifbZ9v9BbBz3ns9mH5ift3matlC00/MvwNck5mrgM8AfzLvvR5MTzFXI9pNwCci4nHgP/jh71+nMU6J/oz3oM3MFzPzxsx8N3Bn1fZv1c+7MvPyzLyadi3ruar9perPpP+l/Z9hffOh1NZIzLR/6z9YPf874J3NhdCzpmKmGv2tBx5qNoSeDT3miFgBvCszv1Zt4m+An2k4jl409f/50cz82cxcD+yn4/tfILp9LnO1rzpNe2+GPRnR74P2xMsxYC1vTN785Kx1lvPGBONdtOvQ0J74ubB6/k7gEHB29fpt1c8A/hTYPupY5yHm7cCHq+dXAgdGHWvTMVdttwGfHXWM8xFz9XiZNyYmtwCfG3Ws8/Bv+63Vz3OALwNXjTrW08S+hu6Tsdfyg5Oxj1ftFwDfpD0Re371/IJq2ezJ2Gt67tOoP5RZH8I1tH9DPw/cWbV9DLihen4T8I1qnXuBc6r2NwHPVI/HgMs7tvkV4OnqH8tfAW8edZzzEPNbaI9qn6b9Z++7Rh1n0zFXyx8BJkcd3zx+zx+svuMnq9gvHnWc8xDzH9G+R/WzwG+POsbTxLwbeAn4P9p/WW+hPQC5rVoewI7qM3kaaHW898PA0epxS0d7q8pfzwP3UJ3o2svDM2MlqXDjVKOXJDXARC9JhTPRS1LhTPSSVDgTvSQVzkQvSYUz0UtS4Uz0klS4/wdf/Fd50bhONAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEGlJREFUeJzt3V+MXGd5x/Hvj0QOKoiQEItCEmNHcQOWKgFaBVSkEigFG5o4pSnYLWpo3bihDTdVJYzoRVupatobJESk1ILU/SM5pCm0dmPkAiEKFwmNqfiTYJksBhS7FBsCllDbhMDTizlGp5vd9czOzI79+vuRVjvnPf8evzN+9t3nvHtOqgpJUrueM+sAJEnTZaKXpMaZ6CWpcSZ6SWqciV6SGmeil6TGmeglqXEmeklqnIlekhp34awDALjssstq/fr1sw5Dks4pX/jCF75bVWvPtN1ZkejXr1/PoUOHZh2GJJ1TknxrmO0s3UhS42aa6JNcn2T3qVOnZhmGJDVtpom+qvZX1c6LL754lmFIUtMs3UhS40z0ktQ4E70kNc5EL0mNM9FLUuNm+gdTSa4Hrr/66qtnGYYkrar1u+776etv3v62qZ/P6ZWS1DhLN5LUOBO9JDXORC9JjTPRS1LjTPSS1DgTvSQ1buKJPsl1ST6X5M4k1036+JKk0QyV6JPcleREkkcXtG9OciTJfJJdXXMBPwSeCxybbLiSpFENO6LfA2zuNyS5ALgD2AJsArYn2QR8rqq2AO8D/nRyoUqSVmKoRF9VDwJPLmi+FpivqqNV9TRwN7C1qn7Srf8+cNHEIpUkrcg497q5HHiit3wMeE2StwNvAV4IfHipnZPsBHYCrFu3bowwJEnLmfhNzarq48DHh9huN7AbYG5uriYdhyRpYJxZN8eBK3vLV3RtQ/Ph4JI0feMk+keAjUk2JFkDbAP2jXIA714pSdM37PTKvcBDwDVJjiXZUVXPALcBB4HDwD1V9dgoJ3dEL0nTN1SNvqq2L9F+ADiw0pNX1X5g/9zc3C0rPYYkaXkzvQWCI3pJmj6fMCVJjfOmZpLUOEs3ktQ4SzeS1DhLN5LUOEs3ktQ4SzeS1DhLN5LUOBO9JDXOGr0kNc4avSQ1ztKNJDXORC9JjTPRS1LjvBgrSY3zYqwkNc7SjSQ1zkQvSY0z0UtS40z0ktQ4E70kNc7plZLUOKdXSlLjLN1IUuNM9JLUOBO9JDXORC9JjTPRS1LjTPSS1LipJPokz0tyKMmvTOP4kqThDZXok9yV5ESSRxe0b05yJMl8kl29Ve8D7plkoJKklRl2RL8H2NxvSHIBcAewBdgEbE+yKckvA18FTkwwTknSCl04zEZV9WCS9QuarwXmq+ooQJK7ga3A84HnMUj+/5PkQFX9ZGIRS5JGMlSiX8LlwBO95WPAa6rqNoAk7wa+u1SST7IT2Amwbt26McKQJC1narNuqmpPVf3rMut3V9VcVc2tXbt2WmFI0nlvnER/HLiyt3xF1zY0714pSdM3TqJ/BNiYZEOSNcA2YN8oB/DulZI0fcNOr9wLPARck+RYkh1V9QxwG3AQOAzcU1WPjXJyR/SSNH3DzrrZvkT7AeDASk9eVfuB/XNzc7es9BiSpOX5hClJapxPmJKkxnlTM0lqnKUbSWqcpRtJapylG0lqnKUbSWqcpRtJatw4d6+UJA1p/a77ZnZuSzeS1DhLN5LUOGfdSFLjTPSS1DgTvSQ1zouxktQ4L8ZKUuMs3UhS40z0ktQ4E70kNc5bIEjSFMzylgcLOaKXpMY5vVKSGuf0SklqnDV6SZqQs6ku32eNXpIaZ6KXpMaZ6CWpcdboJWkMZ2tdvs8RvSQ1buKJPskrktyZ5N4k75n08SVJoxkq0Se5K8mJJI8uaN+c5EiS+SS7AKrqcFXdCrwDeN3kQ5YkjWLYEf0eYHO/IckFwB3AFmATsD3Jpm7dDcB9wIGJRSpJWpGhEn1VPQg8uaD5WmC+qo5W1dPA3cDWbvt9VbUF+M1JBitJGt04s24uB57oLR8DXpPkOuDtwEUsM6JPshPYCbBu3boxwpCk1XUuzLTpm/j0yqp6AHhgiO12A7sB5ubmatJxSJIGxpl1cxy4srd8Rdc2NO9eKUnTN06ifwTYmGRDkjXANmDfKAfw7pWSNH3DTq/cCzwEXJPkWJIdVfUMcBtwEDgM3FNVj41yckf0kjR9Q9Xoq2r7Eu0HGGMKZVXtB/bPzc3dstJjSJKW5xOmJKlxPmFKkhrnTc0kqXGWbiSpcTO9H70XYyWdK861v4bts3QjSY2zdCNJjXPWjSQ1ztKNJDXOh4NLUs+5fNF1KdboJalx1uglqXHW6CWpcSZ6SWqciV6SGufFWElqnPe6kXTea3FKZZ+lG0lqnIlekhpnopekxpnoJalxJnpJapzTKyWpcU6vlHRean1KZZ+lG0lqnIlekhpnopekxpnoJalxPkpQ0nnjfLoA2+eIXpIaN5URfZIbgbcBLwA+WlX/No3zSJLObOgRfZK7kpxI8uiC9s1JjiSZT7ILoKr+uapuAW4F3jnZkCVJoxildLMH2NxvSHIBcAewBdgEbE+yqbfJH3frJUkzMnTppqoeTLJ+QfO1wHxVHQVIcjewNclh4Hbgk1X1HxOKVZJGdr5egO0b92Ls5cATveVjXdt7gTcBNyW5dbEdk+xMcijJoZMnT44ZhiRpKVO5GFtVHwI+dIZtdgO7Aebm5moacUiSxh/RHweu7C1f0bUNxbtXStL0jZvoHwE2JtmQZA2wDdg37M5Vtb+qdl588cVjhiFJWsoo0yv3Ag8B1yQ5lmRHVT0D3AYcBA4D91TVYyMc0xG9JE3ZKLNuti/RfgA4sJKTez96SZo+nzAlSY2baaK3Ri9J0+dNzSSpcTO9TXGS64Hrr7766lmGIakB/gXs0izdSFLjLN1IUuPO+dJN/9e1b97+tglEJUltsXQjSY3zmbGSzllegB2ONXpJapx/GStJjbNGL0mNs3QjSY0z0UtS46zRS1LjrNFLUuMs3UhS40z0ktQ4E70kNc5EL0mNM9FLUuPO+dsUSzq/eCOz0Tm9UpIaZ+lGkhrX1P3ofdqUJD2bI3pJapyJXpIaZ6KXpMaZ6CWpcSZ6SWrcxBN9kquSfDTJvZM+tiRpdEMl+iR3JTmR5NEF7ZuTHEkyn2QXQFUdraod0whWkjS6YUf0e4DN/YYkFwB3AFuATcD2JJsmGp0kaWxDJfqqehB4ckHztcB8N4J/Grgb2DrsiZPsTHIoyaGTJ08OHbAkaTTj1OgvB57oLR8DLk/yoiR3Aq9K8v6ldq6q3VU1V1Vza9euHSMMSdJyJn4LhKr6HnDrMNt690pJmr5xRvTHgSt7y1d0bUPz7pWSNH3jjOgfATYm2cAgwW8DfmOUAziilzQM70E/nmGnV+4FHgKuSXIsyY6qega4DTgIHAbuqarHRjm5I3pJmr6hRvRVtX2J9gPAgZWe3BG9JE2fT5iSpMZ5rxtJalyzDwf3aVOSNGDpRpIaZ+lGkho300Sf5Poku0+dOjXLMCSpaZZuJKlxlm4kqXEmeklqXLPTKyWd27y/zeRYo5ekxlm6kaTGmeglqXEmeklqnBdjJZ01vAA7HV6MlaTGWbqRpMaZ6CWpcSZ6SWqciV6SGmeil6TGnRfTK32soHR28f/k6nJ6pSQ1ztKNJDXORC9JjTPRS1LjTPSS1DgTvSQ1zkQvSY0z0UtS40z0ktS4VNWsYyDJSeBbK9z9MuC7EwxnUoxrNMY1mrM1Ljh7Y2sxrpdV1dozbXRWJPpxJDlUVXOzjmMh4xqNcY3mbI0Lzt7Yzue4LN1IUuNM9JLUuBYS/e5ZB7AE4xqNcY3mbI0Lzt7Yztu4zvkavSRpeS2M6CVJyzgnEn2SX0/yWJKfJFny6nSSzUmOJJlPsqvXviHJ57v2jyVZM6G4Lk3yqSSPd98vWWSbNyT5Yu/rf5Pc2K3bk+QbvXWvXK24uu1+3Dv3vl77LPvrlUke6t7vLyd5Z2/dRPtrqc9Lb/1F3b9/vuuP9b117+/ajyR5yzhxrCCuP0zy1a5/PpPkZb11i76nqxTXu5Oc7J3/d3vrbu7e98eT3LzKcX2wF9PXkvygt26a/XVXkhNJHl1ifZJ8qIv7y0le3Vs32f6qqrP+C3gFcA3wADC3xDYXAF8HrgLWAF8CNnXr7gG2da/vBN4zobj+CtjVvd4F/OUZtr8UeBL4mW55D3DTFPprqLiAHy7RPrP+An4O2Ni9finwbeCFk+6v5T4vvW1+H7ize70N+Fj3elO3/UXAhu44F6xiXG/ofYbeczqu5d7TVYrr3cCHF9n3UuBo9/2S7vUlqxXXgu3fC9w17f7qjv2LwKuBR5dY/1bgk0CA1wKfn1Z/nRMj+qo6XFVHzrDZtcB8VR2tqqeBu4GtSQK8Ebi32+5vgRsnFNrW7njDHvcm4JNV9d8TOv9SRo3rp2bdX1X1tap6vHv9n8AJ4Ix/ELICi35elon3XuCXuv7ZCtxdVU9V1TeA+e54qxJXVX229xl6GLhiQuceK65lvAX4VFU9WVXfBz4FbJ5RXNuBvRM697Kq6kEGA7ulbAX+rgYeBl6Y5CVMob/OiUQ/pMuBJ3rLx7q2FwE/qKpnFrRPwour6tvd6/8CXnyG7bfx7A/Zn3e/tn0wyUWrHNdzkxxK8vDpchJnUX8luZbBKO3rveZJ9ddSn5dFt+n64xSD/hlm32nG1beDwajwtMXe09WM69e69+feJFeOuO8046IrcW0A7u81T6u/hrFU7BPvr5k+HLwvyaeBn11k1Qeq6l9WO57Tlourv1BVlWTJKUzdT+qfBw72mt/PIOGtYTDF6n3An61iXC+rquNJrgLuT/IVBslsxSbcX38P3FxVP+maV9xfLUryLmAOeH2v+VnvaVV9ffEjTNx+YG9VPZXk9xj8NvTGVTr3MLYB91bVj3tts+yvVXPWJPqqetOYhzgOXNlbvqJr+x6DX4ku7EZlp9vHjivJd5K8pKq+3SWmE8sc6h3AJ6rqR71jnx7dPpXkb4A/Ws24qup49/1okgeAVwH/xIz7K8kLgPsY/JB/uHfsFffXIpb6vCy2zbEkFwIXM/g8DbPvNOMiyZsY/PB8fVU9dbp9ifd0EonrjHFV1fd6ix9hcE3m9L7XLdj3gQnENFRcPduAP+g3TLG/hrFU7BPvr5ZKN48AGzOYMbKGwZu6rwZXNz7LoD4OcDMwqd8Q9nXHG+a4z6oNdsnudF38RmDRq/PTiCvJJadLH0kuA14HfHXW/dW9d59gULu8d8G6SfbXop+XZeK9Cbi/6599wLYMZuVsADYC/z5GLCPFleRVwF8DN1TViV77ou/pKsb1kt7iDcDh7vVB4M1dfJcAb+b//2Y71bi62F7O4MLmQ722afbXMPYBv9XNvnktcKobzEy+vyZ9pXkaX8CvMqhTPQV8BzjYtb8UONDb7q3A1xj8RP5Ar/0qBv8R54F/BC6aUFwvAj4DPA58Gri0a58DPtLbbj2Dn9LPWbD//cBXGCSsfwCev1pxAb/QnftL3fcdZ0N/Ae8CfgR8sff1ymn012KfFwaloBu618/t/v3zXX9c1dv3A91+R4AtE/68nymuT3f/D073z74zvaerFNdfAI915/8s8PLevr/T9eM88NurGVe3/CfA7Qv2m3Z/7WUwa+xHDPLXDuBW4NZufYA7uri/Qm9G4aT7y7+MlaTGtVS6kSQtwkQvSY0z0UtS40z0ktQ4E70kNc5EL0mNM9FLUuNM9JLUuP8D1QA8zeWYdzwAAAAASUVORK5CYII=\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": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "doublets\n", + "1046526\n", + "916430\n", + "760030\n", + "808590\n", + "436318\n", + "165447\n", + "934476\n", + "942520\n", + "triplets and quads\n", + "1567123\n", + "4597455\n", + "1070839\n", + "888560\n", + "1707720\n", + "pentuplets\n", + "1229586\n", + "triplets\n", + "197639\n", + "82324\n", + "8264574\n", + " det1 phi1 pt1 r1 tkz1 trackID ys1 z1 det2 \\\n", + "0 51 -2.743778 428 3.0782 -10.6632 10108743 38 -3.1901 149 \n", + "1 51 -2.743778 428 3.0782 -10.6632 10108743 38 -3.1901 149 \n", + "2 51 -2.743778 428 3.0782 -10.6632 10108743 38 -3.1901 220 \n", + "3 51 -2.743778 428 3.0782 -10.6632 10108743 38 -3.1901 220 \n", + "4 51 -2.743778 428 3.0782 -10.6632 10108743 38 -3.1901 276 \n", + "\n", + " phi2 ... tkz3 ys3 z3 det4 phi4 pt4 r4 \\\n", + "0 1.556257 ... -10.6632 15 16.2390 896 2.790262 428 15.8156 \n", + "1 1.556257 ... -10.6632 15 -26.1485 896 2.790262 428 15.8156 \n", + "2 -2.697440 ... -10.6632 15 16.2390 896 2.790262 428 15.8156 \n", + "3 -2.697440 ... -10.6632 15 -26.1485 896 2.790262 428 15.8156 \n", + "4 -1.312738 ... -10.6632 15 16.2390 896 2.790262 428 15.8156 \n", + "\n", + " tkz4 ys4 z4 \n", + "0 -10.6632 29 -20.3185 \n", + "1 -10.6632 29 -20.3185 \n", + "2 -10.6632 29 -20.3185 \n", + "3 -10.6632 29 -20.3185 \n", + "4 -10.6632 29 -20.3185 \n", + "\n", + "[5 rows x 29 columns]\n", + " det1 phi1 pt1 r1 tkz1 trackID ys1 z1 det2 \\\n", + "0 16 1.563797 520 3.3717 -0.5042 10109870 8 -24.5975 120 \n", + "1 16 1.563797 520 3.3717 -0.5042 10109870 8 -24.5975 120 \n", + "2 16 1.563797 520 3.3717 -0.5042 10109870 8 -24.5975 176 \n", + "3 16 1.563797 520 3.3717 -0.5042 10109870 8 -24.5975 176 \n", + "4 16 1.563797 520 3.3717 -0.5042 10109870 8 -24.5975 288 \n", + "\n", + " phi2 ... tkz3 ys3 z3 det4 phi4 pt4 r4 \\\n", + "0 0.844785 ... -0.5042 8 -23.8326 1584 2.232684 520 6.2797 \n", + "1 0.844785 ... -0.5042 22 -16.4548 1584 2.232684 520 6.2797 \n", + "2 2.428455 ... -0.5042 8 -23.8326 1584 2.232684 520 6.2797 \n", + "3 2.428455 ... -0.5042 22 -16.4548 1584 2.232684 520 6.2797 \n", + "4 -0.699882 ... -0.5042 8 -23.8326 1584 2.232684 520 6.2797 \n", + "\n", + " tkz4 ys4 z4 \n", + "0 -0.5042 24 -32.4748 \n", + "1 -0.5042 24 -32.4748 \n", + "2 -0.5042 24 -32.4748 \n", + "3 -0.5042 24 -32.4748 \n", + "4 -0.5042 24 -32.4748 \n", + "\n", + "[5 rows x 29 columns]\n", + " det1 phi1 pt1 r1 tkz1 trackID ys1 z1 det2 \\\n", + "0 9 0.607251 527 2.7912 -10.6632 10108665 38 -16.9096 112 \n", + "1 9 0.607251 527 2.7912 -10.6632 10108665 38 -16.9096 112 \n", + "2 1 0.407491 817 3.0819 -10.6632 10108708 39 -17.8389 104 \n", + "3 1 0.407491 817 3.0819 -10.6632 10108708 39 -17.8389 104 \n", + "4 18 1.477864 636 3.3147 -2.6296 10109242 61 -12.4370 144 \n", + "\n", + " phi2 ... tkz3 ys3 z3 det4 phi4 pt4 r4 \\\n", + "0 0.583124 ... -10.6632 23 -33.3398 1713 0.503327 527 12.3503 \n", + "1 0.563076 ... -10.6632 23 -33.3398 1713 0.503327 527 12.3503 \n", + "2 0.392422 ... -10.6632 16 -32.6356 1712 0.346636 817 11.6805 \n", + "3 0.383833 ... -10.6632 16 -32.6356 1712 0.346636 817 11.6805 \n", + "4 1.421299 ... -2.6296 15 -34.3925 1718 1.436485 636 11.2936 \n", + "\n", + " tkz4 ys4 z4 \n", + "0 -10.6632 12 -38.2960 \n", + "1 -10.6632 12 -38.2960 \n", + "2 -10.6632 12 -37.9141 \n", + "3 -10.6632 12 -37.9141 \n", + "4 -2.6296 8 -39.6887 \n", + "\n", + "[5 rows x 29 columns]\n", + " det1 phi1 pt1 r1 tkz1 trackID ys1 z1 det2 \\\n", + "0 1 0.407491 817 3.0819 -10.6632 10108708 39 -17.8389 1578 \n", + "1 17 1.449064 1170 3.3014 -0.7713 30000000 96 -17.4924 1525 \n", + "2 17 1.449064 1170 3.3014 -0.7713 30000000 96 -17.4924 1581 \n", + "3 49 -2.780616 2358 3.0667 -0.7713 30000020 69 -13.9557 1532 \n", + "4 49 -2.780616 2358 3.0667 -0.7713 30000020 69 -13.9557 1532 \n", + "\n", + " phi2 ... tkz3 ys3 z3 det4 phi4 pt4 r4 \\\n", + "0 0.431484 ... -10.6632 12 -37.9141 1745 0.429261 817 6.2581 \n", + "1 1.465457 ... -0.7713 8 -41.5832 1749 1.481807 1170 9.9986 \n", + "2 1.465131 ... -0.7713 8 -41.5832 1749 1.481807 1170 9.9986 \n", + "3 -2.770849 ... -0.7713 10 -39.9314 1785 -2.762848 2358 10.8542 \n", + "4 -2.770849 ... -0.7713 10 -39.9314 1841 -2.762935 2358 10.8153 \n", + "\n", + " tkz4 ys4 z4 \n", + "0 -10.6632 27 -48.7891 \n", + "1 -0.7713 12 -51.4282 \n", + "2 -0.7713 12 -51.4282 \n", + "3 -0.7713 11 -47.4613 \n", + "4 -0.7713 8 -47.2809 \n", + "\n", + "[5 rows x 29 columns]\n", + " det1 phi1 pt1 r1 tkz1 trackID ys1 z1 det2 \\\n", + "0 90 -0.121753 1503 2.7600 -10.6632 10108654 15 -9.3011 314 \n", + "1 26 2.061310 1979 2.8019 -10.6632 10108681 16 -8.3855 171 \n", + "2 11 0.549674 1200 2.8279 -10.6632 10108723 38 -4.6503 116 \n", + "3 2 0.185950 692 3.0690 -10.6632 10108725 8 -10.2526 98 \n", + "4 2 0.474354 992 3.1161 -10.6632 10108762 12 -11.6205 114 \n", + "\n", + " phi2 ... tkz4 ys4 z4 det5 phi5 pt5 r5 tkz5 ys5 z5 \n", + "0 -0.136638 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "1 2.052115 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "2 0.529316 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "3 0.217089 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "4 0.495698 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN \n", + "\n", + "[5 rows x 36 columns]\n", + " det1 phi1 pt1 r1 tkz1 trackID ys1 z1 det2 \\\n", + "0 1 0.407491 817 3.0819 -10.6632 10108708 39 -17.8389 104 \n", + "1 1 0.407491 817 3.0819 -10.6632 10108708 39 -17.8389 104 \n", + "2 49 -2.780616 2358 3.0667 -0.7713 30000020 69 -13.9557 229 \n", + "3 49 -2.780616 2358 3.0667 -0.7713 30000020 69 -13.9557 229 \n", + "4 52 -2.967134 638 3.0724 3.4827 30113381 47 3.4712 177 \n", + "\n", + " phi2 ... tkz4 ys4 z4 det5 phi5 pt5 r5 \\\n", + "0 0.392422 ... -10.6632 12 -37.9141 1745 0.429261 817 6.2581 \n", + "1 0.383833 ... -10.6632 12 -37.9141 1745 0.429261 817 6.2581 \n", + "2 -2.469347 ... -0.7713 10 -39.9314 1785 -2.762848 2358 10.8542 \n", + "3 -2.469347 ... -0.7713 10 -39.9314 1841 -2.762935 2358 10.8153 \n", + "4 2.312844 ... 3.4827 8 -39.7081 1756 -2.802110 638 7.7169 \n", + "\n", + " tkz5 ys5 z5 \n", + "0 -10.6632 27 -48.7891 \n", + "1 -10.6632 27 -48.7891 \n", + "2 -0.7713 11 -47.4613 \n", + "3 -0.7713 8 -47.2809 \n", + "4 3.4827 8 -49.2893 \n", + "\n", + "[5 rows x 36 columns]\n" + ] + } + ], + "source": [ + "t12 = pd.merge(build(hb1,'1'),build(hb2,'2'),on='trackID')\n", + "t23 = pd.merge(build(hb2,'1'),build(hb3,'2'),on='trackID')\n", + "t34 = pd.merge(build(hb3,'1'),build(hb4,'2'),on='trackID')\n", + "\n", + "print 'doublets'\n", + "print len(t12)\n", + "print len(t23)\n", + "print len(t34)\n", + "t11 = pd.merge(build(hb1,'1'),build(hf1,'2'),on='trackID')\n", + "print len(t11)\n", + "\n", + "t21 = pd.merge(build(hb2,'1'),build(hf1,'2'),on='trackID')\n", + "print len(t21)\n", + "t31 = pd.merge(build(hb3,'1'),build(hf1,'2'),on='trackID')\n", + "print len(t31)\n", + "\n", + "t12f = pd.merge(build(hf1,'1'),build(hf2,'2'),on='trackID')\n", + "print len(t12f)\n", + "t23f = pd.merge(build(hf2,'1'),build(hf3,'2'),on='trackID')\n", + "print len(t23f)\n", + "\n", + "lpairs = [t12,t23,t34,t11,t21,t31,t12f,t23f]\n", + "\n", + "print 'triplets and quads'\n", + "t123 = pd.merge(t12,build(hb3,'3'),on='trackID')\n", + "print len(t123)\n", + "t1234 = pd.merge(t123,build(hb4,'4'),on='trackID')\n", + "print len(t1234)\n", + "t1231 = pd.merge(t123,build(hf1,'4'),on='trackID')\n", + "print len(t1231)\n", + "t121 = pd.merge(t12,build(hf1,'3'),on='trackID')\n", + "t1212 = pd.merge(t121,build(hf2,'4'),on='trackID')\n", + "print len(t1212)\n", + "t11 = pd.merge(build(hb1,'1'),build(hf1,'2'),on='trackID')\n", + "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()\n", + "print t1230.head()\n", + "print t12123.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "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": 23, + "metadata": {}, + "outputs": [], + "source": [ + "def plotTriplets(quad,mpt,kq) :\n", + " \n", + " print \" \"\n", + " print \"New QUAD\",kq\n", + " print \" \"\n", + " \n", + " \n", + " d1 = (quad['r1']*quad['z2']-quad['z1']*quad['r2'])/(quad['r1']-quad['r2'])\n", + " d2 = (quad['r2']*quad['z3']-quad['z2']*quad['r3'])/(quad['r2']-quad['r3'])\n", + " d3 = (quad['r3']*quad['z4']-quad['z3']*quad['r4'])/(quad['r3']-quad['r4'])\n", + " \n", + " z0cut = np.logical_and(abs(d1)<10.,np.logical_and(abs(d2)<10.,abs(d3)<10.))\n", + "\n", + " quadc = quad[np.logical_and(z0cut,quad['pt1']>mpt)]\n", + "\n", + "# print 'dpt'\n", + "# plt.hist(quad['pt1']-quad['pt2'],log=True, bins=100)\n", + "# plt.show()\n", + "# plt.hist(quad['pt2']-quad['pt3'],log=True, bins=100)\n", + "# plt.show()\n", + "# plt.hist(quad['pt3']-quad['pt4'],log=True, bins=100)\n", + "# plt.show()\n", + "\n", + "\n", + " print 'delta123', len(quadc)\n", + " \n", + "#dt,dtn = ml(t123['tpt'],t123['tpz'],t123['tpt2'],t123['tpz2'])\n", + "#plt.hist(dt[dt<0.1], log=True, bins=100)\n", + "#plt.show()\n", + "#plt.hist(dtn[dtn<0.1], log=True, bins=100)\n", + "#plt.show()\n", + "\n", + " \n", + " thcut = alignRZ(quadc,'r',0.6,True)\n", + " pzcut = alignRZ(quadc,'phi',1.0,True)\n", + " thcut2 = alignRPZ(quadc,'r',True)\n", + " pzcut2 = alignRPZ(quadc,'phi',True)\n", + "\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)<5],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(dc[abs(dc)<0.3],log=True, bins=100)\n", + " plt.show()\n", + "\n", + " print 'thcut2,pzcut2',len(thcut2)\n", + " plt.hist(thcut2[abs(thcut2)<0.6],log=True, bins=100)\n", + " plt.show()\n", + " plt.hist(pzcut2[abs(pzcut2)<0.2],log=True, bins=100)\n", + " plt.show()\n", + " \n", + " print 'delta234'\n", + "\n", + " thcut = alignRZ(quadc,'r',0.6,False)\n", + " pzcut = alignRZ(quadc,'phi',1.0,False)\n", + " thcut2 = alignRPZ(quadc,'r',False)\n", + " pzcut2 = alignRPZ(quadc,'phi',False)\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)<5],log=True, bins=100)\n", + " plt.show()\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(dc[abs(dc)<0.3],log=True, bins=100)\n", + " plt.show()\n", + "\n", + " \n", + " print 'thcut2,pzcut2',len(thcut2)\n", + " plt.hist(thcut2[abs(thcut2)<0.6],log=True, bins=100)\n", + " plt.show()\n", + " plt.hist(pzcut2[abs(pzcut2)<0.2],log=True, bins=100)\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "kq=0\n", + "def plotDoublets(quad,mpt,kq) :\n", + " quadc = quad[quad['pt1']>mpt]\n", + "\n", + " maxc = 1000./(mpt*87.78)\n", + "\n", + " print \" \"\n", + " print \"New QUAD\",kq\n", + " print \" \"\n", + " \n", + " print 'dphi'\n", + " d1 = quadc['phi2']-quadc['phi1']\n", + " plt.hist(d1[abs(d1)<.1], bins=100,log=True)\n", + " plt.show()\n", + " d2 = quadc['phi3']-quadc['phi2']\n", + " plt.hist(d2[abs(d2)<.1], bins=100,log=True)\n", + " plt.show()\n", + " d3 = quadc['phi4']-quadc['phi3']\n", + " plt.hist(d3[abs(d3)<.1], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " pcut = np.logical_and(abs(d1)<0.05,np.logical_and(abs(d2)<0.05,abs(d3)<0.05))\n", + "\n", + "\n", + " print 'dphiNor'\n", + " \n", + " pc = phicut(quadc['r1'],quadc['r2'],maxc)\n", + " d1 = (quadc['phi2']-quadc['phi1'])/pc\n", + " plt.hist(d1[abs(d1)<2.], bins=100,log=True)\n", + " plt.show()\n", + " pc = phicut(quadc['r2'],quadc['r3'],maxc)\n", + " d2 = (quadc['phi3']-quadc['phi2'])/pc\n", + " plt.hist(d2[abs(d2)<2.], bins=100,log=True)\n", + " plt.show()\n", + " pc = phicut(quadc['r3'],quadc['r4'],maxc)\n", + " d3 = (quadc['phi4']-quadc['phi3'])/pc\n", + " plt.hist(d3[abs(d3)<2.], bins=100,log=True)\n", + " plt.show()\n", + "\n", + "\n", + " print 'zinner'\n", + " d1 = quadc['z1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<35)], bins=100,log=True)\n", + " plt.show()\n", + " d2 = quadc['z2']\n", + " plt.hist(d2[np.logical_and(pcut,abs(d2)<35)], bins=100,log=True)\n", + " plt.show()\n", + " d3 = quadc['z3']\n", + " plt.hist(d3[np.logical_and(pcut,abs(d3)<35)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'dz'\n", + " d1 = quadc['z2']-quadc['z1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<35)], bins=100,log=True)\n", + " plt.show()\n", + " d2 = quadc['z3']-quadc['z2']\n", + " plt.hist(d2[np.logical_and(pcut,abs(d2)<35)], bins=100,log=True)\n", + " plt.show()\n", + " d3 = quadc['z4']-quadc['z3']\n", + " plt.hist(d3[np.logical_and(pcut,abs(d3)<35)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'z0'\n", + " d1 = (quadc['r1']*quadc['z2']-quadc['z1']*quadc['r2'])/(quadc['r1']-quadc['r2'])\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<50)], bins=100,log=True)\n", + " plt.show()\n", + " d2 = (quadc['r2']*quadc['z3']-quadc['z2']*quadc['r3'])/(quadc['r2']-quadc['r3'])\n", + " plt.hist(d2[np.logical_and(pcut,abs(d2)<50)], bins=100,log=True)\n", + " plt.show()\n", + " d3 = (quadc['r3']*quadc['z4']-quadc['z3']*quadc['r4'])/(quadc['r3']-quadc['r4'])\n", + " plt.hist(d3[np.logical_and(pcut,abs(d3)<50)], bins=100,log=True)\n", + " plt.show()\n", + " \n", + "\n", + " pcut = np.logical_and(abs(d1)<10.,np.logical_and(abs(d2)<10.,abs(d3)<10.))\n", + "\n", + "\n", + " print 'dr'\n", + " d1 = quadc['r2']-quadc['r1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<20)], bins=100,log=True)\n", + " plt.show()\n", + " d2 = quadc['r3']-quadc['r2']\n", + " plt.hist(d2[np.logical_and(pcut,abs(d2)<20)], bins=100,log=True)\n", + " plt.show()\n", + " d3 = quadc['r4']-quadc['r3']\n", + " plt.hist(d3[np.logical_and(pcut,abs(d3)<20)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'dphi zcut'\n", + " d1 = quadc['phi2']-quadc['phi1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<.1)], bins=100,log=True)\n", + " plt.show()\n", + " d2 = quadc['phi3']-quadc['phi2']\n", + " plt.hist(d2[np.logical_and(pcut,abs(d2)<.1)], bins=100,log=True)\n", + " plt.show()\n", + " d3 = quadc['phi4']-quadc['phi3']\n", + " plt.hist(d3[np.logical_and(pcut,abs(d3)<.1)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'zinner zcut'\n", + " d1 = quadc['z1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<35)], bins=100,log=True)\n", + " plt.show()\n", + " d2 = quadc['z2']\n", + " plt.hist(d2[np.logical_and(pcut,abs(d2)<35)], bins=100,log=True)\n", + " plt.show()\n", + " d3 = quadc['z3']\n", + " plt.hist(d3[np.logical_and(pcut,abs(d3)<35)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'module'\n", + " d1 = quadc['det1']\n", + " plt.hist(d1[pcut], range=[0,96], bins=96, log=False)\n", + " plt.show()\n", + " d2 = quadc['det2']\n", + " plt.hist(d2[pcut], range=[0,2000], bins=2000, log=False)\n", + " plt.show()\n", + " d3 = quadc['det3']\n", + " plt.hist(d3[pcut], range=[0,2000], bins=2000, log=False)\n", + " plt.show()\n", + "\n", + "\n", + "\n", + " norm = 1./8.\n", + " print 'dysize'\n", + " d1 = (quadc['ys2']-quadc['ys1'])*norm\n", + " c = np.logical_and(quadc['ys2']>0,quadc['ys1']>0)\n", + " plt.hist(d1[np.logical_and(pcut,c)], range=[-8,8],bins=32,log=True)\n", + " plt.show()\n", + " c = np.logical_and(quadc['ys2']>0,quadc['ys3']>0)\n", + " d2 = (quadc['ys3']-quadc['ys2'])*norm\n", + " plt.hist(d2[np.logical_and(pcut,c)], range=[-6,6],bins=26,log=True)\n", + " plt.show()\n", + " c = np.logical_and(quadc['ys4']>0,quadc['ys3']>0)\n", + " d3 = (quadc['ys4']-quadc['ys3'])*norm\n", + " plt.hist(d3[np.logical_and(pcut,c)], range=[-6,6],bins=26,log=True)\n", + " plt.show()\n", + "\n", + " \n", + " print 'ysize'\n", + " d1 = quadc['ys1']*norm\n", + " plt.hist(d1[pcut], bins=30,range=[0,30],log=True)\n", + " plt.show()\n", + " d2 = quadc['ys2']*norm\n", + " plt.hist(d2[pcut], bins=30,range=[0,30],log=True)\n", + " plt.show()\n", + " d3 = quadc['ys3']*norm\n", + " plt.hist(d3[pcut], bins=30,range=[0,30],log=True)\n", + " plt.show()\n", + " \n", + " print 'ys-pred'\n", + " th = 0.0285\n", + " ptc = 0.015\n", + " fac = th/ptc\n", + " d1 = fac*(quadc['z1']-quadc['z2'])/(quadc['r1']-quadc['r2'])\n", + " d1 = quadc['ys1']*norm - abs(d1)\n", + " plt.hist(d1[np.logical_and(pcut,quadc['ys1']>0)], range=[-8,8],bins=32,log=True)\n", + " plt.show()\n", + " d2 = fac*(quadc['z3']-quadc['z2'])/(quadc['r3']-quadc['r2'])\n", + " d2 = quadc['ys2']*norm - abs(d2)\n", + " plt.hist(d2[np.logical_and(pcut,quadc['ys2']>0)], range=[-8,8],bins=32,log=True)\n", + " plt.show()\n", + " d3 = fac*(quadc['z3']-quadc['z4'])/(quadc['r3']-quadc['r4'])\n", + " d3 = quadc['ys3']*norm - abs(d3)\n", + " plt.hist(d3[np.logical_and(pcut,quadc['ys3']>0)], range=[-8,8],bins=32,log=True)\n", + " plt.show()\n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "New QUAD 0\n", + " \n", + "delta123 453415\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 453415\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 453415\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 453415\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 453415\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": [ + " \n", + "New QUAD 1\n", + " \n", + "delta123 73463\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 73463\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 73463\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/UCwAAD11JREFUeJzt3X+s3Xddx/Hny86NBPQ66ETSrdwurcRqjMTrSCQaIgidWxlRYlqMQV1swMz/TChBY0JiMo2JgWTJ0sAoxrAxZ4Idq87BnPMP1HXIj41lcikja4NugFQ0BDJ5+8f9zhwuvbfn9/ecz30+kqXnfM+P+/6cH699zvv7OeebqkKS1K7v67sASdJsGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxl3WdwEAu3fvrtXV1b7LkKSl8uijj36lqq661PUWIuhXV1c5c+ZM32VI0lJJ8qVhrtdr6ybJ4SQnLly40GcZktS0XoO+qu6tqmMrKyt9liFJTXNnrCQ1zqCXpMYZ9JLUOINekhrnqhtJapyrbiSpcQvxhSlpUa0ev+//Tz916w09ViKNzx69JDXOGb20yeAsXmqBM3pJapxBL0mNM+glqXEGvSQ1zqCXpMb5zVhJapzfjJWkxtm6kaTGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktS4mQR9khcmOZPkxlncvyRpeEMFfZI7kjyT5LFN2w8leTLJepLjAxe9A7h7moVKksYz7Iz+JHBocEOSXcBtwPXAQeBokoNJfhH4HPDMFOuUJI1pqIODV9XDSVY3bb4OWK+qswBJ7gJuAl4EvJCN8P9mktNV9Z2pVSxJGslQQb+FPcDTA+fPAa+qqlsAkvwG8JWtQj7JMeAYwN69eycoQ5K0nZmtuqmqk1X10W0uP1FVa1W1dtVVV82qDEna8SYJ+vPANQPnr+62SZIWyCRB/whwIMm+JJcDR4BTo9yBhxKUpNkbdnnlncAngFckOZfk5qp6DrgFuB94Ari7qh4f5Y97KEFJmr1hV90c3WL7aeD0uH88yWHg8P79+8e9C0nSJXhwcElqnL91I0mN6zXo3RkrSbNn60aSGmfrRpIaZ9BLUuPs0UtS4+zRS1LjbN1IUuMMeklqnD16SWqcPXpJapytG0lqnEEvSY0z6CWpcQa9JDXOVTeS1DhX3UhS42zdSFLjDHpJapxBL0mNM+glqXGuupGkxrnqRpIaZ+tGkhpn0EtS4wx6SWqcQS9Jjbus7wKkZbF6/L7vOv/UrTf0VIk0GoNe4ntDXGqJrRtJapxBL0mN85uxktQ4vxkrSY2zdSNJjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY0z6CWpcVMP+iQ/luT2JPckefu071+SNJqhgj7JHUmeSfLYpu2HkjyZZD3JcYCqeqKq3gb8KvDq6ZcsSRrFsDP6k8ChwQ1JdgG3AdcDB4GjSQ52l70RuA84PbVKJUljGeoIU1X1cJLVTZuvA9ar6ixAkruAm4DPVdUp4FSS+4APTa9caXEMHpXKwwpqkU1yKME9wNMD588Br0ryGuCXgSvYZkaf5BhwDGDv3r0TlCFJ2s7UjxlbVQ8BDw1xvRPACYC1tbWadh2SpA2TrLo5D1wzcP7qbtvQPMKUJM3eJDP6R4ADSfaxEfBHgLeMcgdVdS9w79ra2m9PUIc0lsEeu9SyYZdX3gl8AnhFknNJbq6q54BbgPuBJ4C7q+rx2ZUqSRrHsKtujm6x/TQTLKFMchg4vH///nHvQpJ0CVPfGTsKWzdqhUsttcj8rRtJalyvQe+qG0mavV6DvqrurapjKysrfZYhSU3rtUcvtch+vRaNPXpJapw9eklqnMsrtaP4bVjtRPbopRmyX69FYI9ekhrX64zen0DQPNiu0U7nOnpJapytG0lqnEEvSY1z1Y2atIh9eVfgqC/ujFUzFjHcpUXgF6akHji71zzZo5ekxtmj11KzXSNdmjN6SWqcQS9JjbN1I/Vsq/aTO2k1Lf4evSQ1LlXVdw2sra3VmTNn+i5DS2In7oB1dq+LSfJoVa1d6nq2brQUdmK4S9PizlhJapxBL0mNM+glqXH26LVQ/A0YafoMei0sd8BK02HQS0vATzqahD16SWqcBx5RL5yhSvPjgUc0Uwa61D979NKS8X+eGpVBr7nZahWNq2uk2TLopUb4c8faiqtuJKlx/kyxps5WzOJydt8Wf6ZY0vdwR+7OZNBrKpzFS4vLoJd2KGf3O4c7YyWpcQa9JDVuJq2bJG8CbgB+EHh/Vf3dLP6OJOnShg76JHcANwLPVNVPDGw/BLwH2AW8r6puraqPAB9JciXwp4BB3wj7utLyGaV1cxI4NLghyS7gNuB64CBwNMnBgav8fne5JKknQ8/oq+rhJKubNl8HrFfVWYAkdwE3JXkCuBX4m6r65JRq1YJxSaW0HCbt0e8Bnh44fw54FfC7wOuAlST7q+r2zTdMcgw4BrB3794Jy5A0CX8np20z2RlbVe8F3nuJ65wATsDGTyDMog5J0uRBfx64ZuD81d22oXiEqX65Y1XaGSYN+keAA0n2sRHwR4C3DHtjjzC1mOy962KcGCyvUZZX3gm8Btid5Bzwh1X1/iS3APezsbzyjqp6fCaV7nCzfpMZ7lK7Rll1c3SL7aeB0+P8cVs30mJzAtCGXn8CoarurapjKysrfZYhSU3zt24kqXG9/kyxrZvpcmeZ5mW715qvw8Vj60aSGueBRyRNZLsdtn7jdjEY9EtuqzeSH5+1jHzdzoY9+iU06pI3l8hJO5s9eklqnK2bBeZMXDuBr/PZM+gXgH1JSbNkj37BOLuRNG29Bv1O/vVKA13SvNi6kTR3TnTmy6CfgL11af58343OoB+RMxFpPgz06XFn7AC/ri2pRe6MnTFnJZL6ZutmBob5/RlJmheDXpKGsMyfzpc+6Belr+5sXdKiWvqg38oy/99Xkqap2aCXJHDSBy6vlLTEDPHhuLxyCPbfJS0zWzeSdqStPg20+ClhxwV9i0+iJG1nxwX9IFsy0nIY5r06retMYlEnkr0eM1aSNHs7ekYvSdtp5VO/M3pJapxBL0mNs3UjSSNa1J2uW9kR34xtpc8mSePotXVTVfdW1bGVlZU+y5Ckptmjl6TGGfSS1Dh3xkrSDGy3b3DeO3ANekk7XusLNmzdSFLjDHpJapxBL0mNM+glqXHujJWkCSzDjlxn9JLUuKkHfZJrk7w/yT3Tvm9J0uiGCvokdyR5Jsljm7YfSvJkkvUkxwGq6mxV3TyLYiVJoxt2Rn8SODS4Icku4DbgeuAgcDTJwalWJ0ma2FBBX1UPA1/btPk6YL2bwX8buAu4acr1SZImNEmPfg/w9MD5c8CeJC9JcjvwyiTv3OrGSY4lOZPkzLPPPjtBGZKk7Ux9eWVVfRV42xDXOwGcAFhbW6tp1yFJ2jDJjP48cM3A+au7bUNLcjjJiQsXLkxQhiRpO5ME/SPAgST7klwOHAFOjXIHHmFKkmZv2OWVdwKfAF6R5FySm6vqOeAW4H7gCeDuqnp8dqVKksYxVI++qo5usf00cHrcPz6vg4NL0k7mwcElqXH+1o0kNa7XoHfVjSTNnq0bSWqcrRtJapxBL0mN6/UIUy6vlLQTDR6V6qlbb5j537NHL0mNs3UjSY0z6CWpca6jl6TG2aOXpMbZupGkxhn0ktQ4g16SGufOWElqXKr6Py53kmeBL415893AV6ZYTp8cy+JpZRzgWBbVJGN5eVVddakrLUTQTyLJmapa67uOaXAsi6eVcYBjWVTzGIs9eklqnEEvSY1rIehP9F3AFDmWxdPKOMCxLKqZj2Xpe/SSpO21MKOXJG1jKYI+yYuTPJDk892/V25xvb9N8vUkH920/WSSLyb5VPffT82n8ovWOOlY9iX55yTrST6c5PL5VH7RGocdy1u763w+yVsHtj+U5MmB5+WH51c9JDnU/f31JMcvcvkV3WO83j3mqwOXvbPb/mSSN8yz7osZdyxJVpN8c+A5uH3etW82xFh+PsknkzyX5M2bLrvoa60PE47jfweek1MTF1NVC/8f8CfA8e70ceCPt7jea4HDwEc3bT8JvLnvcUxpLHcDR7rTtwNvX+SxAC8Gznb/XtmdvrK77CFgrafadwFfAK4FLgc+DRzcdJ3fAW7vTh8BPtydPthd/wpgX3c/u3p8HiYZyyrwWF+1jzmWVeAngT8ffF9v91pbpnF0l/33NOtZihk9cBPwwe70B4E3XexKVfVx4BvzKmpMY48lSYBfAO651O3nZJixvAF4oKq+VlX/CTwAHJpTfdu5DlivqrNV9W3gLjbGM2hwfPcAr+2eg5uAu6rqW1X1RWC9u7++TDKWRXPJsVTVU1X1GeA7m267SK+1ScYxdcsS9C+tqi93p/8deOkY9/FHST6T5M+SXDHF2kY1yVheAny9qp7rzp8D9kyzuBENM5Y9wNMD5zfX/IHu4+kfzDl4LlXXd12ne8wvsPEcDHPbeZpkLAD7kvxrkn9I8nOzLvYSJnlsF+l5mbSWFyQ5k+Sfkkw8mev14OCDknwM+JGLXPSuwTNVVUlGXSr0TjaC6HI2ljK9A3j3OHUOY8ZjmasZj+XXqup8kh8A/gr4dTY+xmp+vgzsraqvJvlp4CNJfryq/qvvwna4l3fvjWuBB5N8tqq+MO6dLUzQV9XrtrosyX8keVlVfTnJy4BnRrzv52ed30ryAeD3Jih1mL83q7F8FfihJJd1s7KrgfMTlrutKYzlPPCagfNXs9Gbp6rOd/9+I8mH2Pi4O6+gPw9cs6muzY/l89c5l+QyYIWN52CY287T2GOpjYbwtwCq6tEkXwB+FDgz86ovbpLHdsvXWg8meo0MvDfOJnkIeCUbPf+xLEvr5hTw/B70twJ/PcqNuxB6vsf9JuCxqVY3mrHH0r0p/x54fg/9yI/FlA0zlvuB1ye5sluV83rg/iSXJdkNkOT7gRuZ7/PyCHCgW8V0ORs7KDevbhgc35uBB7vn4BRwpFvJsg84APzLnOq+mLHHkuSqJLsAutnjATZ2YvZlmLFs5aKvtRnVeSljj6Or/4ru9G7g1cDnJqqmjz3SY+zBfgnwceDzwMeAF3fb14D3DVzvH4FngW+y0RN7Q7f9QeCzbATJXwAvWuKxXMtGqKwDfwlcsQRj+a2u3nXgN7ttLwQeBT4DPA68hzmvXAF+Cfg3NmZK7+q2vRt4Y3f6Bd1jvN495tcO3PZd3e2eBK7v6zmYdCzAr3SP/6eATwKHl2AsP9O9J/6HjU9Yj2/3Wlu2cQA/2+XVp7t/b560Fr8ZK0mNW5bWjSRpTAa9JDXOoJekxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mN+z+xG+/V0ekd+gAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 73463\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADglJREFUeJzt3V2MXOddx/Hvr46SitCuUpyrJM46cqhwKqTCkiIQUESrOrSbVDRCSUFqIYqV0sAFNwSlEhLcBISQWhEpsmgUehM39CKyVZdQoCZCaiBOCM2bQh03VWwh8lK0CCitQv9c7AmdbHbtGc/LOfvs9yOtfObMmZm/jz2/eeZ5nvNsqgpJUrve0ncBkqT5MuglqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9Jjbug7wIAdu/eXcvLy32XIUnbymOPPfZKVV16ruMGEfTLy8ucOHGi7zIkaVtJ8s1xjrPrRpIaZ9BLUuMMeklqnEEvSY3rNeiTrCY5tLa21mcZktS0XoO+qo5W1cGlpaU+y5Ckptl1I0mNM+glqXGDuGBqGst3fPH/t1+464M9ViJJw2SLXpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDVu20+vHOVUS0l6M1v0ktQ4g16SGmfQS1LjDHpJapxBL0mNm0vQJ7k4yYkkH5rH80uSxjdW0Ce5N8lLSZ7asP9AkueSnExyx8hdvwM8MMtCJUnnZ9wW/X3AgdEdSXYBdwPXAfuBm5PsT/J+4BngpRnWKUk6T2NdMFVVDydZ3rD7WuBkVZ0CSHIYuAH4QeBi1sP/20mOVdX3Nj5nkoPAQYA9e/acb/1b8uIpSVo3zZWxlwEvjtw+Dbynqm4HSPJx4JXNQh6gqg4BhwBWVlZqijokSWcxtyUQquq+eT23JGl808y6OQNcMXL78m6fJGlApgn6R4Grk+xNciFwE3BkkidIsprk0Nra2hRlSJLOZtzplfcDXwXemeR0kluq6jXgduAh4Fnggap6epIXr6qjVXVwaWlp0rolSWMad9bNzVvsPwYcm2lFkqSZ6nUJBLtuJGn+eg16u24kaf5c1EySGmfQS1Lj7KOXpMbZRy9JjZvbEghD4gJnknYy++glqXEGvSQ1zsFYSWqcg7GS1Di7biSpcQa9JDXOoJekxjkYK0mNczBWkhpn140kNc6gl6TGGfSS1DiDXpIaZ9BLUuN6XaY4ySqwum/fvoW9pksWS9ppnF4pSY2z60aSGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zmWKJalxzqOXpMbZdSNJjTPoJalxBr0kNc6gl6TG9bp6Zd9GV7IEV7OU1CZb9JLUOINekhpn0EtS4wx6SWqcQS9JjXOtG0lqnGvdSFLj7LqRpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjdvRq1duNLqapStZSmqFLXpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS42Ye9El+JMk9Sb6Q5BOzfn5J0mTGCvok9yZ5KclTG/YfSPJckpNJ7gCoqmer6jbgl4Gfnn3JkqRJjNuivw84MLojyS7gbuA6YD9wc5L93X3XA18Ejs2sUknSeRkr6KvqYeBbG3ZfC5ysqlNV9V3gMHBDd/yRqroO+JVZFitJmtw0i5pdBrw4cvs08J4k7wV+CbiIs7TokxwEDgLs2bNnijIkSWcz89Urq+o4cHyM4w4BhwBWVlZq1nVMy5UsJbVimlk3Z4ArRm5f3u2TJA3INEH/KHB1kr1JLgRuAo5M8gRJVpMcWltbm6IMSdLZjDu98n7gq8A7k5xOcktVvQbcDjwEPAs8UFVPT/LiVXW0qg4uLS1NWrckaUxj9dFX1c1b7D+GUygladBcAkGSGtdr0NtHL0nz1+svB6+qo8DRlZWVW/us41ycailpO7PrRpIaZ9BLUuPso5ekxvUa9M6jl6T563UwdjtyYFbSdmMfvSQ1zj56SWqcffSS1Di7biSpcQa9JDXOoJekxhn0ktQ4Z91IUuNcvXIKXjwlaTuw60aSGmfQS1LjDHpJapxBL0mNM+glqXFOr5SkxrmomSQ1zl88MgfOr5c0JAb9jIyG+1b7DX1JfXAwVpIaZ9BLUuMMeklqnEEvSY3rdTA2ySqwum/fvj7LWBgHZiX1wXn0ktQ4p1f2xNa9pEUx6Bvih4ekzRj0A7DVxVaGtaRZcNaNJDXOFv2A2RUjaRZs0UtS4wx6SWqcXTfb3FYDuZL0Olv0ktQ4W/Q7wLwGdR0slrYH17rZhuyukTSJXoO+qo4CR1dWVm7ts44WLfrDwNa9NFx23WwTtuIlnS+DXhOZ9APHlr7UP4Neb+K3B6ktBv0OtojZOJL65zx6SWqcLXoBi2mFj/Ma9uNLs2fQ7zB2q0g7j103ktQ4g16SGmfQS1LjDHpJapyDsRoUr6SVZs+g17YwzQeAHx7a6ey6kaTG2aLXtrPVtQC29KXNzSXok3wY+CDwduCzVfVX83gdaej8MNEQjB30Se4FPgS8VFXvGtl/APg0sAv4s6q6q6oeBB5Mcgnwx4BBr4nN4yrerZ7TQFbLJmnR3wf8KfC513ck2QXcDbwfOA08muRIVT3THfKp7n5p7lzeQdrc2EFfVQ8nWd6w+1rgZFWdAkhyGLghybPAXcCXqurxGdUqLYSte7Vm2lk3lwEvjtw+3e37TeB9wI1JbtvsgUkOJjmR5MTLL788ZRmSpK3MZTC2qj4DfOYcxxwCDgGsrKzUPOqQZmlj11BfrX2/cWhS0wb9GeCKkduXd/ukJtjvrxZMG/SPAlcn2ct6wN8EfHTcBydZBVb37ds3ZRlSv7xyV0M2yfTK+4H3AruTnAZ+r6o+m+R24CHWp1feW1VPj/ucVXUUOLqysnLrZGVL/Rtaa98PDG1lklk3N2+x/xhwbGYVSQIMbs2OSyBIDfJDQqN6XdQsyWqSQ2tra32WIUlN67VFbx+9NGx+M2iDXTfSgAxtgHfR/GCZD4Ne6sHQAn1eAWtwD0OvQe88erVoaCG+le1S57zspA8h++ilBdnpwTorOymgZ8WuG2kb88ND4zDopcbNuwXsh83wOY9ekhpnH72khbOffbHsupF2ELtZdiaDXtIg2eqfnV776CVJ82eLXtoGhtDlYgt7On2eP2fdSFLjnHUj6Q2G8O1h0Vr/tmLXjaSFmMcHyFbP2WJYT8OglzR4fX3LaKWlb9BLmljL3Tst/t0Mekm9WmSwtvpa5+J69JKaNqvA3c7dOL1Or6yqo1V1cGlpqc8yJKlpXhkrSY2zj15Sc4bUPz4EtuglqXG26CVpRob6TcIWvSQ1zqCXpMYZ9JLUOC+YkqQFW/TFV14wJUmNs+tGkhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc61biRpQkNd02YrtuglqXEGvSQ1zqCXpMb1GvRJVpMcWltb67MMSWqaa91IUuPsupGkxhn0ktQ4g16SGmfQS1LjUlV910CSl4FvnufDdwOvzLCcWbGuyVjX5IZam3VNZpq6rqyqS8910CCCfhpJTlTVSt91bGRdk7GuyQ21NuuazCLqsutGkhpn0EtS41oI+kN9F7AF65qMdU1uqLVZ12TmXte276OXJJ1dCy16SdJZDDrokxxI8lySk0nu2OT+i5J8vrv/H5Isj9z3u93+55J8YAh1JVlO8u0kT3Q/9yy4rp9N8niS15LcuOG+jyX5evfzsQHV9b8j5+vIguv67STPJPlakr9JcuXIfX2er7PV1ef5ui3Jk91r/32S/SP39fl+3LSuvt+PI8d9JEklWRnZN9vzVVWD/AF2Ac8DVwEXAv8M7N9wzG8A93TbNwGf77b3d8dfBOztnmfXAOpaBp7q8XwtAz8KfA64cWT/O4BT3Z+XdNuX9F1Xd99/9ni+fh74gW77EyP/jn2fr03rGsD5evvI9vXAX3bbfb8ft6qr1/djd9zbgIeBR4CVeZ2vIbforwVOVtWpqvoucBi4YcMxNwB/3m1/AfiFJOn2H66q71TVN4CT3fP1Xdc8nbOuqnqhqr4GfG/DYz8AfLmqvlVV/w58GTgwgLrmaZy6vlJV/93dfAS4vNvu+3xtVdc8jVPXf4zcvBh4fQCw1/fjWeqap3FyAuAPgD8E/mdk38zP15CD/jLgxZHbp7t9mx5TVa8Ba8APjfnYPuoC2Jvkn5L8XZKfmVFN49Y1j8fO+7nfmuREkkeSfHhGNZ1PXbcAXzrPxy6qLuj5fCX5ZJLngT8CfmuSx/ZQF/T4fkzyY8AVVbXxF9DO/Hz5y8EX61+BPVX1apIfBx5Mcs2GFofe6MqqOpPkKuBvkzxZVc8vsoAkvwqsAD+3yNc9ly3q6vV8VdXdwN1JPgp8Cpjp+MX52qKu3t6PSd4C/Anw8Xm/Fgy7RX8GuGLk9uXdvk2PSXIBsAS8OuZjF15X91XsVYCqeoz1vrcfXmBd83jsXJ+7qs50f54CjgPvXmRdSd4H3AlcX1XfmeSxPdTV+/kacRh4/RtF7+drs7p6fj++DXgXcDzJC8BPAke6AdnZn695DETMaDDjAtYHufby/cGMazYc80neOOj5QLd9DW8czDjF7AZ/pqnr0tfrYH2Q5gzwjkXVNXLsfbx5MPYbrA8sXtJtD6GuS4CLuu3dwNfZZEBrjv+O72b9zX/1hv29nq+z1NX3+bp6ZHsVONFt9/1+3KquQbwfu+OP8/3B2Jmfr6n/QvP8AX4R+JfuP/Wd3b7fZ70VA/BW4C9YH6z4R+Cqkcfe2T3uOeC6IdQFfAR4GngCeBxYXXBdP8F6f99/sf7N5+mRx/56V+9J4NeGUBfwU8CT3X/6J4FbFlzXXwP/1v17PQEcGcj52rSuAZyvT4/8//4KI8HW8/tx07r6fj9uOPY4XdDP43x5ZawkNW7IffSSpBkw6CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjDHpJatz/AW2FNdFsMTUoAAAAAElFTkSuQmCC\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 73463\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": [ + " \n", + "New QUAD 2\n", + " \n", + "delta123 262521\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 262521\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 262521\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/UCwAAEQVJREFUeJzt3X+s3Xddx/Hniy6DyI/yoxNw26Uj7RYbTEBPNo0hTGWxc3QjaKAFEjBLGyDzH2NCDSZGjXFoNIFsZjZsKRjZmAti60rGD11KzIbtBHFdMygV3J1IhUET/DUmb/+4Z9vJZbf3e+75eT/3+Uhues7nfO/3vD89977v57y/n/P5pKqQJLXrWbMOQJI0WSZ6SWqciV6SGmeil6TGmeglqXEmeklqnIlekhpnopekxpnoJalx5806AIAtW7bU1q1bZx2GJK0rDzzwwLeq6oLVjptpok+yC9i1bds2jh8/PstQJGndSfL1LsfNtHRTVYerat/mzZtnGYYkNc0avSQ1zkQvSY0z0UtS40z0ktQ4E70kNc5EL0mNG3uiT3Jlks8luSXJleM+vyRpOJ0+MJXkNuANwJmqetVA+07gA8Am4ENVdSNQwPeA5wCLY49YGpOt++9+6vbXbrxmhpFIk9V1RH8Q2DnYkGQTcDNwNbAD2JNkB/C5qroaeC/wO+MLVZK0Fp0SfVUdBR5b1nw5cKqqTlfV48AdwHVV9YP+498Bnj22SCVJazLKWjcXAo8M3F8ErkjyJuAXgRcCN630zUn2AfsAFhYWRghDknQuY1/UrKo+Dny8w3EHgAMAvV6vxh2HJGnJKLNuHgUuHrh/Ub+tsyS7khw4e/bsCGFIks5llER/DNie5JIk5wO7gUPDnMDVKyVp8jol+iS3A/cBlyVZTHJ9VT0B3ADcA5wE7qyqE8M8uSN6SZq8TjX6qtqzQvsR4Mhan7yqDgOHe73e3rWeQ5J0bjNdAsERvSRNnjtMSVLjXNRMkhpn6UaSGmfpRpIaN/ZPxkrzxlUqtdFZupGkxlm6kaTGOetGkhpnopekxs30YmySXcCubdu2zTIMbSCDF2aljcIavSQ1ztKNJDXORC9JjbNGryZZi5eeZo1ekhpn6UaSGmeil6TGmeglqXEmeklqnIlekhrnMsWS1DinV0pS4yzdSFLjTPSS1DgTvSQ1zkQvSY0z0UtS40z0ktS4iST6JM9NcjzJGyZxfklSd50SfZLbkpxJ8uCy9p1JHk5yKsn+gYfeC9w5zkAlSWvTdUR/ENg52JBkE3AzcDWwA9iTZEeSq4CHgDNjjFOStEaddpiqqqNJti5rvhw4VVWnAZLcAVwHPA94LkvJ/7+THKmqH4wtYknSUEbZSvBC4JGB+4vAFVV1A0CSdwLfWinJJ9kH7ANYWFgYIQxJ0rlMbNZNVR2sqr85x+MHqqpXVb0LLrhgUmFI0oY3SqJ/FLh44P5F/bbOXL1SkiZvlER/DNie5JIk5wO7gUPDnMDVKyVp8rpOr7wduA+4LMlikuur6gngBuAe4CRwZ1WdGObJHdFL0uR1nXWzZ4X2I8CRtT55VR0GDvd6vb1rPYck6dzcYUqSGucOU5LUOBc1k6TGWbqRpMZZupGkxlm6kaTGWbqRpMaNsqjZyJxHr3mxdf/dT93+2o3XzDASafws3UhS40z0ktQ4a/SS1DinV0pS4yzdSFLjTPSS1Dhr9JLUOOfRqxmDc+ElPc3SjSQ1zkQvSY0z0UtS40z0ktQ4E70kNc7plZLUOKdXSsu4ZLFaY+lGkhpnopekxs20dCONyk/DSqtzRC9JjTPRS1LjTPSS1LixJ/okP57kliR3JXn3uM8vSRpOp0Sf5LYkZ5I8uKx9Z5KHk5xKsh+gqk5W1buANwM/O/6QpenZuv/up76k9arriP4gsHOwIckm4GbgamAHsCfJjv5j1wJ3A0fGFqkkaU06JfqqOgo8tqz5cuBUVZ2uqseBO4Dr+scfqqqrgbeNM1hJ0vBGmUd/IfDIwP1F4IokVwJvAp7NOUb0SfYB+wAWFhZGCEMbjWUUaThj/8BUVd0L3NvhuAPAAYBer1fjjkOStGSUWTePAhcP3L+o39aZq1dK0uSNMqI/BmxPcglLCX438NZhTuDqlVpPlpeMXNlS60XX6ZW3A/cBlyVZTHJ9VT0B3ADcA5wE7qyqE8M8uSN6SZq8VM2+PN7r9er48eOzDkNzbB4vwDqi16wleaCqeqsd5w5TktS4mSb6qjpcVfs2b948yzAkqWkuaiZJjZvpxiNJdgG7tm3bNsswNEfmsRYvrXduDi6tkZuIa72wdCNJjbN0o5mzXCNNlqUbzYTJXZoeSzeS1DgTvSQ1zhq9NAbOwNE8s0YvjZlJX/PG0o0kNc5EL0mNc/VKSWqc69Frajb63Hnr9Rq3dbEevSRp8kz0ktQ4E70kNc5EL0mNm+kHpqSNxA9SaVZcAkETtdFn2kjzwM3BJalxlm40do7i187yjibBRK9VmXyk9c1ZN5LUOEf00gz4LknTZKLXWFiXl+aXpRtJatxERvRJ3ghcA7wAuLWqPjWJ59FsOYofD/8fNWmdR/RJbktyJsmDy9p3Jnk4yakk+wGq6hNVtRd4F/CW8YYsSRrGMCP6g8BNwEeebEiyCbgZuApYBI4lOVRVD/UP+a3+45KG5AVbjUvnRF9VR5NsXdZ8OXCqqk4DJLkDuC7JSeBG4JNV9Y9jilVjYPKQNp5Ra/QXAo8M3F8ErgB+DXg9sDnJtqq6Zfk3JtkH7ANYWFgYMQxNi/Xk2fOPtYY1kYuxVfVB4IOrHHMAOABLWwlOIg5J0ujTKx8FLh64f1G/rRM3B5ekyRt1RH8M2J7kEpYS/G7grV2/uaoOA4d7vd7eEeOQNqSVSmmWdDRomOmVtwP3AZclWUxyfVU9AdwA3AOcBO6sqhNDnNMRvSRN2DCzbvas0H4EOLKWJ3dEP16jXKTzAt988yK4RuEOU40ycW9svv4aNNNE74h+Pjl6lNri6pXSBuJIf2OydCM1bth3aP4xaI+lmw3MEo2G4R+A9cv16CWpcTNN9M6jl6TJs3SzAViikTY2Z91IG5Q1943DWTeSVnzX57vBNsy0Rl9Vh6tq3+bNm2cZhiQ1zdLNOuQoS9IwnF4pSY0z0UtS47wYK2kqnOUzO86jlzRT/gGYPC/GSpo6JxRMlzV6SWqciV6SGmeil6TGWaOfY16k0kazUX7mp91Pp1dKmnsb5Q/ApLjWjSQ1ztKNpJEsnyrpiHv+mOjnjPOLJY2biX4OmNyl+dHi9QATvaSJmfQgpktSbjFxD8tEL2lo8/gudN6T/iz/z/zAlCQ1buyJPskrk9ya5K5xn1uSNLxOpZsktwFvAM5U1asG2ncCHwA2AR+qqhur6jRwvYn+h83j211JK2ulvt91RH8Q2DnYkGQTcDNwNbAD2JNkx1ijkySNrFOir6qjwGPLmi8HTlXV6ap6HLgDuG7M8UmSRjTKrJsLgUcG7i8CVyR5CfD7wGuS/GZV/cEzfXOSfcA+gIWFhRHCkDRPLFHOn7FPr6yqbwPv6nDcAeAAQK/Xq3HHIUlaMkqifxS4eOD+Rf22ztbL6pWtXJCRWuA7huGNMr3yGLA9ySVJzgd2A4eGOYGrV0rS5HWdXnk7cCWwJcki8NtVdWuSG4B7WJpeeVtVnRjmyed5RD9vo4Z5i0fS+tEp0VfVnhXajwBH1vrkVXUYONzr9fau9RySpHNzhylJc8l3sePjDlOS1DgXNZOkxlm6kbQhbaRp05ZuJKlxlm4kqXEzTfRJdiU5cPbs2VmGIUlNs3QjSY2zdCNJjdvQs26WfyCjy5X3ed+AWJKWs3QjSY2zdCNJjTPRS1LjTPSS1LgNdzF2nCviubqetHGs50kWXoyVpMZZupGkxpnoJalxJnpJapyJXpIaZ6KXpMat++mV45zyNInpkk7BlDRrTq+UpMZZupGkxpnoJalxJnpJapyJXpIaZ6KXpMaZ6CWpcWOfR5/kucCfAo8D91bVX4z7OSRJ3XUa0Se5LcmZJA8ua9+Z5OEkp5Ls7ze/CbirqvYC1445XknSkLqWbg4COwcbkmwCbgauBnYAe5LsAC4CHukf9n/jCVOStFadEn1VHQUeW9Z8OXCqqk5X1ePAHcB1wCJLyb7z+SVJkzNKjf5Cnh65w1KCvwL4IHBTkmuAwyt9c5J9wD6AhYWFEcJYnevNSJq2eco7Y78YW1X/Cfxqh+MOAAcAer1ejTsOSdKSUUorjwIXD9y/qN/WWZJdSQ6cPXt2hDAkSecySqI/BmxPckmS84HdwKFhTuDqlZI0eV2nV94O3AdclmQxyfVV9QRwA3APcBK4s6pODPPkjuglafI61eiras8K7UeAI2t98qo6DBzu9Xp713oOSdK5zXT6oyN6SZo8d5iSpMb5gSZJapylG0lqXKpm/1mlJP8BfL3DoVuAb004nGmwH/PFfswX+9HdK6rqgtUOmotE31WS41XVm3Uco7If88V+zBf7MX7W6CWpcSZ6SWrcekv0B2YdwJjYj/liP+aL/RizdVWjlyQNb72N6CVJQ5rrRJ/kxUk+neQr/X9ftMJxC0k+leRkkoeSbJ1upOfWtR/9Y1/QXzjupmnG2EWXfiR5dZL7kpxI8qUkb5lFrM9khT2OBx9/dpKP9R///Lz9HD2pQz9+vf978KUkn03yilnEuZrV+jFw3C8nqSRzMYNluS79SPLm/mtyIslHpx0jVTW3X8AfAvv7t/cD71/huHuBq/q3nwf8yKxjX0s/+o9/APgocNOs415LP4BLge392z8GfAN44RzEvgn4KvBK4Hzgn4Ady455D3BL//Zu4GOzjnuN/fi5J38HgHev1370j3s+cBS4H+jNOu41vh7bgS8AL+rf/9FpxznXI3qW9qD9cP/2h4E3Lj+gvyH5eVX1aYCq+l5V/df0Quxk1X4AJPkp4KXAp6YU17BW7UdVfbmqvtK//W/AGWDVD3RMwUp7HA8a7N9dwC8kyRRj7GLVflTV3w38DtzP03s4z5MurwfA7wHvB/5nmsENoUs/9gI3V9V3AKrqzJRjnPtE/9Kq+kb/9r+zlASXuxT4bpKPJ/lCkj9Ksml6IXayaj+SPAv4Y+A3phnYkLq8Hk9JcjlLo5yvTjqwDp5pj+MLVzqmlvZbOAu8ZCrRddelH4OuBz450YjWZtV+JPlJ4OKqmp/NV39Yl9fjUuDSJH+f5P4kO6cWXd/Y94wdVpLPAC97hofeN3inqirJM00ROg94LfAa4F+BjwHvBG4db6TnNoZ+vAc4UlWLsxxEjqEfT57n5cCfA++oqh+MN0p1keTtQA943axjGVZ/4PMnLP0ur3fnsVS+uZKld1dHk/xEVX13mgHMVFW9fqXHknwzycur6hv9xPFMb3kWgS9W1en+93wC+GmmnOjH0I+fAV6b5D0sXWc4P8n3qmrFi1STMIZ+kOQFwN3A+6rq/gmFOqwuexw/ecxikvOAzcC3pxNeZ532ak7yepb+OL+uqv53SrENY7V+PB94FXBvf+DzMuBQkmur6vjUolxdl9djEfh8VX0f+JckX2Yp8R+bTojzX7o5BLyjf/sdwF8/wzHHgBcmebIO/PPAQ1OIbRir9qOq3lZVC1W1laXyzUemneQ7WLUf/f2D/4ql+O+aYmyr6bLH8WD/fgX42+pfPZsjq/YjyWuAPwOunUU9uKNz9qOqzlbVlqra2v+duJ+l/sxTkoduP1efYGk0T5ItLJVyTk8zyJlftT7XF0v10c8CXwE+A7y4394DPjRw3FXAl4B/Bg4C58869rX0Y+D4dzKfs25W7QfwduD7wBcHvl4969j7sf0S8GWWrhm8r9/2uywlEIDnAH8JnAL+AXjlrGNeYz8+A3xz4P//0KxjXks/lh17L3M466bj6xGWylAP9XPU7mnH6CdjJalx8166kSSNyEQvSY0z0UtS40z0ktQ4E70kNc5EL0mNM9FLUuNM9JLUuP8HwHlVsLpuFf8AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "delta curv\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAD6VJREFUeJzt3W2MpWddx/Hvj627JKBLYRFJ22W2mZW4EqNxbBONppGHbmmHEuTFrsSgEjZi6jsTlqAhkpBUY6IQGpsNlIUYWyoS3KWrlQdreYHaLfLQ0lSGpaS7QcrjCoZAKn9fzL14GHZ2zpyn+8w130+y2XPuc58z/+ucOb+55n9fc59UFZKkdj2l7wIkSdNl0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIad1nfBQDs2bOnFhYW+i5DkraUBx988KtV9eyN9puLoF9YWOD06dN9lyFJW0qSLw6zn60bSWqcQS9JjZt40Ce5LsnHktye5LpJP74kaXOGCvokdyR5IslDa7YfTPJokpUkR7vNBXwbeCpwdrLlSpI2a9gZ/XHg4OCGJDuA24AbgAPA4SQHgI9V1Q3A64E/mVypkqRRDBX0VXU/8PU1m68BVqrqTFV9D7gLuLmqvt/d/g1g18QqlSSNZJzllVcAjw9cPwtcm+QVwPXAM4C3r3fnJEeAIwB79+4dowxJ0qVMfB19Vb0feP8Q+x0DjgEsLS35eYaSNCXjBP054KqB61d224aWZBlYXlxcHKMMaXoWjt7zg8uP3Xpjj5VIoxsn6B8A9ifZx2rAHwJ+czMPUFUngZNLS0uvHaMOaaIGw11qwbDLK+8EPg48P8nZJK+pqieBW4B7gUeAu6vq4emVKkkaxVAz+qo6vM72U8CpUb+4rRtJmr5eT4FQVSer6sju3bv7LEOSmua5biSpcQa9JDWu16BPspzk2Pnz5/ssQ5KaZo9ekhpn60aSGmfQS1Lj7NFLUuPs0UtS42zdSFLjDHpJapw9eklqnD16SWqcrRtJapxBL0mNM+glqXEGvSQ1zlU3ktQ4V91IUuNs3UhS4wx6SWqcQS9JjTPoJalxBr0kNc7llZLUOJdXSlLjbN1IUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjPAWCJDXOUyBIUuNs3UhS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklq3GV9FyDNg4Wj92x6n8duvXFa5UgT5Yxekho3laBP8rQkp5PcNI3HlyQNb6igT3JHkieSPLRm+8EkjyZZSXJ04KbXA3dPslBJ0miGndEfBw4ObkiyA7gNuAE4ABxOciDJi4HPAk9MsE5J0oiGOhhbVfcnWViz+RpgparOACS5C7gZeDrwNFbD/ztJTlXV99c+ZpIjwBGAvXv3jlq/JGkD46y6uQJ4fOD6WeDaqroFIMlvA1+9WMgDVNUx4BjA0tJSjVGHJOkSpra8sqqOT+uxJUnDG2fVzTngqoHrV3bbhuZnxkrS9I0T9A8A+5PsS7ITOASc2MwD+JmxkjR9wy6vvBP4OPD8JGeTvKaqngRuAe4FHgHurqqHp1eqJGkUw666ObzO9lPAqVG/eJJlYHlxcXHUh5AkbaDXUyDYupGk6fNcN5LUuF6D3lU3kjR9tm4kqXG2biSpcQa9JDXOHr0kNc4evSQ1ztaNJDXOoJekxhn0ktQ4D8ZKUuM8GCtJjbN1I0mNm9pHCUqtWzh6zw8uP3brjT1WIl2aM3pJapwzem1bgzNyqWWuupGkxrnqRpIaZ49ekhpn0EtS4zwYK02ASy01z5zRS1LjDHpJapzLKyWpcS6vlKTG2bqRpMYZ9JLUOJdXaluZxfltXGqpeeOMXpIaZ9BLUuMMeklqnD16aYrs12seOKOXpMb1OqNPsgwsLy4u9lmGGucnSWm78y9jJalx9uilGbFfr77Yo5ekxhn0ktQ4g16SGmePXuqB/XrNkkGvJrmkUvp/tm4kqXEGvSQ1zqCXpMbZo5d65oFZTZszeklqnDN6NcOVNtLFTXxGn+Rnktye5H1JXjfpx5ckbc5QM/okdwA3AU9U1QsGth8E3grsAN5RVbdW1SPA7yV5CvAe4K8mX7a0qrVZvP16TcOwM/rjwMHBDUl2ALcBNwAHgMNJDnS3vQy4Bzg1sUolSSMZKuir6n7g62s2XwOsVNWZqvoecBdwc7f/iaq6AXjVJIuVJG3eOAdjrwAeH7h+Frg2yXXAK4BdXGJGn+QIcARg7969Y5Sh7aa1ds16bONoUia+6qaq7gPuG2K/Y8AxgKWlpZp0HZKkVeOsujkHXDVw/cpumyRpjowT9A8A+5PsS7ITOASc2MwDJFlOcuz8+fNjlCFJupRUbdw1SXIncB2wB/gy8KaqemeSlwJ/yeryyjuq6i2jFLG0tFSnT58e5a7aJrZLX34Y9ut1QZIHq2ppo/2G6tFX1eF1tp/CJZSSNNd6PdeNrRtJmr6hWjfTZutGF2O7ZmO2cba3YVs3nr1SkhrX69krkywDy4uLi32WoTniLF6avF6DvqpOAieXlpZe22cd6pfhLk2XrRtJapxBL0mNs0cvbWGe+EzDsEcvNcLQ13r8zFj1wgOw0uwY9FKDnN1rkEGvqTJwpP55MFYzY7tG6ocHYzVxBro0X2zdSI2zfSaDXtpGDP3tyaDXpqzXljE0pPll0Gsi7MtL88tVN/oRa0Pb2bq0tfV6UrOqOllVR3bv3t1nGZLUNFs32pBtmTZ5YHb78DTFktQ4Z/SNcrYm6QJn9JLUOGf0AuzDb3f+Btg2l1dKGovLceefJzWT9EOGmd37G+DWYo9ekhpnj34bc1amjdi7b4NBL2koTgy2Lls3ktQ4g16SGmfrpiHr/Wptn1Xa3gz6LWic4LbPqmlzYjF/DPotzuCWtBH/MnYO+Acq2g6c6ffHv4ydIb/RJfXB1o2kqXGBwHww6OeMbwBJk2bQ92SYnrt9eUmT4B9MSVLjDHpJapytmyH4B0qStjKDfgoMd2l4671fBidVLlIYT1NB7zeDJP2opoJ+FvxhImmrMejHYOhLs+f7bvMM+nVsts9uX16aLN9Tk+PySklq3FRm9EleDtwI/ATwzqr6p2l8HRjvp/7a+/proLS12MYZztBBn+QO4Cbgiap6wcD2g8BbgR3AO6rq1qr6APCBJJcDfw5MLeglaS1/APywzczojwNvB95zYUOSHcBtwIuBs8ADSU5U1We7Xf6ou31u2PeTtq/t+gNg6KCvqvuTLKzZfA2wUlVnAJLcBdyc5BHgVuAfquoTE6p1U7brCypJa43bo78CeHzg+lngWuAPgBcBu5MsVtXta++Y5AhwBGDv3r1jliFJF+dv8VM6GFtVbwPetsE+x4BjAEtLSzWNOiRps1rsBowb9OeAqwauX9ltmyv+RJd0Ka1nxLjr6B8A9ifZl2QncAg4MeydkywnOXb+/Pkxy5AkrWczyyvvBK4D9iQ5C7ypqt6Z5BbgXlaXV95RVQ8P+5jz9uHgrf9Ul1rm+3d9m1l1c3id7aeAUxOrSJI0Ub2eAsHWjSRNX69BX1Unq+rI7t27+yxDkprm2SslaQhbedmlrRtJapytG0lqnOejl6TGGfSS1LheD8YmWQaWFxcX+yxD0jY0qQ8t2goHZu3RS1LjXF4pSeto5bQK9uglqXEGvSQ1zoOxkjQDfR7A7TXo5+00xZK0WVthBY4HYyVpCubpQK49eklqnDN6SZqxWbd7PHulJDXOg7GSNCHz1JcfZI9ekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc519JLUOD9hSpIaZ+tGkhqXquq7BpJ8BfjiiHffA3x1guX0ybHMn1bGAY5lXo0zludV1bM32mkugn4cSU5X1VLfdUyCY5k/rYwDHMu8msVYbN1IUuMMeklqXAtBf6zvAibIscyfVsYBjmVeTX0sW75HL0m6tBZm9JKkS9gSQZ/kmUk+lORz3f+Xr7PfPyb5ZpIPrtl+PMkXknyy+/fzs6n8ojWOO5Z9Sf4tyUqS9ybZOZvKL1rjsGN5dbfP55K8emD7fUkeHXhdfnJ21UOSg93XX0ly9CK37+qe45XuOV8YuO0N3fZHk1w/y7ovZtSxJFlI8p2B1+D2Wde+ps6NxvFrST6R5Mkkr1xz20W/z/oy5lj+d+A1OTF2MVU19/+APwOOdpePAn+6zn4vBJaBD67Zfhx4Zd/jmNBY7gYOdZdvB143z2MBngmc6f6/vLt8eXfbfcBST7XvAD4PXA3sBD4FHFizz+8Dt3eXDwHv7S4f6PbfBezrHmdHj6/DOGNZAB7qq/YRxrEA/BzwnsH39KW+z7baWLrbvj3JerbEjB64GXh3d/ndwMsvtlNVfQT41qyKGtHIY0kS4NeB9210/xkZZizXAx+qqq9X1TeADwEHZ1TfpVwDrFTVmar6HnAXq+MZNDi+9wEv7F6Dm4G7quq7VfUFYKV7vL6MM5Z5suE4quqxqvo08P01952377NxxjJxWyXon1NVX+ou/xfwnBEe4y1JPp3kL5LsmmBtmzXOWJ4FfLOqnuyunwWumGRxmzTMWK4AHh+4vrbmd3W/nv7xjINno7p+aJ/uOT/P6mswzH1naZyxAOxL8h9J/iXJr0672EsY53ndiq/JpTw1yekk/5pk7Mlcrx8OPijJh4GfushNbxy8UlWVZLNLhd7AahDtZHUp0+uBN49S5zCmPJaZmvJYXlVV55L8OPB3wG+x+musZudLwN6q+lqSXwQ+kORnq+q/+y5sm3te9964Gvhoks9U1edHfbC5CfqqetF6tyX5cpLnVtWXkjwXeGKTj31h1vndJO8C/nCMUof5etMay9eAZyS5rJuVXQmcG7PcS5rAWM4B1w1cv5LV3jxVda77/1tJ/obVX3dnFfTngKvW1LX2ubywz9kklwG7WX0NhrnvLI08llptCH8XoKoeTPJ54KeB01Ov+keN87yu+33Wk7G+RwbeG2eS3Af8Aqs9/5FsldbNCeDCUfRXA3+/mTt3IXShx/1y4KGJVrc5I4+le1P+M3DhCP2mn4sJG2Ys9wIvSXJ5tyrnJcC9SS5LsgcgyY8BNzHb1+UBYH+3imknqwco165uGBzfK4GPdq/BCeBQt5JlH7Af+PcZ1X0xI48lybOT7ADoZo/7WT2Q2YdhxrGei36fTanOYYw8lm4Mu7rLe4BfAT47VjV9HZXe5BHsZwEfAT4HfBh4Zrd9CXjHwH4fA74CfIfVntj13faPAp9hNUj+Gnj6Fh7L1ayGygrwt8CuLTCW3+3qXQF+p9v2NOBB4NPAw8BbmfHKFeClwH+yOlN6Y7ftzcDLustP7Z7jle45v3rgvm/s7vcocENfr8G4YwF+o3v+Pwl8Alie83H8Uvd++B9Wf7t6+FLfZ1txLMAvd3n1qe7/14xbi38ZK0mN2yqtG0nSiAx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIa93+7pfxfkebhDQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 262521\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADiZJREFUeJzt3V+MXOddxvHvU0dNRUhXKfFVEseOnFQ4FVJhSBAIKKJVHdpNKlqhJCC1EMVqaOCCG4JSCQluWoSQWhEpsmgUykXc0IvIS11CgZoIqYGsQ2jiRKGOGxRbiPxDi4DSKPTHxY6bycZrz+zM7Dn77vcjrTxz5pzZn489z7zzvu95J1WFJKldb+u6AEnSfBn0ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMZd0OUvT7IILF588cW3X3PNNV2WIklbzrFjx16uqp3n2y99WAJhMBjU8vJy12VI0paS5FhVDc63n103ktQ4g16SGtdp0CdZTHJwZWWlyzIkqWmdBn1VLVXVgYWFhS7LkKSm2XUjSY0z6CWpcQa9JDXOwVhJalynV8ZW1RKwNBgMbt/oc+y+6yvfv/38Zz40i7IkqSl23UhS4wx6SWqcQS9JjTPoJalxvVimeO/evTN5PgdmJemtXAJBkhpn140kNc6gl6TGGfSS1DiDXpIa51o3ktQ4Z91IUuPsupGkxnV6wdQ8efGUJK2yRS9JjTPoJalxBr0kNc6gl6TGGfSS1DgvmJKkxnnBlCQ1zq4bSWqcQS9JjTPoJalxBr0kNa7ZtW5Gue6NpO3MFr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnIuaSVLjXNRMkhpn140kNc6gl6TGGfSS1DiDXpIaty0WNRvlAmeSthtb9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaN5egT3JRkuUkH57H80uSxjdW0Ce5L8mLSZ5as31/kmeTnEhy18hDvw08OMtCJUkbM26L/n5g/+iGJDuAe4AbgH3ALUn2JfkA8DTw4gzrlCRt0FirV1bVI0l2r9l8HXCiqk4CJDkE3AT8IHARq+H/nSRHqup7M6t4hkZXsgRXs5TUpmmWKb4MeGHk/ing+qq6EyDJJ4CX1wv5JAeAAwC7du2aogxJ0rnMbdZNVd1fVX9xjscPVtWgqgY7d+6cVxmStO1NE/SngStG7l8+3CZJ6pFpgv4x4Ooke5K8HbgZODzJEyRZTHJwZWVlijIkSecy7vTKB4BvAO9OcirJbVX1OnAn8DDwDPBgVR2f5JdX1VJVHVhYWJi0bknSmMaddXPLOtuPAEdmWpEkaaZcAkGSGtdp0NtHL0nz12nQ20cvSfNn140kNc6gl6TG2UcvSY2bZq2bqVXVErA0GAxu77KOM0YXOXOBM0mtsOtGkhpn0EtS4+yjl6TGOY9ekhpn140kNc6gl6TGGfSS1DiDXpIa56wbSWqcs24kqXGdLoHQZy6HIKkV9tFLUuMMeklqnEEvSY0z6CWpcU6vlKTGOb1Skhpn140kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnIuajcEFziRtZV4wJUmN84IpSWqcffSS1DiDXpIaZ9BLUuMMeklqnNMrJ+RUS0lbjS16SWqcQS9JjTPoJalxBr0kNc6gl6TGudaNJDWu0+mVVbUELA0Gg9u7rGOjnGopaSuw60aSGmfQS1LjDHpJapxBL0mNM+glqXEuajYjzsCR1Fe26CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjnF45B061lNQntuglqXEGvSQ1buZBn+SHk9yb5MtJ7pj180uSJjNW0Ce5L8mLSZ5as31/kmeTnEhyF0BVPVNVnwR+Cfip2Ze8tey+6yvf/5GkLow7GHs/8MfAF89sSLIDuAf4AHAKeCzJ4ap6OsmNwB3An8223K1tmkHa9d4oHOyVdD5jteir6hHg1TWbrwNOVNXJqnoNOATcNNz/cFXdAPzyLIuVJE1umumVlwEvjNw/BVyf5H3ALwIXAkfWOzjJAeAAwK5du6YoQ5J0LjOfR19VR4GjY+x3EDgIMBgMatZ19J1z7SVtlmlm3ZwGrhi5f/lwmySpR6Zp0T8GXJ1kD6sBfzNw6yRPkGQRWNy7d+8UZWjW1g78+olD2trGCvokDwDvAy5Ncgr43ar6QpI7gYeBHcB9VXV8kl9eVUvA0mAwuH2ysreHvkzJtJtJ2trGCvqqumWd7Uc4x4CrxmOQSponl0CQpMZ1GvRJFpMcXFlZ6bIMSWpap8sU20f/Vn3pl5fUDrtuJKlxfvHIFudArqTzsY9ekhrXadBX1VJVHVhYWOiyDElqmn30ktQ4g16SGudgbKMcpJV0RqdB76Jmm2O90PfNQNoevGCqIZtxsZVvDtLWY9fNNuOVt9L242CsJDXOFr2A2bb07d6R+sUrYyWpcV4ZK0mNs+tGMzFO149dOlI3DHrNlbN8pO4560aSGmeLXhs2TWt93t04dhNJbzDo1Vsu3SDNhmvdqHMGtzRfrnWjXlmvO8hBXWnj7LrRljbppwE/PWg7MujVDFv90tk5vVKSGmeLXs2zpa/tzqDXtmV/vbYLg17COftqm0EvbdB6XUK+UahvXI9ekhqXquq6BgaDQS0vL2/oWAfatNXZ0tdGJTlWVYPz7WfXjdQIu4m0HufRS1LjbNFLPWKrXPNg0Esdm3ScyTcDTcqgl7aAzfySF99I2mPQSz016ReuS+sx6KUG+QagUc66kaTG2aKXthH737cnW/SS1DjXupGkxvnl4JLWZVdPG+yjlzRTvjn0j330ktQ4W/TSNtXHK2bn/Tu266cNg17SptuugdsVg16SV9I2zqCXtCn69mYyznf+tsKglzSxWYZ2394AWmTQS9pS7N+fnEEvSZugyzcog17S3My7W8bW/Xi8YEqSGmeLXlLvzeqrFLcrg15SL/Ut3LdyN5FBL0nr6NubzUbZRy9JjbNFL6kJfWpB981cgj7JR4APAe8EvlBVfzWP3yNJOr+xgz7JfcCHgRer6j0j2/cDnwN2AH9SVZ+pqoeAh5JcAvwhYNBLal5fP1VM0kd/P7B/dEOSHcA9wA3APuCWJPtGdvn08HFJUkfGDvqqegR4dc3m64ATVXWyql4DDgE3ZdVnga9W1eNne74kB5IsJ1l+6aWXNlq/JOk8pu2jvwx4YeT+KeB64DeA9wMLSfZW1b1rD6yqg8BBgMFgUFPWIUmbZqvNqZ/LYGxVfR74/DyeW5LmaTP62Tf7jWLaefSngStG7l8+3CZJ6olpW/SPAVcn2cNqwN8M3DruwUkWgcW9e/dOWYYkdaOvM21Gjd2iT/IA8A3g3UlOJbmtql4H7gQeBp4BHqyq4+M+Z1UtVdWBhYWFSeuWJI1p7BZ9Vd2yzvYjwJGZVSRJmqlO17pJspjk4MrKSpdlSFLTOg16u24kaf5cvVKSGmfQS1Lj7KOXpMbZRy9JjbPrRpIal6ru1xNL8hLwrxs8/FLg5RmWMyvWNRnrmkxf64L+1tZiXVdW1c7z7dSLoJ9GkuWqGnRdx1rWNRnrmkxf64L+1rad67LrRpIaZ9BLUuNaCPqDXRewDuuajHVNpq91QX9r27Z1bfk+eknSubXQopcknUOvgz7J/iTPJjmR5K6zPH5hki8NH/+HJLtHHvud4fZnk3ywD3Ul2Z3kO0meGP685bt051zXzyR5PMnrST625rGPJ/nW8OfjParr/0bO1+FNruu3kjyd5JtJ/ibJlSOPdXm+zlVXl+frk0meHP7uv0+yb+SxLl+PZ62r69fjyH4fTVJJBiPbZnu+qqqXP8AO4DngKuDtwD8D+9bs8+vAvcPbNwNfGt7eN9z/QmDP8Hl29KCu3cBTHZ6v3cCPAF8EPjay/V3AyeGflwxvX9J1XcPH/qvD8/VzwA8Mb98x8u/Y9fk6a109OF/vHLl9I/CXw9tdvx7Xq6vT1+Nwv4uBR4BHgcG8zlefW/TXASeq6mRVvQYcAm5as89NwJ8Ob38Z+PkkGW4/VFXfrapvAyeGz9d1XfN03rqq6vmq+ibwvTXHfhD4WlW9WlX/AXwN2N+DuuZpnLq+XlX/M7z7KKvfiQzdn6/16pqncer6z5G7FwFnBgA7fT2eo655GicnAH4f+CzwvyPbZn6++hz0lwEvjNw/Ndx21n1q9WsNV4AfGvPYLuoC2JPkn5L8XZKfnlFN49Y1j2Pn/dzvSLKc5NEkH5lRTRup6zbgqxs8drPqgo7PV5JPJXkO+APgNyc5toO6oMPXY5IfBa6oqrVfOjvz8zXtl4NrMv8G7KqqV5L8GPBQkmvXtDj0ZldW1ekkVwF/m+TJqnpuMwtI8ivAAPjZzfy957NOXZ2er6q6B7gnya3Ap4GZjl9s1Dp1dfZ6TPI24I+AT8z7d0G/W/SngStG7l8+3HbWfZJcACwAr4x57KbXNfwo9gpAVR1jte/tmk2sax7HzvW5q+r08M+TwFHgvZtZV5L3A3cDN1bVdyc5toO6Oj9fIw4BZz5RdH6+zlZXx6/Hi4H3AEeTPA/8BHB4OCA7+/M1j4GIGQ1mXMDqINce3hjMuHbNPp/izYOeDw5vX8ubBzNOMrvBn2nq2nmmDlYHaU4D79qsukb2vZ+3DsZ+m9WBxUuGt/tQ1yXAhcPblwLf4iwDWnP8d3wvqy/+q9ds7/R8naOurs/X1SO3F4Hl4e2uX4/r1dWL1+Nw/6O8MRg78/M19V9onj/ALwD/MvxPffdw2++x2ooBeAfw56wOVvwjcNXIsXcPj3sWuKEPdQEfBY4DTwCPA4ubXNePs9rf99+sfvI5PnLsrw3rPQH8ah/qAn4SeHL4n/5J4LZNruuvgX8f/ns9ARzuyfk6a109OF+fG/n//XVGgq3j1+NZ6+r69bhm36MMg34e58srYyWpcX3uo5ckzYBBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4/4fs5NgVwy/hA0AAAAASUVORK5CYII=\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 262521\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": [ + " \n", + "New QUAD 3\n", + " \n", + "delta123 716071\n", + "field\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEJBJREFUeJzt3XuMXGd5x/HvDwcHFUi42AVqe3HQGoRFJSgrp7RCuIW0DsGEUgQ2IIEUxSIo/FNVwlUq0cs/gQokUCIFi0QGJBJSRKldjMKlREYooXbKpbGtgHED2ZBirpGiXiDi6R8zIcvGG5/ZmdmZff39SJZnzpyd89PszrPvPu8756SqkCS16wmTDiBJGi8LvSQ1zkIvSY2z0EtS4yz0ktQ4C70kNc5CL0mNs9BLUuMs9JLUuPMmHQBg3bp1tXnz5knHkKRV5a677vpxVa0/234TLfRJdgI7Z2dnOXr06CSjSNKqk+R7XfabaOumqg5W1Z4LL7xwkjEkqWn26CWpcRMt9El2Jtn34IMPTjKGJDXN1o0kNc7WjSQ1ztaNJDXO1o0kNc7WjSQ1bio+GStNq817P/vr2/dee9kEk0jL54hekhrnZKwkNc7JWElqnK0bSWqchV6SGmehl6TGWeglqXEWeklqnMsrJalxLq+UpMZ5CgRpkYWnPZBaYI9ekhpnoZekxlnoJalxFnpJatzIC32S7Um+kuSGJNtH/fySpMF0WnWT5CbgNcDpqnrRgu07gA8Ca4CPVNW1QAEPAU8C5keeWBoDV9qoZV1H9PuBHQs3JFkDXA9cCmwFdifZCnylqi4F3g387eiiSpKWo1Ohr6rDwE8Xbd4GnKyqU1X1C+AW4PKq+lX/8Z8B548sqSRpWYb5wNQG4L4F9+eBi5O8HvhT4GnAdUt9cZI9wB6AmZmZIWJIkh7PyD8ZW1WfBj7dYb99wD6Aubm5GnUOSVLPMKtu7gc2Lbi/sb+tM09qJknjN0yhPwJsSXJRkrXALuDAIE/gSc0kafw6FfokNwN3AC9IMp/kiqp6GLgauA04AdxaVccGObgjekkav049+qravcT2Q8Ch5R68qg4CB+fm5q5c7nNIkh6fFx6RpMZ54RFJapwnNZOkxtm6kaTG2bqRpMbZupGkxtm6kaTG2bqRpMbZupGkxtm6kaTG2bqRpMbZupGkxo38wiNSqxZfQPzeay+bUBJpMI7oJalxTsZKUuOcjJWkxtm6kaTGWeglqXEWeklqnIVekhpnoZekxrm8UpIa5/JKSWqcrRtJapyFXpIaZ6GXpMZZ6CWpcRZ6SWrcWAp9kicnOZrkNeN4fklSd50KfZKbkpxOcvei7TuS3JPkZJK9Cx56N3DrKINKkpan64h+P7Bj4YYka4DrgUuBrcDuJFuTXAIcB06PMKckaZk6XUqwqg4n2bxo8zbgZFWdAkhyC3A58BTgyfSK//8kOVRVvxpZYmlEFl8aUGrVMNeM3QDct+D+PHBxVV0NkOTtwI+XKvJJ9gB7AGZmZoaIIUl6PGNbdVNV+6vqXx7n8X1VNVdVc+vXrx9XDEk65w1T6O8HNi24v7G/rTNPaiZJ4zdMoT8CbElyUZK1wC7gwCBP4EnNJGn8ui6vvBm4A3hBkvkkV1TVw8DVwG3ACeDWqjo2yMEd0UvS+KWqJp2Bubm5Onr06KRj6BwzylU391572cieS+oqyV1VNXe2/bzwiCQ1zguPSFLjhllHP7QkO4Gds7Ozk4yhc4gfktK5yBG9JDXO0xRLUuOcjJWkxtm6kaTG2bqRpMZZ6CWpcS6vVPNWYknlwmP4KVlNG3v0ktQ4WzeS1DgLvSQ1zkIvSY3zA1OS1DgnYyWpcRNdXimNi2eplB5lj16SGueIXhoxPzylaeOIXpIa56obSWqcq24kqXH26NUMV9pIZ2ahl8bIiVlNAydjJalxFnpJapyFXpIaZ49eq5oTsNLZjXxEn+SFSW5I8qkkV436+SVJg+lU6JPclOR0krsXbd+R5J4kJ5PsBaiqE1X1DuCNwB+OPrIkaRBdWzf7geuAjz2yIcka4HrgEmAeOJLkQFUdT/Ja4Crg46ONK61eLrXUpHQa0VfVYeCnizZvA05W1amq+gVwC3B5f/8DVXUp8JZRhpUkDW6YydgNwH0L7s8DFyfZDrweOB84tNQXJ9kD7AGYmZkZIobOBU66Sss38lU3VXU7cHuH/fYB+wDm5uZq1DkkST3DrLq5H9i04P7G/rbOPHulJI3fMCP6I8CWJBfRK/C7gDePJJXUOCdmtZI6FfokNwPbgXVJ5oH3VNWNSa4GbgPWADdV1bFBDl5VB4GDc3NzVw4WW+cC+/LSaHQq9FW1e4nth3icCVdJ0uRN9BQISXYCO2dnZycZQ5oo2zgaN68wJUmN85qxktQ4R/SS1DjPRy9JjbN1I0mNm+iqG9fRC1wvvxRX42hUvMKUNEX8padxsEcvSY2zRy9JjbNHL60C9us1DFs3ktQ4C70kNc5CL0mN8+yV0ipjv16D8lw3ktQ4PzClFeNIVJoMC70mwk+ASivHyVhJapwjeo2VI3dp8jwFgiQ1zlU3ktQ4WzcaCVfUSNPLQi+tYv6CVRcWeo2cE7DSdLHQS41wdK+lWOilBln0tZCFXstmi0ZaHcZS6JO8DrgMuAC4sao+P47jSDo7R/fqvI4+yU1JTie5e9H2HUnuSXIyyV6AqvpMVV0JvAN402gjS5IGMcgHpvYDOxZuSLIGuB64FNgK7E6ydcEuf91/XJI0IZ1bN1V1OMnmRZu3ASer6hRAkluAy5OcAK4FPldV/z6irJoC9uWl1WfYHv0G4L4F9+eBi4F3Aa8CLkwyW1U3LP7CJHuAPQAzMzNDxpA0KHv3546xTMZW1YeAD51ln31JHgB2rl279qXjyCFJGv6kZvcDmxbc39jf1oknNZOk8Ru20B8BtiS5KMlaYBdwoOsXe5piSRq/zq2bJDcD24F1SeaB91TVjUmuBm4D1gA3VdWxrs9ZVQeBg3Nzc1cOFlvScjiZfm4aZNXN7iW2HwIOLefgSXYCO2dnZ5fz5ZKkDrzwiCQ1znPdSFqypeOyyzZ4zVhJatxER/ROxq4OTuBJq5utGwF+SlJqma0bSWqcq24kqXETLfSSpPGzR38Oc5JVZ+PcTRvs0UtS41xeqcdwpC+1xR69JDXOQi9JjZto68azV46W5yuRdCauo5ekxtm6kaTGWeglqXEWeklqnIVekhrnKRAkdTKu0yF4moXx8xQIktQ4l1dKUuNs3ZxjPI+NdO6x0EsayuLBg3326WOhn2JOUknd+X5ZmssrJalxjugljY2j7OngiF6SGjfyEX2S5wHXABdW1RtG/fzL4ahCWjlLrezyfTg5nUb0SW5KcjrJ3Yu270hyT5KTSfYCVNWpqrpiHGElSYPrOqLfD1wHfOyRDUnWANcDlwDzwJEkB6rq+KhD6jc5MtKkDft5DD/PsbI6FfqqOpxk86LN24CTVXUKIMktwOVAp0KfZA+wB2BmZqZjXC2HbyqtFg5ixmOYydgNwH0L7s8DG5I8M8kNwEuS/NVSX1xV+6pqrqrm1q9fP0QMSdLjGflkbFX9BHhHl329Zqwkjd8wI/r7gU0L7m/sb+vMk5pJ0vgNM6I/AmxJchG9Ar8LePMgT+CI/rEG7afbf5cea6le/7k6B9B1eeXNwB3AC5LMJ7miqh4GrgZuA04At1bVsUEO7oheksav66qb3UtsPwQcWu7BHdFLGoZ/0XbjhUckqXGe60aSGjfRs1fauunOP1Gl0TqXJmZt3UhS42zdSFLjbN1IapptT1s3ktQ8WzeS1DgLvSQ1btX36AddInUuLamSJLBHL0nNs3UjSY2z0EtS41Z9j15Sm5Za/+7c2uDs0UtS42zdSFLjLPSS1DgLvSQ1zkIvSY2z0EtS41xeOSGeOlVannG8d1Z6KedKn4rF5ZWS1DhbN5LUOAu9JDXOQi9JjbPQS1LjLPSS1DgLvSQ1zkIvSY2z0EtS41JVk85Akh8B3xvyadYBPx5BnFEz1+CmNZu5Bjet2aY1FwyW7blVtf5sO01FoR+FJEeram7SORYz1+CmNZu5Bjet2aY1F4wnm60bSWqchV6SGtdSod836QBLMNfgpjWbuQY3rdmmNReMIVszPXpJ0pm1NKKXJJ3Bqi30SZ6R5AtJvtP//+lL7Pe+JMeSnEjyoSSZklwzST7fz3U8yeZpyNXf94Ik80muG2emQbIleXGSO/rfy28ledMY8+xIck+Sk0n2nuHx85N8sv/418b9vRsg11/0f5a+leRLSZ67Erm6ZFuw358nqSQrsuKlS64kb+y/bseSfGIacvXrw5eTfL3//Xz1UAesqlX5D3gfsLd/ey/w3jPs8wfAV4E1/X93ANsnnav/2O3AJf3bTwF+axpy9R//IPAJ4Lop+l4+H9jSv/07wAPA08aQZQ3wXeB5wFrgm8DWRfu8E7ihf3sX8MkVeI265PqjR36OgKtWIlfXbP39ngocBu4E5qYhF7AF+Drw9P79356SXPuAq/q3twL3DnPMVTuiBy4HPtq//VHgdWfYp4An0XsxzweeCPxw0rmSbAXOq6ovAFTVQ1X135PO1c/2UuBZwOfHnGehs2arqm9X1Xf6t38AnAbO+kGRZdgGnKyqU1X1C+CWfr6l8n4KeOW4/1Lskquqvrzg5+hOYOOYM3XO1vf3wHuB/52iXFcC11fVzwCq6vSU5Crggv7tC4EfDHPA1Vzon1VVD/Rv/xe94vQbquoO4Mv0Rn8PALdV1YlJ56I3Ov15kk/3/zT7hyRrJp0ryROA9wN/OeYsi3V5zX4tyTZ6v7y/O4YsG4D7Ftyf72874z5V9TDwIPDMMWQZNNdCVwCfG2uiR501W5LfAzZV1UpeLLnLa/Z84PlJvprkziQ7piTX3wBvTTIPHALeNcwBJ3px8LNJ8kXg2Wd46JqFd6qqkjxm+VCSWeCFPDqy+UKSl1fVVyaZi97r/nLgJcD3gU8CbwdunHCudwKHqmp+1APUEWR75HmeA3wceFtV/WqkIRuR5K3AHPCKSWeBXw8gPkDvZ3zanEevfbOdXp04nOR3q+rnE00Fu4H9VfX+JC8DPp7kRcv9mZ/qQl9Vr1rqsSQ/TPKcqnqg/+Y/059cfwbcWVUP9b/mc8DLgKEK/QhyzQPfqKpT/a/5DPD7DFnoR5DrZcDLk7yT3rzB2iQPVdWSk2srmI0kFwCfBa6pqjuHzbSE+4FNC+5v7G870z7zSc6j96f1T8aUZ5BcJHkVvV+er6iq/xtzpq7Zngq8CLi9P4B4NnAgyWur6ugEc0Hvvfi1qvol8J9Jvk2v8B+ZcK4rgB3Q60wkeRK9c+Asq7W0mls3B4C39W+/DfjnM+zzfeAVSc5L8kR6I5xxt2665DoCPC3JIz3mPwaOTzpXVb2lqmaqajO99s3HRlHkR5EtyVrgn/qZPjXGLEeALUku6h9zVz/fUnnfAPxr9WfNJpkryUuADwOvXaFec6dsVfVgVa2rqs39n607+xnHWeTPmqvvM/RG8yRZR6+Vc2oKcn0feGU/1wvpzTX+aNlHHPcM87j+0euJfgn4DvBF4Bn97XPAR+rR2e0P0yvux4EPTEOu/v1LgG8B/wHsB9ZOQ64F+7+dlVt10+V7+Vbgl8A3Fvx78ZjyvBr4Nr05gGv62/6OXnGi/6b7R+Ak8G/A81bodTpbri/SW2zwyOtzYCVydcm2aN/bWYFVNx1fs9BrKx3vvxd3TUmurfRWDH6z/738k2GO5ydjJalxq7l1I0nqwEIvSY2z0EtS4yz0ktQ4C70kNc5CL0mNs9BLUuMs9JLUuP8HZksDWdviuy0AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 716071\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 716071\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/UCwAAD0VJREFUeJzt3W2spGddx/Hvj63dJqCHwiKStsvZZmvjaozGY5toNI08dGu7lCCJW4xBbdiAqfGNCUvQF5KYFN8YCI3NBsuChpYKBHfpauXBWl5U7Rah9CGVpdR0N2jlaQVDIJW/L+YuTk/POTtzZubcM9f5fpLNztxzz5z/deac37nmf19zT6oKSVK7ntd3AZKk2TLoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY07r+8CAHbt2lXLy8t9lyFJC+WBBx74alW95Fz7zUXQLy8vc/Lkyb7LkKSFkuTfR9mv19ZNkgNJjpw9e7bPMiSpab0GfVUdr6pDS0tLfZYhSU3zYKwkNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklq3Fy8YUqaV8uH7/rB5SduvrbHSqTNM+ilVYbDXWqBrRtJapynQJCkxnkKBElqnK0bSWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxk096JNcleQzSW5NctW0H1+SNJ6Rgj7JbUmeSvLQqu37kzyW5FSSw93mAr4NXACcnm65kqRxjTqjPwrsH96QZAdwC3ANsA+4Ick+4DNVdQ3wVuCPp1eqJGkzRgr6qroX+PqqzVcAp6rq8ar6HnAHcH1Vfb+7/RvAzqlVKknalEk+HPwi4Mmh66eBK5O8DrgaeCHwnvXunOQQcAhg9+7dE5QhSdrIJEG/pqr6KPDREfY7AhwBWFlZqWnXIUkamGTVzRngkqHrF3fbRuaHg0vS7E0S9PcDlyXZk+R84CBwbJwH8MPBJWn2Rl1eeTtwH3B5ktNJbqyqp4GbgLuBR4E7q+rh2ZUqSdqMkXr0VXXDOttPACemWpEkaap6PQWCPXpJmr1eg94evSTNnic1k6TG2bqRpMbZupGkxtm6kaTGGfSS1Dh79JLUOHv0ktQ4WzeS1Lipn6ZYatXy4buedf2Jm6/tqRJpPAa9xHNDXGqJB2MlqXEejJWkxnkwVpIaZ9BLUuMMeklqnEEvSY1z1Y0kNc5VN5LUOFs3ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXGuo5ekxrmOXpIaZ+tGkhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIa5ykQJKlxngJBkhpn60aSGnde3wVIi2r58F0/uPzEzdf2WIm0MWf0ktQ4Z/TatoZn5FLLnNFLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjZhL0SZ6f5GSS62bx+JKk0Y0U9EluS/JUkodWbd+f5LEkp5IcHrrprcCd0yxUkrQ5o87ojwL7hzck2QHcAlwD7ANuSLIvyauAR4CnplinJGmTRjqpWVXdm2R51eYrgFNV9ThAkjuA64EXAM9nEP7fSXKiqr4/tYolSWOZ5OyVFwFPDl0/DVxZVTcBJPkt4KvrhXySQ8AhgN27d09QhiRpIzM7TXFVHT3H7UeAIwArKys1qzqkreCHkGieTbLq5gxwydD1i7ttI/PDwSVp9iaZ0d8PXJZkD4OAPwi8YZwHqKrjwPGVlZU3TVCHNDI/bETb0ajLK28H7gMuT3I6yY1V9TRwE3A38ChwZ1U9PLtSJUmbMeqqmxvW2X4CODHViiRJU9XrKRDs0UvS7PUa9FV1vKoOLS0t9VmGJDXNk5pJUuNs3UhS42b2hqlRuLxSLfLNU5o3tm4kqXEGvSQ1zh69JDXOHr2a52kPtN3ZupGkxvU6o5da5woczQNn9JLUOA/GSlLjPNeNJDXOHr20RezXqy8GvZrkkkrp/3kwVpIaZ9BLUuN6bd0kOQAc2Lt3b59lSFvOfr22kqtuJKlxtm4kqXGuulEzXGkjrc0ZvSQ1zhm91DMPzGrWnNFLUuOc0WuhtdaXd3avWfDslZLUOD9KUJpTzu41LfboJalxBr0kNc6DsVo4rR2AlWbNGb0kNc6gl6TG2brRQtju7Zr1VuC4MkejMOilBbPd/+hpfLZuJKlxzug1t5y5StPhKRAkqXGeAkFqhAdmtR579JLUOHv0miv25aXpM+ilBtnG0TCDXr1zFi/Nlj16SWqcM3qpceu9YrKls304o5ekxjmjVy/sy/fPA7bbhzN6SWqcQS9JjbN1I8k2TuOc0UtS45zRa6acKUr9m3rQJ/kJ4PeBXcCnqurPp/01JM2Of5zbM1LQJ7kNuA54qqp+amj7fuBdwA7gvVV1c1U9Crw5yfOADwAG/Taz3tJJl1QuNv8ALK5RZ/RHgfcwCG4AkuwAbgFeBZwG7k9yrKoeSfIa4C3AX063XElbyT/ObRgp6Kvq3iTLqzZfAZyqqscBktwBXA88UlXHgGNJ7gI+OL1yNa8MBGl+TdKjvwh4cuj6aeDKJFcBrwN2AifWu3OSQ8AhgN27d09QhiRpI1M/GFtV9wD3jLDfEeAIwMrKSk27Ds2es/jty379YplkHf0Z4JKh6xd32yRJc2SSGf39wGVJ9jAI+IPAG8Z5gCQHgAN79+6doAxtJWfx0uIZaUaf5HbgPuDyJKeT3FhVTwM3AXcDjwJ3VtXD43zxqjpeVYeWlpbGrVuSNKJRV93csM72E2xwwFWS1L9eT4Fg60ZafKvbeR6cnT+p6n/By8rKSp08ebLvMrQO+/LaLEN/tpI8UFUr59rPk5pJmhmXYc6HXk9TnORAkiNnz57tswxJalqvM/qqOg4cX1lZeVOfdejZbNVIbbF1I2lL2Mbpj0EvwFm8+uMfgNmzRy9JjXN55TbmLF7zzNn9uY26vNIPB5ekxtmjb9R6fU9n8dL2Y9BLmksepJ0eD8ZKUuN8w9QCGnemY7tG2t5s3Sw4X95KOheDviHO3CWtxaCXNPfGXUXmq9tnM+glLRRfuY7PT5haEP5wS6Pz2NWzuepmzhjokqbNUyBIUuMMeklqnEEvSY0z6CWpcS6vnAMegJU0SwZ9Twx3aWu41NJ19JK2qe30rlrX0UvaNrbrK2lbNzPmy0ZpsbT4O2vQz8B6s4btOpuQ1C+DfkoMcaltizzTN+glaR2tTOAMekka06LN7g16SZqB1a8G+vyDYNBPoJWXdZKmY14zYVsH/UZPynp/fef1iZTUj0XIhG0d9BtZtB6cJK3HUyCMYBH+YkvSeno9TXFVHa+qQ0tLS32WIUlN23atG2fnkvrQ50nUtkXQG+6StrNmg95wl6QBP0pQkhrX1IzeWbwkPZczeklqnEEvSY1b+NaN7RpJ2pgzeklqnEEvSY0z6CWpcQa9JDXOoJekxs1k1U2S1wLXAj8C/EVV/f0svo4k6dxGntEnuS3JU0keWrV9f5LHkpxKchigqj5WVW8C3gz8+nRLliSNY5zWzVFg//CGJDuAW4BrgH3ADUn2De3yh93tkqSejBz0VXUv8PVVm68ATlXV41X1PeAO4PoMvBP426r67PTKlSSNa9Ie/UXAk0PXTwNXAr8HvBJYSrK3qm5dfcckh4BD3dVvJ3lskzXsAr66yfvOG8cyf1oZBziWuZR3TjSWl4+y00wOxlbVu4F3n2OfI8CRSb9WkpNVtTLp48wDxzJ/WhkHOJZ5tRVjmXR55RngkqHrF3fbJElzYtKgvx+4LMmeJOcDB4Fjk5clSZqWcZZX3g7cB1ye5HSSG6vqaeAm4G7gUeDOqnp4NqWua+L2zxxxLPOnlXGAY5lXMx9LqmrWX0OS1CNPgSBJjVuIoE/yoiSfSPLF7v8L19nv75J8M8nHV20/muTLST7X/fuZral8zRonHcueJP/cvRP5Q92xkV6MMZY3dvt8Mckbh7bf072r+pnn5Ue3rvq139W96vad3ff4VPc9Xx667W3d9seSXL2Vda9ls2NJspzkO0PPwXOWQm+lEcbxy0k+m+TpJK9fdduaP2d9mXAs/zv0nEx+3LOq5v4f8KfA4e7yYeCd6+z3CuAA8PFV248Cr+97HFMay53Awe7yrcBb5nkswIuAx7v/L+wuX9jddg+w0lPtO4AvAZcC5wOfB/at2ud3gVu7yweBD3WX93X77wT2dI+zo8fnYZKxLAMP9VX7JsaxDPw08IHh3+mNfs4WbSzdbd+eZj0LMaMHrgfe311+P/DatXaqqk8B39qqojZp02NJEuBXgA+f6/5bZJSxXA18oqq+XlXfAD7BqlNp9GTNd3Wv2md4fB8GXtE9B9cDd1TVd6vqy8Cp7vH6MslY5sk5x1FVT1TVg8D3V9133n7OJhnL1C1K0L+0qr7SXf4P4KWbeIw/SfJgkj9LsnOKtY1rkrG8GPhmDVY7weCdyBdNs7gxjTKWtd49PVzz+7qXp3+0xcFzrrqetU/3PT/L4DkY5b5baZKxAOxJ8q9J/jHJL8262A1M8n1dxOdkIxckOZnknzI4G/BE5ubDwZN8EvixNW56+/CVqqok4y4VehuDIDqfwVKmtwLv2Eydo5jxWLbUjMfyG1V1JskPAx8BfpPBy1htna8Au6vqa0l+DvhYkp+sqv/uu7Bt7uXd78alwKeTfKGqvrTZB5uboK+qV653W5L/TPKyqvpKkpcBT4352M/MOr+b5H3AH0xQ6ihfb1Zj+RrwwiTndbOymb8TeQpjOQNcNXT9Yga9earqTPf/t5J8kMHL3a0K+lHe1f3MPqeTnAcsMXgO5u0d4ZseSw0awt8FqKoHknwJ+HHg5Myrfq5Jvq/r/pz1ZKKfkaHfjceT3AP8LIOe/6YsSuvmGPDMUfQ3An8zzp27EHqmx/1a4KGN7zFTmx5L90v5D8AzR+jH/l5M2ShjuRt4dZILu1U5rwbuTnJekl0ASX4IuI6tfV5GeVf38PheD3y6ew6OAQe7lSx7gMuAf9miutey6bEkeUkGpxunmz1exuBAZh8meaf9mj9nM6pzFJseSzeGnd3lXcAvAo9MVE1fR6XHPIL9YuBTwBeBTwIv6ravAO8d2u8zwH8B32HQE7u62/5p4AsMguSvgBcs8FguZRAqp4C/BnYuwFh+p6v3FPDb3bbnAw8ADwIPA+9ii1euAL8K/BuDmdLbu23vAF7TXb6g+x6f6r7nlw7d9+3d/R4DrunrOZh0LMCvdd//zwGfBQ7M+Th+vvt9+B8Gr64e3ujnbBHHAvxCl1ef7/6/cdJafGesJDVuUVo3kqRNMuglqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4wx6SWrc/wEcHLIpfUeuXAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "thcut,pzcut,dcacut 716071\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 716071\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": [ + "kq=0\n", + "for quad in [t1234,t1231,t1212,t1123] :\n", + " plotTriplets(quad,600, kq)\n", + " kq+=1" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "New QUAD 0\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADe1JREFUeJzt3VuMJGUZxvHncVdQUUdwwQMLzpJFCZ7jeLjRICqgMGAC0UWi4GmNxgvvXKPeGBPRKzWYkI3xsBeCiFFZQA0qqDEe2EVEUJHZFeOuqKAyGiUYwutFfaNl2z3TPVPdVf32/5dstru6Dm9/PfX0119VdTsiBADI6xFtFwAAGC+CHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBILnNbRcgSVu2bIn5+fm2ywCAqbJ///77IuLYtebrRNDPz89r3759bZcBAFPF9m+HmY+hGwBIrtWgt71oe/fy8nKbZQBAaq0GfUTsjYidc3NzbZYBAKkxdAMAyRH0AJAcQQ8AyRH0AJAcQQ8AyXXigikAkzG/67r/3L770rNbrASTRNCjcYQJ0C0EPZBc/Y130HTekHNjjB4AkiPoASA5hm7QiEHDAwDaR9BjrAa9ATAm3IxB4+y88aKOoRsASI4ePdaNXiMwHejRA0ByBD0AJMfQDVrBxTr9tdUuvB65EfSYarMSULPyPDEeBD3QsqYOanNwHIMwRg8AydGjx0jG0Wsc5qIfhiv+i547RkXQo1MIsf+iLdAUhm4AIDl69JgKGYZxMjwHTCd69ACQHEEPAMkxdIOpw0FKYDT06AEgOXr0AP4HB43zoUcPAMnRo8eaGBNvHm2KSaJHDwDJEfQAkBxBDwDJEfQAkBxBDwDJcdYN0uD8b6A/evQAkBxBDwDJEfQAkBxBDwDJEfQAkBxBDwDJcXolUurKqZZ8eRm6gB49ACRHjx7/h14oVnTlkxE2hh49ACQ3lqC3fZTtfbbPGcf6AQDDGyrobX/G9p9s394z/Szbd9pesr2r9tB7JV3VZKEAgPUZdoz+c5Iuk7RnZYLtTZI+JelVkg5Jutn2NZKOl/QLSY9qtFJgnRhnxqwbKugj4nu253smv0jSUkQclCTbV0o6T9JjJR0l6VRJD9i+PiIe7l2n7Z2SdkrSiSeeuN76AQBr2MhZN8dL+l3t/iFJL46Id0uS7Usk3dcv5CUpInZL2i1JCwsLsYE6AACrGNvplRHxuXGtGwAwvI2cdXNY0gm1+1vLNABAh2ykR3+zpJNtb1MV8DskvaGRqoApxgVn6JphT6+8QtIPJT3D9iHbb42IhyS9W9I3Jf1S0lURccf4SgUArMewZ91cOGD69ZKub7QiAECj+AoEAEiu1aC3vWh79/LycptlAEBqrQZ9ROyNiJ1zc3NtlgEAqTF0AwDJEfQAkBxBDwDJEfQAkBw/JYiZwlcWYxbRoweA5OjRAw3g+23QZVwwBQDJccEUACTH0A0kMfQAZMbBWABIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQ4vRLAUPieoOnFlbEAkBxXxgJAcozRA0ByBD0AJEfQA0BynHWDmcVZJJgV9OgBIDmCHgCSY+gGWCe+wx/Tgh49ACRHjx4QB2aRG0E/wxh6AGZDq0Fve1HS4vbt29ssAxgKb4yYVnzXDQAkx8FYAEiOoAeA5Ah6AEiOoAeA5Ah6AEiOoAeA5Ah6AEiOoAeA5Ah6AEiOoAeA5PhSMwAj49s+pws9egBIrtWgt71oe/fy8nKbZQBAanx7JQAkx9ANACRH0ANAcgQ9ACTH6ZVAD04dRDb06AEgOXr0wCr4QXBkQI8eAJIj6AEgOYIeAJIj6AEgOYIeAJIj6AEgOYIeAJIj6AEgOYIeAJLjh0cAIDl+eAQAkmPoBgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBIDl+YWrG8ItJwOyhRw8AyRH0AJAcQQ8AyRH0AJAcQQ8AyXHWDYAN6T2T6+5Lzx7rNsax/uzo0QNAcgQ9ACRH0ANAcgQ9ACRH0ANAcgQ9ACRH0ANAcq0Gve1F27uXl5fbLAMAUms16CNib0TsnJuba7MMAEiNoRsASI6vQOgYLvUG1od9Z7CpD3peXABY3dQHPfrjDRDTjp+9bA5j9ACQHEEPAMkxdJMIH3UB9EPQA2gUx4e6h6CfcvTiAayFMXoASI4e/Qyg1w/MNnr0AJAcQQ8AyTF0M4UYigEwCoJ+AziNDGgXnZ7hMHQDAMnRoweAIUzzJ3h69ACQ3Ez36HvH96btXRoAhkGPHgCSI+gBILlUQzfTfLAEAMYlVdADQJu62tmciaDvauMDmH7TcNHWTAQ9APSadAewzQ4nQd9h09BTADLLsg/OXNBneeEANCf78O7MBT2AyckeoNOi1aC3vShpcfv27W2WAaAj+MQ9Hq0GfUTslbR3YWHh7U2ve9J/MIO2Ry8GQNsYuqnhYyYwGexrk8VXIABAcgQ9ACTH0M2IOFgEdN+goaFZ3X/p0QNAcvToAbRq3L3sWe3F1xH0AKYKwT06hm4AIDl69APQawCwEV3KEIJ+zLhiFkDbCPqWcGUggElhjB4AkiPoASA5gh4AkmOMHgBqunS2TFMIegATkTFApwVDNwCQHD36DqCnA2Cc6NEDQHIEPQAkx9ANAEzYpK+Mp0cPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQHBdMAcCIpu37qejRA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0Byjoi2a5DteyX9dp2Lb5F0X4PlNIW6RkNdo+lqXVJ3a8tY19Mi4ti1ZupE0G+E7X0RsdB2Hb2oazTUNZqu1iV1t7ZZrouhGwBIjqAHgOQyBP3utgsYgLpGQ12j6WpdUndrm9m6pn6MHgCwugw9egDAKjob9LaPsX2D7bvK/0cPmO8btu+3fW3P9G22f2x7yfYXbR9Rph9Z7i+Vx+fHVNfFZZ67bF9cpj3O9q21f/fZ/nh57BLb99Yee9uk6irTb7J9Z237x5XpbbbXY2xfZ/tXtu+wfWlt/nW1l+2zyvNcsr2rz+MDn6/t95Xpd9o+c9h1jrMu26+yvd/2z8v/p9eW6fuaTqiuedsP1LZ9eW2ZF5R6l2x/0rYnWNdFPfvgw7afVx6bRHu9zPYtth+yfUHPY4P2zQ23lyKik/8kfUzSrnJ7l6SPDpjvFZIWJV3bM/0qSTvK7cslvbPcfpeky8vtHZK+2HRdko6RdLD8f3S5fXSf+fZLelm5fYmky8bZXqvVJekmSQt9lmmtvSQ9RtLLyzxHSPq+pFevt70kbZJ0QNJJZX0/k3TqMM9X0qll/iMlbSvr2TTMOsdc1/MlPbXcfpakw7Vl+r6mE6prXtLtA9b7E0kvkWRJX195TSdRV888z5Z0YMLtNS/pOZL2SLpgyH1zQ+0VEd3t0Us6T9Lny+3PS3ptv5ki4tuS/l6fVt7xTpd0dZ/l6+u9WtIrRnyHHKauMyXdEBF/iYi/SrpB0lk9NT5d0nGqwqsJjdS1xnon2l4R8c+IuFGSIuJfkm6RtHWEbfd6kaSliDhY1ndlqW9QvfXne56kKyPiwYj4jaSlsr5h1jm2uiLipxHx+zL9DkmPtn3kiNtvvK5BK7T9FEmPj4gfRZViezRg355AXReWZZuyZl0RcXdE3Cbp4Z5l++4DDbVXp4P+SRFxT7n9B0lPGmHZJ0q6PyIeKvcPSTq+3D5e0u8kqTy+XOZvsq7/bKPP9les9DLqR8PPt32b7attnzBCTU3V9dnykfWDtZ2iE+1l+wmqPrl9uzZ51PYa5nUZ9HwHLTvMOsdZV935km6JiAdr0/q9ppOqa5vtn9r+ru2X1uY/tMY6x13XitdLuqJn2rjba9Rlm2ivdn8c3Pa3JD25z0Pvr9+JiLA9sdODJlTXDklvrN3fK+mKiHjQ9jtU9UZOry8w5rouiojDth8n6cultj3DLDju9rK9WdUO+cmIOFgmr9les8T2MyV9VNIZtcnrfk0bcI+kEyPiz7ZfIOmrpcZOsP1iSf+MiNtrk9tsr7FqNegj4pWDHrP9R9tPiYh7yseXP42w6j9LeoLtzeXdfKukw+Wxw5JOkHSoBMhcmb/Jug5LOq12f6uq8b+VdTxX0uaI2F/bZr2GT6sa2/4f46wrIg6X//9u+wuqPobuUQfaS9V5xndFxMdr21yzvQZsp97zr/9d9M7T+3xXW3atdY6zLtneKukrkt4UEQdWFljlNR17XeWT6oNl+/ttH5D09DJ/ffht4u1V7FBPb35C7bXasqf1LHuTmmmvTg/dXCNp5cjzxZK+NuyC5Y/sRkkrR7Xry9fXe4Gk7/QMnzRR1zclnWH7aFdnmZxRpq24UD1/ZCUEV5wr6Zcj1LShumxvtr2l1PFISedIWunptNpetj+said9T32BdbbXzZJOdnVG1hGqdvZrVqm3/nyvkbTD1dkc2ySdrOog2TDrHFtdZUjrOlUHvH+wMvMar+kk6jrW9qay/ZNUtdfBMoz3N9svKUMjb9II+/ZG6yr1PELS61Qbn59gew3Sdx9oqL06fdbNE1WNx94l6VuSjinTFyR9ujbf9yXdK+kBVeNXZ5bpJ6naEZckfUnSkWX6o8r9pfL4SWOq6y1lG0uS3tyzjoOSTumZ9hFVB9N+pupN6pRJ1SXpKFVnAN1WaviEpE1tt5eq3kuoCvFby7+3baS9JL1G0q9VnR3x/jLtQ5LOXev5qhqKOiDpTtXOfOi3znX8va+rLkkfkPSPWvvcquog/8DXdEJ1nV+2e6uqg+iLtXUuqArRA5IuU7lwcxJ1lcdOk/SjnvVNqr1eqCqn/qHqE8Yda2VGE+3FlbEAkFyXh24AAA0g6AEgOYIeAJIj6AEgOYIeAJIj6AEgOYIeAJIj6AEguX8Dv4Mg4wnAZ5IAAAAASUVORK5CYII=\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": [ + "dphiNor\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": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADnhJREFUeJzt3V+MnNddxvHvU0NykQoDtVUq24uNbFWYCKnSKOGCi0gU1SZxXKoCtrhoIcoqCCOQkCAhSOGmIhISooEUtGosFymKFRUoNnWU/hHBvWghToUgrglYgSqOQk0aYRAgopAfFztphsW7+45nZmfmzPdzk50zs++co3Yfn/mdM+9JVSFJatc7pt0BSdJkGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY0z6CWpcQa9JDXOoJekxn3btDsAsGPHjtq7d++0uyFJc+W55557tap2bva6mQj6vXv3cuHChWl3Q5LmSpKvd3mdpRtJapxBL0mNM+glqXFTDfokR5KsXLt2bZrdkKSmTTXoq+psVS1v3759mt2QpKZZupGkxhn0ktQ4g16SGjcTX5iSpm3v/Z/91s//9PCdU+yJNH4GvRbKYKCP83f9x0GzzKCXxsBPBJplBr2aN6lZfJffMfQ1Cwx6NWmUcB8nQ1+zYCJBn+QW4C+A36iqP5vEe0hrzUq4S7OmU9AnOQncBVytqlsH2g8BHwe2AZ+sqof7T/0q8OSY+yptiUn9g+HsXtPSdR/9KeDQYEOSbcCjwGHgIHA8ycEkPwp8Dbg6xn5Kkm5Qpxl9VZ1PsndN823A5ap6ESDJaeAo8E7gFlbD/7+SnKuqN8fWY2nAvJZrnN1rK41So98FvDTw+Apwe1WdAEjyUeDV9UI+yTKwDLC0tDRCNyRJG5nYrpuqOrXJ8yvACkCv16tJ9UPtmddZ/Hqc3WvSRrnXzcvAnoHHu/ttnXk/ekmavFGC/lngQJJ9SW4CjgFnhrmA96OXpMnrur3yCeAOYEeSK8BDVfVYkhPA06xurzxZVReHefMkR4Aj+/fvH67XWjitlWvWYxlHk9B1183xddrPAedu9M2r6ixwttfr3Xuj15AkbcxbIEgzytm9xmWqQW/pRhtZlHKNNGlTDXpLN1rLcJfGz6MEJalxlm6kOWC9XqOY6ozeffSSNHmWbiSpcQa9JDVuqkHvvW4kafKs0UtS4/xmrKbCXSTS1jHopTnmP5jqwsVYSWqci7GS1DgXYyWpcdbotWW8YZk0Hama/rncvV6vLly4MO1uaMIM+q3jwuxiSPJcVfU2e52LsZLUOINekhrnbYo1UZZrpsP99RrkrhtJapylG0lqnEEvSY0z6CWpcQa9JDXOb8Zq7NxpI80Wg15qnFst5d0rJalx7qOXpMa5GCtJjbNGr7FwAVaaXc7oJalxzuilBeIOnMXkjF6SGmfQS1LjDHpJapw1emlBWa9fHGOf0Sf5/iR/kOTTSX5u3NeXJA2n04w+yUngLuBqVd060H4I+DiwDfhkVT1cVZeA+5K8A/hD4PfH323NAvfOS/Oh64z+FHBosCHJNuBR4DBwEDie5GD/ubuBzwLnxtZTSdIN6RT0VXUeeG1N823A5ap6sapeB04DR/uvP1NVh4GfHmdnJUnDG2Uxdhfw0sDjK8DtSe4APgTczAYz+iTLwDLA0tLSCN2QJG1k7LtuquoZ4JkOr1sBVgB6vV6Nux+SpFWj7Lp5Gdgz8Hh3v60z70cvSZM3StA/CxxIsi/JTcAx4MwwF/B+9JI0eV23Vz4B3AHsSHIFeKiqHktyAnia1e2VJ6vq4sR6qpnglso2+eWptnUK+qo6vk77OUbYQpnkCHBk//79N3oJSdImPEpQkhrn4eCS1Dhn9JLUOG9TLEmN8zbF2pQ7bRaLO3DaY41ekhpnjV6SGmeNXpIaZ+lGkhpn6UaSGmfpRpIa5/ZK/T9up9Rb3GrZBmf0ktQ4F2MlqXEuxkpS4yzdSFLjDHpJapy7biR14g6c+WXQC3BLpdQyd91IUuPcdSNJjXMxVpIaZ9BLUuMMeklqnEEvSY1ze6Wkobmnfr44o5ekxrmPXpIaN9XSTVWdBc72er17p9mPReW3YaXFYOlGkhrnYqykkaz9ZOji7OxxRi9JjTPoJalxBr0kNc4a/YJxp420eJzRS1LjDHpJapxBL0mNm0iNPskHgTuB7wAeq6rPTeJ9JEmb6zyjT3IyydUkz69pP5TkhSSXk9wPUFWfqap7gfuAnxpvlyVJwximdHMKODTYkGQb8ChwGDgIHE9ycOAlv95/XpI0JZ1LN1V1PsneNc23AZer6kWAJKeBo0kuAQ8DT1XVV693vSTLwDLA0tLS8D3XhrxfuKS3jLoYuwt4aeDxlX7bLwDvBz6c5L7r/WJVrVRVr6p6O3fuHLEbkqT1TGQxtqoeAR6ZxLUlScMZdUb/MrBn4PHuflsnHjwiSZM3atA/CxxIsi/JTcAx4EzXX66qs1W1vH379hG7IUlazzDbK58Avgy8N8mVJPdU1RvACeBp4BLwZFVdHOKazuglacKG2XVzfJ32c8C5G3lzjxKUpMnz7pWStoRbfqdnqve6sXQjSZM31Rm9pRupPZ55MHss3SwA//CkxWbpRpIaN9Wgdx+9JE2eB49IUuMMeklqnDV6SWqcNXpJapylG0lqnEEvSY2zRi9JjfMWCJK2nDc421qWbiSpcQa9JDXOm5o1xJuXSboeZ/SS1LipzuiTHAGO7N+/f5rdkDRFLsxOnt+MlaTGWbqRpMYZ9JLUOINekhrn9kpJM88F29EY9JI0oMV/VCzdSFLj3Ec/5/w2rKTNePfKOdHix0lpUvx7+b8s3UhS4wx6SWqcQS9JjXN75RxyAVbSMAz6LeQCkbSxWZvEtPI3a+lGkhpn0EtS4yzdSNII5qG844xekho39hl9ku8DHgS2V9WHx339Uc3iv76z2CdJ65u1RePNdJrRJzmZ5GqS59e0H0ryQpLLSe4HqKoXq+qeSXRWkjS8rqWbU8ChwYYk24BHgcPAQeB4koNj7Z0kaWSdgr6qzgOvrWm+Dbjcn8G/DpwGjo65f5KkEY1So98FvDTw+Apwe5J3AR8D3pfkgar6zev9cpJlYBlgaWlphG5IWlSub3Uz9sXYqvomcF+H160AKwC9Xq/G3Q9J0qpRgv5lYM/A4939ts48eGRj87ayL2k2jbKP/lngQJJ9SW4CjgFnhrlAVZ2tquXt27eP0A1J0kY6zeiTPAHcAexIcgV4qKoeS3ICeBrYBpysqovDvPk4ZvTW6CRtZL1PxouUF52CvqqOr9N+Djh3o2/uUYKSNHneAkGSGjfVm5rN42LseqWiYUtILrRK4zULf1OzWkqe6ozexVhJmjxLN5LUuIUu3az9qLfeR61Z+EgoadW0/h7nOQcs3UhS4yzdSFLjDHpJatxC1+glaVK6rgFuBWv0ktQ4SzeS1DiDXpIa12yNfl73vM5rvyXNLmv0ktQ4SzeS1DiDXpIaZ9BLUuMMeklqXLO7bkbVZfeLO2Sk+bXVf7/TPJTEXTeS1DhLN5LUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNc4vTE3ANL8YIWn2bXVG+IUpSWqcpRtJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS48b+zdgktwCfAF4Hnqmqx8f9HpKk7jrN6JOcTHI1yfNr2g8leSHJ5ST395s/BHy6qu4F7h5zfyVJQ+paujkFHBpsSLINeBQ4DBwEjic5COwGXuq/7H/G001J0o3qFPRVdR54bU3zbcDlqnqxql4HTgNHgSushn3n60uSJmeUGv0u3p65w2rA3w48AvxekjuBs+v9cpJlYBlgaWlphG6Mz+Ad5Wb5mpI0jLEvxlbVfwA/0+F1K8AKQK/Xq3H3Q5K0apTSysvAnoHHu/ttnSU5kmTl2rVrI3RDkrSRUYL+WeBAkn1JbgKOAWeGuYD3o5ekyeu6vfIJ4MvAe5NcSXJPVb0BnACeBi4BT1bVxWHe3Bm9JE1epxp9VR1fp/0ccO5G37yqzgJne73evTd6DUnSxtz+KEmNm2rQW7qRpMnzcHBJapylG0lqXKqm/12lJP8CfH3a/RijHcCr0+7EFliEcS7CGGExxtniGL+3qnZu9qKZCPrWJLlQVb1p92PSFmGcizBGWIxxLsIY12PpRpIaZ9BLUuMM+slYmXYHtsgijHMRxgiLMc5FGON1WaOXpMY5o5ekxhn0Y5Tkt5L8XZK/SfInSb5z4LkH+mfrvpDkA9Ps5yiS/ESSi0neTNJb81wTY3zLOmciz73rnQGd5LuTfD7JP/T/+13T7OOokuxJ8udJvtb//+sv9tubGmdXBv14fR64tap+EPh74AGA/lm6x4AfYPXs3U/0z9ydR8+zegD8+cHGxsa40ZnILTjFmjOggfuBL1bVAeCL/cfz7A3gl6vqIPBDwM/3//drbZydGPRjVFWf69++GeArvH127lHgdFX9d1X9I3CZ1TN3505VXaqqF67zVDNj7FvvTOS5t84Z0EeBT/V//hTwwS3t1JhV1StV9dX+z//O6q3Ud9HYOLsy6CfnZ4Gn+j9f73zdXVveo8lqbYytjWcz766qV/o//zPw7ml2ZpyS7AXeB/wlDY9zI2M/M7Z1Sb4AfM91nnqwqv60/5oHWf3o+PhW9m1cuoxR7aqqStLEdrwk7wT+CPilqvq3JN96rqVxbsagH1JVvX+j55N8FLgL+JF6e+/qyOfrbqXNxriOuRpjB62NZzPfSPKeqnolyXuAq9Pu0KiSfDurIf94Vf1xv7m5cXZh6WaMkhwCfgW4u6r+c+CpM8CxJDcn2QccAP5qGn2coNbGOPKZyHPmDPCR/s8fAeb6k1tWp+6PAZeq6rcHnmpqnF35hakxSnIZuBn4Zr/pK1V1X/+5B1mt27/B6sfIp65/ldmW5MeB3wV2Av8K/HVVfaD/XBNjfEuSHwN+B9gGnKyqj025S2PRPwP6Dlbv5vgN4CHgM8CTwBKrd5L9yapau2A7N5L8MPAl4G+BN/vNv8Zqnb6ZcXZl0EtS4yzdSFLjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhr3v66z/OW7kDL5AAAAAElFTkSuQmCC\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": [ + "dz\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": [ + "z0\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": [ + "dr\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": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADwZJREFUeJzt3X+sW+ddx/HPZwnNWBlZu3RjJA031e0Y4bdm2kmIKXSsTSm3RbQaCRVrgTUw1D/4j6CBQAiJjr/G1ElVNEqXP9auFAG5TaDqxrJNiEGbrisNJfQmdGpCoe3GwrRVnap++cPPHQdzfW1fH/scf/1+SVHs43OOv358/fHj5zw+dkQIAJDXa5ouAAAwWQQ9ACRH0ANAcgQ9ACRH0ANAcgQ9ACRH0ANAcgQ9ACRH0ANAcpubLkCStm3bFgsLC02XAQAz5cSJEy9GxCWD1mtF0C8sLOjRRx9tugwAmCm2vzTMegzdAEByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJNeKL0whl4WDR791+Zk7rmuwEkg8HyDoMWHVkKkicOrRL8T7tTuhP58IejSCwAGmh6BHLfr1IEfdltD/X6N+GhrnOUBuBD1ahdAfrK5Ap63nx0SC3vaFkj4j6fci4sFJ3AeaN+ke5DyO79MrxyQMNb3S9t22n7f9ZM/yvbZP2V6xfbBy029Kur/OQgEAGzNsj/4eSXdKOry6wPYmSR+R9G5JZyU9YvuIpO2S/lnSa2utFEiKXjwmbaigj4jP2l7oWXyFpJWIOCNJtu+TdIOk75B0oaTdkl6yfSwiXq2tYsy9eRzSAcYxzhj9dknPVq6flXRlRNwuSbZvlfRiv5C3fUDSAUnauXPnGGUAANYzsVk3EXHPgNsPSTokSZ1OJyZVB+rFMENOzMDJbZygPyfp0sr1HWUZgAF4w8Q0jRP0j0i63PYudQN+n6RfqKUqICHCHU0ZKuht3ytpj6Rtts9K+t2I+BPbt0t6SNImSXdHxMmJVQoMwPADsLZhZ93s77P8mKRjG71z20uSlhYXFze6CwDAAI2ejz4iliPiwNatW5ssAwBS44dHACA5TmqGgTiICMw2gh4pcWB242i7fBodurG9ZPvQ+fPnmywDAFJrtEcfEcuSljudzm1N1gFMCsNeaAMOxgJAcgQ9ACRH0ANAcgQ9ACTHrBsASI5TIABAcgzdAEByfDMW/w9zv4FcCHqkN+2v9Gd6o+R0CDkwdAMAyRH0AJAc0ysBIDmmVwJAcgzdAEByBD0AJEfQA0ByBD0AJEfQA0ByfDMWc4VvemIeNRr0tpckLS0uLjZZBjC2TKc9QD7MoweA5BijB4DkCHoASI6gB4DkCHoASI6gB4DkCHoASI6gB4Dk+GYsJPGFHyAzfmEKAJJrtEcfEcuSljudzm1N1gFgMM4TNLsYoweA5Ah6AEiOg7HABnEAG7OCoMfcYswZ84KhGwBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjpOaAUByjQZ9RCxHxIGtW7c2WQYApMbQDQAkxykQgCFxbhvMKnr0AJAcQQ8AyRH0AJAcY/SAOGUxcqNHDwDJEfQAkBxDN3OM6YLAfCDoAYyMYxqzhaEbAEiOoAeA5Ah6AEiOoAeA5Ah6AEiOoAeA5PiFKQBIrtF59BGxLGm50+nc1mQdQBVzxJENQzcAkBzfjAXWwWkikAE9egBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOT4KcE5w0/jAfOHoAcwlt7OwzN3XNdQJeiHoRsASI6gB4DkCHoASK72oLf9fbbvsv2A7ffXvX8AwGiGCnrbd9t+3vaTPcv32j5le8X2QUmKiKci4tckvUfSj9dfMgBgFMP26O+RtLe6wPYmSR+RdK2k3ZL2295dbrte0lFJx2qrFACwIUMFfUR8VtJXehZfIWklIs5ExDcl3SfphrL+kYi4VtLNdRYLABjdOPPot0t6tnL9rKQrbe+R9HOStmidHr3tA5IOSNLOnTvHKAMAsJ7avzAVEcclHR9ivUOSDklSp9OJuusAAHSNM+vmnKRLK9d3lGUAgBYZJ+gfkXS57V22L5C0T9KResoCANRl2OmV90r6e0nfa/us7V+JiFck3S7pIUlPSbo/Ik5OrlQAwEYMNUYfEfv7LD+mMaZQ2l6StLS4uLjRXQAABmj0FAgRsRwRB7Zu3dpkGQCQGue6AYDkCHoASI6gB4DkGg1620u2D50/f77JMgAgNQ7GAkByDN0AQHIEPQAkR9ADQHIEPQAkx6wbAEiOWTcAkFztPzyC9lk4eLTpEgA0iDF6AEiOoAeA5Ah6AEiOoAeA5JheCQDJNTrrJiKWJS13Op3bmqwDQH2qs7yeueO6BivBKoZuACA5gh4AkuMLUwAmhmGcdqBHDwDJEfQAkBxBDwDJMY8eAJLjNMUAkBxDNwCQHNMrE+G88wDWQtADQB9ZvgfA0A0AJEfQA0ByDN0AmLosQyKzgqAHMBWjThbgzaA+BP2I+OMDMGv4ZiwAJMcvTLUMnxgA1I1ZNwCQHEEPAMlxMLbF+s1SqA7pcNoDAIMQ9ABaY5yOC8e3+mPoBgCSm+sefW/vYVZ6AQzXYN7QWx8PPXoASI6gB4Dk5nropi0YisE84+9/8gj6mjCGCKCtCPo+CG4AWTQa9LaXJC0tLi5O9H7aGNp8XAUwLalOataWQB/mG60AMC0M3VTQywaQEUEPYKYM88m9LZ/u24J59ACQHD16AHMv+yeAtEHPeDuQ3zRf57P8ZpA26NuINx9g+mY5oOtC0A+BgAYwy2Y+6EcNYUIbmF/z2ruf+aBvEm8aAGYB0ysBIDl69ABQk2GHhqY9hESPHgCSo0cPYC7N0zE2evQAkBxBDwDJEfQAkNxc/MIUAAwr49h9ql+YAoC2avINhKEbAEiOoAeA5Ah6AEiOoAeA5Ah6AEiOoAeA5DjXDQCMYRbm3dOjB4DkCHoASI6gB4DkCHoASI6gB4DkmHUDACMaZqZNm2bj0KMHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOQcEU3XINsvSPrSBjffJunFGsupC3WNhrpG09a6pPbWlrGu74mISwat1IqgH4ftRyOi03QdvahrNNQ1mrbWJbW3tnmui6EbAEiOoAeA5DIE/aGmC+iDukZDXaNpa11Se2ub27pmfoweALC+DD16AMA6Whv0ti+2/bDtp8v/F/VZ729sf9X2gz3Ld9n+B9srtj9h+4KyfEu5vlJuX5hQXbeUdZ62fUtZ9nrbj1f+vWj7Q+W2W22/ULntfdOqqyw/bvtU5f7fVJY32V6vs33U9r/YPmn7jsr6G2ov23vL41yxfXCN2/s+Xtu/VZafsn3NsPucZF223237hO1/Kv9fVdlmzed0SnUt2H6pct93VbZ5e6l3xfaHbXuKdd3c8xp81faPlNum0V7vtP2Y7Vds39RzW7/X5tjtpYho5T9JfyTpYLl8UNIH+6z3LklLkh7sWX6/pH3l8l2S3l8u/7qku8rlfZI+UXddki6WdKb8f1G5fNEa652Q9M5y+VZJd06yvdarS9JxSZ01tmmsvSS9TtJPlnUukPQ5SddutL0kbZJ0WtJlZX9flLR7mMcraXdZf4ukXWU/m4bZ54Tr+lFJ310u/4Ckc5Vt1nxOp1TXgqQn++z3HyW9Q5Il/fXqczqNunrW+UFJp6fcXguSfkjSYUk3DfnaHKu9IqK9PXpJN0j6WLn8MUk/u9ZKEfEpSV+rLivveFdJemCN7av7fUDSu0Z8hxymrmskPRwRX4mI/5L0sKS9PTW+VdKb1A2vOtRS14D9TrW9IuIbEfFpSYqIb0p6TNKOEe671xWSViLiTNnffaW+fvVWH+8Nku6LiJcj4t8krZT9DbPPidUVEV+IiH8vy09K+nbbW0a8/9rr6rdD22+R9J0R8fnopthh9XltT6Gu/WXbugysKyKeiYgnJL3as+2ar4Ga2qvVQf/miHiuXP4PSW8eYds3SvpqRLxSrp+VtL1c3i7pWUkqt58v69dZ17fuY437X7Xay6geDb/R9hO2H7B96Qg11VXXn5aPrL9TeVG0or1sv0HdT26fqiwetb2GeV76Pd5+2w6zz0nWVXWjpMci4uXKsrWe02nVtcv2F2x/xvZPVNY/O2Cfk65r1c9Lurdn2aTba9Rt62ivZn8c3PYnJX3XGjd9oHolIsL21KYHTamufZJ+sXJ9WdK9EfGy7V9VtzdyVXWDCdd1c0Scs/16SX9eajs8zIaTbi/bm9V9QX44Is6UxQPba57Y/n5JH5R0dWXxhp/TGjwnaWdEfNn22yX9ZamxFWxfKekbEfFkZXGT7TVRjQZ9RPxUv9ts/6ftt0TEc+Xjy/Mj7PrLkt5ge3N5N98h6Vy57ZykSyWdLQGytaxfZ13nJO2pXN+h7vjf6j5+WNLmiDhRuc9qDR9Vd2z7/5hkXRFxrvz/NdsfV/dj6GG1oL3UnWf8dER8qHKfA9urz/1Ue/7Vv4vedXof73rbDtrnJOuS7R2S/kLSeyPi9OoG6zynE6+rfFJ9udz/CdunJb21rF8dfpt6exX71NObn1J7rbftnp5tj6ue9mr10M0RSatHnm+R9FfDblj+yD4tafWodnX76n5vkvS3PcMnddT1kKSrbV/k7iyTq8uyVfvV80dWQnDV9ZKeGqGmseqyvdn2tlLHt0n6GUmrPZ1G28v2H6j7Iv2N6gYbbK9HJF3u7oysC9R9sR9Zp97q4z0iaZ+7szl2Sbpc3YNkw+xzYnWVIa2j6h7w/rvVlQc8p9Oo6xLbm8r9X6Zue50pw3j/bfsdZWjkvRrhtT1uXaWe10h6jyrj81Nsr37WfA3U1F6tnnXzRnXHY5+W9ElJF5flHUkfraz3OUkvSHpJ3fGra8ryy9R9Ia5I+jNJW8ry15brK+X2yyZU1y+X+1iR9Es9+zgj6W09y/5Q3YNpX1T3Tept06pL0oXqzgB6otTwx5I2Nd1e6vZeQt0Qf7z8e9847SXppyX9q7qzIz5Qlv2+pOsHPV51h6JOSzqlysyHtfa5gb/3DdUl6bclfb3SPo+re5C/73M6pbpuLPf7uLoH0Zcq++yoG6KnJd2p8sXNadRVbtsj6fM9+5tWe/2Yujn1dXU/YZwclBl1tBffjAWA5No8dAMAqAFBDwDJEfQAkBxBDwDJEfQAkBxBDwDJEfQAkBxBDwDJ/Q9BZKrLcH2IFAAAAABJRU5ErkJggg==\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": [ + "zinner zcut\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": [ + "module\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": [ + "dysize\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": [ + "ysize\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": [ + "ys-pred\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": [ + " \n", + "New QUAD 1\n", + " \n", + "dphi\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": [ + "dphiNor\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/UCwAADZdJREFUeJzt3VuMnHUZx/HfzyKYKK7ComApLqREqVeSCSIS06gxpVrqWYiJELG1GoxemSYYTbzxlHhBxJiNbdTEFCOeuloCnghXIIUABQtSGghtkFZNVo0JiD5ezFscp53tzO57fOb7STadw9udZ/4785v/PO9/3nFECACQ14uaLgAAUC2CHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBILlTmi5AkmZnZ2Nubq7pMgCgU+69994/R8RZJ9uuFUE/NzenvXv3Nl0GAHSK7SfH2Y7WDQAkR9ADQHIEPQAkR9ADQHIEPQAkR9ADQHIEPQAk12jQ295ke35xcbHJMgAgtUY/MBURC5IWer3elibrQLnmtv/yhdNPfOVdDVaS0zjjy98Ag2jdAEByBD0AJEfQA0ByrTioGfIa1SumhwzUhxk9ACTHjB7ogMF3QFX8Tt5V5UbQAx1WVlizZDM3gh61qWJWisnwN5hOBD2Ow8ytmwhxjELQY0l1hz4vMkD5CHq0FqEPlIOgh6Tx3vYPb0P4At1A0KMU9IeB9iLosWxlhTsvEtVayfjyt8mBoAfwfwj3fDgEAgAkx4weaClm1igLQT/FuhQkmZdadunvgG6idQMAyRH0AJAcQQ8AydGjBzCxzPtMMmo06G1vkrRp7dq1TZaBJAgf4MQaDfqIWJC00Ov1tjRZB7qFQAcmQ+sGndbVpYldrRvdRNBPGQIGmD4EPYAVoZXWfiyvBIDkCHoASI7WDVAh2hpoA2b0AJAcQQ8AyRH0AJAcQQ8AyRH0AJAcq26AmvCpZDSFGT0AJMeMHimxfh34H2b0AJAcQQ8AyRH0AJAcPXqkR78e044ZPQAkR9ADQHIEPQAkR9ADQHIEPQAkx6oboGQc0wZtw4weAJIj6AEgOYIeAJIj6AEgOYIeAJIj6AEgOYIeAJIj6AEgOYIeAJIrPehtX2T727Zvsf3Jsn8/AGAyYwW97Z22j9h+aOjyDbYftX3A9nZJioj9EbFN0ockvaX8kgEAkxh3Rv9dSRsGL7C9StJNkq6QtE7S1bbXFdddKemXkvaUVikAYFnGCvqIuFPSX4cuvkTSgYg4GBHPSbpZ0uZi+90RcYWkj5RZLABgcis5euVqSU8NnD8k6U2210t6n6TTtMSM3vZWSVsl6bzzzltBGQCApZR+mOKIuEPSHWNsNy9pXpJ6vV6UXQeA+vFF7O20klU3hyWtGTh/bnEZAKBFVhL090i60Pb5tk+VdJWk3eWUBQAoy1itG9u7JK2XNGv7kKQvRsQO29dLuk3SKkk7I+LhyirFsvGNR/9DawHTaKygj4irR1y+RyyhBIBWa/QQCLY32Z5fXFxssgwASK3RoI+IhYjYOjMz02QZAJBa6csrgWnDPhC0HUevBIDkCHoASI6gB4DkCHoASI7llQCQHMsrASA5WjcAkBxBDwDJEfQAkBxBDwDJEfQAkBxBDwDJsY4eAJJr9OiVEbEgaaHX621psg5Mp1FHneSbp5ANrRsASI6gB4DkCHoASI5vmEpksOdMnxnAMQQ9AJSkrZMtgh4Y0tYnK7BcjQa97U2SNq1du7bJMlLiC6sBHMM6emAZeCFFl3S+dcOHXgBgaZ0PeqBKzNyRAevoASA5gh4AkiPoASC5qe7Rs14awDRgRg8AyRH0AJAcQQ8AyXEIBABTY1r3yzU6o4+IhYjYOjMz02QZAJAarRsASI6gB4DkpnodPYDqDB8naJp64m3DjB4AkmNGD6BR07oSpk7M6AEgOWb0AGrBzL05zOgBIDlm9CVhtgK0B8/H/0fQA+gUQnxytG4AIDkOajYhZhPA9MjyfOegZgCQHK0bAEiOoAeA5Ah6AEiOoAeA5Ah6AEiOoAeA5Ah6AEiOQyAUsnwwAgCGMaMHgOSY0QPAgOHvus2AoAfQWbRcx0PQA6hd12fNXXuBIegr0LUHAYDcCHoAKXT9XUKVOB49gKk0Te+8Gw36iFiQtNDr9bY0WUfTpukBB6B+tG46iBcGoFuafs7ygSkASI4ZPQBUoE07h5nRA0ByUzGjb7o/BgBNYkYPAMkR9ACQHEEPAMlNRY9+UJv2hANAHZjRA0ByBD0AJDd1rZtsaEVhGmR6nA/flzqWfBP0FWMNP4Cm0boBgOSY0Y8h09tGANOHGT0AJEfQA0BytG5WgJYOgC5IG/SEMAD0pQ16AN3T1HLk7BNDevQAkFyjQW97k+35xcXFJssAgNQabd1ExIKkhV6vt6XJOgBgubrQ9qF1AwDJsTO2IV2YBQDIgRk9ACTHjB4AxtDld+EEfctwWGMAZSPoO6LLswlgOXjMl4cePQAkR9ADQHK0bgCkRguIGT0ApEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByBD0AJEfQA0ByfPFIjfgCBABNYEYPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQXOkfmLL9HknvkvRySTsi4vaybwMAML6xZvS2d9o+Yvuhocs32H7U9gHb2yUpIn4WEVskbZP04fJLBgBMYtzWzXclbRi8wPYqSTdJukLSOklX2143sMnni+sBAA0aq3UTEXfanhu6+BJJByLioCTZvlnSZtv7JX1F0q0RcV+JtdaGY9IAyGQlO2NXS3pq4Pyh4rJPS3qHpA/Y3jbqP9veanuv7b1Hjx5dQRkAgKWUvjM2Im6UdOMY281LmpekXq8XZdcBAOhbyYz+sKQ1A+fPLS4DALTISoL+HkkX2j7f9qmSrpK0u5yyAABlGat1Y3uXpPWSZm0fkvTFiNhh+3pJt0laJWlnRDxcWaVTiJ3CAMow7qqbq0dcvkfSnlIrAgCUqtFDINjeZHt+cXGxyTIAILVGgz4iFiJi68zMTJNlAEBqHNQMAJIj6AEgOYIeAJIj6AEgOYIeAJJjeSUAJOeI5o8nZvuopCeX+d9nJf25xHLKQl2Toa7JtLUuqb21ZazrtRFx1sk2akXQr4TtvRHRa7qOYdQ1GeqaTFvrktpb2zTXRY8eAJIj6AEguQxBP990ASNQ12SoazJtrUtqb21TW1fne/QAgKVlmNEDAJbQuaC3/XXbj9h+0PZPbb9ixHYbbD9q+4Dt7TXU9UHbD9v+j+2Re9BtP2F7n+37be9tUV11j9cZtn9l+7Hi31eO2O7fxVjdb7uybzA72f23fZrtHxbX3217rqpaJqzrWttHB8bo4zXVtdP2EdsPjbjetm8s6n7Q9sUtqWu97cWB8fpCTXWtsf07238ono+fOcE21Y1ZRHTqR9I7JZ1SnP6qpK+eYJtVkh6XdIGkUyU9IGldxXVdJOl1ku6Q1FtiuyckzdY4Xietq6Hx+pqk7cXp7Sf6OxbX/aOGMTrp/Zf0KUnfLk5fJemHLanrWknfrOvxNHC7b5V0saSHRly/UdKtkizpUkl3t6Su9ZJ+0cB4nSPp4uL06ZL+eIK/ZWVj1rkZfUTcHhHPF2fvUv9LyYddIulARByMiOck3Sxpc8V17Y+IR6u8jeUYs67ax6v4/d8rTn9P0nsqvr2ljHP/B+u9RdLbbbsFdTUiIu6U9NclNtks6fvRd5ekV9g+pwV1NSIino6I+4rTf5e0X9Lqoc0qG7POBf2Qj6n/CjhstaSnBs4f0vGD2pSQdLvte21vbbqYQhPj9eqIeLo4/SdJrx6x3Uts77V9l+2qXgzGuf8vbFNMNBYlnVlRPZPUJUnvL97q32J7TcU1javNz8E3237A9q2231D3jRdtvzdKunvoqsrGbKzvjK2b7V9LOvsEV90QET8vtrlB0vOSftCmusZweUQctv0qSb+y/UgxC2m6rtItVdfgmYgI26OWf722GK8LJP3W9r6IeLzsWjtsQdKuiHjW9ifUf9fxtoZrarP71H9M/cP2Rkk/k3RhXTdu+2WSfizpsxHxt7put5VBHxHvWOp629dKerekt0fR3BpyWNLgzObc4rJK6xrzdxwu/j1i+6fqvz1fUdCXUFft42X7GdvnRMTTxdvTIyN+x7HxOmj7DvVnQmUH/Tj3/9g2h2yfImlG0l9KrmPiuiJisIbvqL/vow0qeUyt1GC4RsQe29+yPRsRlR8Dx/aL1Q/5H0TET06wSWVj1rnWje0Nkj4n6cqI+OeIze6RdKHt822fqv7Os8pWbIzL9kttn37stPo7lk+4OqBmTYzXbknXFKevkXTcOw/br7R9WnF6VtJbJP2hglrGuf+D9X5A0m9HTDJqrWuoh3ul+r3fNtgt6aPFSpJLJS0OtOoaY/vsY/tWbF+ifgZW/YKt4jZ3SNofEd8YsVl1Y1b33ueV/kg6oH4f6/7i59hKiNdI2jOw3Ub192w/rn4Lo+q63qt+T+1ZSc9Ium24LvVXTzxQ/DzclroaGq8zJf1G0mOSfi3pjOLynqTvFKcvk7SvGK99kq6rsJ7j7r+kL6k/oZCkl0j6UfH4+72kC6oeozHr+nLxWHpA0u8kvb6munZJelrSv4rH13WStknaVlxvSTcVde/TEivRaq7r+oHxukvSZTXVdbn6++ceHMiujXWNGZ+MBYDkOte6AQBMhqAHgOQIegBIjqAHgOQIegBIjqAHgOQIegBIjqAHgOT+C1LXvwyi6lM2AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\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": [ + "dz\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": [ + "z0\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": [ + "dr\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": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEAJJREFUeJzt3W+MZfVdx/HPp4ugNe0UWForyzpLlrauf5teoYmxQVphsR1ohLS7aSxVyogGow+3aY2J0bT4qJISyaQg5UGhiLHd6W5FikUa0yq7FJAV151d27ArygJ2bNqGhvTrg3MGj7dzZ86de+49Z773/Uomc++555z7nd+d+7m/+zv/HBECAOT1irYLAACMF0EPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQHEEPAMkR9ACQ3BltFyBJW7dujdnZ2bbLAIBN5fDhw89FxHnrzdeJoJ+dndWhQ4faLgMANhXb36gzX6tDN7bnbC8sLy+3WQYApNZq0EfEYkTMz8zMtFkGAKTGxlgASI6gB4DkCHoASI6gB4DkCHoASI6gB4DkOnHAFPKa3Xfg5dtf/9g7W6wkv2pbV9HuIOjRCkJpOKO0Fx+2IOixYcMGyKCwwsYN26aDXjM+DHIj6FFb3VAh0IFuIejRiKbCnR7n/xn3ByYfyNODoEdnEUTtmMYP1ewIeqBD+HDDOLQa9LbnJM3t3LmzzTKwBoIH2Pw4TTEAJMfQDX5A13vxGfbB73obIxdOgQAAydGjBzAQe+DkQI8eAJKjR4806H0CqyPogQlhAyzawtANACRH0ANAcgQ9ACTHGD0kMX4MZEbQIyX2wGkebbp5EfTAGPFNCV3AGD0AJEfQA0ByBD0AJEfQA0BybIxFeuwtgmlHjx4AkiPoASC5xodubP+kpN+TtFXSgxHx500/B9Bl7DuPrqnVo7d9h+1nbT/ZN3237aO2l2zvk6SIeCoibpT0Hkm/2HzJANo2u+/Ayz/ovrpDN3dK2l2dYHuLpFslXSlpl6S9tneVj10l6YCkg41VCgDYkFpDNxHxsO3ZvskXS1qKiBOSZPseSVdL+peI2C9pv+0Dkj7dXLloEr0xYDqMMkZ/vqSnK/dPSrrE9qWSfk3SWVqjR297XtK8JG3fvn2EMgAAa2l8Y2xEPCTpoRrzLUhakKRerxdN1wEAKIyye+UpSRdU7m8rpwEAOmSUoH9E0kW2d9g+U9IeSfubKQsA0JRaQze275Z0qaSttk9K+sOIuN32TZLul7RF0h0RcWSYJ7c9J2lu586dw1UNdAgbtdF1dfe62Ttg+kGNsAtlRCxKWuz1ejdsdB3AMDjvDaYRp0AAgOQIegBIrtWgtz1ne2F5ebnNMgAgtVaDPiIWI2J+ZmamzTIAIDWGbgAgOYIeAJIj6AEgOTbGAkByrV4cnAOmgM2Pg9C6j6EbAEiOoAeA5FodugE2K05khs2k1aDn7JWTR0AB04cjYwEgOcboASA5xugxtdgtENOCHj0AJEfQA0ByBD0AJMe5bgAgOXavBIDkGLoBgOTYvRIQu1oiN3r0AJAcQQ8AyRH0AJAcY/QAGsO2jm5iP3oASI5rxgI1cS5/bFaM0QNAcgQ9ACTHxtgpwJADMN0IemANfEgiA4ZuACA5gh4AkiPoASA5xuiBPozLIxuOjAWA5LjCFAAkxxg9ACRH0ANAcgQ9ACRH0ANAcgQ9ACRH0ANAcgQ9ACTHkbFJcXQngBUEPYCx6O9scLHw9jB0AwDJEfQAkBxBDwDJEfQAkBynKQaA5DhNMQAkx9ANACRH0ANAcgQ9ACRH0ANAcgQ9ACTHuW42uer5RDiXCIDV0KMHgOQIegBIjqAHgOQIegBIjqAHgOTY6yYRLh8IYDX06AEgOYIeAJIj6AEgOYIeAJJjY+wmxEZXAMOgRw8AyRH0AJBc40M3tt8t6Z2SXi3p9oj426afA8Dmw5lW21OrR2/7DtvP2n6yb/pu20dtL9neJ0kR8dmIuEHSjZLe23zJAIBh1B26uVPS7uoE21sk3SrpSkm7JO21vasyy0fKxwEALaoV9BHxsKQX+iZfLGkpIk5ExPck3SPpahdulvSFiHi02XIBAMMaZWPs+ZKertw/WU77XUnvkHSt7RsHLWx73vYh24dOnz49QhkAgLU0vjE2Im6RdEuN+RYkLUhSr9eLpusAABRG6dGfknRB5f62choAoENGCfpHJF1ke4ftMyXtkbS/mbIAAE2pu3vl3ZK+IumNtk/avj4iXpJ0k6T7JT0l6d6IODLMk9ues72wvLw8bN0AgJpqjdFHxN4B0w9KOrjRJ4+IRUmLvV7vho2uAwCwNk6BAADJcfbKDuOQcQBNcER7ezbanpM0t3PnzhuOHTvWWh1dwimIMW3oxGyc7cMR0VtvvlaHbiJiMSLmZ2Zm2iwDAFJjjB4AkiPoASA5NsYC6Ax2QBiPVoO+sjG2zTIAtIgdEMav1aCftgOm6K0AzeI9VQ9DNy3hHxTApBD0ADYVhnqGR9AD6DzCfTRsjO0A/okBjBMbY2tgPB3AZsbQDYBOGvab7qD56ZxxZCwApEePvsTwDICs6NEDQHL06EfAtwAAm0GrPXouDg4A48fulatgv3YAmTBGDwDJEfQAkBxBDwDJTd1eN+PaU2bQehnvB9A2evQAkBxBDwDJcZriIdUZimG4BkCXtNqjj4jFiJifmZlpswwASI2hGwBIbur2ugGAftnPW0WPHgCSI+gBILlNP3ST/SsXAIyKHj0AJLfpe/SD0NMH0G9ac4EePQAkl+rIWI5IBdCPXODIWABIL+0YfR180gOYBozRA0ByU9Gjp+cOYJpNRdADQL9p6gAydAMAyRH0AJAcQQ8AyRH0AJAcQQ8AyRH0AJAcQQ8AyRH0AJAcQQ8AybUa9LbnbC8sLy+3WQYApMZpigEgOc51AwA1DHsZwi5dtpAxegBIjh49AFR0qSfeFHr0AJAcPXoAGLO2vyXQoweA5OjRA8AAWa5CRY8eAJIj6AEgOYIeAJIj6AEgOYIeAJIj6AEgOXavBIARtH0wVB306AEgOYIeAJJj6AYAGtLVI2np0QNAcgQ9ACTXeNDbvtD27bbva3rdAIDh1Qp623fYftb2k33Td9s+anvJ9j5JiogTEXH9OIoFAAyvbo/+Tkm7qxNsb5F0q6QrJe2StNf2rkarAwCMrFbQR8TDkl7om3yxpKWyB/89SfdIurrh+gAAIxpl98rzJT1duX9S0iW2z5X0J5LebPtDEfHR1Ra2PS9pXpK2b98+QhkAMFmj7EbZv+wkjqZtfD/6iHhe0o015luQtCBJvV4vmq4DAFAYZa+bU5IuqNzfVk4DAHTIKEH/iKSLbO+wfaakPZL2N1MWAKApdXevvFvSVyS90fZJ29dHxEuSbpJ0v6SnJN0bEUeGeXLbc7YXlpeXh60bAFBTrTH6iNg7YPpBSQc3+uQRsShpsdfr3bDRdQAA1sYpEAAgOYIeAJJrNegZoweA8Ws16CNiMSLmZ2Zm2iwDAFJzRPvHKtk+LekbG1x8q6TnGiynKdQ1HOoaDnUNp6t1SaPV9hMRcd56M3Ui6Edh+1BE9Nquox91DYe6hkNdw+lqXdJkamNjLAAkR9ADQHIZgn6h7QIGoK7hUNdwqGs4Xa1LmkBtm36MHgCwtgw9egDAGjob9LbPsf2A7WPl77MHzPc3tr9p+/N903fY/sfyerafKc+wKdtnlfeXysdnx1TXdeU8x2xfV057le3HKj/P2f54+dgHbJ+uPPbBSdVVTn+ovP7vyvO/tpzeZnu90vYB2/9q+4jtj1Xm31B7rXad477HB/69tj9UTj9q+4q66xxnXbZ/xfZh2/9c/r6sssyqr+mE6pq1/d3Kc99WWeYtZb1Ltm+x7QnW9b6+9+D3bf98+dgk2uttth+1/ZLta/seG/TeHLm9FBGd/JH0p5L2lbf3Sbp5wHxvlzQn6fN90++VtKe8fZuk3y5v/46k28rbeyR9pum6JJ0j6UT5++zy9tmrzHdY0tvK2x+Q9IlxttdadUl6SFJvlWVaay9Jr5T0y+U8Z0r6sqQrN9pekrZIOi7pwnJ9j0vaVefvVXFd5MclnSVpR7meLXXWOea63izpx8vbPy3pVGWZVV/TCdU1K+nJAev9J0lvlWRJX1h5TSdRV988PyPp+ITba1bSz0q6S9K1Nd+bI7VXRHS3R6/i+rOfKm9/StK7V5spIh6U9K3qtPIT7zJJ962yfHW990l6+5CfkHXqukLSAxHxQkT8t6QH9IMXV3+DpNeqCK8mNFLXOuudaHtFxHci4kuSFMV1iR9VcYGbjapzneNBf+/Vku6JiBcj4t8lLZXra+LayRuuKyK+FhH/UU4/IulHbJ815PM3XtegFdp+vaRXR8RXo0ixuzTgvT2BuvaWyzZl3boi4usR8YSk7/ctu+p7oKH26nTQvy4inilv/6ek1w2x7LmSvhnFOfOl4nq255e3X77Wbfn4cjl/k3Wtdj3d8/vmWellVLeGX2P7Cdv32b5Aw2mirr8ov7L+QeVN0Yn2sv0aFd/cHqxMHra96rwug/7eQcvWWec466q6RtKjEfFiZdpqr+mk6tph+2u2/972L1XmP7nOOsdd14r3Srq7b9q422vYZZtor+avGTsM21+U9GOrPPTh6p2ICNsT2z1oQnXtkfTrlfuLku6OiBdt/5aK3shl1QXGXNf7IuKU7VdJ+quytrvqLDju9rJ9hoo35C0RcaKcvG57TRPbPyXpZkmXVyZv+DVtwDOStkfE87bfIumzZY2dYPsSSd+JiCcrk9tsr7FqNegj4h2DHrP9X7ZfHxHPlF9fnh1i1c9Leo3tM8pP8+r1bFeudXuyDJCZcv4m6zol6dLK/W0qxv9W1vFzks6IiMOV56zW8EkVY9v/zzjriohT5e9v2f60iq+hd6kD7aViP+NjEfHxynOu214Dnme96xwP+nvXWnbUayePUpdsb5P015LeHxHHVxZY4zUde13lN9UXy+c/bPu4pDeU81eH3ybeXqU96uvNT6i91lr20r5lH1Iz7dXpoZv9kla2PF8n6XN1Fyz/yb4kaWWrdnX56nqvlfR3fcMnTdR1v6TLbZ/tYi+Ty8tpK/aq75+sDMEVV6m4POMwNlyX7TNsby3r+CFJ75K00tNptb1s/7GKN+nvVxfYYHvVuc7xoL93v6Q9Lvbm2CHpIhUbyZq4dvKG6yqHtA6o2OD9Dyszr/OaTqKu82xvKZ//QhXtdaIcxvsf228th0beryHe26PWVdbzCknvUWV8foLtNciq74GG2qvTe92cq2I89pikL0o6p5zek/TJynxflnRa0ndVjF9dUU6/UMUbcUnSX0o6q5z+w+X9pfLxC8dU12+Wz7Ek6Tf61nFC0pv6pn1Uxca0x1V8SL1pUnVJ+lEVewA9UdbwZ5K2tN1eKnovoSLEHyt/PjhKe0n6VUn/pmLviA+X0/5I0lXr/b0qhqKOSzqqyp4Pq61zA//vG6pL0kckfbvSPo+p2Mg/8DWdUF3XlM/7mIqN6HOVdfZUhOhxSZ9QeeDmJOoqH7tU0lf71jep9voFFTn1bRXfMI6slxlNtBdHxgJAcl0eugEANICgB4DkCHoASI6gB4DkCHoASI6gB4DkCHoASI6gB4Dk/heavSCvD/om+wAAAABJRU5ErkJggg==\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": [ + "zinner zcut\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": [ + "module\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": [ + "dysize\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": [ + "ysize\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": [ + "ys-pred\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": [ + " \n", + "New QUAD 2\n", + " \n", + "dphi\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": [ + "dphiNor\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": [ + "zinner\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": [ + "dz\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": [ + "z0\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": [ + "dr\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": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADvxJREFUeJzt3X+sZGddx/HPh113lYpLyy6I3a53m1uC6+8wtiRGshZpt8ptjW1gNw20GljF9A//cw0aEmMi+BcSSJoN1NI/bMEadbddbQqyQkyQ7i2ldMW1d1dId622BVkJNCWkX/+Y55LT8c6dX2fmnPnO+5Xc3Jkz55z5zjMzn/vMc5451xEhAEBeL2u6AADAdBH0AJAcQQ8AyRH0AJAcQQ8AyRH0AJAcQQ8AyRH0AJAcQQ8AyW1tugBJ2rlzZywtLTVdBgDMldXV1eciYteg9VoR9EtLSzp16lTTZQDAXLH9tWHWm8rQje1LbJ+y/dZp7B8AMLyhgt72Xbafsf1Ez/IDts/YXrN9pHLT70v6ZJ2FAgDGM2yP/m5JB6oLbG+R9BFJN0jaJ+mQ7X223yLpXyU9U2OdAIAxDTVGHxGftb3Us/hqSWsRcU6SbN8n6SZJPyzpEnXD/3nbJyLixdoqBgCMZJKDsZdLeqpy/bykayLiDkmyfbuk5/qFvO3Dkg5L0p49eyYoAwCwmanNo4+IuyPigU1uPxoRnYjo7No1cHYQAGBMkwT9BUlXVK7vLssAAC0ySdA/Iukq23ttb5N0UNKxesoCANRlqDF62/dK2i9pp+3zkt4XER+zfYekhyRtkXRXRJyeWqUAhrZ05MHvX/7q+3+twUrQBsPOujnUZ/kJSSfGvXPbK5JWlpeXx90FAGAAR0TTNajT6QSnQMij2pusqvYs6XHWo19bD4N2n3+2VyOiM2i9VpzrBothklBadLQdJkHQo3HDfAIAMD6CHmgpevGoC0GPWkwjlBjHB+rRaNAz6wZ4KXrxmIZGgz4ijks63ul03t1kHWg/evf1o00XB0M3mDsEFDAagh5jY5ihHrQjpm1qZ68EALQDQQ8AyTHrBmgAwzWYJWbdYK5xYLYetGNuHIzFSOiJjo+2Q1MIeqRBrxTYGAdjASA5gh4AkiPoASA5gh4AkmMePYCX4KB2Po326CPieEQc3rFjR5NlAEBqTK9ESm3plTJ3Hm1A0GMgwgqYbxyMBYDkCHoASI6gB4DkCHoASK7RoLe9YvvoxYsXmywDAFJjHj0AJMfQDQAkxzx6pNeWL08BTSHogZrxBTO0DUGP/4egwjo+DeXAGD0AJEfQA0ByBD0AJEfQA0ByBD0AJMcpEAAgOU6BAADJMXQDAMkR9ACQHN+MxULhm55YRPToASA5evQAhsKnoflFjx4AkqNHD9SAM36izejRA0ByBD0AJEfQA0ByjNFDEmPMQGb06AEgOc5eCQDJcfZKAEiOoRsASI6gB4DkCHoASI6gB4DkCHoASI4vTAFj4ktmmBcEPRYW51fHomDoBgCSI+gBIDmCHgCSI+gBIDkOxi4wZo0Ai4EePQAkR9ADQHIM3QAYGd9BmC/06AEgOYIeAJLjXwkCQHL8K0EASI6hGwBIjqAHgOSYXgmI6YLIjR49ACRH0ANAcgQ9ACTHGD0wJM72iXlFjx4AkiPoASA5gh4AkiPoASA5gh4AkiPoASA5gh4AkiPoASA5gh4AkiPoASA5ToGwYPga/2hoL2RA0AOYSO8fQ87n3z4M3QBAcvTogR4M1yAbevQAkBxBDwDJEfQAkBxBDwDJEfQAkBxBDwDJ1R70tn/C9p2277f9nrr3DwAYzVBBb/su28/YfqJn+QHbZ2yv2T4iSRHxlYj4HUlvk/SL9ZcMABjFsD36uyUdqC6wvUXSRyTdIGmfpEO295XbbpT0oKQTtVUKABjLUEEfEZ+V9I2exVdLWouIcxHxXUn3SbqprH8sIm6QdGudxQIARjfJKRAul/RU5fp5SdfY3i/pNyRt1yY9etuHJR2WpD179kxQBgBgM7Wf6yYiTko6OcR6RyUdlaROpxN11wEA6Jpk1s0FSVdUru8uywAALTJJ0D8i6Srbe21vk3RQ0rF6ygIA1GWooRvb90raL2mn7fOS3hcRH7N9h6SHJG2RdFdEnJ5apRgbp90FFttQQR8Rh/osP6EJplDaXpG0sry8PO4uAAADNHoKhIg4HhGHd+zY0WQZAJAa/2EKQK2qQ4X8/9h24KRmAJAcQQ8AyRH0AJBco0Fve8X20YsXLzZZBgCkxqwbAEiOoRsASI6gB4DkCHoASI6gB4DkmHUDAMkx6wYAkmPoBgCSI+gBIDmCHgCSI+gBIDnOR58I5wEHsJFGg55/JQjkRuejHZheCQDJMUYPAMkxRp9U9SMzgMVG0AOYOcbuZ4ugBzATfMpsDmP0AJAcQQ8AyTF0A6BRjNdPH+ejB4Dk+MIUACTHGD0AJEfQA0ByHIydc8xNBjAIPXoASI4ePYDWYwrmZAh6AK1BoE8HQT+HGJcHMAqCfk4Q7gDGxcFYAEiOUyAAQHKcAgEAkmOMHsDcYpbOcAj6FuMALBYZr//6EPQtw4sbQN2YdQMAydGjnyHGEwE0gR49ACRHj74h9O4BzIojouka1Ol04tSpU02XMXUcaAVmY1E6T7ZXI6IzaD169ABS49MzY/QAkB49+oq6/vLTgwDaaVHfm5zUDACSa7RHHxHHJR3vdDrvbrIOAIste0+foZsJZH9xAPOKGW4vRdCPiBcQkMMivZeZdQMAydGjr8ki9Q4AzBeCfsr4AwCgaQzdAEBy9OgBYAjzPMuOHj0AJEfQA0ByDN30Mc8f0wCgih49ACRH0ANAcgsxdMMwDIBFRo8eAJIj6AEguVRDN8MM0XBKAgDDypIX9OgBIDmCHgCSa3ToxvaKpJXl5eUmywCAkczbTD7+Z+wQsozTARgs4/s91cFYAGhSW3v6jNEDQHIEPQAkR9ADQHJzP0af8cAJgHyaHL+f+6AHgCbNQ2eToRsASI6gB4DkCHoASI4xegCYsVkfmKVHDwDJpe3Rz8ORcACYBXr0AJBc2h49ADSpTaMK9OgBIDmCHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBIDmCHgCSI+gBIDlHRNM1yPazkr425uY7JT1XYzl1oa7RUNdo2lqX1N7aMtb14xGxa9BKrQj6Sdg+FRGdpuvoRV2joa7RtLUuqb21LXJdDN0AQHIEPQAklyHojzZdQB/UNRrqGk1b65LaW9vC1jX3Y/QAgM1l6NEDADbR2qC3fZnth20/WX5f2me9f7D9TdsP9Czfa/tfbK/Z/oTtbWX59nJ9rdy+NKW6bivrPGn7trLsFbYfq/w8Z/uD5bbbbT9bue1ds6qrLD9p+0zl/l9dljfZXi+3/aDtf7N92vb7K+uP1V62D5THuWb7yAa39328tv+gLD9j+/ph9znNumy/xfaq7S+X39dWttnwOZ1RXUu2n6/c952Vbd5Q6l2z/SHbnmFdt/a8B1+0/XPltlm015tsP2r7e7Zv6bmt33tz4vZSRLTyR9KfSTpSLh+R9IE+671Z0oqkB3qWf1LSwXL5TknvKZd/V9Kd5fJBSZ+ouy5Jl0k6V35fWi5fusF6q5LeVC7fLunD02yvzeqSdFJSZ4NtGmsvSS+X9MtlnW2SPifphnHbS9IWSWclXVn29yVJ+4Z5vJL2lfW3S9pb9rNlmH1Oua6fl/Rj5fJPSbpQ2WbD53RGdS1JeqLPfr8g6Y2SLOnv15/TWdTVs85PSzo74/ZakvQzku6RdMuQ782J2isi2tujl3STpI+Xyx+X9OsbrRQRn5b0reqy8hfvWkn3b7B9db/3S3rziH8hh6nrekkPR8Q3IuJ/JD0s6UBPja+T9Gp1w6sOtdQ1YL8zba+I+E5EfEaSIuK7kh6VtHuE++51taS1iDhX9ndfqa9fvdXHe5Ok+yLihYj4D0lrZX/D7HNqdUXEFyPiP8vy05J+yPb2Ee+/9rr67dD2ayX9SER8Propdo/6vLdnUNehsm1dBtYVEV+NiMclvdiz7YbvgZraq9VB/5qIeLpc/i9Jrxlh21dJ+mZEfK9cPy/p8nL5cklPSVK5/WJZv866vn8fG9z/uvVeRvVo+M22H7d9v+0rRqiprrr+onxk/aPKm6IV7WX7lep+cvt0ZfGo7TXM89Lv8fbbdph9TrOuqpslPRoRL1SWbfSczqquvba/aPufbP9SZf3zA/Y57brWvV3SvT3Lpt1eo25bR3s1+8/BbX9K0o9ucNN7q1ciImzPbHrQjOo6KOkdlevHJd0bES/Y/m11eyPXVjeYcl23RsQF26+Q9NeltnuG2XDa7WV7q7pvyA9FxLmyeGB7LRLbPynpA5Kuqywe+zmtwdOS9kTE122/QdLflhpbwfY1kr4TEU9UFjfZXlPVaNBHxK/0u832f9t+bUQ8XT6+PDPCrr8u6ZW2t5a/5rslXSi3XZB0haTzJUB2lPXrrOuCpP2V67vVHf9b38fPStoaEauV+6zW8FF1x7ZfYpp1RcSF8vtbtv9S3Y+h96gF7aXuPOMnI+KDlfsc2F597qfa86++LnrX6X28m207aJ/TrEu2d0v6G0nvjIiz6xts8pxOva7ySfWFcv+rts9Kel1Zvzr8NvP2Kg6qpzc/o/babNv9PdueVD3t1eqhm2OS1o883ybp74bdsLzIPiNp/ah2dfvqfm+R9I89wyd11PWQpOtsX+ruLJPryrJ1h9TzIishuO5GSV8ZoaaJ6rK91fbOUscPSHqrpPWeTqPtZftP1H2T/l51gzHb6xFJV7k7I2ubum/2Y5vUW328xyQddHc2x15JV6l7kGyYfU6trjKk9aC6B7z/eX3lAc/pLOraZXtLuf8r1W2vc2UY739tv7EMjbxTI7y3J62r1PMySW9TZXx+hu3Vz4bvgZraq9Wzbl6l7njsk5I+Jemysrwj6aOV9T4n6VlJz6s7fnV9WX6lum/ENUl/JWl7Wf6D5fpauf3KKdX1W+U+1iT9Zs8+zkl6fc+yP1X3YNqX1P0j9fpZ1SXpEnVnAD1eavhzSVuabi91ey+hbog/Vn7eNUl7SfpVSf+u7uyI95ZlfyzpxkGPV92hqLOSzqgy82GjfY7xeh+rLkl/KOnblfZ5TN2D/H2f0xnVdXO538fUPYi+UtlnR90QPSvpwypf3JxFXeW2/ZI+37O/WbXXL6ibU99W9xPG6UGZUUd78c1YAEiuzUM3AIAaEPQAkBxBDwDJEfQAkBxBDwDJEfQAkBxBDwDJEfQAkNz/AY4ltDJXF93oAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEAlJREFUeJzt3X2spGdZx/Hvj62tgrAUFhD74m6zBVzfw7ElMZLK61Y4lAiBXYiAQlc0Nfqfa8CYGBPBv7CB2Gyglv5hS8WIXbpYAakQA9rdUrC11p6ukO6KdguyEiAlDZd/zLMwDOfsmTln3s49309ycmbueV6uuefMNfdcz/08J1WFJKldj5t1AJKkyTLRS1LjTPSS1DgTvSQ1zkQvSY0z0UtS40z0ktQ4E70kNc5EL0mNO2fWAQDs2LGjdu7cOeswJGlLOXbs2CNV9bT1lpuLRL9z506OHj066zAkaUtJ8sVhlrN0I0mNM9FLUuNM9JLUOBO9JDXORC9JjZtIok/yhCRHk7x8EtuXJA1vqESf5PokDye5Z6B9b5L7k6wkOdj30O8Bt4wzUEnSxgw7or8B2NvfkGQb8B7gSmAPsD/JniQvBv4NeHiMcUqSNmioE6aq6pNJdg40XwasVNVxgCQ3A1cBPww8gV7y/2aSI1X17bFFLPXZefC279z+wjteNsNIpPm1mTNjLwAe6rt/Ari8qq4BSPIm4JG1knySA8ABgIsvvngTYWjR9Cf3FvhhpUmb2CUQquqGdR4/BBwCWFpaqknFocWxlRLmWh9WW+k5aOvYTKI/CVzUd//Crk0auxZG8S08B21Nm0n0dwKXJtlFL8HvA143lqgkTIyO7jUuQyX6JDcBVwA7kpwA/rCq3pfkGuB2YBtwfVXdO8rOkywDy7t37x4tammLGNeHlUlfmzHsrJv9a7QfAY5sdOdVdRg4vLS0dPVGtyGtxsQofddcXI9eOmPRyzXD8ENMo5ppord0IzC5b4ZJX8OYaaK3dKMW+cGleWPpRjMxzWToqFeLzssUS1LjHNFLjfCbi9Yy0xF9kuUkh06fPj3LMCSpaTNN9FV1uKoObN++fZZhSFLTLN1oohZlBsq8PU/LOOpnotdCMQFqEZnopcb54SbPjNXYzVsZQ1p0HoyVpMZZutFYOIqX5pdnxkpS4xzRSwvEA7OLyRG9JDXOWTfaMOvy0tbg9eilBWUZZ3FYo9fC2myi8xuNtgpr9JLUOBO9JDXORC9JjTPRS1LjnF6pkXgAUtp6nF4pyamWjbN0I0mNcx691mW5ZrE4um+PI3pJapyJXpIaZ6KXpMaZ6CWpcSZ6SWqcJ0zp+zjLRmqLJ0xJWpNTLdtg6UaSGucJUxKOXNU2R/SS1DhH9NKQPEitrcpEL2kolre2LhO9AEerUsus0UtS4xzRLzBH8dJicEQvSY0z0UtS4yzdSBqZM3C2Fkf0ktS4mSb6JMtJDp0+fXqWYUhS07x65YJxpo20eCzdSFLjPBgraVMGvyV6cHb+OKKXpMaZ6CWpcSZ6SWqcNfoF4EybjbPv1AITvTTA5L45njU7fyzdSFLjHNE3ylGppDNM9A0xuUtajaUbSWqciV6SGmfpRtLEOANnPjiil6TGmeglqXEmeklqnIlekho39oOxSX4c+B1gB/Dxqvrzce9D3+XceUnrGWpEn+T6JA8nuWegfW+S+5OsJDkIUFX3VdVbgdcAvzD+kCVtRTsP3vadH03XsKWbG4C9/Q1JtgHvAa4E9gD7k+zpHnsFcBtwZGyRSpI2ZKhEX1WfBL4y0HwZsFJVx6vqW8DNwFXd8rdW1ZXA69faZpIDSY4mOXrq1KmNRS9JWtdmavQXAA/13T8BXJ7kCuBXgPM4y4i+qg4BhwCWlpZqE3FIks5i7Adjq+oO4I5xb1eStDGbmV55Erio7/6FXZskaY5sZkR/J3Bpkl30Evw+4HWjbCDJMrC8e/fuTYSxeJy1IGkUw06vvAn4NPDsJCeSvLmqHgOuAW4H7gNuqap7R9l5VR2uqgPbt28fNW5J0pCGGtFX1f412o/gFEpJI/KqltPlJRAkqXEzTfRJlpMcOn369CzDkKSmzTTRW6OXpMnzP0xtEc60Uaus10+eNXpJapw1eklqnDV6SWqcNfo5Zl1e0jhYo5ekxpnoJalxMy3deFEzSf2cajkZHoyVpMZZupGkxpnoJalxJnpJapyJXpIa56wbSXPJGTjj46wbSWqcl0CYM172QNK4WaOXpMY5opc096zXb44jeklqnIlekhrnf5iSpMY5vVKSGmfpRpIa56ybOeDceUmT5IhekhpnopekxpnoJalxJnpJapwHY2fEA7CSpsURvSQ1zjNjJalxnhkrSY2zRi9pS/GSxaMz0U+RB2AlzYIHYyWpcSZ6SWqciV6SGmeil6TGmeglqXHOupkAp39JmieO6CWpcSZ6SWqciV6SGjfTGn2SZWB59+7dswxjojwbVtKszTTRV9Vh4PDS0tLVs4xD0tbkxIfhWLqRpMaZ6CWpcc6jl9QEyzhrM9GPiQddJc2rhU70g8nZUYCkFlmjl6TGLdyI/mwlFmt8klrkiF6SGmeil6TGmeglqXELV6PfiLXq+tbxJW0FC5HoNzLH3Xnx0tbl4Ox7LUSinxQ/DCRtBdboJalxJnpJapyJXpIa11SN3jNbJen7TSTRJ3kl8DLgScD7qurvJ7EfSdL6hi7dJLk+ycNJ7hlo35vk/iQrSQ4CVNWHqupq4K3Aa8cbsiRpFKPU6G8A9vY3JNkGvAe4EtgD7E+yp2+Rt3ePS5JmZOhEX1WfBL4y0HwZsFJVx6vqW8DNwFXpeSfwkaq6a7XtJTmQ5GiSo6dOndpo/JKkdWy2Rn8B8FDf/RPA5cBvAy8CtifZXVXXDa5YVYeAQwBLS0u10QA8aUmSzm4iB2Or6lrg2klse1h+AEgatKgz8zab6E8CF/Xdv7Brk6S5tkhJf7MnTN0JXJpkV5JzgX3ArcOunGQ5yaHTp09vMgxJ0lpGmV55E/Bp4NlJTiR5c1U9BlwD3A7cB9xSVfcOu82qOlxVB7Zv3z5q3JKkIQ1duqmq/Wu0HwGOjC0iSdJYea0bSWrcTBO9NXpJmryZJnpr9JI0eZZuJKlxTV2mWJLGqZW59tboJalx1uglqXGWbiSpT4vXyfJgrCQ1zkQvSY3zYKwkNc6DsZLUOEs3ktQ4Z91I0hC28slTjuglqXGO6CVpTOZ11O+sG0lq3ExH9FV1GDi8tLR09SzjkKSN2gpn0lqjl6TGmeglqXEmeklqnIlekhpnopekxs101k2SZWB59+7dswxD0oLbCjNnNsOLmklS4yzdSFLjTPSS1DgTvSQ1zkQvSY0z0UtS40z0ktQ4E70kNc5/PCJJUzbtf1DiPx6RpMZ5ZqwkNc7SjSSNaKtdG8eDsZLUOBO9JDXORC9JjTPRS1LjPBgrSRMwTwdsHdFLUuNM9JLUOBO9JDXORC9JjTPRS1LjvKiZJDXOi5pJUuMs3UhS40z0ktS4VNWsYyDJKeCLG1x9B/DIGMMZF+MajXGNbl5jM67RbCauH6uqp6230Fwk+s1IcrSqlmYdxyDjGo1xjW5eYzOu0UwjLks3ktQ4E70kNa6FRH9o1gGswbhGY1yjm9fYjGs0E49ry9foJUln18KIXpJ0FnOb6JM8JclHkzzQ/T5/jeX+LslXk3x4oH1Xkn9OspLkA0nO7drP6+6vdI/vnFBcb+yWeSDJG7u2Jya5u+/nkSTv6h57U5JTfY+9ZVpxde13JLm/b/9P79pn2V+PT3Jbkn9Pcm+Sd/Qtv6H+SrK3e54rSQ6u8viazzfJ73ft9yd56bDbnGRcSV6c5FiSf+1+v6BvnVVf0ynFtTPJN/v2fV3fOs/t4l1Jcm2STDGu1w+8B7+d5Ge7x6bRX89PcleSx5K8euCxtd6bm+4vqmouf4A/BQ52tw8C71xjuRcCy8CHB9pvAfZ1t68DfrO7/VvAdd3tfcAHxh0X8BTgePf7/O72+assdwx4fnf7TcC7J9lfZ4sLuANYWmWdmfUX8Hjgl7plzgU+BVy50f4CtgEPApd02/scsGeY5wvs6ZY/D9jVbWfbMNuccFw/B/xod/sngZN966z6mk4prp3APWts91+A5wEBPnLmNZ1GXAPL/BTw4JT7ayfw08CNwKuHfG9uqr+qan5H9MBVwPu72+8HXrnaQlX1ceBr/W3dJ94LgA+usn7/dj8IvHDET8hh4nop8NGq+kpV/S/wUWDvQIzPAp5OL3mNw1jiWme7U+2vqvpGVX0CoKq+BdwFXDjCvgddBqxU1fFuezd38a0Vb//zvQq4uaoerar/BFa67Q2zzYnFVVWfrar/6trvBX4oyXkj7n/sca21wSTPBJ5UVZ+pXha7kTXe21OIa3+37risG1dVfaGqPg98e2DdVd8DY+qvuU70z6iqL3W3/xt4xgjrPhX4alU91t0/AVzQ3b4AeAige/x0t/w44/rOPlbZ/xlnRhn9R8NfleTzST6Y5KIRYhpXXH/RfWX9g743xVz0V5In0/vm9vG+5lH7a5jXZa3nu9a6w2xzknH1exVwV1U92te22ms6rbh2Jflskn9M8ot9y59YZ5uTjuuM1wI3DbRNur9GXXcc/TXbfw6e5GPAj6zy0Nv671RVJZna9KApxbUP+NW++4eBm6rq0SS/QW808oL+FSYc1+ur6mSSJwJ/3cV24zArTrq/kpxD7w15bVUd75rX7a9FkuQngHcCL+lr3vBrOgZfAi6uqi8neS7woS7GuZDkcuAbVXVPX/Ms+2uiZproq+pFaz2W5H+SPLOqvtR9fXl4hE1/GXhyknO6T/MLgZPdYyeBi4ATXQLZ3i0/zrhOAlf03b+QXv3vzDZ+Bjinqo717bM/hvfSq21/j0nGVVUnu99fS/KX9L6G3sgc9Be9ecYPVNW7+va5bn+tsZ/+kX//38XgMoPP92zrrrfNScZFkguBvwHeUFUPnlnhLK/pxOPqvqk+2u3/WJIHgWd1y/eX36beX519DIzmp9RfZ1v3ioF172A8/TXXpZtbgTNHnt8I/O2wK3Z/ZJ8AzhzV7l+/f7uvBv5hoHwyjrhuB16S5Pz0Zpm8pGs7Yz8Df2RdEjzjFcB9I8S0qbiSnJNkRxfHDwAvB86MdGbaX0n+mN6b9Hf7V9hgf90JXJrejKxz6b3Zbz1LvP3P91ZgX3qzOXYBl9I7SDbMNicWV1fSuo3eAe9/OrPwOq/pNOJ6WpJt3f4voddfx7sy3v8leV5XGnkDI7y3NxtXF8/jgNfQV5+fYn+tZdX3wJj6a65n3TyVXj32AeBjwFO69iXgvX3LfQo4BXyTXv3qpV37JfTeiCvAXwHnde0/2N1f6R6/ZEJx/Xq3jxXg1wa2cRx4zkDbn9A7mPY5eh9Sz5lWXMAT6M0A+nwXw58B22bdX/RGL0Uvid/d/bxlM/0F/DLwH/RmR7yta/sj4BXrPV96pagHgfvpm/mw2jY38Pe+obiAtwNf7+ufu+kd5F/zNZ1SXK/q9ns3vYPoy33bXKKXRB8E3k134uY04uoeuwL4zMD2ptVfP08vT32d3jeMe9fLGePoL8+MlaTGzXPpRpI0BiZ6SWqciV6SGmeil6TGmeglqXEmeklqnIlekhpnopekxv0/Z0tLdmN/l9cAAAAASUVORK5CYII=\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": [ + "zinner zcut\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": [ + "module\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYoAAAD8CAYAAABpcuN4AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEfZJREFUeJzt3X/sXXV9x/Hna3RMxWhBGoItriw2LszEyb6BLiwLAYMFjOUP5tgPaQhb/xjb3A/jiv90U0kwWUSJk6SRbmVxIGNkNIONNShxS0ZnEaMCMzQo0qZAtYCLbGrde3/cT+FS+v3Qfu/32+/93vt8JN/ccz7nc879nJ577+uez/nc01QVkiTN5qcWuwGSpPFmUEiSugwKSVKXQSFJ6jIoJEldBoUkqcugkCR1GRSSpC6DQpLUtWyxGzBXp556aq1evXqxmyFJS8aDDz743apacazrLdmgWL16Nbt27VrsZkjSkpHkibmsZ9eTJKnLoJAkdRkUkqQug0KS1GVQSJK6DApJUpdBIUnqMigkSV0GhSSpa8n+MltaalZvuvvF6W9ff+kitkQ6Np5RSJK6DApJUpdBIUnqMigkSV0GhSSpy1FPmrNpG8UzvL8wHfssgUGh42DaAkWaNHY9SZK6DApJUpdBIUnqMigkSV1ezB7iRVdJeiWDQhozfmE5Nv57LbxX7XpKsjXJM0m+MVR2SpIdSR5rjye38iS5McnuJF9LcvbQOhta/ceSbBgq/6UkX2/r3Jgk872TkjSK1ZvufvFvEp7nWB3NGcXfAJ8Gbhkq2wTcV1XXJ9nU5v8MuBhY0/7OBW4Czk1yCrAZmAEKeDDJ9qp6ttX5XWAncA+wDvjn0XdNOnp+Kx0//sBxfLxqUFTVl5KsPqx4PXB+m94G3M8gKNYDt1RVAQ8kWZ7k9FZ3R1UdAEiyA1iX5H7gDVX1QCu/BbgMg0LSBFnqX0Tmeo3itKra16afAk5r0yuBJ4fq7WllvfI9Ryifekv9hSVpcox8MbuqKknNR2NeTZKNwEaAt7zlLcfjKaWJ5BcRHYu5BsXTSU6vqn2ta+mZVr4XOGOo3qpWtpeXuqoOld/fylcdof4RVdUWYAvAzMzMcQmnI/FNdnz57y0trrn+4G47cGjk0gbgrqHyK9vop7XA862L6l7goiQntxFSFwH3tmXfT7K2jXa6cmhbkuZgXEfOaOl61TOKJLcyOBs4NckeBqOXrgduT3I18ATwvlb9HuASYDfwAnAVQFUdSPJR4Mut3kcOXdgGfo/ByKrXMriIPdEXsh3JIWmpOZpRT78xy6ILj1C3gGtm2c5WYOsRyncBb3+1dkiSFof3epIkdXkLD0lTzcESr84zCklSl2cUmgh+K5QWjmcUkqQug0KS1GVQSJK6DApJUpdBIUnqMigkSV0GhSSpy6CQJHUZFJKkLoNCktRlUEiSugwKSVKXQSFJ6jIoJEld3mZc0jHztu7TxTMKSVKXQSFJ6jIoJEldBoUkqcuL2VNs+IIkeFFS88+L3pPBMwpJUpdBIUnqMigkSV0GhSSpy6CQJHWNFBRJ/jjJw0m+keTWJK9JcmaSnUl2J/l8khNb3Z9p87vb8tVD27m2lX8zybtH2yVJ0nyac1AkWQn8ITBTVW8HTgCuAD4O3FBVbwWeBa5uq1wNPNvKb2j1SHJWW+8XgHXAZ5KcMNd2SZLm16hdT8uA1yZZBrwO2AdcANzRlm8DLmvT69s8bfmFSdLKb6uqH1bVt4DdwDkjtkuSNE/m/IO7qtqb5C+B7wD/A/wr8CDwXFUdbNX2ACvb9ErgybbuwSTPA29q5Q8MbXp4Hc2RP3SSNF9G6Xo6mcHZwJnAm4GTGHQdLZgkG5PsSrJr//79C/lUkqRmlK6ndwHfqqr9VfVj4E7gPGB564oCWAXsbdN7gTMA2vI3At8bLj/COi9TVVuqaqaqZlasWDFC0yVJR2uUoPgOsDbJ69q1hguBR4AvApe3OhuAu9r09jZPW/6FqqpWfkUbFXUmsAb4zxHaJUmaR6Nco9iZ5A7gK8BB4CFgC3A3cFuSj7Wym9sqNwN/m2Q3cIDBSCeq6uEktzMImYPANVX1k7m2S5I0v0a6e2xVbQY2H1b8OEcYtVRV/wv82izbuQ64bpS2SJIWhr/MliR1GRSSpC6DQpLUZVBIkroMCklSl0EhSeoyKCRJXQaFJKnLoJAkdRkUkqQug0KS1GVQSJK6DApJUpdBIUnqMigkSV0GhSSpy6CQJHUZFJKkLoNCktRlUEiSugwKSVKXQSFJ6jIoJEldBoUkqcugkCR1GRSSpC6DQpLUZVBIkroMCklS10hBkWR5kjuS/FeSR5P8cpJTkuxI8lh7PLnVTZIbk+xO8rUkZw9tZ0Or/1iSDaPulCRp/ox6RvEp4F+q6ueBdwCPApuA+6pqDXBfmwe4GFjT/jYCNwEkOQXYDJwLnANsPhQukqTFN+egSPJG4FeBmwGq6kdV9RywHtjWqm0DLmvT64FbauABYHmS04F3Azuq6kBVPQvsANbNtV2SpPk1yhnFmcB+4K+TPJTks0lOAk6rqn2tzlPAaW16JfDk0Pp7Wtls5ZKkMTBKUCwDzgZuqqp3Aj/gpW4mAKqqgBrhOV4mycYku5Ls2r9//3xtVpLUMUpQ7AH2VNXONn8Hg+B4unUp0R6facv3AmcMrb+qlc1W/gpVtaWqZqpqZsWKFSM0XZJ0tOYcFFX1FPBkkre1oguBR4DtwKGRSxuAu9r0duDKNvppLfB866K6F7goycntIvZFrUySNAaWjbj+HwCfS3Ii8DhwFYPwuT3J1cATwPta3XuAS4DdwAutLlV1IMlHgS+3eh+pqgMjtkuSNE9GCoqq+iowc4RFFx6hbgHXzLKdrcDWUdoiSVoY/jJbktRlUEiSugwKSVKXQSFJ6jIoJEldBoUkqcugkCR1GRSSpC6DQpLUZVBIkroMCklSl0EhSeoyKCRJXQaFJKnLoJAkdRkUkqQug0KS1GVQSJK6DApJUpdBIUnqMigkSV0GhSSpy6CQJHUtW+wGaDKt3nT3YjdB0jzxjEKS1GVQSJK67HrSvLCrSZpcnlFIkroMCklS18hBkeSEJA8l+ac2f2aSnUl2J/l8khNb+c+0+d1t+eqhbVzbyr+Z5N2jtknS5Fm96e4X/3R8zccZxQeAR4fmPw7cUFVvBZ4Frm7lVwPPtvIbWj2SnAVcAfwCsA74TJIT5qFdkqR5MNLF7CSrgEuB64A/SRLgAuA3W5VtwJ8DNwHr2zTAHcCnW/31wG1V9UPgW0l2A+cA/zFK25YivylJk2/4ff7t6y9dxJYcvVHPKD4JfAj4vzb/JuC5qjrY5vcAK9v0SuBJgLb8+Vb/xfIjrPMySTYm2ZVk1/79+0dsuiTpaMz5jCLJe4BnqurBJOfPX5NmV1VbgC0AMzMzdTyeU5IWylLpRRil6+k84L1JLgFeA7wB+BSwPMmydtawCtjb6u8FzgD2JFkGvBH43lD5IcPrTLyl8kKRNL3mHBRVdS1wLUA7o/hgVf1Wkr8HLgduAzYAd7VVtrf5/2jLv1BVlWQ78HdJPgG8GVgD/Odc2yXNJ4N8PHlcjq+F+GX2nwG3JfkY8BBwcyu/GfjbdrH6AIORTlTVw0luBx4BDgLXVNVPFqBdkqQ5mJegqKr7gfvb9OMMRi0dXud/gV+bZf3rGIyckiSNGe/1JE0Ju2s0VwaFNMEMB80H7/UkSeryjEI6jN/CpZfzjEKS1GVQSJK6DApJUpdBIUnqMigkSV0GhSSpy6CQJHUZFJKkLoNCktRlUEiSugwKSVKXQSFJ6jIoJEldBoUkqcugkCR1GRSSpC6DQpLUZVBIkroMCklSl/9n9iyG/9/kb19/6SK2RJIWl2cUkqQuzyi0ZA2f9UlaOAbFUfADaXxM6rGY1P3SZDAopEVgMIwnj8uRzfkaRZIzknwxySNJHk7ygVZ+SpIdSR5rjye38iS5McnuJF9LcvbQtja0+o8l2TD6bkmS5ssoF7MPAn9aVWcBa4FrkpwFbALuq6o1wH1tHuBiYE372wjcBINgATYD5wLnAJsPhYskafHNOSiqal9VfaVN/zfwKLASWA9sa9W2AZe16fXALTXwALA8yenAu4EdVXWgqp4FdgDr5touSdL8mpfhsUlWA+8EdgKnVdW+tugp4LQ2vRJ4cmi1Pa1stnJJ0hgYOSiSvB74B+CPqur7w8uqqoAa9TmGnmtjkl1Jdu3fv3++NitJ6hgpKJL8NIOQ+FxV3dmKn25dSrTHZ1r5XuCModVXtbLZyl+hqrZU1UxVzaxYsWKUpkuSjtIoo54C3Aw8WlWfGFq0HTg0cmkDcNdQ+ZVt9NNa4PnWRXUvcFGSk9tF7ItamSRpDIzyO4rzgPcDX0/y1Vb2YeB64PYkVwNPAO9ry+4BLgF2Ay8AVwFU1YEkHwW+3Op9pKoOjNAuSdI8mnNQVNW/A5ll8YVHqF/ANbNsayuwda5tkSQtHG8KKEnq8hYeE2TU2w94a3X1eHuL6eUZhSSpy6CQJHUZFJKkLoNCktRlUEiSuhz1JGkkRztazlF1S5dBMQV8g0oahV1PkqQuzyikBeSP1MaTx+XYTH1Q+ILROFtq3Ya+nybT1AfFqJbaG3kp8FYkGmeHvz6n4TXmNQpJUpdnFFPGrgFJx8ozCklSl0EhSeoyKCRJXV6jkKQxNE6j9wwKHRMvhktHZ5LeKwbFcTBJLxi9ZJy+8U0K3yvjyaDQcTVOH65+KA1M4w/IdGwMiiVgnD5cNZ4MPS0kg0LSxPBL1cJweKwkqcugkCR1GRSSpC6DQpLUZVBIkrrGJiiSrEvyzSS7k2xa7PZIkgbGIiiSnAD8FXAxcBbwG0nOWtxWSZJgTIICOAfYXVWPV9WPgNuA9YvcJkkS4xMUK4Enh+b3tDJJ0iJLVS12G0hyObCuqn6nzb8fOLeqfv+wehuBjW32bcA35/iUpwLfneO6S9007ztM9/6779Pr0P7/bFWtONaVx+UWHnuBM4bmV7Wyl6mqLcCWUZ8sya6qmhl1O0vRNO87TPf+u+/Tue8w+v6PS9fTl4E1Sc5MciJwBbB9kdskSWJMziiq6mCS3wfuBU4AtlbVw4vcLEkSYxIUAFV1D3DPcXq6kbuvlrBp3neY7v1336fXSPs/FhezJUnja1yuUUiSxtRUBcW03SYkyRlJvpjkkSQPJ/lAKz8lyY4kj7XHkxe7rQslyQlJHkryT23+zCQ722vg823wxMRJsjzJHUn+K8mjSX55yo77H7fX/DeS3JrkNZN87JNsTfJMkm8MlR3xeGfgxvbv8LUkZ7/a9qcmKKb0NiEHgT+tqrOAtcA1bZ83AfdV1RrgvjY/qT4APDo0/3Hghqp6K/AscPWitGrhfQr4l6r6eeAdDP4NpuK4J1kJ/CEwU1VvZzBA5gom+9j/DbDusLLZjvfFwJr2txG46dU2PjVBwRTeJqSq9lXVV9r0fzP4sFjJYL+3tWrbgMsWp4ULK8kq4FLgs20+wAXAHa3KRO57kjcCvwrcDFBVP6qq55iS494sA16bZBnwOmAfE3zsq+pLwIHDimc73uuBW2rgAWB5ktN725+moJjq24QkWQ28E9gJnFZV+9qip4DTFqlZC+2TwIeA/2vzbwKeq6qDbX5SXwNnAvuBv27dbp9NchJTctyrai/wl8B3GATE88CDTMexHzbb8T7mz8JpCoqpleT1wD8Af1RV3x9eVoNhbxM39C3Je4BnqurBxW7LIlgGnA3cVFXvBH7AYd1Mk3rcAVpf/HoGgflm4CRe2S0zVUY93tMUFEd1m5BJk+SnGYTE56rqzlb89KFTzfb4zGK1bwGdB7w3ybcZdDNewKDffnnrjoDJfQ3sAfZU1c42fweD4JiG4w7wLuBbVbW/qn4M3Mng9TANx37YbMf7mD8Lpykopu42Ia1P/mbg0ar6xNCi7cCGNr0BuOt4t22hVdW1VbWqqlYzONZfqKrfAr4IXN6qTeq+PwU8meRtrehC4BGm4Lg33wHWJnldew8c2v+JP/aHme14bweubKOf1gLPD3VRHdFU/eAuySUM+q0P3SbkukVu0oJK8ivAvwFf56V++g8zuE5xO/AW4AngfVV1+IWwiZHkfOCDVfWeJD/H4AzjFOAh4Ler6oeL2b6FkOQXGVzEPxF4HLiKwRfDqTjuSf4C+HUGI/8eAn6HQT/8RB77JLcC5zO4S+zTwGbgHznC8W7h+WkG3XEvAFdV1a7u9qcpKCRJx26aup4kSXNgUEiSugwKSVKXQSFJ6jIoJEldBoUkqcugkCR1GRSSpK7/B9QmTQ889sc6AAAAAElFTkSuQmCC\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": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADHVJREFUeJzt3W+spOVdxvHvJSutpfRU0zUmLOtiFlCstjZTWiVqK61ZhIXEFw3EGqvNbkoE26aJbtto4jvSGpWkJGbTIqklEEpROWWV1mjtm4Ls0tYCK7pBhKU1QEyPJv4hhJ8vZg6erN2zc87OOc+c334/r848Z3bmyu7sNffccz/3k6pCktTXdw0dQJK0sSx6SWrOopek5ix6SWrOopek5ix6SWrOopek5ix6SWrOopek5rYN+eRJ9gJ7zz333H0XXXTRkFEkacs5cuTI81W1/VT3yzxsgTAajerw4cNDx5CkLSXJkaoanep+Tt1IUnMWvSQ1Z9FLUnODFn2SvUkOLi0tDRlDklobtOirarGq9i8sLAwZQ5Jac+pGkpqz6CWpOYtekpob9MxYad7tOnDfVPd78qYrNziJtH6uupGk5lx1I0nNOUcvSc1Z9JLUnEUvSc1Z9JLUnKtuJKk5V91IUnNO3UhSc54ZqzPWtGe9SludI3pJas6il6TmLHpJas6il6TmXEcvSc25jl6SmnPqRpKas+glqTmLXpKas+glqTmLXpKas+glqTmLXpKa84QpSWrOE6YkqTmnbiSpOYtekpqz6CWpOYtekpqz6CWpOYtekpqz6CWpuW1DB5BmbdeB+4aOIM0Vi16agWneXJ686cpNSCL9f07dSFJz7nUjSc25140kNefUjSQ1Z9FLUnMWvSQ1Z9FLUnMWvSQ1Z9FLUnOeGSttkmm3ZvAMWs2aI3pJas6il6TmLHpJas6il6TmLHpJas6il6TmLHpJas796CWpOfejl6TmnLqRpOYseklqzqKXpOYseklqzqKXpOYseklqzv3otaVMu6e7pP/jiF6SmrPoJak5i16SmrPoJak5i16SmrPoJak5i16SmrPoJak5i16SmrPoJak5i16SmrPoJak5i16SmrPoJak5i16SmrPoJam5DSn6JOckOZzkqo14fEnS9KYq+iS3Jnk2ySMnHN+T5PEkx5IcWPGr3wLummVQSdL6TDuivw3Ys/JAkrOAW4ArgEuA65JckuSdwGPAszPMKUlap6muGVtVX06y64TDlwLHquoJgCR3AtcArwbOYVz+/5XkUFW9NLPEkqQ1OZ2Lg58HPL3i9nHgLVV1A0CS9wDPn6zkk+wH9gPs3LnzNGJIklazYatuquq2qvr8Kr8/WFWjqhpt3759o2JI0hnvdIr+GeD8Fbd3TI5JkubI6RT9Q8CFSS5IcjZwLXDvbGJJkmZl2uWVdwBfAS5OcjzJe6vqReAG4H7gKHBXVT26lidPsjfJwaWlpbXmliRNadpVN9ed5Pgh4NB6n7yqFoHF0Wi0b72PIUlanVsgSFJzFr0kNWfRS1Jzgxa9X8ZK0sYbtOirarGq9i8sLAwZQ5Jac+pGkpqz6CWpOYtekpqz6CWpOVfdSFJzrrqRpOacupGk5ix6SWrOopek5ix6SWrOVTeS1JyrbiSpOaduJKm5qS4lKGnz7Dpw3ynv8+RNV25CEnXhiF6SmrPoJak5i16SmnN5pSQ15/JKSWrOVTeaC9OsNJG0Ps7RS1JzFr0kNWfRS1JzFr0kNeeXsVvAtF9Uelq8pO/EEb0kNeeIXtqC/JSntRi06JPsBfbu3r17yBhnFAtC88DX4eYatOirahFYHI1G+4bMIenUPKlt63KOXpKas+glqTmLXpKas+glqTmXV0qaW14/dzYc0UtSc47oJbl0sjmLXtKW5slXp2bRbxBffJLmhVsgaN18M5O2Bi8OLknNOXWjDecXfdKwLPpGZlmolrPUh+voJak5i16SmnPqRmrMKTiBI3pJas+il6TmLHpJas6il6TmLHpJas6il6TmXF4p6YxwJl+tyhG9JDXniH5gntAiaaM5opek5gYt+iR7kxxcWloaMoYktTbo1E1VLQKLo9Fo35A5JAn6XjXNqRtJas6il6TmXHWzQtePbZLObI7oJak5i16SmrPoJak5i16SmrPoJak5i16SmrPoJak5i16SmvOEqXVwa2FJW4kjeklqzqKXpOYseklqzjl6SVqjrXahcUf0ktScRS9JzVn0ktScRS9JzVn0ktScRS9JzVn0ktTczIs+yY8k+aMkdye5ftaPL0lam6mKPsmtSZ5N8sgJx/ckeTzJsSQHAKrqaFW9D3gXcNnsI0uS1mLaM2NvAz4BfHr5QJKzgFuAdwLHgYeS3FtVjyW5Grge+JPZxpWkrWHaXW434wzaqUb0VfVl4N9OOHwpcKyqnqiqF4A7gWsm97+3qq4AfmmWYSVJa3c6e92cBzy94vZx4C1J3gb8IvAK4NDJ/nCS/cB+gJ07d55GDEnSama+qVlVfQn40hT3OwgcBBiNRjXrHJKksdNZdfMMcP6K2zsmxyRJc+R0iv4h4MIkFyQ5G7gWuHc2sSRJszLt8so7gK8AFyc5nuS9VfUicANwP3AUuKuqHl3LkyfZm+Tg0tLSWnNLkqY01Rx9VV13kuOHWOUL1ykedxFYHI1G+9b7GJKk1bkFgiQ1Z9FLUnMWvSQ1N+jFwZPsBfbu3r173Y8xT6cZS9I8GnREX1WLVbV/YWFhyBiS1JpTN5LUnEUvSc1Z9JLU3KBF75mxkrTx/DJWkppz6kaSmrPoJak5i16SmrPoJak5i16SmnN5pSQ1N+imZpt54ZFpNz+TpG6cupGk5ix6SWrOopek5ix6SWrOopek5lxeKUnNuXulJDXn1I0kNWfRS1JzqaqhM5DkOeBf1vnHXwc8P8M4s2KutTHX2sxrLpjfbB1z/WBVbT/Vneai6E9HksNVNRo6x4nMtTbmWpt5zQXzm+1MzuXUjSQ1Z9FLUnMdiv7g0AFOwlxrY661mddcML/ZzthcW36OXpK0ug4jeknSKloUfZI3JnkgydeSHE5y6dCZliW5Mck/JHk0yceGzrNSkg8lqSSvGzoLQJKPT/6u/j7JnyZ57cB59iR5PMmxJAeGzLIsyflJ/ibJY5PX1PuHzrRSkrOSfDXJ54fOsizJa5PcPXltHU3yk0NnAkjywcm/4SNJ7kjyyo16rhZFD3wM+N2qeiPwO5Pbg0vyduAa4A1V9aPA7w0c6WVJzgd+Hnhq6CwrfBF4fVX9OPCPwIeHCpLkLOAW4ArgEuC6JJcMlWeFF4EPVdUlwFuBX5+TXMveDxwdOsQJbgb+sqp+GHgDc5AvyXnAbwCjqno9cBZw7UY9X5eiL+A1k58XgG8OmGWl64Gbqup/AKrq2YHzrPQHwG8y/rubC1X1hap6cXLzAWDHgHEuBY5V1RNV9QJwJ+M37UFV1beq6uHJz//BuLTOGzbVWJIdwJXAJ4fOsizJAvAzwKcAquqFqvr2sKletg34niTbgFexgb3Vpeg/AHw8ydOMR82DjQRPcBHw00keTPK3Sd48dCCAJNcAz1TV14fOsopfA/5iwOc/D3h6xe3jzEmhLkuyC/gJ4MFhk7zsDxkPHl4aOsgKFwDPAX88mVL6ZJJzhg5VVc8w7qqngG8BS1X1hY16vkEvDr4WSf4K+IHv8KuPApcDH6yqzyV5F+N373fMQa5twPcx/oj9ZuCuJD9Um7DU6RS5PsJ42mbTrZarqv58cp+PMp6iuH0zs20lSV4NfA74QFX9+xzkuQp4tqqOJHnb0HlW2Aa8Cbixqh5McjNwAPjtIUMl+V7GnxAvAL4NfDbJu6vqMxvxfFum6KvqpMWd5NOM5wYBPssmfnQ8Ra7rgXsmxf53SV5ivK/Fc0PlSvJjjF9cX08C4+mRh5NcWlX/OlSuFfneA1wFXL4Zb4ireAY4f8XtHZNjg0vy3YxL/vaqumfoPBOXAVcn+QXglcBrknymqt49cK7jwPGqWv7Uczfjoh/aO4B/rqrnAJLcA/wUsCFF32Xq5pvAz05+/jngnwbMstKfAW8HSHIRcDYDb6pUVd+oqu+vql1VtYvxf4Q3bUbJn0qSPYw/+l9dVf85cJyHgAuTXJDkbMZflN07cCYyfnf+FHC0qn5/6DzLqurDVbVj8pq6FvjrOSh5Jq/rp5NcPDl0OfDYgJGWPQW8NcmrJv+ml7OBXxJvmRH9KewDbp58qfHfwP6B8yy7Fbg1ySPAC8CvDDxKnXefAF4BfHHyaeOBqnrfEEGq6sUkNwD3M14RcWtVPTpElhNcBvwy8I0kX5sc+0hVHRow07y7Ebh98ob9BPCrA+dhMo10N/Aw42nKr7KBZ8h6ZqwkNddl6kaSdBIWvSQ1Z9FLUnMWvSQ1Z9FLUnMWvSQ1Z9FLUnMWvSQ1979A1m9dyyURJwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADNxJREFUeJzt3W+snvVdx/H3xyIzYRlTwZhA62EpQ+t02XIGGqLZsmmKpWCMMTRqnBIaiJCZLNGy6UNjp4vKIolpoPLABdLgRDo62YxOnjCkoHNAxTQEpURTkIgajaTh64NzLzmptL3/ct33t+/XI+7rXOe+v1fK+Zzf+V6/3+9KVSFJ6utbhi5AkrRYBr0kNWfQS1JzBr0kNWfQS1JzBr0kNWfQS1JzBr0kNWfQS1JzFwxdAMAll1xSa2trQ5chSSvlqaeeerWqLj3XeUsR9Gtraxw9enToMiRppST5p3HOG7R1k2R3kgOvv/76kGVIUmuDBn1VHa6qvRdffPGQZUhSa96MlaTmDHpJas6gl6TmvBkrSc15M1aSmrN1I0nNLcWCqWW2tu+Rsc99cf+uBVYiSdNxRC9JzXkzVpKaG7R1U1WHgcPr6+u3DFnHUCZpC4GtIUnTsXUjSc0Z9JLUnEEvSc2dd9MrJ+2LS9KqW/mgN7gl6eycXilJzbnXjSQ1t/Ktm2ViG0nSMnLWjSQ1Z9BLUnMGvSQ1Z49+hbg3jqRpOKKXpOYMeklqzqCXpOZcGStJzbkyVpKas3UjSc0Z9JLUnEEvSc0Z9JLUnCtjG5tkJa2raKW+HNFLUnMGvSQ1Z9BLUnMGvSQ1Z9BLUnMGvSQ1Z9BLUnMLCfokFyU5muT6Rby/JGl8YwV9koNJTiZ55rTjO5M8n+R4kn2bvvRrwKF5FipJms64I/r7gJ2bDyTZAtwNXAfsAPYk2ZHkx4DngJNzrFOSNKWxtkCoqseSrJ12+GrgeFW9AJDkAeBG4J3ARWyE//8kOVJVb86tYi2EDx6X+pplr5vLgJc2vT4BXFNVtwMk+Tjw6plCPsleYC/Atm3bZihDknQ2C5t1U1X3VdUXz/L1A1W1XlXrl1566aLKkKTz3ixB/zKwddPry0fHxuYzYyVp8WYJ+ieBK5NckeRC4Cbg4UnewGfGStLijTu98n7gceCqJCeS3FxVp4DbgUeBY8Chqnp2caVKkqYx7qybPWc4fgQ4Mu2HJ9kN7N6+ffu0byFJOodBt0CwdSNJi+deN5LU3KBB76wbSVo8WzeS1JytG0lqbpYtEKT23ANIHQwa9E6vXF0GoLQ67NFLUnP26CWpOYNekpoz6CWpORdMSVJz3oyVpOZs3UhScy6YkubI9QVaRga9Vp7hKp2dN2MlqblBR/RVdRg4vL6+fsuQdWi5TDpCl3R23oyVpOYMeklqzpuxOu/YGtL5xhG9JDVn0EtSc7Zu9LawXSINx3n0ktScm5pJUnP26CWpOYNekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekppzZawkNefKWElqztaNJDVn0EtScwa9JDXnfvTSgCbdp//F/bsWVIk6c0QvSc0Z9JLUnEEvSc0Z9JLUnEEvSc0Z9JLUnEEvSc3NPeiTfF+SP0zyYJLb5v3+kqTJjBX0SQ4mOZnkmdOO70zyfJLjSfYBVNWxqroV+Bng2vmXLEmaxLgj+vuAnZsPJNkC3A1cB+wA9iTZMfraDcAjwJG5VSpJmspYQV9VjwGvnXb4auB4Vb1QVW8ADwA3js5/uKquA352nsVKkiY3y143lwEvbXp9ArgmyYeBnwLewVlG9En2AnsBtm3bNkMZkqSzmfumZlX1VeCrY5x3ADgAsL6+XvOuQ5K0YZZZNy8DWze9vnx0bGw+SlCSFm+WoH8SuDLJFUkuBG4CHp7kDXyUoCQt3rjTK+8HHgeuSnIiyc1VdQq4HXgUOAYcqqpnF1eqJGkaY/Xoq2rPGY4fYYYplEl2A7u3b98+7VtIks5h0C0QbN1I0uK5140kNWfQS1Jzgwa90yslafHs0UtSc7ZuJKk5WzeS1JytG0lqztaNJDVn0EtScwa9JDXnzVhJas6bsZLUnK0bSWrOoJek5gx6SWrOm7GS1NxYT5halKo6DBxeX1+/Zcg6pFWxtu+Rsc99cf+uBVaiVTJo0EtanEl+KYC/GDqzRy9JzRn0ktScQS9JzRn0ktScQS9JzTmPXpKac1MzSWrO1o0kNWfQS1JzBr0kNWfQS1JzBr0kNWfQS1JzBr0kNWfQS1JzroyVpOZcGStJzdm6kaTmDHpJas6gl6TmDHpJas6gl6TmDHpJas6gl6TmDHpJas6gl6TmDHpJas6gl6TmLljEmyb5SWAX8C7g3qr68iI+R5J0bmMHfZKDwPXAyap636bjO4G7gC3APVW1v6oeAh5K8u3AZwGDXlpya/semej8F/fvWlAlmrdJWjf3ATs3H0iyBbgbuA7YAexJsmPTKb8++rokaSBjB31VPQa8dtrhq4HjVfVCVb0BPADcmA2fAb5UVU/Pr1xJ0qRmvRl7GfDSptcnRsfuAD4G/HSSW9/qG5PsTXI0ydFXXnllxjIkSWeykJuxVfU54HPnOOcAcABgfX29FlGHJGn2Ef3LwNZNry8fHRuLjxKUpMWbNeifBK5MckWSC4GbgIfH/WYfJShJizd20Ce5H3gcuCrJiSQ3V9Up4HbgUeAYcKiqnl1MqZKkaYzdo6+qPWc4fgQ4Ms2HJ9kN7N6+ffs03y5JGsOgWyDYupGkxXOvG0lqbiHTKyX1N8mWCW6XMKxBR/ROr5SkxbNHL0nN2aOXpOYMeklqzh69JDVnj16SmrN1I0nNGfSS1Jw9eklqzh69JDVn60aSmjPoJak5g16SmjPoJam5Qbcp9glT0vlhki2NwW2N581ZN5LUnK0bSWrOJ0xJOu+cb0/HckQvSc0Z9JLUnEEvSc25qZkkNef0SklqztaNJDXn9EpJS8eVtPPliF6SmjPoJak5g16SmjPoJak5g16SmjPoJak5V8ZKUnOujJWk5lwwJUlztIyLvezRS1JzjuglrbxJR9HnG0f0ktScQS9Jzdm6kaSz6NAWckQvSc0Z9JLUnEEvSc0Z9JLUnEEvSc3NPeiTvCfJvUkenPd7S5ImN1bQJzmY5GSSZ047vjPJ80mOJ9kHUFUvVNXNiyhWkjS5cUf09wE7Nx9IsgW4G7gO2AHsSbJjrtVJkmY2VtBX1WPAa6cdvho4PhrBvwE8ANw45/okSTOaZWXsZcBLm16fAK5J8p3AbwIfSHJnVf3WW31zkr3A3tHL/0ry/JR1XAK8OuX3LhuvZfl0uQ7wWpZSPjPTtXzPOCfNfQuEqvo34NYxzjsAHJj185Icrar1Wd9nGXgty6fLdYDXsqzejmuZZdbNy8DWTa8vHx2TJC2RWYL+SeDKJFckuRC4CXh4PmVJkuZl3OmV9wOPA1clOZHk5qo6BdwOPAocAw5V1bOLK/WMZm7/LBGvZfl0uQ7wWpbVwq8lVbXoz5AkDcgtECSpuTZBn+SOJP+Q5Nkkvz10PbNK8skkleSSoWuZRpLfGf17/H2SP03y7qFrmtRbrfxeRUm2JvmrJM+Nfj4+MXRNs0iyJcnfJvni0LXMIsm7kzw4+jk5luSHF/VZLYI+yUfYWKz1/qr6fuCzA5c0kyRbgR8H/nnoWmbwFeB9VfWDwD8Cdw5cz0Sarfw+BXyyqnYAPwT88gpfC8An2LgvuOruAv68qr4XeD8LvKYWQQ/cBuyvqv8FqKqTA9czq98DfhVY2RsoVfXl0Q17gK+xMf12lbRZ+V1V/1JVT4/++z/ZCJTLhq1qOkkuB3YB9wxdyyySXAz8KHAvQFW9UVX/vqjP6xL07wV+JMkTSf46yYeGLmhaSW4EXq6qrw9dyxz9EvCloYuY0Fut/F7JcNwsyRrwAeCJYSuZ2u+zMQh6c+hCZnQF8ArwR6M21D1JLlrUh63Mw8GT/AXw3W/xpU+zcR3fwcafpR8CDiV5Ty3plKJzXMun2GjbLL2zXUdV/dnonE+z0Tr4/NtZm/6/JO8E/gT4lar6j6HrmVSS64GTVfVUkg8PXc+MLgA+CNxRVU8kuQvYB/zGoj5sJVTVx870tSS3AV8YBfvfJHmTjb0wXnm76pvEma4lyQ+w8Zv+60lgo93xdJKrq+pf38YSx3K2fxOAJB8Hrgc+uqy/dM+i1crvJN/KRsh/vqq+MHQ9U7oWuCHJTwDfBrwryR9X1c8NXNc0TgAnquqbf1k9yEbQL0SX1s1DwEcAkrwXuJAV3PCoqr5RVd9VVWtVtcbG/wwfXMaQP5ckO9n4E/uGqvrvoeuZQpuV39kYNdwLHKuq3x26nmlV1Z1VdfnoZ+Mm4C9XNOQZ/Uy/lOSq0aGPAs8t6vNWZkR/DgeBg6MHo7wB/MIKjiC7+QPgHcBXRn+dfK2qzrnZ3bKoqlNJvrnyewtwcKCV3/NwLfDzwDeS/N3o2Keq6siANQnuAD4/Gki8APzioj7IlbGS1FyX1o0k6QwMeklqzqCXpOYMeklqzqCXpOYMeklqzqCXpOYMeklq7v8ARZ341wbdFegAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADX9JREFUeJzt3W+snvVdx/H3x05YwrKiAWNCi60pQyu6uJwVDdGwbGprKZjFaBs1TgkNRMhMlji26QOfVV1UlpEsDVQeSCDNZEi3Tjajkz1gSEHngIo2BKVE0xK0ajSShq8Pzm08wZ3T+y/XOV/er0e9fvd9rut75fR8znW+1+/+XakqJEl9fcvQBUiSFsugl6TmDHpJas6gl6TmDHpJas6gl6TmDHpJas6gl6TmDHpJau5tQxcAcNlll9W2bduGLkOSNpSnnnrqlaq6/ELvWxdBv23bNk6cODF0GZK0oST5h3HeZ+tGkpoz6CWpOYNekpoz6CWpubkHfZLrk3w1yWeSXD/v/UuSJjNW0Cc5kuRMkmfeML47yfNJTiW5czRcwH8AbwdOz7dcSdKkxr2ivw/YvXIgySbgbmAPsBM4kGQn8NWq2gN8FPjN+ZUqSZrGWEFfVY8Br75heBdwqqpeqKrXgAeBm6rq9dHr/wJcvNo+kxxMciLJibNnz05RuiRpHLN8YOoK4KUV26eBa5N8EPgJ4FLg06t9cVUdBg4DLC0t+eBarUvb7vzCRO9/8dDeBVUiTW/un4ytqoeAh+a9X0nSdGaZdfMysHXF9pbR2NiS7Ety+Ny5czOUIUlayyxB/yRwVZLtSS4C9gOPTLKDqjpWVQc3b948QxmSpLWMO73yAeBx4Ookp5PcXFXngduBR4GTwNGqenZxpUqSpjFWj76qDqwyfhw4Pu3Bk+wD9u3YsWPaXUiSLmDQJRBs3UjS4rnWjSQ1Z9BLUnODPmHKHr2GMOmHoKSNzh69JDVn60aSmlsXDweXZmErRlrboFf0LoEgSYtnj16SmrNHL0nNGfSS1JxBL0nNeTNWkprzZqwkNWfrRpKaM+glqTmDXpKacwkEaY4mXY7hxUN7F1SJ9H+cdSNJzTnrRpKas0cvSc0Z9JLUnEEvSc0Z9JLUnEEvSc05vVKSmnN6pSQ1Z+tGkpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekppzCQRJam7QZ8ZW1THg2NLS0i1D1qH1ZdLnrkpam60bSWrOoJek5gx6SWrOoJek5gx6SWrOoJek5gx6SWrOoJek5gx6SWrOoJek5gx6SWrOoJek5gx6SWpuIUGf5JIkJ5LcsIj9S5LGN9YyxUmOADcAZ6rqmhXju4G7gE3APVV1aPTSR4Gjc65VamfSJZlfPLR3QZWos3Gv6O8Ddq8cSLIJuBvYA+wEDiTZmeTHgOeAM3OsU5I0pbGu6KvqsSTb3jC8CzhVVS8AJHkQuAl4B3AJy+H/X0mOV9Xrc6tYkjSRWZ4wdQXw0ort08C1VXU7QJIPAa+sFvJJDgIHAa688soZypAkrWVhs26q6r6q+vwarx+uqqWqWrr88ssXVYYkveXNEvQvA1tXbG8ZjUmS1pFZgv5J4Kok25NcBOwHHplkB0n2JTl87ty5GcqQJK1lrKBP8gDwOHB1ktNJbq6q88DtwKPASeBoVT07ycGr6lhVHdy8efOkdUuSxjTurJsDq4wfB47PtSJJ0lwNugSCrRtJWrxBg97WjSQtnouaSVJztm4kqTlbN5LUnK0bSWrOoJek5uzRS1Jz9uglqblZlimWxjbpk5QkzY89eklqzqCXpOa8GStJzQ3ao6+qY8CxpaWlW4asQ5Oz5y5tHLZuJKk5g16SmjPoJak5g16SmnPWjSQ15xIIktScrRtJas6gl6TmDHpJas6gl6TmDHpJas6gl6TmnEcvSc05j16SmvNRgtIGMsny0C8e2rvASrSRGPRSU5M+M8BfDH15M1aSmjPoJak5g16SmjPoJak5g16SmjPoJak5p1cKmHwqnqSNwyUQJKk5l0CQpObs0UtScwa9JDVn0EtScwa9JDVn0EtScwa9JDXnB6YkAa5f35lX9JLUnEEvSc0Z9JLUnEEvSc0Z9JLUnEEvSc3NPeiTfG+SzyT5bJLb5r1/SdJkxgr6JEeSnEnyzBvGdyd5PsmpJHcCVNXJqroV+BnguvmXLEmaxLhX9PcBu1cOJNkE3A3sAXYCB5LsHL12I/AF4PjcKpUkTWWsoK+qx4BX3zC8CzhVVS9U1WvAg8BNo/c/UlV7gJ9bbZ9JDiY5keTE2bNnp6teknRBsyyBcAXw0ort08C1Sa4HPghczBpX9FV1GDgMsLS0VDPUIUlaw9zXuqmqrwBfmfd+JUnTmWXWzcvA1hXbW0ZjY/Ph4JK0eLME/ZPAVUm2J7kI2A88MskOfDi4JC3euNMrHwAeB65OcjrJzVV1HrgdeBQ4CRytqmcXV6okaRpj9eir6sAq48eZYQplkn3Avh07dky7C0nSBQy6BIKtG0laPNe6kaTmDHpJam7QoHd6pSQt3qAPB6+qY8CxpaWlW4aso6tJH/YsqSdbN5LUnEEvSc3Zo5ek5pxHL0nNDXozVtLGNcnN/hcP7V1gJboQe/SS1JxBL0nNeTNWkprzZqwkNWfrRpKaM+glqTmDXpKaM+glqTln3UhSc866kaTmbN1IUnMGvSQ1Z9BLUnOuXilp4SZ9rKWrXc6XV/SS1JzTKyWpOadXSlJztm4kqTlvxg7IG1SS3gxe0UtScwa9JDVn62YDmbTVI0ngFb0ktWfQS1Jztm4krTvOSJsvr+glqTmXQJCk5lwCQZKas3UjSc0Z9JLUnEEvSc0Z9JLUnEEvSc0Z9JLUnEEvSc25BMIFTPJRbD+GLWk9MuglveW81S7gbN1IUnMGvSQ1Z+tGkuZoPS6xbNBL2vB8zObaFhL0SX4K2Au8E7i3qr60iONIki5s7KBPcgS4AThTVdesGN8N3AVsAu6pqkNV9TDwcJJvAz4JvCWC3qsKSevRJDdj7wN2rxxIsgm4G9gD7AQOJNm54i2/PnpdkjSQsYO+qh4DXn3D8C7gVFW9UFWvAQ8CN2XZbwFfrKqn51euJGlSs06vvAJ4acX26dHYHcAHgJ9Ocus3+8IkB5OcSHLi7NmzM5YhSVrNQm7GVtWngE9d4D2HgcMAS0tLtYg6JEmzB/3LwNYV21tGY5LUQodJFrO2bp4ErkqyPclFwH7gkXG/OMm+JIfPnTs3YxmSpNWMHfRJHgAeB65OcjrJzVV1HrgdeBQ4CRytqmfH3WdVHauqg5s3b560bknSmMZu3VTVgVXGjwPH51aRJGmuBl3UzNaNJC3eoEFv60aSFs9liiWpOVs3ktScrRtJas7WjSQ1Z9BLUnODPmEqyT5g344dO6bex3p8bJckrSeDBn1VHQOOLS0t3fJmHbPDuhWSNAlbN5LUnEEvSc0Z9JLUnB+YkqTm/MCUJDVn60aSmjPoJak5g16SmjPoJam5VNVwBx8tgQD8LPD3U+7mMuCVuRU1LM9l/elyHuC5rFeznMt3VdXlF3rToEE/D0lOVNXS0HXMg+ey/nQ5D/Bc1qs341xs3UhScwa9JDXXIegPD13AHHku60+X8wDPZb1a+Lls+B69JGltHa7oJUlraBP0Se5I8rdJnk3y20PXM6skH0lSSS4bupZpJPmd0ffjb5J8LsmlQ9c0qSS7kzyf5FSSO4euZ1pJtib58yTPjX4+Pjx0TbNIsinJXyX5/NC1zCLJpUk+O/o5OZnkhxd1rBZBn+R9wE3Au6vq+4BPDlzSTJJsBX4c+Meha5nBl4FrquoHgL8DPjZwPRNJsgm4G9gD7AQOJNk5bFVTOw98pKp2Aj8E/MoGPheADwMnhy5iDu4C/qSqvgd4Nws8pxZBD9wGHKqq/waoqjMD1zOr3wN+DdiwN1Cq6ktVdX60+TVgy5D1TGEXcKqqXqiq14AHWb6Y2HCq6p+q6unRv/+d5UC5YtiqppNkC7AXuGfoWmaRZDPwo8C9AFX1WlX966KO1yXo3wX8SJInkvxFkvcOXdC0ktwEvFxVXx+6ljn6ZeCLQxcxoSuAl1Zsn2aDhuNKSbYBPwg8MWwlU/t9li+CXh+6kBltB84CfzBqQ92T5JJFHWzQh4NPIsmfAt/5TV76BMvn8e0s/1n6XuBoku+udTql6ALn8nGW2zbr3lrnUVV/PHrPJ1huHdz/Ztam/y/JO4A/An61qv5t6HomleQG4ExVPZXk+qHrmdHbgPcAd1TVE0nuAu4EfmNRB9sQquoDq72W5DbgoVGw/2WS11leP+Lsm1XfJFY7lyTfz/Jv+q8ngeV2x9NJdlXVP7+JJY5lre8JQJIPATcA71+vv3TX8DKwdcX2ltHYhpTkW1kO+fur6qGh65nSdcCNSX4SeDvwziR/WFU/P3Bd0zgNnK6q//3L6rMsB/1CdGndPAy8DyDJu4CL2IALHlXVN6rqO6pqW1VtY/k/w3vWY8hfSJLdLP+JfWNV/efQ9UzhSeCqJNuTXATsBx4ZuKapZPmq4V7gZFX97tD1TKuqPlZVW0Y/G/uBP9ugIc/oZ/qlJFePht4PPLeo422YK/oLOAIcSfIM8BrwixvwCrKbTwMXA18e/XXytaq6ddiSxldV55PcDjwKbAKOVNWzA5c1reuAXwC+keSvR2Mfr6rjA9YkuAO4f3Qh8QLwS4s6kJ+MlaTmurRuJEmrMOglqTmDXpKaM+glqTmDXpKaM+glqTmDXpKaM+glqbn/ARfKFAZkQXJTAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADjtJREFUeJzt3X+onuddx/H3x+yXdJputoyRJibzlGoQ2eQhRRxSxGlijZmj1J4pbBAaK0Ym/tP4A9yUYREVGZaNIwuZoI1h62ZDI93+WOmEMpPWziUN1VgymlCb1LpqQSy1X/84d+GY5Zw8P/Oc+8r7BSXnuZ5f35s7+Zy73/u6rztVhSSpXd817wIkSbNl0EtS4wx6SWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIa96Z5FwBwww031NatW+ddhiT1yhNPPPFiVd14pddNPeiT3Ab8AXAKOFxVj17pPVu3buXEiRPTLkWSmpbkW8O8bqjWTZKDSS4kOXnJ+M4kzyQ5k+RAN1zAK8DbgHOjFC1Jmr5he/SHgJ0rB5JsAO4HdgHbgcUk24GvVdUu4F7gE9MrVZI0jqGCvqoeA166ZHgHcKaqnq2qV4HDwJ6qer17/j+At06tUknSWCbp0W8Cnlvx+Bxwa5IPAT8DXA/8+WpvTrIP2AewZcuWCcqQJK1l6idjq+pB4MEhXrcELAEMBgMXxZekGZlkHv15YPOKxzd1Y0NLsjvJ0ssvvzxBGZKktUwS9MeBm5NsS/IW4C7goVE+oKqOVtW+jRs3TlCGJGktw06vfAB4HLglybkke6vqNWA/8AhwGjhSVadmV6okaRxD9eiranGV8WPAsXG/PMluYPfCwsK4H3FN2nrg4aFed/a+22dciaQ+mOtaN7ZuJGn21sVaN1o27JG6JI1irkf0zrqRpNmzdSNJjXM9eklqnK0bSWqcrRtJapyzbhrmfHtJYI9ekppnj16SGmePXpIaZ+tGkhpn0EtS4wx6SWqcJ2MlqXGejJWkxtm6kaTGGfSS1DiXQLgKvKGIpHnyiF6SGmfQS1LjnF4pSY2ba4++qo4CRweDwd3zrONaN8o5BJc0lvrH1o0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklqnEEvSY3zgilJatw1c8GUFwVJulbZupGkxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMbNJOiTXJfkRJKfm8XnS5KGN1TQJzmY5EKSk5eM70zyTJIzSQ6seOpe4Mg0C5UkjWfYI/pDwM6VA0k2APcDu4DtwGKS7Uk+ADwNXJhinZKkMQ21qFlVPZZk6yXDO4AzVfUsQJLDwB7g7cB1LIf/fyc5VlWvT61iSdJIJlm9chPw3IrH54Bbq2o/QJKPAi+uFvJJ9gH7ALZs2TJBGZKktcxsmeKqOnSF55eAJYDBYFCzqmMcwy5p7HLGkvpgklk354HNKx7f1I0NzRuPSNLsTXJEfxy4Ock2lgP+LuDDo3zA1bzxiKbD/9uR+mfY6ZUPAI8DtyQ5l2RvVb0G7AceAU4DR6rq1OxKlSSNY9hZN4urjB8Djo375Ul2A7sXFhbG/Yi5GuX2hJI0L3NdAqGqjlbVvo0bN86zDElqmmvdSFLj5hr0zrqRpNmzdSNJjbN1I0mNs3UjSY2zdSNJjbN1I0mNM+glqXH26CWpcfboJalxtm4kqXEGvSQ1zqCXpMZ5MlaSGufJWElqnK0bSWqcQS9JjTPoJalxBr0kNc5ZN5LUOGfdSFLjbN1IUuMMeklq3JvmXYDatPXAw0O97ux9t8+4Ekke0UtS4wx6SWqcQS9JjTPoJalxXjAlSY3zgilJapytG0lqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGmfQS1LjDHpJapxBL0mNm3rQJ/mhJJ9J8vkkvzrtz5ckjWaooE9yMMmFJCcvGd+Z5JkkZ5IcAKiq01V1D3An8OPTL1mSNIphj+gPATtXDiTZANwP7AK2A4tJtnfP/TzwMHBsapVKksYyVNBX1WPAS5cM7wDOVNWzVfUqcBjY073+oaraBfzSap+ZZF+SE0lOXLx4cbzqJUlXNMnNwTcBz614fA64NcltwIeAt7LGEX1VLQFLAIPBoCaoQ5K0hkmC/rKq6lHg0Wl/riRpPJPMujkPbF7x+KZubGjeYUqSZm+SI/rjwM1JtrEc8HcBHx7lA6rqKHB0MBjcPUEd6rGtBx4e6nVn77t9xpVI7Rp2euUDwOPALUnOJdlbVa8B+4FHgNPAkao6NcqXe0QvSbM31BF9VS2uMn6MCaZQekQvSbPnEgiS1Li5Br2tG0mavbkGfVUdrap9GzdunGcZktQ0WzeS1DiDXpIaN/UrY0eRZDewe2FhYZ5lqCHOy5e+01yD3umVGtawAS7pO9m6kaTGGfSS1Djn0UtS45xHL0mNs3UjSY0z6CWpcQa9JDXOk7GS1DgvmNI1yStodS2xdSNJjTPoJalxBr0kNc6gl6TGOetGkhrnEgiS1Li5Tq+U1rtR1sF3KqbWK3v0ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXFeMCVJjfOCKUlqnK0bSWqcQS9JjTPoJalxBr0kNc6gl6TGGfSS1DiDXpIa53r00pQMu3a969brajPopavMXwi62mYS9Ek+CNwOfC/w2ar68iy+R5J0ZUP36JMcTHIhyclLxncmeSbJmSQHAKrqS1V1N3AP8IvTLVmSNIpRTsYeAnauHEiyAbgf2AVsBxaTbF/xkt/tnpckzcnQrZuqeizJ1kuGdwBnqupZgCSHgT1JTgP3AX9XVU9OqVbpmmIvX9My6fTKTcBzKx6f68Z+Hfgp4I4k91zujUn2JTmR5MTFixcnLEOStJqZnIytqk8Bn7rCa5aAJYDBYFCzqEOSNPkR/Xlg84rHN3VjkqR1YtKgPw7cnGRbkrcAdwEPDftm7zAlSbM3yvTKB4DHgVuSnEuyt6peA/YDjwCngSNVdWrYz/QOU5I0e6PMullcZfwYcGycL0+yG9i9sLAwztslSUPwnrGS1DhXr5Skxs016D0ZK0mzN9fVK6vqKHB0MBjcPc86pGuBV9peu1ymWNJYhv3FAf7ymDdbN5LUOGfdSFLjnHUjSY2zRy/13Ci9cl2b7NFLUuPs0UtS42zdSOodrwkYjSdjJalxBr0kNW6urRuXKZauDc4Mmi9PxkpS42zdSFLjDHpJapzTKyX9P/bT2+MRvSQ1zqCXpMa51o0kNc7plZLUOFs3ktQ4g16SGmfQS1LjDHpJapxBL0mNM+glqXEGvSQ1zgumJKlxXjAlSY1z9UpJzfIm4svs0UtS4wx6SWqcQS9JjbNHL0lD6mvP3yN6SWqcQS9JjTPoJalxBr0kNW7qJ2OTvAf4HWBjVd0x7c+XpGkb9iRrXw11RJ/kYJILSU5eMr4zyTNJziQ5AFBVz1bV3lkUK0ka3bCtm0PAzpUDSTYA9wO7gO3AYpLtU61OkjSxoYK+qh4DXrpkeAdwpjuCfxU4DOyZcn2SpAlN0qPfBDy34vE54NYk3wd8Enhfkt+qqj+83JuT7AP2AWzZsmWCMiRpfRml5381Lq6a+snYqvp34J4hXrcELAEMBoOadh2SpGWTTK88D2xe8fimbmxo3nhEkmZvkqA/DtycZFuStwB3AQ+N8gHeeESSZm/Y6ZUPAI8DtyQ5l2RvVb0G7AceAU4DR6rq1OxKlSSNY6gefVUtrjJ+DDg27pcn2Q3sXlhYGPcjJElX4D1jJalxrnUjSY2ba9A760aSZs/WjSQ1LlXzv1YpyUXgW2O+/QbgxSmWM09uy/rTynaA27JeTbIt319VN17pResi6CeR5ERVDeZdxzS4LetPK9sBbst6dTW2xZOxktQ4g16SGtdC0C/Nu4ApclvWn1a2A9yW9Wrm29L7Hr0kaW0tHNFLktbQ66C/3D1r+yrJ2STfTPJUkhPzrmcUl7uncJJ3JvlKkn/p/nzHPGscxirb8fEk57v98lSSn51njcNKsjnJV5M8neRUko91473aL2tsR+/2S5K3JfmHJN/otuUT3fi2JF/vcuxvutWAp/vdfW3ddPes/WfgAyzf3eo4sFhVT8+1sDElOQsMqqp3c4OT/ATwCvCXVfXD3dgfAS9V1X3dL+F3VNW986zzSlbZjo8Dr1TVH8+ztlEleTfw7qp6Msn3AE8AHwQ+So/2yxrbcSc92y9JAlxXVa8keTPw98DHgN8EHqyqw0k+A3yjqj49ze/u8xG996xdJ1a5p/Ae4HPdz59j+R/nurbKdvRSVT1fVU92P/8Xy0uJb6Jn+2WN7eidWvZK9/DN3X8F/CTw+W58Jvukz0F/uXvW9vIvQKeALyd5orufbt+9q6qe737+N+Bd8yxmQvuT/FPX2lnXrY7LSbIVeB/wdXq8Xy7ZDujhfkmyIclTwAXgK8C/At/u7u8BM8qxPgd9a95fVT8K7AJ+rWsjNKGW+4P97BHCp4EfAN4LPA/8yXzLGU2StwNfAH6jqv5z5XN92i+X2Y5e7peq+t+qei/Lt17dAfzg1fjePgf9xPesXU+q6nz35wXgiyz/JeizF7r+6ht91gtzrmcsVfVC94/zdeAv6NF+6frAXwD+qqoe7IZ7t18utx193i8AVfVt4KvAjwHXJ3njJlAzybE+B/3E96xdL5Jc151oIsl1wE8DJ9d+17r3EPCR7uePAH87x1rG9kYodn6BnuyX7sTfZ4HTVfWnK57q1X5ZbTv6uF+S3Jjk+u7n72Z5IslplgP/ju5lM9knvZ11A9BNqfozYANwsKo+OeeSxpLkPSwfxcPy7R3/uk/b0t1T+DaWV+F7Afg94EvAEWALyyuT3llV6/pE5yrbcRvL7YECzgK/sqLHvW4leT/wNeCbwOvd8G+z3N/uzX5ZYzsW6dl+SfIjLJ9s3cDyQfaRqvr97t//YeCdwD8Cv1xV/zPV7+5z0EuSrqzPrRtJ0hAMeklqnEEvSY0z6CWpcQa9JDXOoJekxhn0ktQ4g16SGvd/74b6a5cTZm4AAAAASUVORK5CYII=\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": [ + "ys-pred\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": [ + " \n", + "New QUAD 3\n", + " \n", + "dphi\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": [ + "dphiNor\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": [ + "zinner\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": "stderr", + "output_type": "stream", + "text": [ + "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/ticker.py:2198: UserWarning: Data has no positive values, and therefore cannot be log-scaled.\n", + " \"Data has no positive values, and therefore cannot be \"\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\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": [ + "z0\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": [ + "dr\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": [ + "dphi zcut\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/UCwAAEOVJREFUeJzt3W+spGdZx/Hvj60UQVwKLYj9425zSnHFP4RjyxtJ5W8rHEqEyC5EQEtXNDX6zhLwjTGx+AoaGpsNYOkLWypG7NLFCkgtMUW7Wwq21trtCumuaLcgKwFS0nD5Yp6D08POnpkzf899vp9kszP3PM8z17ln5jr3XM/93CdVhSSpXU+ZdwCSpOky0UtS40z0ktQ4E70kNc5EL0mNM9FLUuNM9JLUOBO9JDXORC9JjTtt3gEAnHnmmbVjx455hyFJm8qhQ4ceq6qz1ttuIRL9jh07OHjw4LzDkKRNJclXh9lurqWbJCtJ9p04cWKeYUhS0+aa6Ktqf1Xt3b59+zzDkKSmeTJWkhpn6UaSGmfpRpIaZ+lGkhpnopekxlmjl6TGzfWCqaraD+xfXl6+cp5xSLOw4+rbfnD7K9e8do6RaKtZiCtjpVb1J3dpXkz0asagpOroWVvdXBN9khVgZWlpaZ5hSBM1zCjeMo5myRq9NjWTqrQ+p1dKUuOs0UsT4ElXLTITvbYUyzjaikz00pz5y0fT5qwbbTqWSaTRuHqlJDXOWTeS1DgTvSQ1zpOx2rLGPQnquQJtFo7oJalxJnpJapzTK6UF4px6TYOLmmlTsB4ubZylG0lqnIlekhpnopekxpnoJalxJnpJapyJXpIaZ6KXpMZ5wZS0oLx4SpPievSS1DhXr9TC8mpYaTJM9BKWSdQ2E700JL9haLNy1o0kNc5EL0mNM9FLUuOs0UubgCeLNQ5H9JLUOEf0WijObJEmzxG9JDXORC9JjZtKok/yjCQHk7xuGseXJA1vqESf5CNJHk1y35r2S5M8mORwkqv7HvoD4JZJBipJ2phhT8beAHwQuHG1Ick24DrgVcBR4O4ktwJnA/8KPG2ikUoCnGqp0Q2V6KvqziQ71jRfBByuqiMASW4GLgd+DHgGsAv4bpIDVfX9iUUsSRrJONMrzwYe6bt/FLi4qq4CSPIO4LFBST7JXmAvwHnnnTdGGNrsnFIpTdfUZt1U1Q1V9clTPL6vqparavmss86aVhiStOWNk+iPAef23T+naxtakpUk+06cODFGGJKkUxmndHM3cEGSnfQS/G7gLaMcoKr2A/uXl5evHCMOacvyxKyGMez0ypuAu4ALkxxNckVVPQFcBdwOPADcUlX3Ty9USdJGDDvrZs+A9gPAgY0+eZIVYGVpaWmjh9Am5QlYaXbmugRCVe2vqr3bt2+fZxiS1DTXupGkxs010TvrRpKmz9KNJDXOPzwiNcKplhrERK+ZcaaNNB/W6CWpcXMd0XtlrBad30LUAks30homd7XGefSS1Dhr9JLUOOfRS1LjrNFLDXJOvfpZo5ekxjmi11Q5g0WaP0f0ktQ4Z91IUuOcdSNJjbN0I0mNM9FLUuOcdSM1zjn1ckQvSY1z1o0kNc5ZN5LUOGv0mjivhpUWizV6SWqciV6SGmeil6TGmeglqXEmeklqnLNupC3Eq2S3Jkf0ktQ4r4yVpMZ5ZawkNc7SjSQ1zpOxmgiXPZAWlyN6SWqciV6SGmeil6TGmeglqXEmeklqnIlekhpnopekxpnoJalxJnpJatzEr4xN8tPA7wFnAp+tqj+b9HNIGp9LFm8dQyX6JB8BXgc8WlUv6mu/FPgAsA34UFVdU1UPAO9K8hTgRsBE3yiXPZA2h2FLNzcAl/Y3JNkGXAdcBuwC9iTZ1T32euA24MDEIpUkbchQib6q7gS+sab5IuBwVR2pqu8BNwOXd9vfWlWXAW8ddMwke5McTHLw+PHjG4tekrSucWr0ZwOP9N0/Clyc5BLgV4HTOcWIvqr2AfsAlpeXa4w4JEmnMPGTsVV1B3DHpI8rSdqYcaZXHgPO7bt/Ttc2NP+UoCRN3ziJ/m7ggiQ7kzwV2A3cOsoB/FOCkjR9QyX6JDcBdwEXJjma5IqqegK4CrgdeAC4parun16okqSNGKpGX1V7BrQfYIwplElWgJWlpaWNHkKStI65LoFg6UaSps+1biSpcXNN9M66kaTpm/g8+lFU1X5g//Ly8pXzjEPDc30bafOxdCNJjZvriF7SYnDJ4rZZo5ekxjm9UpIaZ41ekhpnopekxlmjl6TGWaOXpMZZupGkxpnoJalxJnpJapwnYyWpcZ6MlaTGudaN1uWKldLmZo1ekhpnopekxlm6kfQkLlncHkf0ktS4uY7ok6wAK0tLS/MMQ2t48lVqi9MrJalx1uglDWS9vg3W6CWpcY7oJQ3F0f3m5YhekhpnopekxpnoJalxJnpJapyJXpIa5x8ekaTGzXV6ZVXtB/YvLy9fOc84JI3GqZabi/PoBbi+jdQyE/0WZnKXtgZPxkpS40z0ktQ4E70kNc4a/RZjXV6TtvY95SycxWOil7SpOLVzdCb6TcI3t6SNskYvSY1zRC9pavwmuhimkuiTvAF4LfDjwIer6u+m8Tx6Mj9Ukk5m6NJNko8keTTJfWvaL03yYJLDSa4GqKpPVNWVwLuAN082ZEnSKEap0d8AXNrfkGQbcB1wGbAL2JNkV98m7+0elyTNydClm6q6M8mONc0XAYer6ghAkpuBy5M8AFwDfKqq7plQrE2xzCJpVsat0Z8NPNJ3/yhwMfC7wCuB7UmWqur6tTsm2QvsBTjvvPPGDGNzm3bS9yIpaWubysnYqroWuHadbfYB+wCWl5drGnGsp4Ur+kziWjS+JxfPuIn+GHBu3/1zujbNmR82LZpB31wtY07fuIn+buCCJDvpJfjdwFuG3TnJCrCytLQ0Zhjt8E0vadJGmV55E3AXcGGSo0muqKongKuA24EHgFuq6v5hj1lV+6tq7/bt20eNW5I0pFFm3ewZ0H4AOLCRJ3dEL2kYliLH4x8HlzRzJu7Zcq2bKbPmLg3Pz8t0mOjHMOqb0lGMpHmYa6K3Ri9pEAdGk2ONfoH5RpdOzVLPcPzDI5LUOEs3kprjSP/JLN308c0hqUVbYtaNCVxqn+e0BtsSiV6SZmFRB5Um+iEM8+It6gssSc2ejPVrnCT1zHV6patXStL0OY9ekhpnjV5S0zzH5ohekpo310SfZCXJvhMnTswzDElqmidjJalxlm4kqXGejJWkGZv1yd8tl+jHvZBqmP2nfbGWF4NJGkVTid4EKEk/zBq9JDXO6ZWS1LhN/4dHLNdI2oitlDss3UhS40z0ktS4pmbdSNKimmepyBG9JDXOEb0kjWizLWtsoh9gK52Rl/T/NlsSH4aJXtKWsVUHcF4wJUmNcz16SWqcs24kqXHW6CVpgFZq+o7oJalxJnpJapyJXpIaZ41eksawGer4juglqXGO6CVpChZppO+IXpIaZ6KXpMaZ6CWpcRNP9EnOT/LhJB+f9LElSaMbKtEn+UiSR5Pct6b90iQPJjmc5GqAqjpSVVdMI1hJ0uiGHdHfAFza35BkG3AdcBmwC9iTZNdEo5MkjW2oRF9VdwLfWNN8EXC4G8F/D7gZuHzC8UmSxjROjf5s4JG++0eBs5M8J8n1wIuTvHvQzkn2JjmY5ODx48fHCEOSdCoTv2Cqqr4OvGuI7fYB+wCWl5dr0nFIknrGSfTHgHP77p/TtY3s0KFDjyX56gbjOBN4bIP7TpNxjWZR44LFjc24RrOQceV9Y8X1U8NsNE6ivxu4IMlOegl+N/CWjRyoqs7aaBBJDlbV8kb3nxbjGs2ixgWLG5txjWYrxzXs9MqbgLuAC5McTXJFVT0BXAXcDjwA3FJV908vVEnSRgw1oq+qPQPaDwAHJhqRJGmiWlgCYd+8AxjAuEazqHHB4sZmXKPZsnGlygkvktSyFkb0kqRTWNhEn+TZST6d5KHu/zMGbPe3Sb6Z5JNr2ncm+aduHZ6PJXlq1356d/9w9/iOKcX19m6bh5K8vWt7ZpJ7+/49luT93WPvSHK877F3ziqurv2Obt2i1ed/btc+z/56epLbkvxbkvuTXNO3/Yb662TrM615fODPm+TdXfuDSV4z7DGnGVeSVyU5lORfuv9f3rfPSV/TGcW1I8l3+577+r59XtLFezjJtUkyw7jeuuYz+P0kv9A9Nov+elmSe5I8keRNax4b9Nkcu7+oqoX8B/wpcHV3+2rgfQO2ewWwAnxyTfstwO7u9vXAb3e3fwe4vru9G/jYpOMCng0c6f4/o7t9xkm2OwS8rLv9DuCD0+yvU8UF3AEsn2SfufUX8HTgl7ttngp8Hrhso/0FbAMeBs7vjvclYNcwPy+99Zy+BJwO7OyOs22YY045rhcDP9ndfhFwrG+fk76mM4prB3DfgOP+M/BSIMCnVl/TWcS1ZpufBR6ecX/tAH4OuBF405CfzbH6q6oWd0RPb92cj3a3Pwq84WQbVdVngW/1t3W/8V4OrC6V3L9//3E/DrxixN+Qw8T1GuDTVfWNqvof4NP88KJwLwCeSy95TcJE4lrnuDPtr6r6TlV9DqB66yndQ+/CvI0aZn2mQT/v5cDNVfV4Vf0HcLg73iTWfNpwXFX1xar6z679fuBHk5w+4vNPPK5BB0zyfODHq+oL1ctiNzLgsz2DuPZ0+07KunFV1Veq6svA99fse9LPwIT6a6ET/fOq6mvd7f8CnjfCvs8Bvlm9uf7QrcPT3f7BGj3d4ye67ScZ10nXAVqzzeooo/9s+BuTfDnJx5Ocy2gmEdefd19Z/7DvQ7EQ/ZXkWfS+uX22r3nU/hrmdRn08w7ad5hjTjOufm8E7qmqx/vaTvaaziqunUm+mOQfkvxS3/ZH1znmtONa9WbgpjVt0+6vUfedRH/N94+DJ/kM8BMneeg9/XeqqpLMbHrQjOLaDfx63/39wE1V9XiS36I3Gnl5/w5TjuutVXUsyTOBv+piu3GYHafdX0lOo/eBvLaqjnTN6/bXVpLkZ4D3Aa/ua97wazoBXwPOq6qvJ3kJ8IkuxoWQ5GLgO1XV/zc25tlfUzXXRF9Vrxz0WJL/TvL8qvpa9/Xl0REO/XXgWUlO636b96/Ds7pGz9EugWzvtp9kXMeAS/run0Ov/rd6jJ8HTquqQ33P2R/Dh+jVtp9kmnFV1bHu/28l+Qt6X0NvZAH6i94844eq6v19z7lufw14nvXWZxr0855q33HXfBonLpKcA/w18Laqenh1h1O8plOPq/um+nj3/IeSPAy8oNu+v/w28/7q7GbNaH5G/XWqfS9Zs+8dTKa/Frp0cyuweub57cDfDLtj9yb7HLB6Vrt///7jvgn4+zXlk0nEdTvw6iRnpDfL5NVd26o9rHmTdUlw1evpLSsxig3HleS0JGd2cfwI8DpgdaQz1/5K8sf0PqS/37/DBvvrB+szpTcLa3cX36B4+3/eW4Hd6c3m2AlcQO8k2TDHnFpcXUnrNnonvP9xdeN1XtNZxHVWen+ciCTn0+uvI10Z73+TvLQrjbyNET7b48bVxfMU4Nfoq8/PsL8GOelnYEL9tdCzbp5Drx77EPAZ4Nld+zLwob7tPg8cB75Lr371mq79fHofxMPAXwKnd+1P6+4f7h4/f0px/Wb3HIeB31hzjCPAC9e0/Qm9k2lfovdL6oWzigt4Br0ZQF/uYvgAsG3e/UVv9FL0kvi93b93jtNfwK8A/05vdsR7urY/Al6/3s9LrxT1MPAgfTMfTnbMDbzfNxQX8F7g2339cy+9k/wDX9MZxfXG7nnvpXcSfaXvmMv0kujDwAfpLtycRVzdY5cAX1hzvFn11y/Sy1PfpvcN4/71csYk+ssrYyWpcYtcupEkTYCJXpIaZ6KXpMaZ6CWpcSZ6SWqciV6SGmeil6TGmeglqXH/ByQv45DNRuQJAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\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": [ + "module\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": [ + "dysize\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": [ + "ysize\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": [ + "ys-pred\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" + } + ], + "source": [ + "kq=0\n", + "for quad in [t1234,t1231,t1212,t1123] :\n", + " plotDoublets(quad,500,kq)\n", + " kq+=1" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "def plotPairs(quad,mpt,kp) :\n", + " quadc = quad[quad['pt1']>mpt]\n", + "\n", + " maxc = 1000./(mpt*87.78)\n", + "\n", + " print \" \"\n", + " print \"NEW PAIR\",kp\n", + " print \" \" \n", + " \n", + " print 'dphi'\n", + " d1 = quadc['phi2']-quadc['phi1']\n", + " plt.hist(d1[abs(d1)<.1], bins=100,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + "\n", + " pcut = abs(d1)<0.06\n", + "\n", + "\n", + " print 'dphiNor'\n", + " \n", + " pc = phicut(quadc['r1'],quadc['r2'],maxc)\n", + " d1 = (quadc['phi2']-quadc['phi1'])/pc\n", + " plt.hist(d1[abs(d1)<2.5], bins=100,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + "\n", + "\n", + " print 'zinner'\n", + " d1 = quadc['z1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<35)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'dz'\n", + " d1 = quadc['z2']-quadc['z1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<35)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'z0'\n", + " d1 = (quadc['r1']*quadc['z2']-quadc['z1']*quadc['r2'])/(quadc['r1']-quadc['r2'])\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<50)], bins=100,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + " \n", + "\n", + " pcut = np.logical_and(pcut,abs(d1)<12.)\n", + "\n", + " print 'z0 res' \n", + " dz = d1 - quadc['tkz1']\n", + " plt.hist(dz[pcut], bins=100,range=[-2.,2.],log=True)\n", + " plt.grid()\n", + " plt.show()\n", + " plt.hist(dz[pcut], bins=100,range=[-0.4,0.4],log=True)\n", + " plt.grid()\n", + " plt.show()\n", + " \n", + " print 'dr'\n", + " d1 = quadc['r2']-quadc['r1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<20)], bins=100,log=True)\n", + " plt.show()\n", + "\n", + " print 'dphi zcut'\n", + " d1 = quadc['phi2']-quadc['phi1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<.1)], bins=100,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + "\n", + " print 'zinner zcut'\n", + " d1 = quadc['z1']\n", + " plt.hist(d1[np.logical_and(pcut,abs(d1)<35)], bins=100,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + "\n", + " \n", + " print 'module'\n", + " d1 = quadc['det1']\n", + " plt.hist(d1[pcut], range=[0,96], bins=96, log=False)\n", + " plt.show()\n", + "\n", + "\n", + " norm = 1./8.\n", + " print 'dysize'\n", + " d1 = (quadc['ys2']-quadc['ys1'])*norm\n", + " c = np.logical_and(quadc['ys2']>0,quadc['ys1']>0)\n", + " plt.hist(d1[np.logical_and(pcut,c)], range=[-8,8],bins=32,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + " \n", + " print 'ysize'\n", + " d1 = quadc['ys1']*norm\n", + " plt.hist(d1[pcut], bins=30,range=[0,20],log=True)\n", + " plt.grid()\n", + " plt.show()\n", + " \n", + " print 'ys-pred'\n", + " th = 0.0285\n", + " ptc = 0.015\n", + " fac = th/ptc\n", + " d1 = fac*(quadc['z1']-quadc['z2'])/(quadc['r1']-quadc['r2'])\n", + " d1 = quadc['ys1']*norm - abs(d1)\n", + " plt.hist(d1[np.logical_and(pcut,quadc['ys1']>0)], range=[-8,8],bins=32,log=True)\n", + " plt.grid()\n", + " plt.show()\n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 0\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAC9xJREFUeJzt3e+rpOdZB/DvZWqkbGV9kbJCEtzAhmAwgnBIlb45qIWN6TZYUBKKsBiyWAwoLGik/0BA6hsbKAuGRQkJwR80SyKxgoe8iZJEiptkTVlCSnYRYigc3SiUpbcvcmJPtz05v2bOc+aaz+fVzjNzZq975pnv3HM/93M/NcYIAH39xNQFADBfgh6gOUEP0JygB2hO0AM0J+gBmhP0AM0JeoDmBD1Ac5+YuoAkueWWW8bx48enLmPXPvjggxw5cmTqMg7UsrV52dqbaPMiee21194fY3x6u8cdiqA/fvx4Xn311anL2LW1tbWsrq5OXcaBWrY2L1t7E21eJFX1nZ08ztANQHOCHqA5QQ/QnKAHaE7QAzQn6AGaE/QAzU0a9FV1qqrOra+vT1kGQGuTnjA1xriQ5MLKysojU9bBwTj+2PM/dvs7j99/wJX0sfk19TqylUNxZizLbasvgM2E2A9s9Xr5ImUrgp6Z20lws71ZvY56/Qh6OER8STIPgp6FoFc6G17H5STomQk9UTi8BD0Lp1uv1Jck8+aEKYDm9OhZaIvau9eL5yAJevZMWMFiEPSwpBb11xC7Z4weoDk9eto47D1UQ11MRY8eoDlBD9CcoAdoTtADNOdgLC0dlgOzDsByGOjRAzQn6AGaE/QAzQl6gOYEPUBzZt2wKxevrue0mSTtbJ4ddP7kkQkrYR706AGaE/QAzRm6ob3DcvIUTEXQw4w5G5bDxtANQHNzCfqqOlJVr1bV5+fx/ADs3I6CvqqerKr3qur1G7afrKq3qupyVT226a4/TvLsLAsFYG922qM/n+Tk5g1VdVOSJ5Lcl+TuJA9V1d1V9bkkbyZ5b4Z1ArBHOzoYO8Z4qaqO37D53iSXxxhvJ0lVPZPkgSSfSnIkH4b//1bVC2OM78+sYgB2ZT+zbm5N8u6m21eSfGaM8WiSVNXpJO9vFfJVdSbJmSQ5duxY1tbW9lHKNK5du7aQde/HsU8mZ++5PnUZe7bb92sv7/Eivz7Jcu7X3ds8t+mVY4zz29x/Lsm5JFlZWRmrq6vzKmVu1tbWsoh178efP/WNfPXi4s7KfedLq7t6/F7e40VfIuL8ySNLt193/yzvZ9bN1SS3b7p928Y2AA6R/QT9K0nurKo7qurmJA8meW42ZQEwKzudXvl0kpeT3FVVV6rq4THG9SSPJnkxyaUkz44x3phfqQDsxU5n3Ty0xfYXkryw1/+8qk4lOXXixIm9PgUA25j0qNoY40KSCysrK49MWQfLwwJnLKPFnT4Bh4iFzDjMLGoG0JygB2hO0AM0N2nQV9Wpqjq3vr4+ZRkArU0a9GOMC2OMM0ePHp2yDIDWDN0ANCfoAZoT9ADNCXqA5gQ9QHOmVwI0Z3olQHOGbgCaE/QAzQl6gOasRw971HUN+otX13N6o20uztKDoGdpudoUy2LSoHfN2MWwORDP3jNhIcCemF4J0JyDsQDNCXqA5gQ9QHOCHqA5QQ/QnKAHaE7QAzRnPXqA5pwwBdCcoRuA5gQ9QHOCHqA5QQ/QnPXoYYe6XmiE/vToAZrTo4e42hS96dEDNCfoAZqzBAJAc5ZAAGjO0A1Ac4IeoDlBD9CcoAdoTtADNCfoAZoT9ADNCXqA5gQ9QHOCHqA5QQ/QnPXo4Qab16Y/f/LIhJXAbEwa9FV1KsmpEydOTFkGbOni1fWcdglBFpzVKwGaM0YP0JygB2hO0AM0J+gBmjO9EtjS5qmm7zx+/4SVsB969ADNCXqA5gQ9QHPG6PkRx50JCq3o0QM0J+gBmhP0AM0JeoDmBD1Ac4IeoDlBD9CcoAdoTtADNDdp0FfVqao6t76+PmUZAK25ZixAc4ZuAJoT9ADNCXqA5gQ9QHOCHqA5QQ/QnKAHaE7QAzQn6AGaE/QAzQl6gOYEPUBzgh6gOUEP0Nwnpi4AWAzHH3v+///9zuP3T1gJu6VHD9CcHj1Jfri3BvSiRw/QnKAHaE7QAzQn6AGaE/QAzQl6gOZMrwR2zclTi0WPHqA5QQ/QnKAHaE7QAzQ386Cvqp+vqq9X1V9X1Zdn/fwA7M6Ogr6qnqyq96rq9Ru2n6yqt6rqclU9liRjjEtjjN9L8ttJPjv7kgHYjZ1Orzyf5GtJ/vKjDVV1U5InknwuyZUkr1TVc2OMN6vqC0m+nOSvZlsus2TFSlgOO+rRjzFeSvLdGzbfm+TyGOPtMcb3kjyT5IGNxz83xrgvyZdmWSwAu7efE6ZuTfLupttXknymqlaTfDHJTyV5Yas/rqozSc4kybFjx7K2traPUqZx7dq1haz7I2fvub7rvzn2yb393aJatvYmu2/zIn8GPrLon+XtzPzM2DHGWpK1HTzuXJJzSbKysjJWV1dnXcrcra2tZRHr/sjpPQzdnL3ner56cXlOqF629iZ7aPPFD37o5iKeKbvon+Xt7GfWzdUkt2+6fdvGNgAOkf0E/StJ7qyqO6rq5iQPJnluNmUBMCs7nV75dJKXk9xVVVeq6uExxvUkjyZ5McmlJM+OMd6YX6kA7MWOBuLGGA9tsf2FfMwB1+1U1akkp06cOLHXpwBgG5MeZRpjXEhyYWVl5ZEp6wDmw3LGh8NyTSfASVLM3Vb7mNCfjqBfAsIdlpvVKwGaE/QAzU0a9FV1qqrOra+vT1kGQGtm3QCTcpB2/gzdADQn6AGaM70SOHCm/B4sQd+IsU4WnX14PiYNemvdzI8eE/ARs24WnEAHtmPoBjiUDOPMjqA/QPvZcfXcgb0yvRKgOT36Q8BPVPh4W/2i9XnZGUEPLCydpJ0R9BPZycUZAGbB6pUAzZlHPyN+QgKHlVk3AM0JeoDmBD1Ac2bdzIGZM8BhIuh3aXOInz95ZMJKAHZG0AMtOHt2a0sd9DfuGHYI6GcnU58vXl3P6Y3HdcwBFx7Zh807B7BYNn8BnL1nwkIOwNKdMOVAKSyvZf38L/XQzY22+om3rDsH0EOroLcMAcCPahX0W9EjB5bZUgQ9wE51HBkQ9FvwKwDoom3QC2qAD1nUDKC5he/R67kD89JlvH7hgx7gICxy6LtmLEBzS7cEAsBBOEyLJhq6AdiHRRjSEfQAM7LTySEH/eVgeiVAc3r0AAdgyqngevQAzenRA+zSop2oqUcP0JygB2hO0AM0J+gBmhP0AM0JeoDmrF4J0NykQT/GuDDGOHP06NEpywBozdANQHM1xpi6hlTVfyb5ztR17MEtSd6fuogDtmxtXrb2Jtq8SH5ujPHp7R50KIJ+UVXVq2OMlanrOEjL1uZla2+izR0ZugFoTtADNCfo9+fc1AVMYNnavGztTbS5HWP0AM3p0QM0J+j3oar+tKr+var+rar+rqp+Zuqa5q2qfquq3qiq71dV21kKSVJVJ6vqraq6XFWPTV3PvFXVk1X1XlW9PnUtB6Wqbq+qf6qqNzf26z+YuqZ5EPT7880kvzDG+MUk307yJxPXcxBeT/LFJC9NXcg8VdVNSZ5Icl+Su5M8VFV3T1vV3J1PcnLqIg7Y9SRnxxh3J/nlJL/f8X0W9PswxviHMcb1jZv/nOS2Kes5CGOMS2OMt6au4wDcm+TyGOPtMcb3kjyT5IGJa5qrMcZLSb47dR0HaYzxH2OMf934938nuZTk1mmrmj1BPzu/m+Tvpy6Cmbk1ybubbl9JwwDgB6rqeJJfSvIv01Yyey4Ovo2q+sckP/tj7vrKGOMbG4/5Sj78CfjUQdY2LztpM3RSVZ9K8jdJ/nCM8V9T1zNrgn4bY4xf/7j7q+p0ks8n+bXRZK7qdm1eEleT3L7p9m0b22imqn4yH4b8U2OMv526nnkwdLMPVXUyyR8l+cIY43+mroeZeiXJnVV1R1XdnOTBJM9NXBMzVlWV5C+SXBpj/NnU9cyLoN+fryX56STfrKpvVdXXpy5o3qrqN6vqSpJfSfJ8Vb04dU3zsHGQ/dEkL+bDA3TPjjHemLaq+aqqp5O8nOSuqrpSVQ9PXdMB+GyS30nyqxuf4W9V1W9MXdSsOTMWoDk9eoDmBD1Ac4IeoDlBD9CcoAdoTtADNCfoAZoT9ADN/R+9/kYj++Fd1QAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 1\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADK9JREFUeJzt3VGopPdZx/Hvz9VEaHGtbKhlk3UDuxTXIAhDKuhFwEo3NtvUoiWrFy2WLAUjFQRNjVBvhIAithqFpVlSoSQE1LjHbkhrsMaLVpMU0aRrdImWbIiNsXgUFEvI48WZ1unJnpw5OzPnnXnm+7nJmfednXn+2ZNf/vO8//m/qSokSX1929AFSJIWy6CXpOYMeklqzqCXpOYMeklqzqCXpOYMeklqzqCXpOYMeklq7tuHLgDg0KFDdfTo0aHLkKSV8vTTT79SVdft9rylCPqjR4/y1FNPDV2GJK2UJF+Z5nm2biSpOYNekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekppbyBemkrwJ+Evg16vqzxbxHtKyOnr3Z77587/c++4BK5G2TBX0Sc4BtwEvV9VNE8dPAh8HDgCfrKp7x6d+BXh4zrVKM9sphKcJ58nnvNGfl5bNtDP6B4DfA/7wGweSHADuA34cuAw8meQ8cBj4MvCdc61U2oP9CN5p3sPZvZbBVEFfVU8kObrt8M3Apap6HiDJQ8DtwJuBNwEngP9JcqGqXptbxdKcOAvXupilR38YeGHi8WXgHVV1F0CSDwKv7BTySc4AZwCOHDkyQxnSlmUP7p3qc6avRVvY7pVV9cAu588CZwFGo1Etqg71ME1vXdKVzRL0LwI3TDy+fnxMWqhu4b7XC8F+AtBezRL0TwLHk9zIVsDfAfzMXKqS6Bfo01jHMWvxpl1e+SBwC3AoyWXgY1V1f5K7gMfYWl55rqqe3cubJzkFnDp27NjeqlZbBp00f9Ouujm9w/ELwIWrffOq2gA2RqPRnVf7GtI6s6WjaSzFrQS1fgyo+fPfqXZi0Evb2D5SNwa99o0BKg1j0KD3Ymx/hvswbONo0qDbFFfVRlWdOXjw4JBlSFJrtm6k5pzdy6DXVbMts3oM/fVk0EtrytBfHwa9JEO/OVfdaFe2aNaLod+Pq24kqTlbN3odZ/D6Bmf3PRj0Agx3qTODXtJUnN2vLoN+jTmL19Uy9FfLoBdjk5xKcnZzc3PIMiSptUFn9N54ZP85i5fWj60bSTPZPnmwlbN8DHppxazSpzJ7+cvBoNfgVim4pFVk0DflTErLxt/J4Qy66kaStHjO6BuxBSLpSlxHL0nNuXulJDVn62bF2a7RKvLC7P7yYqwkNWfQS1JzBr0kNWePfg3Yx5fWm0G/ggxuLTN/P5ePrRtJam7QGX2SU8CpY8eODVmGpAG51HLx/MKUJDVn60aSmjPoJak5V91IWhr26xfDoF8RLlmTdLVs3UhScwa9JDVn0EtScwa9JDVn0EtScwa9JDXnXjeSlpJr6ufHvW4kqTm/MLXE/JKUpHmwRy9JzRn0ktScQS9JzRn0ktScQS9JzRn0ktScQS9JzRn0ktScQS9JzRn0ktScWyBIWnpucDYbg37JuL+NpHmzdSNJzRn0ktTcoEGf5FSSs5ubm0OWIUmteeMRSWrO1o0kNWfQS1JzBr0kNWfQS1JzBr0kNWfQS1JzBr0kNedeN5JWihuc7Z0zeklqzhn9EnDHSkmL5Ixekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOYNekpoz6CWpOTc1k7Sy3LJ4OnMP+iTfD3wEOAQ8XlV/MO/36MAdKyXtl6laN0nOJXk5yTPbjp9M8lySS0nuBqiqi1X1YeD9wI/Mv2RJ0l5M26N/ADg5eSDJAeA+4FbgBHA6yYnxufcAnwEuzK1SSdJVmSroq+oJ4GvbDt8MXKqq56vq68BDwO3j55+vqluBn51nsZKkvZulR38YeGHi8WXgHUluAd4HXMsbzOiTnAHOABw5cmSGMiRJb2TuF2Or6vPA56d43lngLMBoNKp51yFJ2jLLOvoXgRsmHl8/PiZJWiKzBP2TwPEkNya5BrgDOD+fsiRJ8zLt8soHgS8Ab09yOcmHqupV4C7gMeAi8HBVPbuXN09yKsnZzc3NvdYtSZrSVD36qjq9w/ELzLCEsqo2gI3RaHTn1b6GJOmNudeNJDVn0EtScwa9JDU3aNB7MVaSFm/QoK+qjao6c/DgwSHLkKTWbN1IUnMGvSQ1Z9BLUnOD3kowySng1LFjx4YsY994VylJQ/BirCQ1583BJbXgjcJ3Zo9ekpoz6CWpOYNekpoz6CWpOfe6kaTmXF4pSc3ZupGk5gx6SWrOoJek5gx6SWrOoJek5lxeKUnNDbqpWVVtABuj0ejOIetYJLcmljQ0WzeS1JzbFEtqxy2Lv5UzeklqzqCXpOYMeklqzqCXpOYMeklqzqCXpOb8ZqwkNeeNRySpOVs3ktScQS9JzRn0ktScQS9JzRn0ktScQS9JzRn0ktScQS9JzRn0ktScQS9JzbnXjSQ15143ktScNweXtDbW9abh9uglqTmDXpKaM+glqTmDXpKaM+glqTlX3czJ5NV8SVomzuglqTmDXpKas3UjaS2t05enDPo9WqdfDkk92LqRpOac0c/AlTbS8vO/U4P+W+zUlvEXReqte0vW/eglqblBZ/RVtQFsjEajO4d4f2fqkrbrOLu3dbMD/ycgqQtX3UhSc87oJWkK0yzWWNZWj0EvSTvo0sK1dSNJzRn0ktTc2rVuunwUk6RprV3QS9KiLOuFWYNekvZo1ToD9uglqTmDXpKaW4vWzap9zJKkeXJGL0nNGfSS1JxBL0nNGfSS1JxBL0nNGfSS1Fzb5ZUuqZSkLc7oJam5tjN6SRrS9q7CkJucOaOXpOYMeklqztaNJO2DIfeqX0jQJ3kv8G7gu4D7q+qzi3gfSdLupg76JOeA24CXq+qmieMngY8DB4BPVtW9VfUI8EiStwC/BSws6Jf1ji6StCz20qN/ADg5eSDJAeA+4FbgBHA6yYmJp/za+LwkaSBTz+ir6okkR7cdvhm4VFXPAyR5CLg9yUXgXuDRqvrSlV4vyRngDMCRI0f2Xrkkraj97kTMuurmMPDCxOPL42O/ALwT+KkkH77SH6yqs1U1qqrRddddN2MZkqSdLORibFV9AvjEIl5bkrQ3swb9i8ANE4+vHx8bhPvbSNLrzdq6eRI4nuTGJNcAdwDnZy9LkjQvUwd9kgeBLwBvT3I5yYeq6lXgLuAx4CLwcFU9u4fXPJXk7Obm5l7rliRNaS+rbk7vcPwCcOFq3ryqNoCN0Wh059X8eUnS7tzrRpKaM+glqTmDXpKaGzTovRgrSYs3aNBX1UZVnTl48OCQZUhSa7ZuJKm5VNXQNZDk34CvDF3HnB0CXhm6iAVzjD2swxih5zi/r6p23SxsKYK+oyRPVdVo6DoWyTH2sA5jhPUZ55XYupGk5gx6SWrOoF+cs0MXsA8cYw/rMEZYn3G+jj16SWrOGb0kNWfQz1mS30zyD0n+LsmfJPnuiXMfTXIpyXNJ3jVknbNI8tNJnk3yWpLRtnMtxgiQ5OR4HJeS3D10PfOQ5FySl5M8M3Hse5J8Lsk/jf/5liFrnFWSG5L8RZIvj39PPzI+3mqce2HQz9/ngJuq6geBfwQ+CpDkBFs3ZvkB4CTw+0kODFblbJ4B3gc8MXmw0xjHdd8H3AqcAE6Px7fqHmDr72bS3cDjVXUceHz8eJW9CvxSVZ0Afhj4+fHfXbdxTs2gn7Oq+uz4hiwAX2Tr9ooAtwMPVdX/VtU/A5eAm4eocVZVdbGqnrvCqTZjZKvuS1X1fFV9HXiIrfGttKp6AvjatsO3A58a//wp4L37WtScVdVLVfWl8c//xdZNkQ7TbJx7YdAv1s8Bj45/Pgy8MHHu8vhYJ53G2Gksu3lrVb00/vlfgbcOWcw8JTkK/BDw1zQe525mvTn4Wkry58D3XuHUPVX1p+Pn3MPWR8hP72dt8zLNGNVPVVWSFkvxkrwZ+CPgF6vqP5N881yncU7DoL8KVfXONzqf5IPAbcCP1f+vX30RuGHiadePjy2l3ca4g5Ua4y46jWU3X03ytqp6KcnbgJeHLmhWSb6DrZD/dFX98fhwu3FOy9bNnCU5Cfwy8J6q+u+JU+eBO5Jcm+RG4DjwN0PUuECdxvgkcDzJjUmuYesi8/mBa1qU88AHxj9/AFjpT2zZmrrfD1ysqt+eONVqnHvhF6bmLMkl4Frg38eHvlhVHx6fu4etvv2rbH2cfPTKr7Lckvwk8LvAdcB/AH9bVe8an2sxRoAkPwH8DnAAOFdVvzFwSTNL8iBwC1s7OX4V+BjwCPAwcIStXWTfX1XbL9iujCQ/CvwV8PfAa+PDv8pWn77NOPfCoJek5mzdSFJzBr0kNWfQS1JzBr0kNWfQS1JzBr0kNWfQS1JzBr0kNfd/Yxu6v7p6mMoAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEHFJREFUeJzt3X+s3XV9x/Hna2UothFdcHcJJWuXIltHp5MruJltt8O5y6CyLMTQKJENbDTDqWkyUbcl/kdUtqEjmkZZs0i4QWSCWIducod/oIPij4IdrmFMWx3FMLuVsZGG9/64p+6mWu45555zv6efPh9Jk36/93vOed17z33d7/18P9/vN1WFJKldP9F1AEnSeFn0ktQ4i16SGmfRS1LjLHpJapxFL0mNs+glqXEWvSQ1zqKXpMad0nUAgDPOOKPWrVs31GOfeuopVq9ePdpAI2CuwZhrMJOaCyY3W4u5du/e/f2qesmSG1ZV5//OO++8GtY999wz9GPHyVyDMddgJjVX1eRmazEX8ED10bEO3UhS4yx6SWqcRS9Jjeu06JNsSbLj0KFDXcaQpKZ1WvRV9Zmq2nb66ad3GUOSmubQjSQ1zqKXpMZZ9JLUuIk4M1aaVHsOHOLKaz+75HaPXXfxCqSRhuMevSQ1zqKXpMZZ9JLUOItekhpn0UtS4yx6SWqcRS9JjRt50SeZSfKlJB9NMjPq55ckDaavok9yU5KDSR46Zv1skkeS7EtybW91AYeB5wP7RxtXkjSofvfodwKzi1ckWQXcCFwEbAS2JtkIfKmqLgLeBbxvdFElScPoq+ir6l7gyWNWnw/sq6pHq+oZYA64tKqe7X38P4DnjSypJGkoWbi/bB8bJuuAu6rq3N7yZcBsVV3dW74CuAD4IvDbwIuAj1TV/HGebxuwDWBqauq8ubm5oT6Bw4cPs2bNmqEeO07mGsyk5jr45CEef3rp7TadubL3VJjUrxdMbrYWc23evHl3VU0vtd3IL2pWVbcDt/ex3Q5gB8D09HTNzMwM9Xrz8/MM+9hxMtdgJjXXh2++g+v3LP1j8tgbZsYfZpFJ/XrB5GY7mXMtp+gPAGctWl7bWyedENb1cVXK7ZtWIIg0ZsuZXnk/cHaS9UlOBS4H7hzkCbxnrCSNX7/TK28B7gPOSbI/yVVVdQS4Brgb2AvcWlUPD/Li3jNWksavr6Gbqtp6nPW7gF0jTSRJGqlOL4Hg0I0kjV+nRe/QjSSNnxc1k6TGOXQjSY1z6EaSGjfyM2Olk1E/J189dt3FK5BE+lGO0UtS4xyjl6TGOUYvSY1zjF7N6We8XDqZOEYvSY1zjF6SGucYvSQ1zqEbSWqcRS9JjbPoJalxFr0kNa7TefRJtgBbNmzY0GUMaUX0O7/fa+Jo1Jx1I0mNc+hGkhpn0UtS4yx6SWqcRS9JjbPoJalxXtRMkhrn9EpJapxDN5LUOItekhpn0UtS4yx6SWqcRS9JjbPoJalxFr0kNc6il6TGeWasJDXOM2MlqXEO3UhS4yx6SWpcpzcHlwbV7w22Jf0/9+glqXEWvSQ1zqKXpMZZ9JLUOItekhpn0UtS4yx6SWqcRS9JjRtL0SdZneSBJJeM4/klSf3rq+iT3JTkYJKHjlk/m+SRJPuSXLvoQ+8Cbh1lUEnScPrdo98JzC5ekWQVcCNwEbAR2JpkY5LfAr4JHBxhTknSkPq61k1V3Ztk3TGrzwf2VdWjAEnmgEuBNcBqFsr/6SS7qurZkSWWJA0kVdXfhgtFf1dVndtbvgyYraqre8tXABdU1TW95SuB71fVXcd5vm3ANoCpqanz5ubmhvoEDh8+zJo1a4Z67DiZazD95tpzYGVvUjN1Gjz+9Iq+JJvOXPr+DJP6fYTJzdZirs2bN++uqumlthvb1SuraucSH98B7ACYnp6umZmZoV5nfn6eYR87TuYaTL+5rlzhq1du33SE6/es7EVeH3vDzJLbTOr3ESY328mcaznv4APAWYuW1/bWSVqGfi7FvHN29QokUSuWM73yfuDsJOuTnApcDtw5yBN4z1hJGr9+p1feAtwHnJNkf5KrquoIcA1wN7AXuLWqHh7kxb1nrCSNX7+zbrYeZ/0uYNdIE0mSRqrTSyA4dCNJ49dp0Tt0I0nj50XNJKlxKztB+BhJtgBbNmzY0GUMTYA9Bw6t+Bx56WTh0I0kNc6hG0lqnEUvSY1zeqUkNc4xeklqnEM3ktS4TqdXShpOv9NRH7vu4hVIo0nnGL0kNc4xeklqnGP0ktQ4i16SGmfRS1LjLHpJapyzbiSpcc66kaTGOXQjSY2z6CWpcRa9JDXOa91o7Nb1cU2W7ZtWIIh0knKPXpIa5/RKSWqc0yslqXGO0UsN6+f4CHjd+tZZ9BpavyUiqVsejJWkxln0ktQ4i16SGmfRS1LjLHpJapxFL0mN88xYSWqcZ8ZKUuMcupGkxln0ktQ4i16SGue1biT1dd0iL3x24nKPXpIaZ9FLUuMseklqnEUvSY2z6CWpcRa9JDXOopekxo18Hn2SXwDeDpwB/ENVfWTUryFp5fV7j+Cds6vHnESD6muPPslNSQ4meeiY9bNJHkmyL8m1AFW1t6reArweePXoI0uSBtHv0M1OYHbxiiSrgBuBi4CNwNYkG3sfex3wWWDXyJJKkobSV9FX1b3Ak8esPh/YV1WPVtUzwBxwaW/7O6vqIuANowwrSRpcqqq/DZN1wF1VdW5v+TJgtqqu7i1fAVwA3Ab8HvA84BtVdeNxnm8bsA1gamrqvLm5uaE+gcOHD7NmzZqhHjtOk5rr4JOHePzppbfbdObS9wjYc2B0N4yZOo2+cq00cw1u/emrJvK9P6k/k8vJtXnz5t1VNb3UdiM/GFtV88B8H9vtAHYATE9P18zMzFCvNz8/z7CPHadJzfXhm+/g+j19fNv3PNXHs43u7bN905H+cq0wcw1u5+zqiXzvT+rP5ErkWs70ygPAWYuW1/bW9c1bCUrS+C2n6O8Hzk6yPsmpwOXAnYM8gbcSlKTx63d65S3AfcA5SfYnuaqqjgDXAHcDe4Fbq+rh8UWVJA2jr0G+qtp6nPW7WMYUyiRbgC0bNmwY9ikkSUvo9BIIDt1I0vh5rRtJapxFL0mN63QirmP0Unv2HDjElUtcAM0bja8sx+glqXEO3UhS4zotes+MlaTxc+hGkhrn0I0kNc6il6TGWfSS1Djn0Utacf3eaNz59qPhwVhJapxDN5LUOItekhpn0UtS4zwYewLwwJWk5fBgrCQ1rtM9eo1WP3v+2zetQBBJE8UxeklqnEUvSY2z6CWpcRa9JDXOopekxnmHKUlqnPPoJalxDt1IUuM8YUrSxOrnJEAv/bE09+glqXEWvSQ1zqKXpMZZ9JLUOItekhpn0UtS4zwzVpIa55mxktQ4T5gakz0HDnFln/d6laRxsuglndD6OXsWYOfs6jEnmVwejJWkxln0ktQ4i16SGucYvaSTQj8TJFq9EqZ79JLUOItekhpn0UtS4yx6SWqcRS9JjRvLrJskvwtcDLwQ+HhVfX4cryNJWlrfRZ/kJuAS4GBVnbto/SxwA7AK+FhVXVdVnwY+neTFwAcBi17SSWWSLs0wyNDNTmB28Yokq4AbgYuAjcDWJBsXbfInvY9LkjrS9x59Vd2bZN0xq88H9lXVowBJ5oBLk+wFrgM+V1UPjiirJI1Vv3vhJ5pUVf8bLxT9XUeHbpJcBsxW1dW95SuAC4BvAW8C7ge+VlUf/THPtQ3YBjA1NXXe3NzcUJ/A4cOHWbNmzVCPHaeDTx7i8ae7TvGjpk7DXAMw1+AmNduk5lp/+qqhO2zz5s27q2p6qe3GcjC2qj4EfGiJbXYAOwCmp6drZmZmqNean59n2MeO04dvvoPr90zeFSa2bzpirgGYa3CTmm1Sc+2cXT32DlvuZ30AOGvR8treur4k2QJs2bBhwzJjjMYo/2zbvmlkTyVJy7LcefT3A2cnWZ/kVOBy4M5+H+ytBCVp/Pou+iS3APcB5yTZn+SqqjoCXAPcDewFbq2qh8cTVZI0jEFm3Ww9zvpdwK5hXnzShm4kqUWdHpmoqs8An5menn7zsM/R7024W73OtCQtxWvdSFLjLHpJalynRZ9kS5Idhw4d6jKGJDWt06J3eqUkjZ9DN5LUOIduJKlxDt1IUuMcupGkxk3epdzGpNXrTEvSUtyjl6TGeTBWkhrnwVhJapxDN5LUOItekhpn0UtS4zwYK0mN82CsJDXOoRtJalyqqusMJHkC+LchH34G8P0RxhkVcw3GXIOZ1FwwudlazPWzVfWSpTaaiKJfjiQPVNV01zmOZa7BmGswk5oLJjfbyZzLoRtJapxFL0mNa6Hod3Qd4DjMNRhzDWZSc8HkZjtpc53wY/SSpOfWwh69JOk5NFH0SV6e5MtJvpbkgSTnd53pqCRvS/LPSR5O8v6u8yyWZHuSSnJG11kAknyg97X6RpK/TfKijvPMJnkkyb4k13aZ5agkZyW5J8k3e++pt3edabEkq5J8NcldXWc5KsmLktzWe2/tTfIrXWcCSPLO3vfwoSS3JHn+uF6riaIH3g+8r6peDvxZb7lzSTYDlwIvq6pfBD7YcaQfSnIW8Frg211nWeQLwLlV9UvAt4B3dxUkySrgRuAiYCOwNcnGrvIscgTYXlUbgVcBfzghuY56O7C36xDHuAH4u6r6eeBlTEC+JGcCfwRMV9W5wCrg8nG9XitFX8ALe/8/Hfhuh1kWeytwXVX9L0BVHew4z2J/AfwxC1+7iVBVn6+qI73FLwNrO4xzPrCvqh6tqmeAORZ+aXeqqr5XVQ/2/v9fLJTWmd2mWpBkLXAx8LGusxyV5HTg14GPA1TVM1X1g25T/dApwGlJTgFewBh7q5WifwfwgSTfYWGvubM9wWO8FPi1JF9J8o9JXtl1IIAklwIHqurrXWd5Dn8AfK7D1z8T+M6i5f1MSKEelWQd8MvAV7pN8kN/ycLOw7NdB1lkPfAE8Ne9IaWPJVnddaiqOsBCV30b+B5wqKo+P67XO2FuDp7k74Gf+TEfei9wIfDOqvpUktez8Nv7NROQ6xTgp1j4E/uVwK1Jfq5WYKrTErnew8KwzYp7rlxVdUdvm/eyMERx80pmO5EkWQN8CnhHVf3nBOS5BDhYVbuTzHSdZ5FTgFcAb6uqryS5AbgW+NMuQyV5MQt/Ia4HfgB8Mskbq+oT43i9E6boq+q4xZ3kb1gYGwT4JCv4p+MSud4K3N4r9n9K8iwL17V4oqtcSTax8Ob6ehJYGB55MMn5VfXvXeValO9K4BLgwpX4hfgcDgBnLVpe21vXuSQ/yULJ31xVt3edp+fVwOuS/A7wfOCFST5RVW/sONd+YH9VHf2r5zYWir5rrwH+taqeAEhyO/CrwFiKvpWhm+8Cv9H7/28C/9JhlsU+DWwGSPJS4FQ6vqhSVe2pqp+uqnVVtY6FH4RXrETJLyXJLAt/+r+uqv674zj3A2cnWZ/kVBYOlN3ZcSay8Nv548DeqvrzrvMcVVXvrqq1vffU5cAXJ6Dk6b2vv5PknN6qC4FvdhjpqG8Dr0rygt739ELGeJD4hNmjX8KbgRt6BzX+B9jWcZ6jbgJuSvIQ8Azwpo73UifdXwHPA77Q+2vjy1X1li6CVNWRJNcAd7MwI+Kmqnq4iyzHeDVwBbAnydd6695TVbs6zDTp3gbc3PuF/Sjw+x3noTeMdBvwIAvDlF9ljGfIemasJDWulaEbSdJxWPSS1DiLXpIaZ9FLUuMseklqnEUvSY2z6CWpcRa9JDXu/wBqEWUAFXzF3wAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 2\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEdZJREFUeJzt3X2MHPV9x/HPp6Y8yJcaEqcXals50Fk0lPujeAUhiapzE8rxYEhTWuFaaaxCrrRy1UqW2otStVH/CWlLpCJcoROxHKTUF0oJ9VPEQ8OV/AHUOAIO49AY5AqfqB0a6dqjKOk13/6xc87m7L2bfZidud+9X9LpdmZnZz87u/vd3/7mtzOOCAEA0vUzZQcAABSLQg8AiaPQA0DiKPQAkDgKPQAkjkIPAImj0ANA4ij0AJA4Cj0AJO68sgNI0tq1a2NgYKCt277zzjtavXp1dwN1AblaQ67WVDWXVN1sKeY6cuTI2xHx/iUXjIjS/zZt2hTtevrpp9u+bZHI1RpytaaquSKqmy3FXJJeiBw1lq4bAEgchR4AEkehB4DEUegBIHEUegBIHIUeABLX9UJve9j2t20/YHu42+sHALQmV6G3vdv2aduvLJg/Yvs128dtj2WzQ9KspAslnexuXABAq/L+MnaPpPslPTQ/w/YqSbskXa96QT9se5+kb0fEv9jul/RlSdu6mhgo2MDYwTOX94xU75eUQKscOU8ObntA0oGIuCqbvk7SFyLihmz6c5IUEV/Mps+X9PcRcXuT9Y1KGpWk/v7+TRMTE209gNnZWfX19bV12yKRqzVVyjU1PXPmcv9F0ql365eH1q0pKdHZqrS9FqpqthRzbd68+UhE1JZarpNj3ayT9GbD9ElJ19r+lKQbJF2s+reAc4qIcUnjklSr1WJ4eLitEJOTk2r3tkUiV2uqlGt7Q4t+59Cc7p2qv01ObBsuKdHZqrS9FqpqtpWcq+sHNYuIRyU92u31AgDa08mom2lJGxqm12fzcrO9xfb4zMzM0gsDANrSSaE/LGmj7cuy/vg7JO1rZQURsT8iRtesqU7fJwCkJu/wyr2SnpV0he2Ttu+MiDlJOyQ9LumYpIcj4mhxUQEA7cjVRx8RW5vMPyTpULt3bnuLpC2Dg4PtrgIAsIRSD4FA1w0AFI9j3QBA4ij0AJC4Ugs9wysBoHj00QNA4ui6AYDEUegBIHH00QNA4uijB4DE0XUDAImj0ANA4ij0AJA4dsYCQOLYGQsAiaPrBgASR6EHgMRR6AEgcRR6AEgco24AIHGMugGAxNF1AwCJO6/sAEAVDIwdLDsCUBgKPZDTwg+DE/fcXFISoDV03QBA4ij0AJA4Cj0AJI5x9ACQOMbRA0Di6LoBgMRR6AEgcRR6AEgchR4AEkehB4DEUegBIHEUegBIHIUeABJHoQeAxHEIBABIHIdAAIDE0XUDAImj0ANA4ij0AJA4Cj0AJI5CDwCJo9ADQOLOKzsAUJaBsYNdu/2Je27uNA5QGFr0AJA4Cj0AJI5CDwCJo9ADQOIo9ACQuEIKve3Vtl+wfUsR6wcA5Jer0Nvebfu07VcWzB+x/Zrt47bHGq76U0kPdzMoAKA9eVv0eySNNM6wvUrSLkk3SrpS0lbbV9q+XtKrkk53MScAoE25fjAVEc/YHlgw+xpJxyPiDUmyPSHpNkl9klarXvzftX0oIn7ctcQAgJY4IvItWC/0ByLiqmz6dkkjEXFXNv1pSddGxI5seruktyPiQJP1jUoalaT+/v5NExMTbT2A2dlZ9fX1tXXbIpGrNWXkmppe+sxm/RdJp95del1D63p78pyqPo9SdbOlmGvz5s1HIqK21HKFHQIhIvYscf24pHFJqtVqMTw83Nb9TE5Oqt3bFolcrSkj1/Ych0DYOTSne6eWfpuc2DbchUT5VfV5lKqbbSXn6mTUzbSkDQ3T67N5AIAK6aTQH5a00fZlts+XdIekfa2sgJODA0Dx8g6v3CvpWUlX2D5p+86ImJO0Q9Ljko5JejgijrZy55wcHACKl3fUzdYm8w9JOtTVRACArir1EAh03QBA8Uot9HTdAEDxOKgZACSOQg8AiSv1nLG2t0jaMjg4WGYMrCCdnic2z3o5fyyqhj56AEgcXTcAkDgKPQAkjnH0AJA4+ugBIHF03QBA4ij0AJA4Cj0AJI6dsQCQOHbGAkDi6LoBgMRR6AEgcRR6AEgchR4AEseoGwBIHKNuACBxpZ54BOiFok42kuf+OAkJqoA+egBIHIUeABJHoQeAxFHoASBxDK8EgMQxvBIAEkfXDQAkjkIPAImj0ANA4ij0AJA4Cj0AJI5CDwCJo9ADQOIo9ACQOAo9ACSu1OPR294iacvg4GCZMYDCcGx6VEGphT4i9kvaX6vVPltmDqSn1ycbAaqMrhsASByFHgASR6EHgMRR6AEgcRR6AEgchR4AEkehB4DEUegBIHEUegBIXKm/jAVWEg6HgLLQogeAxFHoASBxdN0gGRzIDDi3rrfobX/I9gO2H7H9+91ePwCgNbkKve3dtk/bfmXB/BHbr9k+bntMkiLiWETcLem3JH20+5EBAK3I26LfI2mkcYbtVZJ2SbpR0pWSttq+MrvuVkkHJR3qWlIAQFtyFfqIeEbSDxbMvkbS8Yh4IyJ+JGlC0m3Z8vsi4kZJ27oZFgDQOkdEvgXtAUkHIuKqbPp2SSMRcVc2/WlJ10p6RNKnJF0g6eWI2NVkfaOSRiWpv79/08TERFsPYHZ2Vn19fW3dtkjkak03ck1Nz3QpzU/0XySderfrq9XQujUd3b6qz6NU3Wwp5tq8efORiKgttVzXR91ExKSkyRzLjUsal6RarRbDw8Nt3d/k5KTavW2RyNWabuTaXsCom51Dc7p3qvuD005sG+7o9lV9HqXqZlvJuToZdTMtaUPD9PpsHgCgQjop9IclbbR9me3zJd0haV8rK7C9xfb4zEz3v3IDAOryDq/cK+lZSVfYPmn7zoiYk7RD0uOSjkl6OCKOtnLnEbE/IkbXrOmsvxIA0FyuzseI2Npk/iExhBIlWq6/huUAZ+ilUo91Q9cNABSv1EJP1w0AFI+jVwJA4ui6AYDE0XUDAImj6wYAEseJR7DsLNchlUBZSi30trdI2jI4OFhmDKBUjKlH0eijB4DE0UcPAImj0ANA4ij0AJA4fjAFAIkrddRNROyXtL9Wq322zByoPoZUAu1jHD1QIQy1RBHooweAxNGiByqK1j26hRY9ACSOQyCgstgBC3QHo25QKRR3oPvougGAxFHoASBxjLoBlgFG4KATtOgBIHEUemCZGRg7qKnpGXZcIze6blAKuiKA3mEcPUo3MHZQO4fmtJ0Wasv4wEQejKMHEkHRRzN03aBn6FMGysHOWABIHIUeABJHoQeAxFHoASBx7IxF17HTtXzNngNG46xMtOgBIHG06IEVinH3KwctegBIHIdAgKTOW3f0yy8PeZ4nWvrp4RAIaAlFAFh+6KNfYaamZ84cPIxCjXl8I0sbffQAkDha9GgbrUBgeaDQY0kUdGB5o9DjLBR2zOMXtmmgjx4AEkeLfgVobJXtHCoxCJKx2Le+PSOrz7kc3wLKQ6Ff5jp5I9FFgyI0DuHNgw+D4lHol4k8bwYKN5YLXqu9RaEHUBmttu75NpAPhR5AT9CKLw+FvsJ4YwDohhVd6BcWUr76AemZf5/vHJrTcLlRSlNIobf9SUk3S/o5SV+JiCeKuJ8U0YoHFsd7pHW5C73t3ZJukXQ6Iq5qmD8i6W8lrZL0YETcExGPSXrM9iWS/kYShR5AS9jR2j2ttOj3SLpf0kPzM2yvkrRL0vWSTko6bHtfRLyaLfJn2fXJa/aiLOLFSosGaM9K/fBwRORf2B6QdGC+RW/7OklfiIgbsunPZYvek/09GRFPNVnXqKRRServ7980MTHR1gOYnZ1VX19fW7edmp75qemhdWvaWs/CdQ2tW3Mm18L5eXIUqf8i6dS7Pbu73MjVmqrmksrLttT7a2GuPO/HTmpCXp3UsM2bNx+JiNpSy3XaR79O0psN0yclXSvpDyV9QtIa24MR8cDCG0bEuKRxSarVajE8PNxWgMnJSS1122af4gt/vXdiW3sZFq7rxLbhM7l+6j6m3smVo0g7h+Z071T19sGTqzVVzSWVl63Z+3d7w87YxlxLLb/YMt2Up4Z1qpBnIyLuk3RfEevuVFW6PaqSA0D6Oi3005I2NEyvz+bl0o2Tg/fi1HgrtV8PQBo6LfSHJW20fZnqBf4OSb+d98ZVPjk4LW5geeEAf821Mrxyr6RhSWttn5T0FxHxFds7JD2u+vDK3RFxtJCky8jA2EHtHJrrad87gJ/oVuFO5cQruQt9RGxtMv+QpEPt3Hk3um7KlHorAEjZSnr/lrrbvsium6KexJX04gDQPWXu66vm+Kw2UYQB9MJyG6DBOWMBIHGltuir1kfPNwIArcpz+JOyJdtHDwBV1fgh0Hgy9aIk1UcPAGWqUiu+EX30AJC4Ugu97S22x2dmenf0RgBYaUot9BGxPyJG16wp/lCgALBS0XUDAImj0ANA4ij0AJA4dsYCQOLYGQsAiaPrBgAS54goO4Nsf1/Sv7d587WS3u5inG4hV2vI1Zqq5pKqmy3FXB+MiPcvtVAlCn0nbL8QEbWycyxErtaQqzVVzSVVN9tKzkXXDQAkjkIPAIlLodCPlx2gCXK1hlytqWouqbrZVmyuZd9HDwBYXAotegDAIpZdobf917a/a/tl29+wfXGT5UZsv2b7uO2xHuT6TdtHbf/YdtM96LZP2J6y/aLtFyqUq9fb6722n7T9vez/JU2W+79sW71oe1+BeRZ9/LYvsP317PrnbQ8UlaXFXNttf79hG93Vo1y7bZ+2/UqT6237viz3y7avrkiuYdszDdvrz3uQaYPtp22/mr0X/+gcyxS7vSJiWf1J+jVJ52WXvyTpS+dYZpWk1yVdLul8SS9JurLgXB+SdIWkSUm1RZY7IWltD7fXkrlK2l5/JWksuzx2rucxu262B9toyccv6Q8kPZBdvkPS1yuSa7uk+3v1emq431+RdLWkV5pcf5Okb0qypA9Ler4iuYYlHejxtrpU0tXZ5fdI+rdzPI+Fbq9l16KPiCciYi6bfE7S+nMsdo2k4xHxRkT8SNKEpNsKznUsIl4r8j7akTNXz7dXtv6vZpe/KumTBd/fYvI8/sa8j0j6uG1XIFcpIuIZST9YZJHbJD0Udc9Jutj2pRXI1XMR8VZEfCe7/N+Sjklat2CxQrfXsiv0C/yu6p+CC62T9GbD9EmdvWHLEpKesH3E9mjZYTJlbK/+iHgru/wfkvqbLHeh7RdsP2e7qA+DPI//zDJZQ2NG0vsKytNKLkn6jezr/iO2NxScKa8qvwevs/2S7W/a/qVe3nHW5ffLkp5fcFWh26uSJwe3/ZSkD5zjqs9HxD9ly3xe0pykr1UpVw4fi4hp2z8v6Unb381aIWXn6rrFcjVORETYbjb864PZ9rpc0rdsT0XE693Ouoztl7Q3In5o+/dU/9bxqyVnqrLvqP6amrV9k6THJG3sxR3b7pP0j5L+OCL+qxf3Oa+ShT4iPrHY9ba3S7pF0scj6+BaYFpSY8tmfTav0Fw51zGd/T9t+xuqfz3vqNB3IVfPt5ftU7YvjYi3sq+op5usY357vWF7UvXWULcLfZ7HP7/MSdvnSVoj6T+7nKPlXBHRmOFB1fd9VEEhr6lONRbYiDhk++9sr42IQo+BY/tnVS/yX4uIR8+xSKHba9l13dgekfQnkm6NiP9psthhSRttX2b7fNV3nhU2YiMv26ttv2f+suo7ls85OqDHythe+yR9Jrv8GUlnffOwfYntC7LLayV9VNKrBWTJ8/gb894u6VtNGhk9zbWgH/dW1ft/q2CfpN/JRpN8WNJMQ1ddaWx/YH7fiu1rVK+BhX5gZ/f3FUnHIuLLTRYrdnv1cu9zN/4kHVe9L+vF7G9+JMQvSDrUsNxNqu/dfl31Loyic/266v1qP5R0StLjC3OpPnripezvaFVylbS93ifpnyV9T9JTkt6bza9JejC7/BFJU9n2mpJ0Z4F5znr8kv5S9QaFJF0o6R+y19+/Srq86G2UM9cXs9fSS5KelvSLPcq1V9Jbkv43e33dKeluSXdn11vSriz3lBYZidbjXDsattdzkj7Sg0wfU33f3MsNdeumXm4vfhkLAIlbdl03AIDWUOgBIHEUegBIHIUeABJHoQeAxFHoASBxFHoASByFHgAS9/+PWhCoUXVrKwAAAABJRU5ErkJggg==\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYkAAAD8CAYAAACCRVh7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADuZJREFUeJzt3H+s3Xddx/Hny143+ZHsZxmjXb3VNZKiEcjJgIBmYWN0CnTRRTc1NGSk/7DID4kWSRwMTJhBhoRJ0mzDuhg2MlFuJLqUDaIxOne6EaGM2TrAtnZboWM6iczK2z/Ot3q4ns/au3PuTu89z0fS3PP9fj/n3vd337s+e77ntqkqJEka5YemPYAk6dRlJCRJTUZCktRkJCRJTUZCktRkJCRJTUZCktRkJCRJTUZCktQ0N+0Bnolzzz235ufnpz2GJK0oe/bs+VZVrV3Kc1ZkJObn5+n3+9MeQ5JWlCTfXOpzvN0kSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkpolEIsmWJA8l2Z9kx4jjpye5ozt+b5L5Rcc3JHkyybsnMY8kaTLGjkSSNcBNwOXAZuDqJJsXLbsGeLyqLgRuBG5YdPwjwF+OO4skabIm8UriImB/VT1cVU8BtwNbF63ZCuzqHt8JXJIkAEmuAL4O7J3ALJKkCZpEJNYBB4a2D3b7Rq6pqmPAE8A5SZ4P/Bbw/gnMIUmasGm/cf0+4MaqevJEC5NsT9JP0j9y5MjyTyZJYm4Cn+MQcMHQ9vpu36g1B5PMAWcA3wZeAVyZ5PeAM4HvJ/nPqvr44i9SVTuBnQC9Xq8mMLck6QQmEYn7gE1JNjKIwVXAryxaswBsA/4OuBK4p6oK+JnjC5K8D3hyVCAkSdMxdiSq6liSa4G7gDXArVW1N8n1QL+qFoBbgNuS7AeOMgiJJOkUl8Ef6FeWXq9X/X5/2mNI0oqSZE9V9ZbynGm/cS1JOoUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDVNJBJJtiR5KMn+JDtGHD89yR3d8XuTzHf7X5dkT5Ivdx9fO4l5JEmTMXYkkqwBbgIuBzYDVyfZvGjZNcDjVXUhcCNwQ7f/W8Abq+qngG3AbePOI0manEm8krgI2F9VD1fVU8DtwNZFa7YCu7rHdwKXJElVPVBV/9rt3ws8J8npE5hJkjQBk4jEOuDA0PbBbt/INVV1DHgCOGfRml8E7q+q701gJknSBMxNewCAJC9hcAvqsqdZsx3YDrBhw4ZnaTJJmm2TeCVxCLhgaHt9t2/kmiRzwBnAt7vt9cCfAW+uqn9ufZGq2llVvarqrV27dgJjS5JOZBKRuA/YlGRjktOAq4CFRWsWGLwxDXAlcE9VVZIzgc8BO6rqbycwiyRpgsaORPcew7XAXcCDwKeram+S65O8qVt2C3BOkv3Au4DjPyZ7LXAh8DtJvtT9esG4M0mSJiNVNe0ZlqzX61W/35/2GJK0oiTZU1W9pTzHv3EtSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkJiMhSWoyEpKkpolEIsmWJA8l2Z9kx4jjpye5ozt+b5L5oWPv6fY/lOT1k5hHkjQZY0ciyRrgJuByYDNwdZLNi5ZdAzxeVRcCNwI3dM/dDFwFvATYAvxh9/kkSaeASbySuAjYX1UPV9VTwO3A1kVrtgK7usd3ApckSbf/9qr6XlV9HdjffT5J0ilgEpFYBxwY2j7Y7Ru5pqqOAU8A55zkcyVJU7Ji3rhOsj1JP0n/yJEj0x5HkmbCJCJxCLhgaHt9t2/kmiRzwBnAt0/yuQBU1c6q6lVVb+3atRMYW5J0IpOIxH3ApiQbk5zG4I3ohUVrFoBt3eMrgXuqqrr9V3U//bQR2AT8wwRmkiRNwNy4n6CqjiW5FrgLWAPcWlV7k1wP9KtqAbgFuC3JfuAog5DQrfs08FXgGPC2qvrvcWeSJE1GBn+gX1l6vV71+/1pjyFJK0qSPVXVW8pzVswb15KkZ5+RkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUtNYkUhydpLdSfZ1H89qrNvWrdmXZFu377lJPpfka0n2JvnQOLNIkiZv3FcSO4C7q2oTcHe3/QOSnA1cB7wCuAi4bigmH66qFwMvA16d5PIx55EkTdC4kdgK7Ooe7wKuGLHm9cDuqjpaVY8Du4EtVfXdqvoCQFU9BdwPrB9zHknSBI0bifOq6nD3+BHgvBFr1gEHhrYPdvv+V5IzgTcyeDUiSTpFzJ1oQZLPAy8ccei9wxtVVUlqqQMkmQM+BXysqh5+mnXbge0AGzZsWOqXkSQ9AyeMRFVd2jqW5NEk51fV4STnA4+NWHYIuHhoez3wxaHtncC+qvroCebY2a2l1+stOUaSpKUb93bTArCte7wN+OyINXcBlyU5q3vD+rJuH0k+CJwBvGPMOSRJy2DcSHwIeF2SfcCl3TZJekluBqiqo8AHgPu6X9dX1dEk6xncstoM3J/kS0neOuY8kqQJStXKu3PT6/Wq3+9PewxJWlGS7Kmq3lKe49+4liQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUpORkCQ1GQlJUtNYkUhydpLdSfZ1H89qrNvWrdmXZNuI4wtJvjLOLJKkyRv3lcQO4O6q2gTc3W3/gCRnA9cBrwAuAq4bjkmSXwCeHHMOSdIyGDcSW4Fd3eNdwBUj1rwe2F1VR6vqcWA3sAUgyfOBdwEfHHMOSdIyGDcS51XV4e7xI8B5I9asAw4MbR/s9gF8APh94LtjziFJWgZzJ1qQ5PPAC0cceu/wRlVVkjrZL5zkpcCPV9U7k8yfxPrtwHaADRs2nOyXkSSN4YSRqKpLW8eSPJrk/Ko6nOR84LERyw4BFw9trwe+CLwK6CX5RjfHC5J8saouZoSq2gnsBOj1eicdI0nSMzfu7aYF4PhPK20DPjtizV3AZUnO6t6wvgy4q6o+UVUvqqp54DXAP7UCIUmajnEj8SHgdUn2AZd22yTpJbkZoKqOMnjv4b7u1/XdPknSKS5VK+/OTa/Xq36/P+0xJGlFSbKnqnpLeY5/41qS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1GQkJElNRkKS1JSqmvYMS5bkCPDNZ/j0c4FvTXCclWSWzx1m+/xn+dxhts9/+Nx/tKrWLuXJKzIS40jSr6retOeYhlk+d5jt85/lc4fZPv9xz93bTZKkJiMhSWqaxUjsnPYAUzTL5w6zff6zfO4w2+c/1rnP3HsSkqSTN4uvJCRJJ2lmIpFkS5KHkuxPsmPa8yy3JBck+UKSrybZm+Tt3f6zk+xOsq/7eNa0Z10uSdYkeSDJX3TbG5Pc230P3JHktGnPuFySnJnkziRfS/JgklfNyrVP8s7ue/4rST6V5EdW87VPcmuSx5J8ZWjfyGudgY91/x3+McnLT/T5ZyISSdYANwGXA5uBq5Nsnu5Uy+4Y8BtVtRl4JfC27px3AHdX1Sbg7m57tXo78ODQ9g3AjVV1IfA4cM1Upnp2/AHwV1X1YuCnGfx3WPXXPsk64NeBXlX9JLAGuIrVfe3/CNiyaF/rWl8ObOp+bQc+caJPPhORAC4C9lfVw1X1FHA7sHXKMy2rqjpcVfd3j/+dwW8S6xic965u2S7giulMuLySrAd+Hri52w7wWuDObslqPvczgJ8FbgGoqqeq6jvMyLUH5oDnJJkDngscZhVf+6r6a+Doot2ta70V+OMa+HvgzCTnP93nn5VIrAMODG0f7PbNhCTzwMuAe4Hzqupwd+gR4LwpjbXcPgr8JvD9bvsc4DtVdazbXs3fAxuBI8Anu9ttNyd5HjNw7avqEPBh4F8YxOEJYA+zc+2Pa13rJf9eOCuRmFlJng/8KfCOqvq34WM1+NG2VffjbUneADxWVXumPcuUzAEvBz5RVS8D/oNFt5ZW8bU/i8GfljcCLwKex/+/FTNTxr3WsxKJQ8AFQ9vru32rWpIfZhCIP6mqz3S7Hz3+8rL7+Ni05ltGrwbelOQbDG4tvpbBPfozu1sQsLq/Bw4CB6vq3m77TgbRmIVrfynw9ao6UlX/BXyGwffDrFz741rXesm/F85KJO4DNnU/4XAagzeyFqY807Lq7sHfAjxYVR8ZOrQAbOsebwM++2zPttyq6j1Vtb6q5hlc63uq6leBLwBXdstW5bkDVNUjwIEkP9HtugT4KjNw7RncZnplkud2/w8cP/eZuPZDWtd6AXhz91NOrwSeGLotNdLM/GW6JD/H4D71GuDWqvrdKY+0rJK8Bvgb4Mv8333532bwvsSngQ0M/iXdX6qqxW96rRpJLgbeXVVvSPJjDF5ZnA08APxaVX1vmvMtlyQvZfCm/WnAw8BbGPyhcNVf+yTvB36ZwU/4PQC8lcF991V57ZN8CriYwb/2+ihwHfDnjLjWXTg/zuAW3HeBt1RV/2k//6xEQpK0dLNyu0mS9AwYCUlSk5GQJDUZCUlSk5GQJDUZCUlSk5GQJDUZCUlS0/8ARSXmFuwInocAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEJNJREFUeJzt3X+s3Xddx/Hny83BaKFowJqsi53pNq2rILtsKFFvHeido8yYBVfHQnVbA3EIpIkMUBP+W8CpIIukgdkQlt2MMWEbxYFhdfwxcOv40W112MwJLbiOINXidGn29o97ijd13T3n9JzzPf3c5yNp0u/3fs/3+7rtva/7vZ/v53y/qSokSe36ka4DSJLGy6KXpMZZ9JLUOItekhpn0UtS4yx6SWqcRS9JjbPoJalxFr0kNe7ULg+eZBOw6YUvfOE155xzzlD7+MEPfsCKFStGG2wEzDUYcw1mWnPB9GZrMdfu3bu/W1UvXXLDqur8z/nnn1/Duueee4Z+7TiZazDmGsy05qqa3mwt5gIeqD461qEbSWqcRS9JjbPoJalxFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuM6fWesNO32HDjElus+s+R2j19/yQTSSMPxjF6SGmfRS1LjRl70SWaTfDHJh5PMjnr/kqTB9FX0SW5KcjDJQ8esn0vyaJJ9Sa7rrS7gMPB8YP9o40qSBtXvGf0OYG7xiiSnADcCFwPrgc1J1gNfrKqLgXcC7x1dVEnSMPoq+qq6F/jeMasvAPZV1WNV9TQwD1xaVc/0Pv7vwPNGllSSNJQs3NK4jw2TtcBdVXVeb/kyYK6qru4tXwlcCHwB+A3gxcBfV9Wu4+xvK7AVYPXq1efPz88P9QkcPnyYlStXDvXacTLXYKY118HvHeKJp5bebsMZq8YfZpFp/feC6c3WYq6NGzfurqqZpbYb+Tz6qroduL2P7bYD2wFmZmZqdnZ2qOPt2rWLYV87TuYazLTm+qubP80Ne5b+Nnn8itnxh1lkWv+9YHqzLedcJzLr5gBw5qLlNb11fUuyKcn2Q4cOnUAMSdJzOZGivx84O8lZSU4DLgfuGGQHVXVnVW1dtWqyv/ZK0nLS7/TKW4D7gHOT7E9yVVUdAa4F7gb2ArdW1cPjiypJGkZfY/RVtfk463cCO4c9eJJNwKZ169YNuwtJ0hI6vQWCQzeSNH7e60aSGtfpbYodulGX1vZx++FtGyYQRBozh24kqXEO3UhS4yx6SWpcp0XvO2Mlafwco5ekxjl0I0mNs+glqXGO0UtS4xyjl6TGdfrOWGkc+nnHaxfHfPz6SyaQRPr/HKOXpMZZ9JLUOC/GSlLjvBgrSY1z6EaSGmfRS1LjLHpJapxFL0mNs+glqXFOr5Skxjm9UpIa59CNJDXOopekxnn3SmlC+r2rpne51Kh5Ri9JjbPoJalxFr0kNc6il6TGWfSS1DjfGStJjfOdsZLUOIduJKlxFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuMseklqnEUvSY3zfvQ6qfR7T3dJ/8czeklq3FiKPsmKJA8ked049i9J6l9fRZ/kpiQHkzx0zPq5JI8m2ZfkukUfeidw6yiDSpKG0+8Z/Q5gbvGKJKcANwIXA+uBzUnWJ3kt8AhwcIQ5JUlD6utibFXdm2TtMasvAPZV1WMASeaBS4GVwAoWyv+pJDur6pmRJZYkDSRV1d+GC0V/V1Wd11u+DJirqqt7y1cCF1bVtb3lLcB3q+qu4+xvK7AVYPXq1efPz88P9QkcPnyYlStXDvXacTLXYPrNtefAZJ9dsPp0eOKpiR6SDWcsfdvuaf1/hOnN1mKujRs37q6qmaW2G9v0yqrascTHtwPbAWZmZmp2dnao4+zatYthXztO5hpMv7m2THh65bYNR7hhz2RnIT9+xeyS20zr/yNMb7blnOtEZt0cAM5ctLymt65vPmFKksbvRIr+fuDsJGclOQ24HLhjkB34hClJGr9+p1feAtwHnJtkf5KrquoIcC1wN7AXuLWqHh5fVEnSMPqddbP5OOt3AjuHPXiSTcCmdevWDbsLSdISfDi4JDXOe91IUuM6LXpn3UjS+Dl0I0mNc+hGkhrng0ekKdPPw1V2zK2YQBK1wjF6SWqcY/SS1DjH6CWpcY7RayrsOXBo4nemlJYLx+glqXGO0UtS4xyjl6TGWfSS1DiLXpIaZ9FLUuOcdSNJjXPWjSQ1zqEbSWqcRS9JjbPoJalxFr0kNa7Tm5ol2QRsWrduXZcxpJNOvzeBe/z6SyaQRtPOWTeS1DiHbiSpcRa9JDXOopekxln0ktQ4i16SGmfRS1LjfDi4xm5tH/O9t22YQBBpmfKMXpIa5/3oJalxvjNWkhrn0I0kNc6il6TGWfSS1DiLXpIa5zx6qWH9vIcBvG996zyjl6TGWfSS1DiLXpIaZ9FLUuO8GKuh9XuhT1K3Rn5Gn+Rnk3w4yW1J3jLq/UuSBtNX0Se5KcnBJA8ds34uyaNJ9iW5DqCq9lbVm4E3AK8efWRJ0iD6PaPfAcwtXpHkFOBG4GJgPbA5yfrex14PfAbYObKkkqSh9FX0VXUv8L1jVl8A7Kuqx6rqaWAeuLS3/R1VdTFwxSjDSpIGl6rqb8NkLXBXVZ3XW74MmKuqq3vLVwIXArcBvw08D/h6Vd14nP1tBbYCrF69+vz5+fmhPoHDhw+zcuXKoV47Tssh154Do3uOwOrT4YmnRra7kVkuuTacMbpbhS+Hr/1ROpFcGzdu3F1VM0ttN/JZN1W1C9jVx3bbge0AMzMzNTs7O9Txdu3axbCvHaflkGvLCGfdbNtwhBv2TN8ksOWS6/ErZke2r+XwtT9Kk8h1IrNuDgBnLlpe01snSZoiJ1L09wNnJzkryWnA5cAdg+zARwlK0vj1O73yFuA+4Nwk+5NcVVVHgGuBu4G9wK1V9fAgB/dRgpI0fn0N8lXV5uOs38kJTKFMsgnYtG7dumF3IUlagg8Hl6TGeVMzSWqcRS9Jjeu06J11I0nj5xi9JDVu+t7yJ2ni+nm2gA8QP3k5dCNJjXPoRpIa56wbSWqcRS9JjbPoJalxXoyVpMZ5MVaSGufQjSQ1zqKXpMZZ9JLUOC/GSlLjvBgrSY1z6EaSGufdKyX1pZ87XALsmFsx5iQalEW/zOw5cIgtfX7DSmqDQzeS1DiLXpIa5/RKSWpcp2P0VXUncOfMzMw1XeaQNDr9XAfysYST5dCNJDXOopekxln0ktQ4i16SGmfRS1LjLHpJapxFL0mN8w1TktQ470cvSY1z6EaSGmfRS1LjvB/9SaDfBz54/xBJz8YzeklqnEUvSY1z6EbSxDkcOVme0UtS4zyjlzS1+jnz96x/aRZ9Q/r5pti2YQJBJE0Vh24kqXEWvSQ1bixDN0l+C7gEeBHw0ar63DiOI0laWt9n9EluSnIwyUPHrJ9L8miSfUmuA6iqT1XVNcCbgd8ZbWRJ0iAGOaPfAXwI+NjRFUlOAW4EXgvsB+5PckdVPdLb5I97H9dx9DufWNKz6/d7aMfcijEnmV59F31V3Ztk7TGrLwD2VdVjAEnmgUuT7AWuBz5bVQ+OKKskDW3PgUNsWeKHQqtTNVNV/W+8UPR3VdV5veXLgLmqurq3fCVwIfAN4E3A/cBXq+rDz7KvrcBWgNWrV58/Pz8/1Cdw+PBhVq5cOdRrx6nfXHsOTPahK6tPhyeemugh+2KuwUxrLpjebP3k2nDG5J+NcSIdtnHjxt1VNbPUdmO5GFtVHwQ+uMQ224HtADMzMzU7OzvUsXbt2sWwrx2nfnMtdYYxats2HOGGPdP39glzDWZac8H0Zusn1+NXzE4mzCKT6LATnV55ADhz0fKa3rq++ChBSRq/E/2xez9wdpKzWCj4y4Hf7ffFVXUncOfMzMw1J5hj6vQzHihJkzDI9MpbgPuAc5PsT3JVVR0BrgXuBvYCt1bVw+OJKkkaxiCzbjYfZ/1OYOcwB0+yCdi0bt26YV4uSepDp7dAqKo7q2rrqlWTv9ItScuF97qRpMZ1OgfKoRtJrZqmd+w6dCNJjXPoRpIaN31vX5OkKXey3YzQMXpJ6jnZCrxfjtFLUuMculmk35/mrd7KVFKbvBgrSY3rtOi9e6UkjZ9j9JLUOIduJKlxFr0kNW7ZzLoZ5fzYfva1bcPIDidJJ8SLsZLUOC/GSlLjHKOXpMZZ9JLUOItekhpn0UtS4yx6SWrcSX8/+j0HDrGl0XtIS9IoOL1Skhrn0I0kNc6il6TGWfSS1DiLXpIaZ9FLUuMseklqnEUvSY3zfvSS1LhUVdcZSPIk8K9DvvwlwHdHGGdUzDUYcw1mWnPB9GZrMddPVdVLl9poKor+RCR5oKpmus5xLHMNxlyDmdZcML3ZlnMux+glqXEWvSQ1roWi3951gOMw12DMNZhpzQXTm23Z5jrpx+glSc+thTN6SdJzaKLok7w8yZeSfDXJA0ku6DrTUUnemuSfkjyc5H1d51ksybYkleQlXWcBSPL+3r/V15P8bZIXd5xnLsmjSfYlua7LLEclOTPJPUke6X1Nva3rTIslOSXJV5Lc1XWWo5K8OMltva+tvUl+setMAEne0fs/fCjJLUmeP65jNVH0wPuA91bVy4E/7S13LslG4FLgZVX1c8CfdRzph5KcCfw68M2usyzyeeC8qvp54BvAu7oKkuQU4EbgYmA9sDnJ+q7yLHIE2FZV64FXAX8wJbmOehuwt+sQx/gA8HdV9TPAy5iCfEnOAP4QmKmq84BTgMvHdbxWir6AF/X+vgr4dodZFnsLcH1V/Q9AVR3sOM9ifwH8EQv/dlOhqj5XVUd6i18C1nQY5wJgX1U9VlVPA/Ms/NDuVFV9p6oe7P39P1korTO6TbUgyRrgEuAjXWc5Kskq4FeAjwJU1dNV9f1uU/3QqcDpSU4FXsAYe6uVon878P4k32LhrLmzM8FjnAP8cpIvJ/mHJK/sOhBAkkuBA1X1ta6zPIffBz7b4fHPAL61aHk/U1KoRyVZC/wC8OVuk/zQX7Jw8vBM10EWOQt4Evib3pDSR5Ks6DpUVR1goau+CXwHOFRVnxvX8Tp9OPggkvw98JPP8qH3ABcB76iqTyZ5Aws/vV8zBblOBX6chV+xXwncmuSnawJTnZbI9W4Whm0m7rlyVdWne9u8h4Uhipsnme1kkmQl8Eng7VX1H1OQ53XAwaranWS26zyLnAq8AnhrVX05yQeA64A/6TJUkh9j4TfEs4DvA59I8saq+vg4jnfSFH1VHbe4k3yMhbFBgE8wwV8dl8j1FuD2XrH/Y5JnWLivxZNd5UqygYUvrq8lgYXhkQeTXFBV/9ZVrkX5tgCvAy6axA/E53AAOHPR8preus4l+VEWSv7mqrq96zw9rwZen+Q3gecDL0ry8ap6Y8e59gP7q+robz23sVD0XXsN8C9V9SRAktuBXwLGUvStDN18G/jV3t9/DfjnDrMs9ilgI0CSc4DT6PimSlW1p6p+oqrWVtVaFr4RXjGJkl9KkjkWfvV/fVX9V8dx7gfOTnJWktNYuFB2R8eZyMJP548Ce6vqz7vOc1RVvauq1vS+pi4HvjAFJU/v6/pbSc7trboIeKTDSEd9E3hVkhf0/k8vYowXiU+aM/olXAN8oHdR47+BrR3nOeom4KYkDwFPA2/q+Cx12n0IeB7w+d5vG1+qqjd3EaSqjiS5FribhRkRN1XVw11kOcargSuBPUm+2lv37qra2WGmafdW4ObeD+zHgN/rOA+9YaTbgAdZGKb8CmN8h6zvjJWkxrUydCNJOg6LXpIaZ9FLUuMseklqnEUvSY2z6CWpcRa9JDXOopekxv0vUqh4ZN7lDr4AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 3\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADqVJREFUeJzt3X+MXOdZxfFz6sppsJulxdW2iU02lQ2qiauCh1QISm21FS7FCQKrTWirWGpjpSHiD4LESkFCgn8oUpAqYlEsiNwg2m2JBPV6TQsJXoVKDSRGxY5TpXGioNoJdgNiwCFQrD78Mdcw2ex6Z2bvzH3nme9HsjIze3f2ZH+cffe578w4IgQAyOt1TQcAAAwXRQ8AyVH0AJAcRQ8AyVH0AJAcRQ8AyVH0AJAcRQ8AyVH0AJDc65sOIEmbNm2KmZmZgd735Zdf1oYNG+oNVANy9Ydc/Sk1l1Rutoy5Tpw48VJEvGXVAyOi8X87d+6MQR0/fnzg9x0mcvWHXP0pNVdEudky5pL0RPTQsYxuACA5ih4AkqPoASA5ih4AkqPoASA5ih4AkqPoASC5Rove9l7bh9rtdpMxACC1Rh8ZGxHzkuZbrdYdTeYAus3MLvzf5cN7ynskJdAvRjfAFZw619bM7MKryh8YNxQ9ACRH0QNAchQ9ACRH0QNAchQ9ACRH0QNAckW8whTQtF62Ty495vnf+dCw4gC1YkUPAMlR9ACQHEUPAMlR9ACQHEUPAMlR9ACQHEUPAMnxwiMAkBwvPAIMqPsBVDx4CiXjkbGYWLyYCCYFM3oASI6iB4DkKHoASI6iB4DkKHoASI6iB4DkKHoASI599Jgow9o7z4OnUDJW9ACQHEUPAMlR9ACQHEUPAMlR9ACQHEUPAMlR9ACQHEUPAMnxgCmkN+oXGOHBUygNK3oASI6iB4DkKHoASI6iB4DkKHoASG4oRW97g+0nbP/cMO4fANC7nore9gO2L9h+csnte2w/bfuM7dmuN/26pC/VGRQAMJhe99EflnS/pAcv32B7naSDkj4g6aykx20fkXSdpKckvaHWpEAfRr13HihZT0UfEY/anlly802SzkTEc5Jke07SLZI2StogabukV2wfi4jv1ZYYGCM8eAolcET0dmCn6I9GxI3V9X2S9kTEJ6vrH5f07oi4u7q+X9JLEXF0hfs7IOmAJE1PT++cm5sb6H/g4sWL2rhx40DvO0zk6k/duU6da9dyP9NXS+dfqeWutOO6qXruSOV+HaVys2XMtXv37hMR0VrtuKE9BUJEHF7l7YckHZKkVqsVu3btGujjLC4uatD3HSZy9afuXPtrGt3cs+OS7jtVz4/J8x/dVcv9SOV+HaVys01yrrXsujknaUvX9c3VbQCAgqyl6B+XtM32DbbXS7pV0pF6YgEA6tLr9sovSPq6pB+2fdb2JyLikqS7JX1V0jclfSkiTvfzwW3vtX2o3a5nngoAeK1ed93ctsLtxyQdG/SDR8S8pPlWq3XHoPcBXMaWSmB5PB89MCJstURTeK4bAEiOogeA5Botek7GAsDwNVr0ETEfEQempup7xCAA4NUY3QBAchQ9ACTH9kqMtXHdO89WS4wSK3oASI5dNwCQHLtuACA5RjcAkBxFDwDJsesGaBg7cDBsFD3GzrhuqQSawq4bAEiOXTcAkBwnYwEgOYoeAJLjZCzGAidggcFR9EBB2GqJYWB0AwDJUfQAkFyjoxvbeyXt3bp1a5MxgCIxxkFd2EcPAMkxugGA5Nh1g2KxpRKoByt6AEiOogeA5Ch6AEiOGT2KwlweqB9FD4wB9tRjLXjhEQBIjgdMAUBynIwFgOQoegBIjqJH42ZmF3TqXJsdNz3i84V+UfQAkBxFDwDJUfQAkBxFDwDJ8chYNIITifXgEbPoBSt6AEiOogeA5HiuGwBIjue6AYDkOBkLJMGJWayEosfIsNMGaAYnYwEgOYoeAJKj6AEgOWb0GCrm8kDzKHogIXbgoBujGwBIjqIHgOQoegBIjhk9kBzzelD0qB07bYCyMLoBgOQoegBIjtENasG4Zjwwr59MvPAIACTHC48AQHKMboAJxRhnclD0GBhzeWA8sOsGAJKj6AEgOYoeAJKj6AEgOU7Goi+cgM2JHTi5saIHgOQoegBIjtENVsW4BhhvrOgBIDlW9ABeZaW/4DhJO75Y0QNAchQ9ACTH6AavwclXIBdW9ACQHCt6AD3h0bPji6KHJMY1QGaMbgAgOYoeAJJjdAOgb8zrxwtFP8GYywOTofbRje132P6s7Ydsf6ru+wcA9Kenorf9gO0Ltp9ccvse20/bPmN7VpIi4psRcaekD0v6yfojAwD60euK/rCkPd032F4n6aCkD0raLuk229urt90saUHSsdqSAgAG0tOMPiIetT2z5OabJJ2JiOckyfacpFskPRURRyQdsb0g6fP1xQVQmqXneg7v2dBQEqzEEdHbgZ2iPxoRN1bX90naExGfrK5/XNK7JT0k6RckXSXpZEQcXOH+Dkg6IEnT09M75+bmBvofuHjxojZu3DjQ+w5Tqbku/Gtb519pOsVrTV8tcvWh1FySdMPUuiK/90v9mVxLrt27d5+IiNZqx9W+6yYiFiUt9nDcIUmHJKnVasWuXbsG+niLi4sa9H2HqdRcv/+nX9Z9p8rbbHXPjkvk6kOpuaTOir7E7/1SfyZHkWstu27OSdrSdX1zdRsAoCBrKfrHJW2zfYPt9ZJulXSknlgAxtWpc23NzC7wOI2C9PS3n+0vSNolaZPts5J+MyL+2Pbdkr4qaZ2kByLi9NCSYmDdP3D37GgwCIBG9Lrr5rYVbj+mNWyhtL1X0t6tW7cOehcAgFU0ejYnIuYlzbdarTuazJERfzajBLzQeBl49koASK7M/VnoGc8iCGA1jRY9M3pgMrFAGa1GRzcRMR8RB6amppqMAQCpMaMHgOQoegBIjpOxibClEsByKPoxRKED6Eejoxvbe20farfbTcYAgNR4ZOyYYBWPrNhqOXyMbgpGuQOoA7tuACA5VvQAisEYZzhY0QNAcjzXTWGYywMdrO7rw64bAMWj9NeG0Q0AJEfRA0By7LoBMFYY4/SPFT0AJMeKviGsSgCMCkVfALZUAhgmnr0SAJJjH/0IsXIH0AROxg7BzOyCTp1rU+wAisCMfsgoe2B42NTQG1b0AJAcRQ8AyTG6AZDCSmNSRjqs6AEgPYoeAJLjhUdqwu4aAKVqdEUfEfMRcWBqaqrJGACQGidj14BVPFC+yz+n9+y4pP0Tuu+eogcw8bI/8IqiBzCRJukvcnbdAEByrOhXMEm/7QHkxooeAJJjRd+FVTyAlYzzCVtW9ACQHEUPAMlN9OiGUQ2ApcZ5RLMSnusGANZgHH4x8OLgALCClf7qH7dpwMSNbsbtCwRgfJS6up+4ogeAUVi6qGyy+Nl1AwDJpV3RM6IBUJImxzpjv6I/da6tmdkFih0AVpBqRU/ZA8BrpSp6ABgH3YvSw3s2DP3jjf3oBgBwZRQ9ACRH0QNAchQ9ACRH0QNAchQ9ACRH0QNAchQ9ACTXaNHb3mv7ULvdbjIGAKTmiGg6g2x/R9I/DfjumyS9VGOcupCrP+TqT6m5pHKzZcx1fUS8ZbWDiij6tbD9RES0ms6xFLn6Q67+lJpLKjfbJOdiRg8AyVH0AJBchqI/1HSAFZCrP+TqT6m5pHKzTWyusZ/RAwCuLMOKHgBwBWNX9LbfbPuvbT9T/fdNVzj2Gttnbd9fQi7b19v+B9vfsH3a9p2F5HqX7a9XmU7a/kgJuarjvmL732wfHXKePbaftn3G9uwyb7/K9hert/+d7Zlh5ukj109X31OXbO8bRaYec/2q7aeq76dHbF9fSK47bZ+qfga/Znt7Cbm6jvtF22G73l04ETFW/yT9rqTZ6vKspE9f4djPSPq8pPtLyCVpvaSrqssbJT0v6doCcv2QpG3V5WslvSjp+5vOVb3tfZL2Sjo6xCzrJD0r6e3V1+gfJW1fcsxdkj5bXb5V0hdH8D3VS64ZSe+U9KCkfcPO1Eeu3ZK+r7r8qYI+X9d0Xb5Z0ldKyFUd90ZJj0p6TFKrzgxjt6KXdIukz1WXPyfp55c7yPZOSdOS/qqUXBHx3Yj47+rqVRrNX1S95PpWRDxTXX5B0gVJqz4IY9i5qjyPSPqPIWe5SdKZiHguIr4raa7K160770OS3mfbTeeKiOcj4qSk7w05S7+5jkfEf1ZXH5O0uZBc/951dYOkUZyk7OX7S5J+W9KnJf1X3QHGseinI+LF6vI/q1Pmr2L7dZLuk/RrJeWSJNtbbJ+U9G11VrEvlJCrK99N6qw6ni0p15Bdp87X47Kz1W3LHhMRlyS1Jf1AAbma0G+uT0j6y6Em6ugpl+1ftv2sOn9V/koJuWz/mKQtEbGgISjyxcFtPyzprcu86d7uKxERtpf7jXyXpGMRcbbORVcNuRQR35b0TtvXSvoL2w9FxPmmc1X38zZJfyLp9ohY8wqxrlwYX7Y/Jqkl6b1NZ7ksIg5KOmj7lyT9hqTbm8xTLUx/T9L+YX2MIos+It6/0ttsn7f9toh4sSqmC8sc9hOS3mP7LnVm4ettX4yIFU+CjChX9329YPtJSe9RZxTQaC7b10hakHRvRDy2ljx15hqRc5K2dF3fXN223DFnbb9e0pSkfykgVxN6ymX7/er8Un9v18iy8Vxd5iT9wVATdayW642SbpS0WC1M3yrpiO2bI+KJOgKM4+jmiP7/N/Dtkr689ICI+GhE/GBEzKgzvnlwrSVfRy7bm21fXV1+k6SfkvR0AbnWS/pzdT5Pa/qlU2euEXpc0jbbN1Sfi1vVydetO+8+SX8T1Rm0hnM1YdVctn9U0h9KujkiRvVLvJdc27qufkjSM03nioh2RGyKiJmqsx5T5/NWS8lf/iBj9U+duegj6nyBHpb05ur2lqQ/Wub4/RrNrptVc0n6gKST6px1PynpQCG5PibpfyR9o+vfu5rOVV3/W0nfkfSKOrPNnxlSnp+V9C11zk3cW932W+r8wEnSGyT9maQzkv5e0tuH/bXrMdePV5+Xl9X5C+N0IbkelnS+6/vpSCG5PiPpdJXpuKQfKSHXkmMXVfOuGx4ZCwDJjePoBgDQB4oeAJKj6AEgOYoeAJKj6AEgOYoeAJKj6AEgOYoeAJL7XwdHYpyYra3nAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADi9JREFUeJzt3XGIpPddx/H316sJ5Q5X5cJa7k43sEfwzAnikCj6xwYr2ZhuUwutd/SPHoYcASMVDvRiBPuPGJGKVqKy2HD9o+QIqPWudyGtwTX+kWqSIl7SM3rEK7kj5ozF1YvFsuTrHzuN0+1tdmbnmX1mvvN+QcjM88zOfH/ZZz75zff5zbORmUiS6vqutguQJI2WQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klTce0bxpBGxG/gb4JOZ+YWtHr93796cm5sbRSmteeutt9i9e3fbZYyUY6xjGsZZcYwvvvjim5l5y1aP6yvoI+Jx4APAtcy8vWf7IvAHwC7gTzPz0e6uXwOe7LfYubk5XnjhhX4fPhFWVlZYWFhou4yRcox1TMM4K44xIr7Wz+P6bd2cAhY3vMAu4DHgHuAQcDQiDkXEzwJfBa71Xa0kaWT6mtFn5rMRMbdh8x3Apcx8FSAiTgP3AXuA3ayH/zci4nxmvt1YxZKkgQzTo98HvNZz/wpwZ2Y+BBARx4A3Nwv5iDgOHAeYnZ1lZWVliFLGz/Xr18uNaSPHWMc0jHMaxriZkZyMBcjMU1vsXwaWATqdTlbrnVXsB27kGOuYhnFOwxg3M8zyyqvAgZ77+7vbJEljZJigfx44GBG3RsRNwBHgTDNlSZKa0lfQR8QTwHPAbRFxJSLuz8w14CHgaeAi8GRmvjzIi0fEUkQsr66uDlq3JKlP/a66ObrJ9vPA+e2+eGaeBc52Op0HtvsckqR3N7KTsRqduZPn3rl9+dF7W6xE2h6P4Z1l0I+x3jdDP4/xDaM2bDxONzsONzueNzuGPbab02rQR8QSsDQ/P99mGWOln3Af9Gd986gtgx7Pg/7PQP1pNejt0bfLN4+2a5gJiXaerZuW+EbRJBjH49QJyuAMen2Hfnuuqmkcw13DsUcv4N3f3JvtO7VY69reUlX26CVx4eoqxyZwJm8bpz+2bnaQH4k1TnqPxxOHWyxEI2fQa9t6Z4HOptQ2Z/ebM+ingJ8k9C07cSx4vI0fT8aO2LQc9M6mxte0HIPanCdj1ThDXxovw1yPXpI0AezRSyrHT5Xfzhm9JBXnjF4j5cyqHZ6AVa9WZ/T+KUFJGr1Wgz4zz2bm8ZmZmTbLkKTSbN1ox9jGGS3bNVub1mPQoG/ItB5AksafQa9W+D9GaecY9NIEs12jfriOXpKKc0Y/AnMnz3Hi8NpE/iEHqZpvfeo5cXiNaY0819FLUnGuo5ek4qbzc4ykqTdNK788GStJxRn0klScrZshuIZZ0iQw6KUJME39ZDXPoNdYMdCk5hn00piyNaimeDJWkoprdUYfEUvA0vz8fJtlqGXOXAfjfy8NqtWgz8yzwNlOp/NAm3UMwjfZzrFfLzXDHr2kqVd9UmGPXpKKM+glqTiDXpKKM+glqThPxmoiVD9ZJo2SQd8Hl1RKmmQGvTRGnFS0r+KnR3v0klScQS9JxRn0klRcq0EfEUsRsby6utpmGZJUWqtBn5lnM/P4zMxMm2VIUmmuutHEqbYqwpU2GjV79JJUnEEvScUZ9JJUnD16SdpElfNBBv0mPEEmqQqDXhOtyoxLGiV79JJUnDN6qQW2BrWTnNFLUnEGvSQVZ9BLUnH26FWGK3CkGzPopR3iCVi1xdaNJBVn0EtScbZuevjRWlJFzuglqbjGZ/QR8cPAJ4C9wDOZ+cdNv4Yk7bRJXtXV14w+Ih6PiGsR8dKG7YsR8UpEXIqIkwCZeTEzHwQ+CvxU8yVLW5s7ee6df6Rp12/r5hSw2LshInYBjwH3AIeAoxFxqLvvg8A54HxjlUqStiUys78HRswBX8jM27v3fxL4ZGbe3b3/MEBm/nbPz5zLzBt+xomI48BxgNnZ2R8/ffr09kfRkAtXVxt7rtn3whvfaOzpxtKkjPHwvplt/+z169fZs2dPI3U0eXw1bVJ+l8NocozDHFNNuuuuu17MzM5WjxumR78PeK3n/hXgzohYAD4M3My7zOgzcxlYBuh0OrmwsDBEKc041uDH/BOH1/jUhdqLmiZljJc/trDtn11ZWaGpY7PJ46tpk/K7HEaTYxzmmGpD47/ZzFwBVpp+XknS9gyzvPIqcKDn/v7uNknSGBkm6J8HDkbErRFxE3AEODPIE0TEUkQsr66Ob+9SkiZdv8srnwCeA26LiCsRcX9mrgEPAU8DF4EnM/PlQV48M89m5vGZmfE4sSFJFfXVo8/Mo5tsP49LKKVNuY5f48BLIEhSca2up4qIJWBpfn6+zTIkaSCTdjmEVmf09uglafRqf0NCYvJmX1LTpjroPVGmUfC40rhptXXjOnpJGj179JJUnMsrJak4g16SijPoJam4qV51o+njUktNI1fdSFJxrrqRpOLs0UtScQa9JBXnyVipAV72QOPMoNfUcgWOpoWrbiSpOFfdSFJxtm6kbbIvL5iMFqCrbiSpOINekooz6CWpuKnr0dtXlTRtXF4pScW1OqPPzLPA2U6n80CbdUj9rJzw06AmlT16SSrOoJek4gx6SSrOoJek4gx6SSrOoJek4qbuC1PSVnqXUZ5a3N1iJVIznNFLUnGtzugjYglYmp+fb7MMaVMXrq5yzC9KacL5h0ckqThbN5JUnEEvScUZ9JJUnEEvScVNxTp6Ly8raZo5o5ek4gx6SSrOoJek4qaiRy9JO6GfP0nZBmf0klScQS9JxbUa9BGxFBHLq6urbZYhSaV5UTNJKs7WjSQVZ9BLUnEGvSQVZ9BLUnFlvzDlhcwkaZ0zekkqzqCXpOLKtm4kqU0b28dtXvvGGb0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxI7kEQkR8CLgX+B7gM5n5xVG8jiRpa33P6CPi8Yi4FhEvbdi+GBGvRMSliDgJkJmfz8wHgAeBX2i2ZEnSIAZp3ZwCFns3RMQu4DHgHuAQcDQiDvU85De6+yVJLek76DPzWeDrGzbfAVzKzFcz85vAaeC+WPc7wFOZ+ZXmypUkDWrYHv0+4LWe+1eAO4FfBt4PzETEfGb+ycYfjIjjwHGA2dlZVlZWhizl2504vNbo8w1q9r3t1zBqjrGOaRhn22NsOuMGMZKTsZn5aeDTWzxmGVgG6HQ6ubCw0GgNx1r+U4InDq/xqQu1L/fvGOuYhnG2PcbLH1to7bWHXV55FTjQc39/d5skaUwMG/TPAwcj4taIuAk4Apzp94cjYikilldXV4csQ5K0mUGWVz4BPAfcFhFXIuL+zFwDHgKeBi4CT2bmy/0+Z2aezczjMzMzg9YtSepT3w2rzDy6yfbzwPnGKpIkNcpLIEhSca0GvT16SRq9VoPeHr0kjZ6tG0kqzqCXpOIMekkqzpOxklScJ2MlqbjaVzGSpDEx13OhxcuP3rujr10q6OdavmKlJI0jT8ZKUnGejJWk4jwZK0nF2bqRpOIMekkqzqCXpOIMekkqzlU3klScq24kqThbN5JUnEEvScUZ9JJUnEEvScUZ9JJUnMsrJak4l1dKUnG2biSpOINekooz6CWpOINekooz6CWpOINekooz6CWpOINekop7T5svHhFLwNL8/HybZUjSjpo7ee6d25cfvXfkr+c3YyWpOFs3klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klRcq1+YakLvFw8kSd/JGb0kFWfQS1JxBr0kFWfQS1JxrQZ9RCxFxPLq6mqbZUhSaV69UpKKs3UjScUZ9JJUnEEvScVFZrZdAxHx78DX2q6jYXuBN9suYsQcYx3TMM6KY/yhzLxlqweNRdBXFBEvZGan7TpGyTHWMQ3jnIYxbsbWjSQVZ9BLUnEG/egst13ADnCMdUzDOKdhjDdkj16SinNGL0nFGfQNi4jfjYh/ioh/jIi/iIjv7dn3cERciohXIuLuNuscRkR8JCJejoi3I6KzYV+JMQJExGJ3HJci4mTb9TQhIh6PiGsR8VLPtu+PiC9FxL90//19bdY4rIg4EBF/HRFf7R6nn+huLzXOQRj0zfsScHtm/ijwz8DDABFxCDgC/AiwCPxRROxqrcrhvAR8GHi2d2OlMXbrfgy4BzgEHO2Ob9KdYv130+sk8ExmHgSe6d6fZGvAicw8BPwE8Evd3121cfbNoG9YZn4xM9e6d78M7O/evg84nZn/m5n/ClwC7mijxmFl5sXMfOUGu8qMkfW6L2Xmq5n5TeA06+ObaJn5LPD1DZvvAz7bvf1Z4EM7WlTDMvP1zPxK9/Z/AxeBfRQb5yAM+tH6ReCp7u19wGs9+650t1VSaYyVxrKV2cx8vXv734DZNotpUkTMAT8G/B2Fx7mVif/j4G2IiL8CfuAGux7JzL/sPuYR1j9Cfm4na2tKP2NUPZmZEVFiKV5E7AH+DPiVzPyviHhnX6Vx9sOg34bMfP+77Y+IY8AHgJ/J/1+/ehU40POw/d1tY2mrMW5iosa4hUpj2cobEfG+zHw9It4HXGu7oGFFxHezHvKfy8w/724uN85+2bppWEQsAr8KfDAz/6dn1xngSETcHBG3AgeBv2+jxhGqNMbngYMRcWtE3MT6SeYzLdc0KmeAj3dvfxyY6E9ssT51/wxwMTN/r2dXqXEOwi9MNSwiLgE3A//R3fTlzHywu+8R1vv2a6x/nHzqxs8y3iLi54E/BG4B/hP4h8y8u7uvxBgBIuLngN8HdgGPZ+ZvtVzS0CLiCWCB9Ss5vgH8JvB54EngB1m/iuxHM3PjCduJERE/DfwtcAF4u7v511nv05cZ5yAMekkqztaNJBVn0EtScQa9JBVn0EtScQa9JBVn0EtScQa9JBVn0EtScf8HBkyiQ790w2sAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADo9JREFUeJzt3X+sZPVZx/H3I1tqs9vcgtusCMQFIUTsRoUJrVqbJVVcQEAb0oCkgqVsiJK0CUY3adIQ/6Ia/ENDalYlVNN0odUql92GYmXln4L8CLBQStmSNbKhrIi5uEhS1z7+MWfJ5HLn7szsnDlzn32/kps9c8537nn2e8587rnf8+NGZiJJqutHui5AktQug16SijPoJak4g16SijPoJak4g16SijPoJak4g16SijPoJam4dV0XALBx48bcvHnzRO998803Wb9+/XQLmgLrGo91jWde64L5ra1iXU888cRrmfn+YzbMzM6/LrzwwpzUQw89NPF722Rd47Gu8cxrXZnzW1vFuoDHc4SMdehGkooz6CWpOINekooz6CWpOINekooz6CWpuE6DPiKuiIidS0tLXZYhSaV1GvSZuZiZ2xcWFrosQ5JKm4s7Y6VZ27xj90jt7t42f3dSSuMy6DVy6AEcuP3yVtZ965Yj3LBKHdNer3QiMejVinF+eEhql0GvUvwBI72Tl1dKUnEe0UtTMupvE55v0Kx5RC9JxRn0klScQzfSjDnEo1kz6DUWr2qR1h6DXmuCP2CkyTlGL0nFeUQvrWLfwaVVH80grQUe0UtScQa9JBXnHx6RpOI6HaPPzEVgsdfr3dRlHVWtdKXKsR4HLKkeh24kqTiDXpKKM+glqTiDXpKKM+glqTjvjJXWOJ+GqWMx6KU5tVqAe5msxuHQjSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnHeMLUGjXonpCSBR/SSVJ5BL0nFGfSSVJxj9NIJwqdcnrg8opek4gx6SSrOoJek4loZo4+I9cC/ALdl5v1trGNc41x77hilpEpGOqKPiLsi4lBEPLts/raIeCEi9kfEjoFFfwjcO81CJUmTGXXo5m5g2+CMiDgJuBO4FDgfuDYizo+IXwW+DRyaYp2SpAmNNHSTmQ9HxOZlsy8C9mfmSwARsQu4CtgArKcf/m9FxJ7M/OHUKpYkjSUyc7SG/aC/PzM/0Ly+GtiWmZ9qXn8C+GBm3tK8vgF4bdgYfURsB7YDbNq06cJdu3ZN9B84fPgwGzZsOGa7fQeXRv6eW05fmKiWQaPWNWicGie16T3w6lutr2Zs1jWeNus63v1/kn1/FirWdfHFFz+Rmb1jtWvthqnMvPsYy3cCOwF6vV5u3bp1ovXs3buXUd57wzgnY6+brJZBo9Y1aJwaJ3XrliPcsW/+7pOzrvG0Wdfx7v+T7PuzcCLXdTyXVx4Ezhx4fUYzT5I0R47nkOAx4NyIOIt+wF8D/NZUquqYt4pLqmTUyyu/DHwLOC8iXo6IGzPzCHAL8ADwPHBvZj43zsoj4oqI2Lm01P7YtCSdqEa96ubaIfP3AHsmXXlmLgKLvV7vpkm/hyRpdT4CQZKKM+glqTiDXpKK6zToPRkrSe3rNOgzczEzty8sHP+dqJKklTl0I0nFzd+93WvIajdW3brlyNuPNPDGKkld8ohekorr9Ig+Iq4ArjjnnHO6LEPSAB8BUo8nYyWpOIduJKk4g16SijPoJak4g16SijPoJak4n3UjScV1eh29f3hEWruGXW8/eFf4UV5z3y2HbiSpOJ91MwOj3mkoSW3wiF6SijPoJak4g16SivPySkkqzqdXSlJxDt1IUnEGvSQVZ9BLUnEGvSQVZ9BLUnEGvSQVZ9BLUnHeMCVJxXnDlCQV59CNJBVn0EtScQa9JBVn0EtScQa9JBVn0EtScQa9JBVn0EtScQa9JBVn0EtScT7rRpKKW9flyjNzEVjs9Xo3dVmHpHZt3rF7pHYHbr+85UpOTA7dSFJxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1Jx/uERSSqu06DPzMXM3L6wsNBlGZJUmkM3klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9JxRn0klTcuq4LkKSjNu/YPVK7A7df3nIltXhEL0nFGfSSVJxBL0nFGfSSVJxBL0nFGfSSVJxBL0nFGfSSVJxBL0nFTf3O2Ij4aeDTwEbgm5n5hWmvQ9KJzTtoxzPSEX1E3BURhyLi2WXzt0XECxGxPyJ2AGTm85l5M/Bx4JemX7IkaRyjDt3cDWwbnBERJwF3ApcC5wPXRsT5zbIrgd3AnqlVKkmayEhBn5kPA68vm30RsD8zX8rMHwC7gKua9vdl5qXAddMsVpI0vsjM0RpGbAbuz8wPNK+vBrZl5qea158APgh8FfgY8G7gmcy8c8j32w5sB9i0adOFu3btmug/cPjwYTZs2HDMdvsOLk30/Se16T3w6lszXeVIrGs81jW+eapty+kLb0+PmhWzdjx1XXzxxU9kZu9Y7aZ+MjYz9wJ7R2i3E9gJ0Ov1cuvWrROtb+/evYzy3htGPHkzLbduOcId++bvKdDWNR7rGt881Xbguq1vT4+aFbM2i7qO5/LKg8CZA6/PaOZJkubI8QT9Y8C5EXFWRJwMXAPcN52yJEnTMurllV8GvgWcFxEvR8SNmXkEuAV4AHgeuDcznxtn5RFxRUTsXFqa7fi5JJ1IRhpIy8xrh8zfw3FcQpmZi8Bir9e7adLvIUlanY9AkKTiDHpJKs6gl6TiOg16T8ZKUvs6DfrMXMzM7QsLC8duLEmaiEM3klScQS9JxRn0klScJ2MlqThPxkpScQ7dSFJxBr0kFWfQS1JxBr0kFWfQS1JxXl4pScV5eaUkFefQjSQVZ9BLUnEGvSQVZ9BLUnEGvSQV5+WVklTcui5XnpmLwGKv17upyzok1bR5x+63p2/dcoQbBl4POnD75bMqqRMO3UhScQa9JBVn0EtScQa9JBVn0EtScQa9JBVn0EtScd4wJUnF+Tx6SSrOoRtJKs6gl6TiDHpJKs6gl6TiDHpJKs6gl6TiDHpJKs6gl6TiDHpJKs6gl6TifNaNJBXns24kqTiHbiSpOINekooz6CWpOINekooz6CWpuHVdFyBJXdu8Y/dI7Q7cfnnLlbTDI3pJKs4jekka0Vo98jfoJWnKRv2BAHD3tvUtVtLn0I0kFWfQS1JxBr0kFWfQS1JxBr0kFWfQS1Jx/uERSSrOPzwiScU5dCNJxUVmdl0DEfEfwL9N+PaNwGtTLGdarGs81jWeea0L5re2inX9ZGa+/1iN5iLoj0dEPJ6Zva7rWM66xmNd45nXumB+azuR63LoRpKKM+glqbgKQb+z6wKGsK7xWNd45rUumN/aTti61vwYvSRpdRWO6CVJq1gzQR8R2yLihYjYHxE7Vlj+7oi4p1n+aERsnkFNZ0bEQxHx7Yh4LiI+vUKbrRGxFBFPNV+fa7uuZr0HImJfs87HV1geEfFnTX89ExEXzKCm8wb64amIeCMiPrOszcz6KyLuiohDEfHswLxTI+LBiHix+feUIe+9vmnzYkRc33JNfxIR32m209ci4n1D3rvqNm+pttsi4uDA9rpsyHtX/fy2UNc9AzUdiIinhry3lT4blg2d7V+ZOfdfwEnA94CzgZOBp4Hzl7X5XeAvmulrgHtmUNdpwAXN9HuB765Q11bg/g767ACwcZXllwFfBwL4EPBoB9v0+/SvA+6kv4CPABcAzw7M+2NgRzO9A/j8Cu87FXip+feUZvqUFmu6BFjXTH9+pZpG2eYt1XYb8PsjbOtVP7/TrmvZ8juAz82yz4ZlQ1f711o5or8I2J+ZL2XmD4BdwFXL2lwFfLGZ/irw0YiINovKzFcy88lm+r+B54HT21znFF0F/E32PQK8LyJOm+H6Pwp8LzMnvVHuuGXmw8Dry2YP7kdfBH5jhbf+GvBgZr6emf8FPAhsa6umzPxGZh5pXj4CnDGNdY1rSH+NYpTPbyt1NRnwceDL01rfiDUNy4ZO9q+1EvSnA/8+8Ppl3hmob7dpPhRLwI/NpDqgGSr6eeDRFRb/QkQ8HRFfj4ifmVFJCXwjIp6IiO0rLB+lT9t0DcM/fF3011GbMvOVZvr7wKYV2nTZd5+k/5vYSo61zdtySzOsdNeQoYgu++uXgVcz88Uhy1vvs2XZ0Mn+tVaCfq5FxAbg74DPZOYbyxY/SX944meBPwf+YUZlfTgzLwAuBX4vIj4yo/UeU0ScDFwJfGWFxV311ztk//foubksLSI+CxwBvjSkSRfb/AvATwE/B7xCf5hknlzL6kfzrfbZatkwy/1rrQT9QeDMgddnNPNWbBMR64AF4D/bLiwi3kV/Q34pM/9++fLMfCMzDzfTe4B3RcTGtuvKzIPNv4eAr9H/9XnQKH3alkuBJzPz1eULuuqvAa8eHcJq/j20QpuZ911E3AD8OnBdExDvMMI2n7rMfDUz/y8zfwj85ZB1drKvNTnwMeCeYW3a7LMh2dDJ/rVWgv4x4NyIOKs5GrwGuG9Zm/uAo2enrwb+edgHYlqa8b+/Bp7PzD8d0ubHj54riIiL6Pd5qz+AImJ9RLz36DT9k3nPLmt2H/Db0fchYGngV8q2DT3K6qK/lhncj64H/nGFNg8Al0TEKc1QxSXNvFZExDbgD4ArM/N/hrQZZZu3UdvgeZ3fHLLOUT6/bfgV4DuZ+fJKC9vss1WyoZv9a9pnm9v6on+VyHfpn73/bDPvj+jv/AA/Sn8oYD/wr8DZM6jpw/R/9XoGeKr5ugy4Gbi5aXML8Bz9Kw0eAX5xBnWd3azv6WbdR/trsK4A7mz6cx/Qm9F2XE8/uBcG5nXSX/R/2LwC/C/9cdAb6Z/X+SbwIvBPwKlN2x7wVwPv/WSzr+0HfqflmvbTH7M9uo8dvbrsJ4A9q23zGfTX3zb7zzP0Q+y05bU1r9/x+W2zrmb+3Uf3q4G2M+mzVbKhk/3LO2Mlqbi1MnQjSZqQQS9JxRn0klScQS9JxRn0klScQS9JxRn0klScQS9Jxf0/hh/iFcDtEyAAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 4\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 5\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 6\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADvVJREFUeJzt3VGMXNddx/Hvr0ZppQClYFMqO4uNbEWYPBRplLzwEKRCHVLXbQXI5qWAZVMJIx6bCqQWVQgLgVBL06KltdxKNJZVtcXbbElLpOI+RMIOqiCOibBMqqxVcEqRJRAicvvnYSdksvVu7uzM7Oyc+X4kKztnZu89utn97Zn/PXNOqgpJUrteN+0OSJImy6CXpMYZ9JLUOINekhpn0EtS4wx6SWqcQS9JjTPoJalxBr0kNe4Hpt0BgJ07d9bevXun3Q1JmilPP/30t6tq12u9blsE/d69e7l8+fK0uyFJMyXJN7u8ztKNJDXOoJekxhn0ktQ4g16SGjf2oE/yYJKvJ/mLJA+O+/iSpOF0CvokZ5LcTPLMmvZDSZ5Lci3JI/3mAv4LeAOwMt7uSpKG1XVEfxY4NNiQZAfwKPAQcBA4luQg8PWqegh4P/AH4+uqJGkzOgV9VV0EvrOm+X7gWlVdr6qXgHPAkar6Xv/5/wRev94xk5xMcjnJ5RdffHETXZckdTHKB6Z2Ay8MPF4BHkjyHuDtwI8AH1vvm6tqEVgE6PV6c7Fx7d5HHv//r58//fAUeyJpnoz9k7FV9Xng811em+QwcHj//v3j7oYkqW+UoL8B3DPweE+/rbOqWgKWer3eiRH6IalxvhsezSjTKy8BB5LsS3IXcBS4MMwBkhxOsnjr1q0RuiFJ2kjX6ZWPAU8B9yZZSXK8qm4Dp4AngKvA+aq6MszJq2qpqk6+8Y1vHLbfkqSOOpVuqurYOu3LwPJmT26NXpImb6rLFM9zjd6ao6Stsi3Wo5ekrhwkDW+qQW/pRtJ6BgNdo5nq6pXejJWkyXOZYklq3FSD3nn0kjR5lm4kqXGWbiSpcQa9JDXOGr0kNc4avSQ1zk/GbgN+0k/SJFmjl6TGGfSS1DhvxkpS47wZK0mN82bshLkCn6Rps0YvSY0z6CWpcQa9JDXOoJekxjm9UpIa5/RKSWqcpRtJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUuIksapbkbuDvgA9V1ZcmcQ5J7XERwMnoFPRJzgDvAG5W1X0D7YeAjwA7gE9W1en+U+8Hzo+5r5Ia4faZW6vriP4s8DHgMy83JNkBPAr8ArACXEpyAdgNPAu8Yaw9nUOT+GVYb8TkL5vuxEBuQ6egr6qLSfauab4fuFZV1wGSnAOOAD8I3A0cBP4nyXJVfW9sPZY0UesNBgz92TVKjX438MLA4xXggao6BZDk14FvrxfySU4CJwEWFhZG6IbGwV9iqV0Tm3VTVWc3uhFbVYtV1auq3q5duybVDUmae6OM6G8A9ww83tNv6yzJYeDw/v37R+iGXoszGaT5NkrQXwIOJNnHasAfBX5tmANU1RKw1Ov1TozQD0kjcjDQtk6lmySPAU8B9yZZSXK8qm4Dp4AngKvA+aq6MszJXY9ekiav66ybY+u0LwPLmz25I/ruvFkqabMm8snYrqzRb09r38b7h0VrOfCYLVMNekf0kkax3h8c/xC92lSDftb5wyRpFrg5uCQ1zs3BJalxlm4a5bxoaZUlVmfdSHNruwwGtks/WmbpRpIa51aCktQ4a/RD8m2m9Gp+wG77s0Y/Jt7wkTbmIGl6/GSsXpN/xKTZZo1ekhpnjb4hvjWWdCcG/TZjWEsaN2/GToBhLWk78QNTktQ4SzeS5sa8ziAz6KU5YllxPhn0kprgH7H1GfSSxmpeyyPbmR+YkqTGOb1yBjlikjQMp1dKUuMs3UhS47wZO+OcaSDptRj0Gor3B6TZY+lGkhpn0EtS4wx6SWrc2Gv0SX4a+F1gJ/BkVX1i3OfYCtaipdE5WWB76BT0Sc4A7wBuVtV9A+2HgI8AO4BPVtXpqroKvC/J64DPADMZ9IP8YZU0y7qWbs4ChwYbkuwAHgUeAg4Cx5Ic7D/3TuBxYHlsPZUkbUqnEX1VXUyyd03z/cC1qroOkOQccAR4tqouABeSPA58dnzdlaTxa71UO0qNfjfwwsDjFeCBJA8C7wFezwYj+iQngZMACwsLI3RDkrSRsd+MraqvAV/r8LpFYBGg1+vVuPshSVo1StDfAO4ZeLyn39aZq1dKk+dkAo0yj/4ScCDJviR3AUeBC8McwNUrJWnyuk6vfAx4ENiZZAX4YFV9Kskp4AlWp1eeqaorw5zcEb2kaZmndzpdZ90cW6d9mRGmUFbVErDU6/VObPYYkqSNTXUJhCSHkyzeunVrmt2QpKa5w5QkNc5FzSSpcZZuJKlxlm4kqXGWbiSpcZZuJKlxlm4kqXGWbiSpcQa9JDVu7MsUD2O7rXUzT2tfjEPrmzVIrbBGL0mNs3QjSY0z6CWpcc6jl6TGWaOXpMZZupGkxhn0ktQ4g16SGmfQS1LjDHpJapzTKyWpcU6vlKTGWbqRpMYZ9JLUuKkuUyxpMlxye/NaXH7bEb0kNc4RvSSto5XRvUGvsWjlF0Jq0VwHvXVMSfNgIkGf5F3Aw8APA5+qqq9M4jxdOdqUNM86B32SM8A7gJtVdd9A+yHgI8AO4JNVdbqqvgh8McmbgD8Bphr0gxzFS5o3w8y6OQscGmxIsgN4FHgIOAgcS3Jw4CW/339ekjQlnUf0VXUxyd41zfcD16rqOkCSc8CRJFeB08CXq+of7nS8JCeBkwALCwvD91ySttAsl4BHnUe/G3hh4PFKv+13gLcBv5zkfXf6xqparKpeVfV27do1YjckSeuZyM3Yqvoo8NHXel2Sw8Dh/fv3T6IbkjQRsza6H3VEfwO4Z+Dxnn5bJ65eKUmTN2rQXwIOJNmX5C7gKHCh6ze7Hr0kTV7noE/yGPAUcG+SlSTHq+o2cAp4ArgKnK+qK12P6YhekiZvmFk3x9ZpXwaWx9YjSZviZ0S0nqkugTDJm7H+0EvSqqkGfVUtAUu9Xu/ENPshSZs1CzNw3Bxckhrn5uCS1Dh3mJKkxlm6kaTGWbqRpMZZupGkxhn0ktQ4a/SS1Dhr9JLUOEs3ktQ4g16SGmfQS1Ljml29UtMzC4s8SfPEm7GS1DhLN5LUOINekhpn0EtS46Z6M3bc3D5Qkr6fI3pJapzTKyVpTLbr1GKnV0pS4yzdSFLjDHpJapxBL0mNM+glqXEGvSQ1zqCXpMaNPeiT/FSSTyX53LiPLUkaXqegT3Imyc0kz6xpP5TkuSTXkjwCUFXXq+r4JDorSRpe1xH9WeDQYEOSHcCjwEPAQeBYkoNj7Z0kaWSdlkCoqotJ9q5pvh+4VlXXAZKcA44Az46zg5LW50J+6mKUtW52Ay8MPF4BHkjyY8AfAj+b5ANV9Ud3+uYkJ4GTAAsLCyN0Q5K2n7V/hKe59s3YFzWrqv8A3tfhdYvAIkCv16tx90OStGqUWTc3gHsGHu/pt3WW5HCSxVu3bo3QDUnSRkYJ+kvAgST7ktwFHAUuDHMAV6+UpMnrOr3yMeAp4N4kK0mOV9Vt4BTwBHAVOF9VV4Y5uSN6SZq8rrNujq3Tvgwsb/bkVbUELPV6vRObPYYkaWNTXQLBEb0kTZ47TElS41zUTJIaZ+lGkhpn6UaSGmfpRpIaZ+lGkhpn6UaSGmfpRpIaZ9BLUuOs0UtS46zRS1LjLN1IUuMMeklqnEEvSY0b+56xw0hyGDi8f//+TR9j7Qa8kqRX82asJDXO0o0kNc6gl6TGGfSS1DiDXpIaZ9BLUuNmfnqlJM2awWnhz59+eOLnc3qlJDXO0o0kNc6gl6TGGfSS1DiDXpIaZ9BLUuMMeklq3Njn0Se5G/g48BLwtar6q3GfQ5LUXacRfZIzSW4meWZN+6EkzyW5luSRfvN7gM9V1QngnWPuryRpSF1LN2eBQ4MNSXYAjwIPAQeBY0kOAnuAF/ov++54uilJ2qxOQV9VF4HvrGm+H7hWVder6iXgHHAEWGE17DsfX5I0OaPU6HfzysgdVgP+AeCjwMeSPAwsrffNSU4CJwEWFhZG6Ia2s61e00PS9xv7zdiq+m/gNzq8bhFYBOj1ejXufkiSVo1SWrkB3DPweE+/rbMkh5Ms3rp1a4RuSJI2MkrQXwIOJNmX5C7gKHBhmAO4eqUkTV7X6ZWPAU8B9yZZSXK8qm4Dp4AngKvA+aq6MszJHdFL0uR1qtFX1bF12peB5c2evKqWgKVer3dis8eQJG1sqtMfHdFL0uS5w5QkNc4PNElS4yzdSFLjUjX9zyoleRH45pROvxP49pTOvZ14HV7htVjldXjFdr0WP1lVu17rRdsi6KcpyeWq6k27H9PmdXiF12KV1+EVs34trNFLUuMMeklqnEHfX1hNXocBXotVXodXzPS1mPsavSS1zhG9JDVuLoM+ya8kuZLke0l6a577QH8P3OeSvH1afZyGJB9KciPJN/r/fmnafdpK6+yBPJeSPJ/kn/o/B5en3Z+tdKc9spP8aJKvJvmX/n/fNM0+Dmsugx54htVNzC8ONvb3vD0K/Ayre+R+vL837jz5s6p6a//fphesmzUb7IE8z36+/3Mws9MKN+ksa/bIBh4BnqyqA8CT/cczYy6DvqquVtVzd3jqCHCuqv63qv4VuMbq3rhq33p7IGvOrLNH9hHg0/2vPw28a0s7NaK5DPoN3Gkf3N1T6su0nEryj/23rzP19nRE/r9/tQK+kuTp/v7O8+7NVfWt/tf/Brx5mp0Z1tj3jN0ukvwt8BN3eOr3quqvt7o/28VG1wX4BPBhVn/JPwz8KfCbW9c7bSM/V1U3kvw48NUk/9wf6c69qqokMzVdsdmgr6q3beLbRt4Hd7vrel2S/CXwpQl3Zztp/v/9MKrqRv+/N5N8gdXS1jwH/b8neUtVfSvJW4Cb0+7QMCzdvNoF4GiS1yfZBxwA/n7Kfdoy/R/gl72b1ZvW82LkPZBbkeTuJD/08tfALzJfPwt3cgF4b//r9wIzVRVodkS/kSTvBv4c2AU8nuQbVfX2qrqS5DzwLHAb+O2q+u40+7rF/jjJW1kt3TwP/NZ0u7N1qup2kpf3QN4BnBl2D+SGvBn4QhJYzYjPVtXfTLdLW6e/R/aDwM4kK8AHgdPA+STHWV1p91en18Ph+clYSWqcpRtJapxBL0mNM+glqXEGvSQ1zqCXpMYZ9JLUOINekhpn0EtS4/4PXkEdraHUaZUAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEKZJREFUeJzt3X+s3Xddx/Hny87BaGFgwGvSLnbmbtO6CrLLBhL11qHcuZUZQ3B1LE7HGoibQJrIADXy3wJMHbhIGpiNYdnNGBPWUR0QVuGPgVv51Y06bWbDWnCFTKrF6dLs7R/3dNwUunvO6Tn3e/rp85E06fd7v+d7Xrfn9nW/53M+3+83VYUkqV0/1nUASdJ4WfSS1DiLXpIaZ9FLUuMseklqnEUvSY2z6CWpcRa9JDXOopekxp3W5ZMn2QhsfP7zn3/tueeeO9Q+vv/977Ny5crRBhsBcw3GXIOZ1FwwudlazLVr167vVtVLltywqjr/c8EFF9Sw7rvvvqEfO07mGoy5BjOpuaomN1uLuYAHq4+OdehGkhpn0UtS4yx6SWqcRS9JjbPoJalxFr0kNc6il6TGdVr0STYm2Xro0KEuY0hS0zo9M7aqtgPbZ2Zmru0yh3Q8uw8c4uobPrXkdvtuvHQZ0kjDcehGkhpn0UtS4yx6SWpcp2P0UpfW9jH2vmX9MgSRxswjeklqnEUvSY2z6CWpcRa9JDXOopekxln0ktS4kRd9ktkkX0jyoSSzo96/JGkwfRV9kluTHEzy0DHr55I8kmRvkht6qws4DDwX2D/auJKkQfV7RL8NmFu8IskK4BbgEmAdsCnJOuALVXUJ8A7gPaOLKkkaRl9FX1WfB544ZvWFwN6qerSqngLmgcur6une1/8TeM7IkkqShpKq6m/DZC1wT1Wd31t+PTBXVW/qLV8FXAR8Dngt8ELgb6pq53H2txnYDDA1NXXB/Pz8UN/A4cOHWbVq1VCPHSdzDaaLXLsPLH0fhKkz4PEnl97X+tVnjiBR/yb1dYTJzdZirg0bNuyqqpmlthv5tW6q6i7grj622wpsBZiZmanZ2dmhnm/nzp0M+9hxMtdgusjVz3Xmt6w/wk27l/5vsu/K2REk6t+kvo4wudlO5VwnMuvmAHDWouU1vXV98w5TkjR+J3JE/wBwTpKzWSj4K4DfHWQH3mFK49DPVSm7eE7vQqWu9Du98nbgfuC8JPuTXFNVR4DrgHuBPcAdVfXw+KJKkobR1xF9VW06zvodwI5hnzzJRmDj9PT0sLuQJC2h00sgVNX2qtp85pnLO2NBkk4lXutGkhrXadE760aSxs+hG0lqnEM3ktQ4h24kqXEO3UhS4xy6kaTGWfSS1DiLXpIa54exktQ4P4yVpMY5dCNJjbPoJalxFr0kNc4PYyWpcX4YK0mNc+hGkhpn0UtS4yx6SWqcRS9JjXPWjSQ1zlk3ktQ4h24kqXEWvSQ1zqKXpMZZ9JLUuNO6DiCdKtbe8Km+ttt346VjTqJTjUf0ktQ4i16SGucJU5LUOE+YkqTGOXQjSY2z6CWpcRa9JDXOopekxln0ktQ4i16SGmfRS1LjLHpJapxFL0mN8+qVOqn0ewVIST8wliP6JCuTPJjksnHsX5LUv76KPsmtSQ4meeiY9XNJHkmyN8kNi770DuCOUQaVJA2n3yP6bcDc4hVJVgC3AJcA64BNSdYl+XXgG8DBEeaUJA2przH6qvp8krXHrL4Q2FtVjwIkmQcuB1YBK1ko/yeT7Kiqp0eWWJI0kFRVfxsuFP09VXV+b/n1wFxVvam3fBVwUVVd11u+GvhuVd1znP1tBjYDTE1NXTA/Pz/UN3D48GFWrVo11GPHyVyDOfjEIR5/susUP2zqDJY91/rVS1+2e1JfR5jcbC3m2rBhw66qmllqu7HNuqmqbUt8fSuwFWBmZqZmZ2eHep6dO3cy7GPHyVyD+eBtn+Sm3ZM3CWzL+iPLnmvflbNLbjOpryNMbrZTOdeJzLo5AJy1aHlNb13fvMOUJI3fiRT9A8A5Sc5OcjpwBXD3IDvwDlOSNH79Tq+8HbgfOC/J/iTXVNUR4DrgXmAPcEdVPTy+qJKkYfQ762bTcdbvAHYM++RJNgIbp6enh92FJGkJnX76VVXbge0zMzPXdplDmiT9XOZh29zKZUiiVnhRM0lqXKdF76wbSRq/ToveWTeSNH4O3UhS4yx6SWqcY/SS1DjH6CWpcQ7dSFLjLHpJalynZ8Z6CQRpOLsPHOLqPs6g3XfjpcuQRpPOMXpJapxDN5LUOItekho3efduU3P6uRrjlvXLEEQ6RXnClCQ1zg9jJalxjtFLUuMseklqnEUvSY2z6CWpcc66kaTGOetGkhrn0I0kNc6il6TGWfSS1DivdaOh9XMNG3Wr39fI69a3zSN6SWqcRS9JjbPoJalxFr0kNc4zYyWpcZ4ZK0mNc+hGkhpn0UtS4yx6SWqcRS9JjbPoJalxFr0kNc6il6TGWfSS1DiLXpIaN/KiT/JzST6U5M4kbxn1/iVJg+mr6JPcmuRgkoeOWT+X5JEke5PcAFBVe6rqzcAbgFePPrIkaRD9HtFvA+YWr0iyArgFuARYB2xKsq73tdcBnwJ2jCypJGkofRV9VX0eeOKY1RcCe6vq0ap6CpgHLu9tf3dVXQJcOcqwkqTBpar62zBZC9xTVef3ll8PzFXVm3rLVwEXAXcCvw08B/h6Vd1ynP1tBjYDTE1NXTA/Pz/UN3D48GFWrVo11GPH6VTItfvA6C4vPXUGPP7kyHY3Mub6gfWr+7vK7Knwsz9KJ5Jrw4YNu6pqZqntRn5z8KraCezsY7utwFaAmZmZmp2dHer5du7cybCPHadTIdfVI7w5+Jb1R7hp9+Tdq95cP7Dvytm+tjsVfvZHaTlyncismwPAWYuW1/TWSZImyIkU/QPAOUnOTnI6cAVw9yA78A5TkjR+/U6vvB24Hzgvyf4k11TVEeA64F5gD3BHVT08yJN7hylJGr++BvmqatNx1u/gBKZQJtkIbJyenh52F5KkJXjPWElqnNe6kaTGdVr0fhgrSePn0I0kNc6hG0lq3OSd8qeJsHaEZ71K6pZj9JLUOMfoJalxDt1I6ku/w3nb5laOOYkG5YexktQ4x+glqXGdDt1U1XZg+8zMzLVd5jiV7D5waKTXkZc0+Ry6kaTGWfSS1DiLXpIa54exktQ4T5iSpMY5dCNJjfPMWEkj1c8U3n03XrpMaQQe0UtS8yx6SWqcs24kqXHOupGkxjl0I0mNs+glqXFOr2xIPzeG2LJ+GYJImigWvaRl1+/dqpxvPxoO3UhS4yx6SWqcRS9JjXOM/iTQ73imJP0onRZ9ko3Axunp6S5jSJpQ/Rzk+IHt0jwzVpIa5xi9JDXOopekxln0ktQ4i16SGuf0SkkntX6nH2+bWznmJJPLI3pJapxH9GPSzw2SwTnAksbPI3pJapxH9JJOCf28y271HbZH9JLUuLEc0Sf5LeBS4AXAR6rq0+N4HknS0vou+iS3ApcBB6vq/EXr54CbgRXAh6vqxqr6BPCJJC8C3g9Y9MfhlSkljdsgQzfbgLnFK5KsAG4BLgHWAZuSrFu0yZ/0vi5J6kiqqv+Nk7XAPUeP6JO8Cvjzqnptb/mdvU1v7P35TFV99jj72gxsBpiamrpgfn5+qG/g8OHDrFq1aqjHjtPBJw7x+JNdp/hhU2dgrgGYa3CTmq2fXOtXL/+VdE+kwzZs2LCrqmaW2u5Ex+hXA48tWt4PXARcD7wGODPJdFV96NgHVtVWYCvAzMxMzc7ODhVg586dDPvYcfrgbZ/kpt2TN6lpy/oj5hqAuQY3qdn6ybXvytnlCbPIcnTYWF6NqvoA8IFx7FuSNJgTnV55ADhr0fKa3rq+JNmYZOuhQ4dOMIYk6XhOtOgfAM5JcnaS04ErgLv7fbB3mJKk8eu76JPcDtwPnJdkf5JrquoIcB1wL7AHuKOqHh5gnx7RS9KY9T1GX1WbjrN+B7BjmCevqu3A9pmZmWuHebwkaWmT99G4JHWk3xMYT7Zr4nR6rRuHbiRp/Dotej+MlaTxc+hmCP28vduyfhmCSFIfLHpJGlA/B3uTNI7vGL0kNc4xeklqnEM3kjQG/U7V3Da3csxJvJWgJDWv0yP6JBuBjdPT013GeIZ3e5LUIsfoJalxDt1IUuMseklqnEUvSY3zhClJalyns26W83r0zqiRdKpy6EaSGmfRS1LjLHpJapxFL0mNO+kvarb7wCGu9oNWSToup1dKUuO81o0kNc4xeklqnEUvSY2z6CWpcRa9JDXOopekxln0ktQ4i16SGpeq6u7JezcHB34H+Lchd/Ni4LsjCzU65hqMuQYzqblgcrO1mOunq+olS23UadGPQpIHq2qm6xzHMtdgzDWYSc0Fk5vtVM7l0I0kNc6il6TGtVD0W7sOcBzmGoy5BjOpuWBys52yuU76MXpJ0rNr4YhekvQsmij6JC9L8sUkX03yYJILu850VJLrk/xLkoeTvLfrPIsl2ZKkkry46ywASd7X+7f6epK/T/LCjvPMJXkkyd4kN3SZ5agkZyW5L8k3ej9Tb+0602JJViT5SpJ7us5yVJIXJrmz97O1J8mrus4EkOTtvdfwoSS3J3nuuJ6riaIH3gu8p6peBvxZb7lzSTYAlwMvraqfB97fcaRnJDkL+A3gm11nWeQzwPlV9QvAvwLv7CpIkhXALcAlwDpgU5J1XeVZ5AiwparWAa8E/nBCch31VmBP1yGOcTPwj1X1s8BLmYB8SVYDfwTMVNX5wArginE9XytFX8ALen8/E/hWh1kWewtwY1X9H0BVHew4z2J/CfwxC/92E6GqPl1VR3qLXwTWdBjnQmBvVT1aVU8B8yz80u5UVX27qr7c+/t/s1Baq7tNtSDJGuBS4MNdZzkqyZnArwAfAaiqp6rqe92mesZpwBlJTgOexxh7q5WifxvwviSPsXDU3NmR4DHOBX45yZeS/FOSV3QdCCDJ5cCBqvpa11mexR8A/9Dh868GHlu0vJ8JKdSjkqwFfhH4UrdJnvFXLBw8PN11kEXOBr4D/G1vSOnDSVZ2HaqqDrDQVd8Evg0cqqpPj+v5Tpqbgyf5LPBTP+JL7wYuBt5eVR9P8gYWfnu/ZgJynQb8BAtvsV8B3JHkZ2oZpjotketdLAzbLLtny1VVn+xt824WhihuW85sJ5Mkq4CPA2+rqv+agDyXAQeraleS2a7zLHIa8HLg+qr6UpKbgRuAP+0yVJIXsfAO8Wzge8DHkryxqj46juc7aYq+qo5b3En+joWxQYCPsYxvHZfI9Rbgrl6x/3OSp1m4rsV3usqVZD0LP1xfSwILwyNfTnJhVf1HV7kW5bsauAy4eDl+IT6LA8BZi5bX9NZ1LsmPs1Dyt1XVXV3n6Xk18Lokvwk8F3hBko9W1Rs7zrUf2F9VR9/13MlC0XftNcC/V9V3AJLcBfwSMJaib2Xo5lvAr/b+/msMf4G0UfsEsAEgybnA6XR8UaWq2l1VP1lVa6tqLQv/EV6+HCW/lCRzLLz1f11V/U/HcR4AzklydpLTWfig7O6OM5GF384fAfZU1V90neeoqnpnVa3p/UxdAXxuAkqe3s/1Y0nO6626GPhGh5GO+ibwyiTP672mFzPGD4lPmiP6JVwL3Nz7UON/gc0d5znqVuDWJA8BTwG/1/FR6qT7a+A5wGd67za+WFVv7iJIVR1Jch1wLwszIm6tqoe7yHKMVwNXAbuTfLW37l1VtaPDTJPueuC23i/sR4Hf7zgPvWGkO4EvszBM+RXGeIasZ8ZKUuNaGbqRJB2HRS9JjbPoJalxFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuP+H455a1+Y9F3hAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "NEW PAIR 7\n", + " \n", + "dphi\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphiNor\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dz\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z0 res\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": [ + "dr\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dphi zcut\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zinner zcut\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAACs9JREFUeJzt3V+I5fdZx/HP08QoJnWVbg3SxG4LaXGJF61LW290Q0W2gSSgRRKo2hK7WKkXKmLFC0UREdELMVJXGqKiTWMuZKORXNQuQWlKNxRL0lKJsW22CukfHdgUjamPF3OiS8hmT2bOzNnzzOsFgXN+8zszz3dn8uac3/nzq+4OAHO9Yt0DALC3hB5gOKEHGE7oAYYTeoDhhB5gOKEHGE7oAYYTeoDhrlz3AEly+PDhPnLkyI5u+8wzz+Tqq69e7UCXOWs+GKz5YNjNmh999NGvdPerL7XfZRH6I0eO5OzZszu67ZkzZ3L8+PHVDnSZs+aDwZoPht2suaq+sMx+Dt0ADCf0AMMJPcBwQg8w3MpDX1Wvr6oPVdX9q/7eALx8S4W+qu6uqqer6rEXbD9RVZ+rqieq6gNJ0t1PdvedezEsAC/fsvfo70ly4sINVXVFkruSvCPJ0SR3VNXRlU4HwK4tFfrufjjJ116w+S1Jnljcg382yb1JblvxfADs0m7eMPWaJE9dcP1ckrdW1auS/GaSN1XVL3f3b73YjavqZJKTSXLttdfmzJkzOxri/PnzO77tprLmg8GaD4b9WPPK3xnb3V9N8tNL7HcqyakkOXbsWO/0nWHeSXcwWPPBYM17YzevuvlSkusvuH7dYhsAl5HdhP6TSW6oqtdV1VVJbk9yejVjAbAqy7688sNJPp7kjVV1rqru7O7nkrw/yUNJPpvkvu5+fO9GBWAnljpG3913XGT7g0keXOlEAKyUj0AAGE7oAYYTeoDh1hr6qrqlqk5tbW2tcwyA0dYa+u5+oLtPHjp0aJ1jAIzm0A3AcEIPMJzQAwwn9ADDCT3AcEIPMJzQAwwn9ADDeWcswHDeGQswnEM3AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwPusGYDifdQMwnEM3AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnA81AxjOh5oBDOfQDcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnM+jBxjO59EDDOfQDcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwTiUIMJxTCQIM59ANwHBCDzCc0AMMJ/QAwwk9wHBCDzCc0AMMJ/QAwwk9wHBCDzCc0AMMJ/QAwwk9wHBCDzCc0AMMJ/QAwwk9wHBCDzCc0AMMJ/QAwwk9wHBCDzCc0AMMt9bQV9UtVXVqa2trnWMAjLbW0Hf3A9198tChQ+scA2A0h24AhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhhN6gOGEHmA4oQcYbq2hr6pbqurU1tbWOscAGG2toe/uB7r75KFDh9Y5BsBoDt0ADCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMMJPcBwQg8wnNADDCf0AMNduepvWFVXJ/nDJM8mOdPdf77qnwHA8pa6R19Vd1fV01X12Au2n6iqz1XVE1X1gcXmH0lyf3e/N8mtK54XgJdp2UM39yQ5ceGGqroiyV1J3pHkaJI7qupokuuSPLXY7RurGROAnVoq9N39cJKvvWDzW5I80d1PdvezSe5NcluSc9mO/dLfH4C9U9293I5VR5L8dXffuLj+ziQnuvunFtd/PMlbk/xSkj9I8p9J/v5ix+ir6mSSk0ly7bXXft+99967owWcP38+11xzzY5uu6ms+WCw5oNhN2u+6aabHu3uY5fab+VPxnb3M0nes8R+p5KcSpJjx4718ePHd/Tzzpw5k53edlNZ88FgzQfDfqx5N4dWvpTk+guuX7fYBsBlZDeh/2SSG6rqdVV1VZLbk5xezVgArMqyL6/8cJKPJ3ljVZ2rqju7+7kk70/yUJLPJrmvux/fu1EB2ImljtF39x0X2f5gkgdXOhEAK+XljwDDCT3AcEIPMNxaQ19Vt1TVqa2trXWOATDa0u+M3dMhqr6c5As7vPnhJF9Z4TibwJoPBms+GHaz5td296svtdNlEfrdqKqzy7wFeBJrPhis+WDYjzU7Rg8wnNADDDch9KfWPcAaWPPBYM0Hw56veeOP0QPw0ibcowfgJWxM6C9yftoLv/7NVfWRxdc/sThRykZbYs0/X1WfqapPV9VHq+q165hzlS615gv2+9Gq6qra+FdoLLPmqvqxxe/68ar6i/2ecZWW+Lv+7qr6WFV9avG3ffM65lyli513+4KvV1X9/uLf5NNV9eaVDtDdl/1/Sa5I8s9JXp/kqiT/mOToC/b5mSQfXFy+PclH1j33Pqz5piTfurj8voOw5sV+r0zycJJHkhxb99z78Hu+IcmnknzH4vp3rnvuPV7vqSTvW1w+muTz6557Bev+gSRvTvLYRb5+c5K/TVJJ3pbkE6v8+Ztyj/5i56e90G1J/mRx+f4kb6+q2scZV+2Sa+7uj3X31xdXH8n/n6t3Uy3ze06S30jy29k+XeWmW2bN701yV3f/e5J099P7POMqLbPeTvJti8uHkvzrPs63J/rFz7t9oduS/GlveyTJt1fVd63q529K6F+T5KkLrp9bbHvRfXr7s/K3krxqX6bbG8us+UJ3ZvsewSa75JoXD2mv7+6/2c/B9tAyv+c3JHlDVf1DVT1SVSf2bbrVW2a9v5bkXVV1Ltsfg/6z+zPaWr3c/99flpWfM5b9V1XvSnIsyQ+ue5a9VFWvSPJ7Sd695lH225XZPnxzPNuP2h6uqu/t7v9Y61R7544k93T371bV9yf5s6q6sbv/Z92DbapNuUe/zPlp/2+fqroy2w/5vrov0+2Npc7JW1U/lORXktza3f+1T7PtlUut+ZVJbkxypqo+n+1jmac3/AnZZX7P55Kc7u7/7u5/SfJP2Q7/JlpmvXcmuS9JuvvjSb4l258HM9menoN7U0K/zPlpTyf5ycXldyb5u148y7GhLrnmqnpTkj/KduQ3+bjt815yzd291d2Hu/tIdx/J9vMSt3b32fWMuxLL/G3/VbbvzaeqDmf7UM6T+znkCi2z3i8meXuSVNX3ZDv0X97XKfff6SQ/sXj1zduSbHX3v63qm2/EoZvufq6qnj8/7RVJ7u7ux6vq15Oc7e7TST6U7Yd4T2T7SY/b1zfx7i255t9Jck2Sv1w87/zF7r51bUPv0pJrHmXJNT+U5Ier6jNJvpHkF7t7Ix+tLrneX0jyx1X1c9l+YvbdG36n7fnzbh9Pcnjx3MOvJvmmJOnuD2b7uYibkzyR5OtJ3rPSn7/h/34AXMKmHLoBYIeEHmA4oQcYTugBhhN6gOGEHmA4oQcYTugBhvtfrXT6VkeCpcIAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "module\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dysize\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD8CAYAAAB5Pm/hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAEMVJREFUeJzt3X+s3Xddx/Hny87BaMPADGvSLbamY1pXQXbZQKLeCsqdW5kxBNeMxelYA3ETSBMpoEb+a4CpQxdJA7MxLGvGmLAf1QFhFf4YuJVf3ajTZjasBVcIUu2cLs3e/nFP56XS3e89Ped+Tz/3+Uia9Pu93/M9r9tz+jrf8/n+SlUhSWrXj/QdQJI0Xha9JDXOopekxln0ktQ4i16SGmfRS1LjLHpJapxFL0mNs+glqXFn9B0A4JxzzqnVq1cP9dgnn3yS5cuXjzbQCJhrYcy1MJOaCyY3W4u59uzZ892qesm8C1ZV738uuuiiGtb9998/9GPHyVwLY66FmdRcVZObrcVcwEPVoWMdupGkxln0ktQ4i16SGmfRS1LjLHpJapxFL0mNs+glqXG9Fn2SjUm2HzlypM8YktS0Xs+Mraq7gbunpqau6zOHdDJ7Dx3hmq33zrvcgW2XLUIaaTgO3UhS4yx6SWqcRS9JjbPoJalxE3GZYqkPqzvsZN2yfhGCSGPmFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuNGXvRJppN8IcmHk0yPev2SpIXpVPRJbklyOMnDJ8yfSfJokv1Jtg5mF3AUeD5wcLRxJUkL1XWLfgcwM3dGkmXAzcClwDpgU5J1wBeq6lLgXcD7RhdVkjSMTkVfVZ8HvnfC7IuB/VX1WFU9DewErqiqZwY//3fgeSNLKkkaSqqq24LJauCeqrpwMP1GYKaq3jKYvhq4BPgc8HrgRcBfVdXuk6xvM7AZYOXKlRft3LlzqF/g6NGjrFixYqjHjpO5FqaPXHsPzX8fhJVnwRNPzb+u9avOHkGi7ib1dYTJzdZirg0bNuypqqn5lhv5JRCq6k7gzg7LbQe2A0xNTdX09PRQz7d7926Gfew4mWthRpmry6UNZs3/9t+y/hg37p1/uQNXTXd8ztGY1NcRJjfbUs51KkV/CDhvzvS5g3mdJdkIbFy7du0pxJD61+XDxZuTqC+ncnjlg8D5SdYkORO4ErhrISuoqruravPZZy/u115JWkq6Hl55G/AAcEGSg0murapjwPXAfcA+4PaqemR8USVJw+g0dFNVm04yfxewa9gnd+hGksav10sgOHQjSePntW4kqXG9Fn2SjUm2Hzky//HMkqThOHQjSY1z6EaSGufQjSQ1zqEbSWqcQzeS1DiLXpIa5xi9JDXOMXpJapxDN5LUOItekhpn0UtS49wZK0mNc2esJDXOoRtJapxFL0mNs+glqXEWvSQ1zqKXpMZ5eKUkNc7DKyWpcQ7dSFLjLHpJatwZfQeQlorVW+/ttNyBbZeNOYmWGrfoJalxFr0kNc6il6TGWfSS1DhPmJKkxnnClCQ1zqEbSWqcRS9JjbPoJalxFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuMseklqnEUvSY0bS9EnWZ7koSSXj2P9kqTuOhV9kluSHE7y8AnzZ5I8mmR/kq1zfvQu4PZRBpUkDafrFv0OYGbujCTLgJuBS4F1wKYk65L8KvAN4PAIc0qShtTpnrFV9fkkq0+YfTGwv6oeA0iyE7gCWAEsZ7b8n0qyq6qeGVliSdKCpKq6LThb9PdU1YWD6TcCM1X1lsH01cAlVXX9YPoa4LtVdc9J1rcZ2AywcuXKi3bu3DnUL3D06FFWrFgx1GPHyVwLM8pcew+N7v4GK8+CJ54a2eo6Wb9q/st2T+rrCJObrcVcGzZs2FNVU/Mt12mLfhhVtWOen28HtgNMTU3V9PT0UM+ze/duhn3sOJlrYUaZ65qt945kPQBb1h/jxr1j+2/yQx24anreZSb1dYTJzbaUc53KO/gQcN6c6XMH8zpLshHYuHbt2lOIoaVk9QhLXFoqTuXwygeB85OsSXImcCVw10JW4B2mJGn8uh5eeRvwAHBBkoNJrq2qY8D1wH3APuD2qnpkfFElScPoetTNppPM3wXsGvbJHbqRpPFb3L1MJ6iqu4G7p6amruszh/q399CRke5ElfR/ei16Sf9flx3OO2aWL0IStaLXi5ol2Zhk+5EjozvuWZL0g3oteo+6kaTx8zLFktQ4h24kqXEO3UhS4xy6kaTGWfSS1DiLXpIa585YSWqcl0CQTkNdLxlxYNtli5BGk86hG0lqnEUvSY2z6CWpce6MlaTGuTNWY9flsrtb1i9CEGmJcuhGkhpn0UtS4yx6SWqcRS9JjfOoG0lqnNejl6TGOXQjSY3r9Th6SePV5RwG8OJnrXOLXpIa5xa9htZ1a1FSv9yil6TGWfSS1DiLXpIa5wlTktQ4T5iSpMY5dCNJjbPoJalxFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuMseklqnEUvSY2z6CWpcSMv+iQ/k+TDSe5I8rZRr1+StDCdij7JLUkOJ3n4hPkzSR5Nsj/JVoCq2ldVbwXeBLxm9JElSQvRdYt+BzAzd0aSZcDNwKXAOmBTknWDn70BuBfYNbKkkqShdCr6qvo88L0TZl8M7K+qx6rqaWAncMVg+buq6lLgqlGGlSQtXKqq24LJauCeqrpwMP1GYKaq3jKYvhq4BLgD+E3gecDXq+rmk6xvM7AZYOXKlRft3LlzqF/g6NGjrFixYqjHjtNSyLX30OjuI7DyLHjiqZGtbmSWSq71q0Z3qfCl8N4fpVPJtWHDhj1VNTXfciO/OXhV7QZ2d1huO7AdYGpqqqanp4d6vt27dzPsY8dpKeS6ZoQ3B9+y/hg37p28e9UvlVwHrpoe2bqWwnt/lBYj16m8Uw4B582ZPncwr7MkG4GNa9euPYUYkk7V6g4f2ge2XbYISTQOp3J45YPA+UnWJDkTuBK4ayEr8A5TkjR+XQ+vvA14ALggycEk11bVMeB64D5gH3B7VT0yvqiSpGF0Grqpqk0nmb+LUziE0qEbSRo/bw4uSY3zWjeS1Lheiz7JxiTbjxwZ3fHYkqQf5NCNJDXOoRtJapxFL0mN6/Xcbg+vnFxdzpSUdHpwjF6SGjd5V2vSWO09dGSkFyOTNPkco5ekxnkcvSQ1zjF6SWqcY/SSOul6JNaOmeVjTqKFcoxekhpn0UtS49wZK0mNc2esJDXOoRtJapxFL0mNs+glqXEWvSQ1zssUSxqpLhfOO7DtskVKI/CoG0lqnkM3ktQ4i16SGmfRS1LjvHqlpEXX9UqY7rQdDYu+IV3+82xZvwhBJE0Uh24kqXEWvSQ1zqGb00DX8UxJ+mG8Hr0kNc4zYyWpcY7RS1LjHKOXNLG67J/yWPv5uUUvSY2z6CWpcRa9JDXOMXpJp7Wu55nsmFk+5iSTyy16SWqcRS9JjbPoJalxFr0kNW4sO2OT/AZwGfBC4KNV9elxPI8kaX6diz7JLcDlwOGqunDO/BngJmAZ8JGq2lZVnwQ+meTFwAeBJVf0ew8d4RrP6pM0ARYydLMDmJk7I8ky4GbgUmAdsCnJujmL/OHg55KknqSqui+crAbuOb5Fn+TVwJ9U1esH0+8eLLpt8OczVfXZk6xrM7AZYOXKlRft3LlzqF/g6NGjrFixYqjHjtPh7x3hiafmX279qvmv3Ln30Ogu47zyLDrlWmzmWphJzQWTm23N2csmsitOpcM2bNiwp6qm5lvuVMfoVwGPz5k+CFwC3AC8Djg7ydqq+vCJD6yq7cB2gKmpqZqenh4qwO7duxn2seP0F7d+ihv3dvjn3ftkh7WNblfKlvXHuuVaZOZamEnNBZObbcfM8onsisXosLG8GlX1IeBD41i3JGlhTvXwykPAeXOmzx3M68Q7TEnS+J1q0T8InJ9kTZIzgSuBu7o+2DtMSdL4dS76JLcBDwAXJDmY5NqqOgZcD9wH7ANur6pHFrBOt+glacw6j9FX1aaTzN8F7BrmyavqbuDuqamp64Z5vCR11eXcllbPa/ESCJLUuF6PgUqyEdi4du3aPmM8q+t1rVv91JfUpl636N0ZK0nj59CNJDVu8k5fk6SetDp82+sWvYdXStL49bpF7+GVkk5HXbb8J2mr36GbIXR5kbesX4QgktSBRS9JY9B1vH/HzPIxJ3GMXpKa53H0ktS4037oxnuzStJz84QpSWqcRS9JjXNnrCQ1zp2xktQ4h24kqXEWvSQ1zqKXpMZZ9JLUuNP+hKmuul53QpJa4+GVktQ4D6+UpMY5Ri9JjbPoJalxFr0kNc6il6TGWfSS1DiLXpIaZ9FLUuNSVf09ebIR2Aj8FvAvQ67mHOC7Iws1OuZaGHMtzKTmgsnN1mKun6yql8y3UK9FPwpJHqqqqb5znMhcC2OuhZnUXDC52ZZyLoduJKlxFr0kNa6Fot/ed4CTMNfCmGthJjUXTG62JZvrtB+jlyQ9txa26CVJz6GJok/y8iRfTPLVJA8lubjvTMcluSHJPyV5JMn7+84zV5ItSSrJOX1nAUjygcG/1deT/G2SF/WcZybJo0n2J9naZ5bjkpyX5P4k3xi8p97ed6a5kixL8pUk9/Sd5bgkL0pyx+C9tS/Jq/vOBJDknYPX8OEktyV5/rieq4miB94PvK+qXg788WC6d0k2AFcAL6uqnwU+2HOkZyU5D/g14Jt9Z5njM8CFVfVzwD8D7+4rSJJlwM3ApcA6YFOSdX3lmeMYsKWq1gGvAn5vQnId93ZgX98hTnAT8PdV9dPAy5iAfElWAb8PTFXVhcAy4MpxPV8rRV/ACwd/Pxv4Vo9Z5nobsK2q/gegqg73nGeuPwP+gNl/u4lQVZ+uqmODyS8C5/YY52Jgf1U9VlVPAzuZ/dDuVVV9u6q+PPj7fzJbWqv6TTUrybnAZcBH+s5yXJKzgV8CPgpQVU9X1ff7TfWsM4CzkpwBvIAx9lYrRf8O4ANJHmd2q7m3LcETvBT4xSRfSvIPSV7ZdyCAJFcAh6rqa31neQ6/C/xdj8+/Cnh8zvRBJqRQj0uyGvh54Ev9JnnWnzO78fBM30HmWAN8B/jrwZDSR5Is7ztUVR1itqu+CXwbOFJVnx7X8502NwdP8lngJ37Ij94LvBZ4Z1V9IsmbmP30ft0E5DoD+DFmv2K/Erg9yU/VIhzqNE+u9zA7bLPonitXVX1qsMx7mR2iuHUxs51OkqwAPgG8o6r+YwLyXA4crqo9Sab7zjPHGcArgBuq6ktJbgK2An/UZ6gkL2b2G+Ia4PvAx5O8uao+No7nO22KvqpOWtxJ/obZsUGAj7OIXx3nyfU24M5Bsf9jkmeYva7Fd/rKlWQ9s2+uryWB2eGRLye5uKr+ra9cc/JdA1wOvHYxPhCfwyHgvDnT5w7m9S7JjzJb8rdW1Z195xl4DfCGJL8OPB94YZKPVdWbe851EDhYVce/9dzBbNH37XXAv1bVdwCS3An8AjCWom9l6OZbwC8P/v4rDH+BtFH7JLABIMlLgTPp+aJKVbW3qn68qlZX1Wpm/yO8YjFKfj5JZpj96v+GqvqvnuM8CJyfZE2SM5ndUXZXz5nI7KfzR4F9VfWnfec5rqreXVXnDt5TVwKfm4CSZ/C+fjzJBYNZrwW+0WOk474JvCrJCwav6WsZ407i02aLfh7XATcNdmr8N7C55zzH3QLckuRh4Gngt3veSp10fwk8D/jM4NvGF6vqrX0EqapjSa4H7mP2iIhbquqRPrKc4DXA1cDeJF8dzHtPVe3qMdOkuwG4dfCB/RjwOz3nYTCMdAfwZWaHKb/CGM+Q9cxYSWpcK0M3kqSTsOglqXEWvSQ1zqKXpMZZ9JLUOItekhpn0UtS4yx6SWrc/wKgqWzCjtTImQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ysize\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ys-pred\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "kp=0\n", + "for quad in lpairs :\n", + " plotPairs(quad,500,kp)\n", + " kp+=1" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/BuildFile.xml b/RecoPixelVertexing/PixelVertexFinding/plugins/BuildFile.xml index 427799cb122b5..99b91b2587bcf 100644 --- a/RecoPixelVertexing/PixelVertexFinding/plugins/BuildFile.xml +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/BuildFile.xml @@ -1,3 +1,4 @@ + @@ -15,10 +16,12 @@ + + - + diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexCollectionTrimmer.cc b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexCollectionTrimmer.cc index c6747707ada73..91dfc0393b432 100644 --- a/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexCollectionTrimmer.cc +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexCollectionTrimmer.cc @@ -3,7 +3,7 @@ // Package: RecoPixelVertexing/PixelVertexFinding // Class: PixelVertexCollectionTrimmer // -/**\class PixelVertexCollectionTrimmer PixelVertexCollectionTrimmer.cc RecoPixelVertexing/PixelVertexFinding/src/PixelVertexCollectionTrimmer.cc +/**\class PixelVertexCollectionTrimmer PixelVertexCollectionTrimmer.cc RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexCollectionTrimmer.cc Description: [one line class summary] diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexProducerCUDA.cc b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexProducerCUDA.cc new file mode 100644 index 0000000000000..e9054dbf17c53 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexProducerCUDA.cc @@ -0,0 +1,125 @@ +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/RunningAverage.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" + +#include "gpuVertexFinder.h" + +class PixelVertexProducerCUDA : public edm::global::EDProducer<> { +public: + explicit PixelVertexProducerCUDA(const edm::ParameterSet& iConfig); + ~PixelVertexProducerCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + + bool m_OnGPU; + + edm::EDGetTokenT> tokenGPUTrack_; + edm::EDPutTokenT tokenGPUVertex_; + edm::EDGetTokenT tokenCPUTrack_; + edm::EDPutTokenT tokenCPUVertex_; + + const gpuVertexFinder::Producer m_gpuAlgo; + + // Tracking cuts before sending tracks to vertex algo + const float m_ptMin; +}; + +PixelVertexProducerCUDA::PixelVertexProducerCUDA(const edm::ParameterSet& conf) + : m_OnGPU(conf.getParameter("onGPU")), + m_gpuAlgo(conf.getParameter("oneKernel"), + conf.getParameter("useDensity"), + conf.getParameter("useDBSCAN"), + conf.getParameter("useIterative"), + conf.getParameter("minT"), + conf.getParameter("eps"), + conf.getParameter("errmax"), + conf.getParameter("chi2max")), + m_ptMin(conf.getParameter("PtMin")) // 0.5 GeV +{ + if (m_OnGPU) { + tokenGPUTrack_ = + consumes>(conf.getParameter("pixelTrackSrc")); + tokenGPUVertex_ = produces(); + } else { + tokenCPUTrack_ = consumes(conf.getParameter("pixelTrackSrc")); + tokenCPUVertex_ = produces(); + } +} + +void PixelVertexProducerCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + // Only one of these three algos can be used at once. + // Maybe this should become a Plugin Factory + desc.add("onGPU", true); + desc.add("oneKernel", true); + desc.add("useDensity", true); + desc.add("useDBSCAN", false); + desc.add("useIterative", false); + + desc.add("minT", 2); // min number of neighbours to be "core" + desc.add("eps", 0.07); // max absolute distance to cluster + desc.add("errmax", 0.01); // max error to be "seed" + desc.add("chi2max", 9.); // max normalized distance to cluster + + desc.add("PtMin", 0.5); + desc.add("pixelTrackSrc", edm::InputTag("caHitNtupletCUDA")); + + auto label = "pixelVertexCUDA"; + descriptions.add(label, desc); +} + +void PixelVertexProducerCUDA::produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const { + if (m_OnGPU) { + edm::Handle> hTracks; + iEvent.getByToken(tokenGPUTrack_, hTracks); + + cms::cuda::ScopedContextProduce ctx{*hTracks}; + auto const* tracks = ctx.get(*hTracks).get(); + + assert(tracks); + + ctx.emplace(iEvent, tokenGPUVertex_, m_gpuAlgo.makeAsync(ctx.stream(), tracks, m_ptMin)); + + } else { + auto const* tracks = iEvent.get(tokenCPUTrack_).get(); + assert(tracks); + + /* + auto const & tsoa = *tracks; + auto maxTracks = tsoa.stride(); + std::cout << "size of SoA " << sizeof(tsoa) << " stride " << maxTracks << std::endl; + + int32_t nt = 0; + for (int32_t it = 0; it < maxTracks; ++it) { + auto nHits = tsoa.nHits(it); + assert(nHits==int(tsoa.hitIndices.size(it))); + if (nHits == 0) break; // this is a guard: maybe we need to move to nTracks... + nt++; + } + std::cout << "found " << nt << " tracks in cpu SoA for Vertexing at " << tracks << std::endl; + */ + + iEvent.emplace(tokenCPUVertex_, m_gpuAlgo.make(tracks, m_ptMin)); + } +} + +DEFINE_FWK_MODULE(PixelVertexProducerCUDA); diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexProducerFromSoA.cc b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexProducerFromSoA.cc new file mode 100644 index 0000000000000..e642e3fd734f9 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexProducerFromSoA.cc @@ -0,0 +1,175 @@ +#include "DataFormats/BeamSpot/interface/BeamSpot.h" +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "DataFormats/TrackReco/interface/Track.h" +#include "DataFormats/TrackReco/interface/TrackExtra.h" +#include "DataFormats/TrackReco/interface/TrackFwd.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" + +#include "CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h" + +#include "DataFormats/VertexReco/interface/Vertex.h" +#include "DataFormats/VertexReco/interface/VertexFwd.h" + +class PixelVertexProducerFromSoA : public edm::global::EDProducer<> { +public: + using IndToEdm = std::vector; + + explicit PixelVertexProducerFromSoA(const edm::ParameterSet &iConfig); + ~PixelVertexProducerFromSoA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions &descriptions); + +private: + void produce(edm::StreamID streamID, edm::Event &iEvent, const edm::EventSetup &iSetup) const override; + + edm::EDGetTokenT tokenVertex_; + edm::EDGetTokenT tokenBeamSpot_; + edm::EDGetTokenT tokenTracks_; + edm::EDGetTokenT tokenIndToEdm_; +}; + +PixelVertexProducerFromSoA::PixelVertexProducerFromSoA(const edm::ParameterSet &conf) + : tokenVertex_(consumes(conf.getParameter("src"))), + tokenBeamSpot_(consumes(conf.getParameter("beamSpot"))), + tokenTracks_(consumes(conf.getParameter("TrackCollection"))), + tokenIndToEdm_(consumes(conf.getParameter("TrackCollection"))) { + produces(); +} + +void PixelVertexProducerFromSoA::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { + edm::ParameterSetDescription desc; + + desc.add("TrackCollection", edm::InputTag("pixelTracks")); + desc.add("beamSpot", edm::InputTag("offlineBeamSpot")); + desc.add("src", edm::InputTag("pixelVertexSoA")); + + descriptions.add("pixelVertexFromSoA", desc); +} + +void PixelVertexProducerFromSoA::produce(edm::StreamID streamID, edm::Event &iEvent, const edm::EventSetup &) const { + auto vertexes = std::make_unique(); + + edm::Handle trackCollection; + iEvent.getByToken(tokenTracks_, trackCollection); + auto const &tracks = *(trackCollection.product()); + edm::Handle indToEdmH; + iEvent.getByToken(tokenIndToEdm_, indToEdmH); + auto const &indToEdm = *indToEdmH; + + edm::Handle bsHandle; + iEvent.getByToken(tokenBeamSpot_, bsHandle); + + float x0 = 0, y0 = 0, z0 = 0, dxdz = 0, dydz = 0; + std::vector itrk; + if (!bsHandle.isValid()) { + edm::LogWarning("PixelVertexProducer") << "No beamspot found. returning vertexes with (0,0,Z) "; + } else { + const reco::BeamSpot &bs = *bsHandle; + x0 = bs.x0(); + y0 = bs.y0(); + z0 = bs.z0(); + dxdz = bs.dxdz(); + dydz = bs.dydz(); + } + + auto const &soa = *(iEvent.get(tokenVertex_).get()); + + int nv = soa.nvFinal; + + // std::cout << "converting " << nv << " vertices " << " from " << indToEdm.size() << " tracks" << std::endl; + + std::set uind; // fort verifing index consistency + for (int j = nv - 1; j >= 0; --j) { + auto i = soa.sortInd[j]; // on gpu sorted in ascending order.... + assert(i < nv); + uind.insert(i); + assert(itrk.empty()); + auto z = soa.zv[i]; + auto x = x0 + dxdz * z; + auto y = y0 + dydz * z; + z += z0; + reco::Vertex::Error err; + err(2, 2) = 1.f / soa.wv[i]; + err(2, 2) *= 2.; // artifically inflate error + //Copy also the tracks (no intention to be efficient....) + for (auto k = 0U; k < indToEdm.size(); ++k) { + if (soa.idv[k] == int16_t(i)) + itrk.push_back(k); + } + auto nt = itrk.size(); + if (nt == 0) { + std::cout << "vertex " << i << " with no tracks..." << std::endl; + continue; + } + if (nt < 2) { + itrk.clear(); + continue; + } // remove outliers + (*vertexes).emplace_back(reco::Vertex::Point(x, y, z), err, soa.chi2[i], soa.ndof[i], nt); + auto &v = (*vertexes).back(); + for (auto it : itrk) { + assert(it < int(indToEdm.size())); + auto k = indToEdm[it]; + if (k > tracks.size()) { + edm::LogWarning("PixelVertexProducer") << "oops track " << it << " does not exists on CPU " << k; + continue; + } + auto tk = reco::TrackRef(trackCollection, k); + v.add(reco::TrackBaseRef(tk)); + } + itrk.clear(); + } + + LogDebug("PixelVertexProducer") << ": Found " << vertexes->size() << " vertexes\n"; + for (unsigned int i = 0; i < vertexes->size(); ++i) { + LogDebug("PixelVertexProducer") << "Vertex number " << i << " has " << (*vertexes)[i].tracksSize() + << " tracks with a position of " << (*vertexes)[i].z() << " +- " + << std::sqrt((*vertexes)[i].covariance(2, 2)); + } + + // legacy logic.... + if (vertexes->empty() && bsHandle.isValid()) { + const reco::BeamSpot &bs = *bsHandle; + + GlobalError bse(bs.rotatedCovariance3D()); + if ((bse.cxx() <= 0.) || (bse.cyy() <= 0.) || (bse.czz() <= 0.)) { + AlgebraicSymMatrix33 we; + we(0, 0) = 10000; + we(1, 1) = 10000; + we(2, 2) = 10000; + vertexes->push_back(reco::Vertex(bs.position(), we, 0., 0., 0)); + + edm::LogInfo("PixelVertexProducer") << "No vertices found. Beamspot with invalid errors " << bse.matrix() + << "\nWill put Vertex derived from dummy-fake BeamSpot into Event.\n" + << (*vertexes)[0].x() << "\n" + << (*vertexes)[0].y() << "\n" + << (*vertexes)[0].z() << "\n"; + } else { + vertexes->push_back(reco::Vertex(bs.position(), bs.rotatedCovariance3D(), 0., 0., 0)); + + edm::LogInfo("PixelVertexProducer") << "No vertices found. Will put Vertex derived from BeamSpot into Event:\n" + << (*vertexes)[0].x() << "\n" + << (*vertexes)[0].y() << "\n" + << (*vertexes)[0].z() << "\n"; + } + } else if (vertexes->empty() && !bsHandle.isValid()) { + edm::LogWarning("PixelVertexProducer") << "No beamspot and no vertex found. No vertex returned."; + } + + iEvent.put(std::move(vertexes)); +} + +DEFINE_FWK_MODULE(PixelVertexProducerFromSoA); diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexSoAFromCUDA.cc b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexSoAFromCUDA.cc new file mode 100644 index 0000000000000..0cadf24580cf7 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/PixelVertexSoAFromCUDA.cc @@ -0,0 +1,65 @@ +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/Common/interface/HostProduct.h" +#include "CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" + +class PixelVertexSoAFromCUDA : public edm::stream::EDProducer { +public: + explicit PixelVertexSoAFromCUDA(const edm::ParameterSet& iConfig); + ~PixelVertexSoAFromCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void acquire(edm::Event const& iEvent, + edm::EventSetup const& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) override; + void produce(edm::Event& iEvent, edm::EventSetup const& iSetup) override; + + edm::EDGetTokenT> tokenCUDA_; + edm::EDPutTokenT tokenSOA_; + + cms::cuda::host::unique_ptr m_soa; +}; + +PixelVertexSoAFromCUDA::PixelVertexSoAFromCUDA(const edm::ParameterSet& iConfig) + : tokenCUDA_(consumes>(iConfig.getParameter("src"))), + tokenSOA_(produces()) {} + +void PixelVertexSoAFromCUDA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + + desc.add("src", edm::InputTag("pixelVertexCUDA")); + descriptions.add("pixelVertexSoA", desc); +} + +void PixelVertexSoAFromCUDA::acquire(edm::Event const& iEvent, + edm::EventSetup const& iSetup, + edm::WaitingTaskWithArenaHolder waitingTaskHolder) { + auto const& inputDataWrapped = iEvent.get(tokenCUDA_); + cms::cuda::ScopedContextAcquire ctx{inputDataWrapped, std::move(waitingTaskHolder)}; + auto const& inputData = ctx.get(inputDataWrapped); + + m_soa = inputData.toHostAsync(ctx.stream()); +} + +void PixelVertexSoAFromCUDA::produce(edm::Event& iEvent, edm::EventSetup const& iSetup) { + // No copies.... + iEvent.emplace(tokenSOA_, ZVertexHeterogeneous(std::move(m_soa))); +} + +DEFINE_FWK_MODULE(PixelVertexSoAFromCUDA); diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksByDensity.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksByDensity.h new file mode 100644 index 0000000000000..b32c7d5b613db --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksByDensity.h @@ -0,0 +1,234 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksByDensity_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksByDensity_h + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + // this algo does not really scale as it works in a single block... + // enough for <10K tracks we have + // + // based on Rodrighez&Laio algo + // + __device__ __forceinline__ void clusterTracksByDensity(gpuVertexFinder::ZVertices* pdata, + gpuVertexFinder::WorkSpace* pws, + int minT, // min number of neighbours to be "seed" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster + ) { + using namespace gpuVertexFinder; + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + if (verbose && 0 == threadIdx.x) + printf("params %d %f %f %f\n", minT, eps, errmax, chi2max); + + auto er2mx = errmax * errmax; + + auto& __restrict__ data = *pdata; + auto& __restrict__ ws = *pws; + auto nt = ws.ntrks; + float const* __restrict__ zt = ws.zt; + float const* __restrict__ ezt2 = ws.ezt2; + + uint32_t& nvFinal = data.nvFinal; + uint32_t& nvIntermediate = ws.nvIntermediate; + + uint8_t* __restrict__ izt = ws.izt; + int32_t* __restrict__ nn = data.ndof; + int32_t* __restrict__ iv = ws.iv; + + assert(pdata); + assert(zt); + + using Hist = cms::cuda::HistoContainer; + __shared__ Hist hist; + __shared__ typename Hist::Counter hws[32]; + for (auto j = threadIdx.x; j < Hist::totbins(); j += blockDim.x) { + hist.off[j] = 0; + } + __syncthreads(); + + if (verbose && 0 == threadIdx.x) + printf("booked hist with %d bins, size %d for %d tracks\n", hist.nbins(), hist.capacity(), nt); + + assert(nt <= hist.capacity()); + + // fill hist (bin shall be wider than "eps") + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + assert(i < ZVertices::MAXTRACKS); + int iz = int(zt[i] * 10.); // valid if eps<=0.1 + // iz = std::clamp(iz, INT8_MIN, INT8_MAX); // sorry c++17 only + iz = std::min(std::max(iz, INT8_MIN), INT8_MAX); + izt[i] = iz - INT8_MIN; + assert(iz - INT8_MIN >= 0); + assert(iz - INT8_MIN < 256); + hist.count(izt[i]); + iv[i] = i; + nn[i] = 0; + } + __syncthreads(); + if (threadIdx.x < 32) + hws[threadIdx.x] = 0; // used by prefix scan... + __syncthreads(); + hist.finalize(hws); + __syncthreads(); + assert(hist.size() == nt); + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + hist.fill(izt[i], uint16_t(i)); + } + __syncthreads(); + + // count neighbours + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (ezt2[i] > er2mx) + continue; + auto loop = [&](uint32_t j) { + if (i == j) + return; + auto dist = std::abs(zt[i] - zt[j]); + if (dist > eps) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; + nn[i]++; + }; + + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __syncthreads(); + + // find closest above me .... (we ignore the possibility of two j at same distance from i) + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + float mdist = eps; + auto loop = [&](uint32_t j) { + if (nn[j] < nn[i]) + return; + if (nn[j] == nn[i] && zt[j] >= zt[i]) + return; // if equal use natural order... + auto dist = std::abs(zt[i] - zt[j]); + if (dist > mdist) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; // (break natural order???) + mdist = dist; + iv[i] = j; // assign to cluster (better be unique??) + }; + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __syncthreads(); + +#ifdef GPU_DEBUG + // mini verification + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] != int(i)) + assert(iv[iv[i]] != int(i)); + } + __syncthreads(); +#endif + + // consolidate graph (percolate index of seed) + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + auto m = iv[i]; + while (m != iv[m]) + m = iv[m]; + iv[i] = m; + } + +#ifdef GPU_DEBUG + __syncthreads(); + // mini verification + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] != int(i)) + assert(iv[iv[i]] != int(i)); + } +#endif + +#ifdef GPU_DEBUG + // and verify that we did not spit any cluster... + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + auto minJ = i; + auto mdist = eps; + auto loop = [&](uint32_t j) { + if (nn[j] < nn[i]) + return; + if (nn[j] == nn[i] && zt[j] >= zt[i]) + return; // if equal use natural order... + auto dist = std::abs(zt[i] - zt[j]); + if (dist > mdist) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; + mdist = dist; + minJ = j; + }; + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + // should belong to the same cluster... + assert(iv[i] == iv[minJ]); + assert(nn[i] <= nn[iv[i]]); + } + __syncthreads(); +#endif + + __shared__ unsigned int foundClusters; + foundClusters = 0; + __syncthreads(); + + // find the number of different clusters, identified by a tracks with clus[i] == i and density larger than threshold; + // mark these tracks with a negative id. + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] == int(i)) { + if (nn[i] >= minT) { + auto old = atomicInc(&foundClusters, 0xffffffff); + iv[i] = -(old + 1); + } else { // noise + iv[i] = -9998; + } + } + } + __syncthreads(); + + assert(foundClusters < ZVertices::MAXVTX); + + // propagate the negative id to all the tracks in the cluster. + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] >= 0) { + // mark each track in a cluster with the same id as the first one + iv[i] = iv[iv[i]]; + } + } + __syncthreads(); + + // adjust the cluster id to be a positive value starting from 0 + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + iv[i] = -iv[i] - 1; + } + + nvIntermediate = nvFinal = foundClusters; + + if (verbose && 0 == threadIdx.x) + printf("found %d proto vertices\n", foundClusters); + } + + __global__ void clusterTracksByDensityKernel(gpuVertexFinder::ZVertices* pdata, + gpuVertexFinder::WorkSpace* pws, + int minT, // min number of neighbours to be "seed" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster + ) { + clusterTracksByDensity(pdata, pws, minT, eps, errmax, chi2max); + } + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksByDensity_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksDBSCAN.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksDBSCAN.h new file mode 100644 index 0000000000000..ffd7fdc948bf8 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksDBSCAN.h @@ -0,0 +1,242 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksDBSCAN_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksDBSCAN_h + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + // this algo does not really scale as it works in a single block... + // enough for <10K tracks we have + __global__ void clusterTracksDBSCAN(ZVertices* pdata, + WorkSpace* pws, + int minT, // min number of neighbours to be "core" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster + ) { + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + if (verbose && 0 == threadIdx.x) + printf("params %d %f %f %f\n", minT, eps, errmax, chi2max); + + auto er2mx = errmax * errmax; + + auto& __restrict__ data = *pdata; + auto& __restrict__ ws = *pws; + auto nt = ws.ntrks; + float const* __restrict__ zt = ws.zt; + float const* __restrict__ ezt2 = ws.ezt2; + + uint32_t& nvFinal = data.nvFinal; + uint32_t& nvIntermediate = ws.nvIntermediate; + + uint8_t* __restrict__ izt = ws.izt; + int32_t* __restrict__ nn = data.ndof; + int32_t* __restrict__ iv = ws.iv; + + assert(pdata); + assert(zt); + + using Hist = cms::cuda::HistoContainer; + __shared__ Hist hist; + __shared__ typename Hist::Counter hws[32]; + for (auto j = threadIdx.x; j < Hist::totbins(); j += blockDim.x) { + hist.off[j] = 0; + } + __syncthreads(); + + if (verbose && 0 == threadIdx.x) + printf("booked hist with %d bins, size %d for %d tracks\n", hist.nbins(), hist.capacity(), nt); + + assert(nt <= hist.capacity()); + + // fill hist (bin shall be wider than "eps") + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + assert(i < ZVertices::MAXTRACKS); + int iz = int(zt[i] * 10.); // valid if eps<=0.1 + // iz = std::clamp(iz, INT8_MIN, INT8_MAX); // sorry c++17 only + iz = std::min(std::max(iz, INT8_MIN), INT8_MAX); + izt[i] = iz - INT8_MIN; + assert(iz - INT8_MIN >= 0); + assert(iz - INT8_MIN < 256); + hist.count(izt[i]); + iv[i] = i; + nn[i] = 0; + } + __syncthreads(); + if (threadIdx.x < 32) + hws[threadIdx.x] = 0; // used by prefix scan... + __syncthreads(); + hist.finalize(hws); + __syncthreads(); + assert(hist.size() == nt); + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + hist.fill(izt[i], uint16_t(i)); + } + __syncthreads(); + + // count neighbours + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (ezt2[i] > er2mx) + continue; + auto loop = [&](uint32_t j) { + if (i == j) + return; + auto dist = std::abs(zt[i] - zt[j]); + if (dist > eps) + return; + // if (dist*dist>chi2max*(ezt2[i]+ezt2[j])) return; + nn[i]++; + }; + + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __syncthreads(); + + // find NN with smaller z... + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (nn[i] < minT) + continue; // DBSCAN core rule + float mz = zt[i]; + auto loop = [&](uint32_t j) { + if (zt[j] >= mz) + return; + if (nn[j] < minT) + return; // DBSCAN core rule + auto dist = std::abs(zt[i] - zt[j]); + if (dist > eps) + return; + // if (dist*dist>chi2max*(ezt2[i]+ezt2[j])) return; + mz = zt[j]; + iv[i] = j; // assign to cluster (better be unique??) + }; + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __syncthreads(); + +#ifdef GPU_DEBUG + // mini verification + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] != int(i)) + assert(iv[iv[i]] != int(i)); + } + __syncthreads(); +#endif + + // consolidate graph (percolate index of seed) + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + auto m = iv[i]; + while (m != iv[m]) + m = iv[m]; + iv[i] = m; + } + + __syncthreads(); + +#ifdef GPU_DEBUG + // mini verification + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] != int(i)) + assert(iv[iv[i]] != int(i)); + } + __syncthreads(); +#endif + +#ifdef GPU_DEBUG + // and verify that we did not spit any cluster... + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (nn[i] < minT) + continue; // DBSCAN core rule + assert(zt[iv[i]] <= zt[i]); + auto loop = [&](uint32_t j) { + if (nn[j] < minT) + return; // DBSCAN core rule + auto dist = std::abs(zt[i] - zt[j]); + if (dist > eps) + return; + // if (dist*dist>chi2max*(ezt2[i]+ezt2[j])) return; + // they should belong to the same cluster, isn't it? + if (iv[i] != iv[j]) { + printf("ERROR %d %d %f %f %d\n", i, iv[i], zt[i], zt[iv[i]], iv[iv[i]]); + printf(" %d %d %f %f %d\n", j, iv[j], zt[j], zt[iv[j]], iv[iv[j]]); + ; + } + assert(iv[i] == iv[j]); + }; + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + __syncthreads(); +#endif + + // collect edges (assign to closest cluster of closest point??? here to closest point) + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + // if (nn[i]==0 || nn[i]>=minT) continue; // DBSCAN edge rule + if (nn[i] >= minT) + continue; // DBSCAN edge rule + float mdist = eps; + auto loop = [&](uint32_t j) { + if (nn[j] < minT) + return; // DBSCAN core rule + auto dist = std::abs(zt[i] - zt[j]); + if (dist > mdist) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; // needed? + mdist = dist; + iv[i] = iv[j]; // assign to cluster (better be unique??) + }; + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __shared__ unsigned int foundClusters; + foundClusters = 0; + __syncthreads(); + + // find the number of different clusters, identified by a tracks with clus[i] == i; + // mark these tracks with a negative id. + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] == int(i)) { + if (nn[i] >= minT) { + auto old = atomicInc(&foundClusters, 0xffffffff); + iv[i] = -(old + 1); + } else { // noise + iv[i] = -9998; + } + } + } + __syncthreads(); + + assert(foundClusters < ZVertices::MAXVTX); + + // propagate the negative id to all the tracks in the cluster. + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] >= 0) { + // mark each track in a cluster with the same id as the first one + iv[i] = iv[iv[i]]; + } + } + __syncthreads(); + + // adjust the cluster id to be a positive value starting from 0 + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + iv[i] = -iv[i] - 1; + } + + nvIntermediate = nvFinal = foundClusters; + + if (verbose && 0 == threadIdx.x) + printf("found %d proto vertices\n", foundClusters); + } + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksDBSCAN_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksIterative.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksIterative.h new file mode 100644 index 0000000000000..49da86e941867 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuClusterTracksIterative.h @@ -0,0 +1,213 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksIterative_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksIterative_h + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + // this algo does not really scale as it works in a single block... + // enough for <10K tracks we have + __global__ void clusterTracksIterative(ZVertices* pdata, + WorkSpace* pws, + int minT, // min number of neighbours to be "core" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster + ) { + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + if (verbose && 0 == threadIdx.x) + printf("params %d %f %f %f\n", minT, eps, errmax, chi2max); + + auto er2mx = errmax * errmax; + + auto& __restrict__ data = *pdata; + auto& __restrict__ ws = *pws; + auto nt = ws.ntrks; + float const* __restrict__ zt = ws.zt; + float const* __restrict__ ezt2 = ws.ezt2; + + uint32_t& nvFinal = data.nvFinal; + uint32_t& nvIntermediate = ws.nvIntermediate; + + uint8_t* __restrict__ izt = ws.izt; + int32_t* __restrict__ nn = data.ndof; + int32_t* __restrict__ iv = ws.iv; + + assert(pdata); + assert(zt); + + using Hist = cms::cuda::HistoContainer; + __shared__ Hist hist; + __shared__ typename Hist::Counter hws[32]; + for (auto j = threadIdx.x; j < Hist::totbins(); j += blockDim.x) { + hist.off[j] = 0; + } + __syncthreads(); + + if (verbose && 0 == threadIdx.x) + printf("booked hist with %d bins, size %d for %d tracks\n", hist.nbins(), hist.capacity(), nt); + + assert(nt <= hist.capacity()); + + // fill hist (bin shall be wider than "eps") + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + assert(i < ZVertices::MAXTRACKS); + int iz = int(zt[i] * 10.); // valid if eps<=0.1 + // iz = std::clamp(iz, INT8_MIN, INT8_MAX); // sorry c++17 only + iz = std::min(std::max(iz, INT8_MIN), INT8_MAX); + izt[i] = iz - INT8_MIN; + assert(iz - INT8_MIN >= 0); + assert(iz - INT8_MIN < 256); + hist.count(izt[i]); + iv[i] = i; + nn[i] = 0; + } + __syncthreads(); + if (threadIdx.x < 32) + hws[threadIdx.x] = 0; // used by prefix scan... + __syncthreads(); + hist.finalize(hws); + __syncthreads(); + assert(hist.size() == nt); + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + hist.fill(izt[i], uint16_t(i)); + } + __syncthreads(); + + // count neighbours + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (ezt2[i] > er2mx) + continue; + auto loop = [&](uint32_t j) { + if (i == j) + return; + auto dist = std::abs(zt[i] - zt[j]); + if (dist > eps) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; + nn[i]++; + }; + + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __shared__ int nloops; + nloops = 0; + + __syncthreads(); + + // cluster seeds only + bool more = true; + while (__syncthreads_or(more)) { + if (1 == nloops % 2) { + for (auto 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 (auto 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)); + if (nn[i] < minT) + continue; // DBSCAN core rule + auto loop = [&](uint32_t j) { + assert(i != j); + if (nn[j] < minT) + return; // DBSCAN core rule + auto dist = std::abs(zt[i] - zt[j]); + if (dist > eps) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; + auto old = atomicMin(&iv[j], iv[i]); + if (old != iv[i]) { + // end the loop only if no changes were applied + more = true; + } + atomicMin(&iv[i], old); + }; + ++p; + for (; p < hist.end(be); ++p) + loop(*p); + } // for i + } + if (threadIdx.x == 0) + ++nloops; + } // while + + // collect edges (assign to closest cluster of closest point??? here to closest point) + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + // if (nn[i]==0 || nn[i]>=minT) continue; // DBSCAN edge rule + if (nn[i] >= minT) + continue; // DBSCAN edge rule + float mdist = eps; + auto loop = [&](int j) { + if (nn[j] < minT) + return; // DBSCAN core rule + auto dist = std::abs(zt[i] - zt[j]); + if (dist > mdist) + return; + if (dist * dist > chi2max * (ezt2[i] + ezt2[j])) + return; // needed? + mdist = dist; + iv[i] = iv[j]; // assign to cluster (better be unique??) + }; + cms::cuda::forEachInBins(hist, izt[i], 1, loop); + } + + __shared__ unsigned int foundClusters; + foundClusters = 0; + __syncthreads(); + + // find the number of different clusters, identified by a tracks with clus[i] == i; + // mark these tracks with a negative id. + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] == int(i)) { + if (nn[i] >= minT) { + auto old = atomicInc(&foundClusters, 0xffffffff); + iv[i] = -(old + 1); + } else { // noise + iv[i] = -9998; + } + } + } + __syncthreads(); + + assert(foundClusters < ZVertices::MAXVTX); + + // propagate the negative id to all the tracks in the cluster. + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] >= 0) { + // mark each track in a cluster with the same id as the first one + iv[i] = iv[iv[i]]; + } + } + __syncthreads(); + + // adjust the cluster id to be a positive value starting from 0 + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + iv[i] = -iv[i] - 1; + } + + nvIntermediate = nvFinal = foundClusters; + + if (verbose && 0 == threadIdx.x) + printf("found %d proto vertices\n", foundClusters); + } + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuClusterTracksIterative_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuFitVertices.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuFitVertices.h new file mode 100644 index 0000000000000..4487cb12ea17b --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuFitVertices.h @@ -0,0 +1,113 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuFitVertices_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuFitVertices_h + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + __device__ __forceinline__ void fitVertices(ZVertices* pdata, + WorkSpace* pws, + float chi2Max // for outlier rejection + ) { + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + auto& __restrict__ data = *pdata; + auto& __restrict__ ws = *pws; + auto nt = ws.ntrks; + float const* __restrict__ zt = ws.zt; + float const* __restrict__ ezt2 = ws.ezt2; + float* __restrict__ zv = data.zv; + float* __restrict__ wv = data.wv; + float* __restrict__ chi2 = data.chi2; + uint32_t& nvFinal = data.nvFinal; + uint32_t& nvIntermediate = ws.nvIntermediate; + + int32_t* __restrict__ nn = data.ndof; + int32_t* __restrict__ iv = ws.iv; + + assert(pdata); + assert(zt); + + assert(nvFinal <= nvIntermediate); + nvFinal = nvIntermediate; + auto foundClusters = nvFinal; + + // zero + for (auto 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 (auto 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] < int(foundClusters)); + auto w = 1.f / ezt2[i]; + atomicAdd(&zv[iv[i]], zt[i] * w); + atomicAdd(&wv[iv[i]], w); + } + + __syncthreads(); + // reuse nn + for (auto i = threadIdx.x; i < foundClusters; i += blockDim.x) { + assert(wv[i] > 0.f); + zv[i] /= wv[i]; + nn[i] = -1; // ndof + } + __syncthreads(); + + // compute chi2 + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] > 9990) + continue; + + auto c2 = zv[iv[i]] - zt[i]; + c2 *= c2 / ezt2[i]; + if (c2 > chi2Max) { + iv[i] = 9999; + continue; + } + atomicAdd(&chi2[iv[i]], c2); + atomicAdd(&nn[iv[i]], 1); + } + __syncthreads(); + for (auto 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); + } + + __global__ void fitVerticesKernel(ZVertices* pdata, + WorkSpace* pws, + float chi2Max // for outlier rejection + ) { + fitVertices(pdata, pws, chi2Max); + } + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuFitVertices_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuSortByPt2.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuSortByPt2.h new file mode 100644 index 0000000000000..89cc9a3844f76 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuSortByPt2.h @@ -0,0 +1,73 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuSortByPt2_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuSortByPt2_h + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#ifdef __CUDA_ARCH__ +#include "HeterogeneousCore/CUDAUtilities/interface/radixSort.h" +#endif + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + __device__ __forceinline__ void sortByPt2(ZVertices* pdata, WorkSpace* pws) { + auto& __restrict__ data = *pdata; + auto& __restrict__ ws = *pws; + auto nt = ws.ntrks; + float const* __restrict__ ptt2 = ws.ptt2; + uint32_t const& nvFinal = data.nvFinal; + + int32_t const* __restrict__ iv = ws.iv; + float* __restrict__ ptv2 = data.ptv2; + uint16_t* __restrict__ sortInd = data.sortInd; + + // if (threadIdx.x == 0) + // printf("sorting %d vertices\n",nvFinal); + + if (nvFinal < 1) + return; + + // fill indexing + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + data.idv[ws.itrk[i]] = iv[i]; + } + + // can be done asynchronoisly at the end of previous event + for (auto i = threadIdx.x; i < nvFinal; i += blockDim.x) { + ptv2[i] = 0; + } + __syncthreads(); + + for (auto i = threadIdx.x; i < nt; i += blockDim.x) { + if (iv[i] > 9990) + continue; + atomicAdd(&ptv2[iv[i]], ptt2[i]); + } + __syncthreads(); + + if (1 == nvFinal) { + if (threadIdx.x == 0) + sortInd[0] = 0; + return; + } +#ifdef __CUDA_ARCH__ + __shared__ uint16_t sws[1024]; + // sort using only 16 bits + radixSort(ptv2, sortInd, sws, nvFinal); +#else + for (uint16_t i = 0; i < nvFinal; ++i) + sortInd[i] = i; + std::sort(sortInd, sortInd + nvFinal, [&](auto i, auto j) { return ptv2[i] < ptv2[j]; }); +#endif + } + + __global__ void sortByPt2Kernel(ZVertices* pdata, WorkSpace* pws) { sortByPt2(pdata, pws); } + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuSortByPt2_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuSplitVertices.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuSplitVertices.h new file mode 100644 index 0000000000000..694915ab02157 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuSplitVertices.h @@ -0,0 +1,139 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuSplitVertices_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuSplitVertices_h + +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/HistoContainer.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" + +#include "gpuVertexFinder.h" + +namespace gpuVertexFinder { + + __device__ __forceinline__ void splitVertices(ZVertices* pdata, WorkSpace* pws, float maxChi2) { + constexpr bool verbose = false; // in principle the compiler should optmize out if false + + auto& __restrict__ data = *pdata; + auto& __restrict__ ws = *pws; + auto nt = ws.ntrks; + float const* __restrict__ zt = ws.zt; + float const* __restrict__ ezt2 = ws.ezt2; + float* __restrict__ zv = data.zv; + float* __restrict__ wv = data.wv; + float const* __restrict__ chi2 = data.chi2; + uint32_t& nvFinal = data.nvFinal; + + int32_t const* __restrict__ nn = data.ndof; + int32_t* __restrict__ iv = ws.iv; + + assert(pdata); + assert(zt); + + // one vertex per block + for (auto kv = blockIdx.x; kv < nvFinal; kv += gridDim.x) { + if (nn[kv] < 4) + continue; + if (chi2[kv] < maxChi2 * float(nn[kv])) + continue; + + constexpr int MAXTK = 512; + assert(nn[kv] < MAXTK); + if (nn[kv] >= MAXTK) + continue; // too bad FIXME + __shared__ uint32_t it[MAXTK]; // track index + __shared__ float zz[MAXTK]; // z pos + __shared__ uint8_t newV[MAXTK]; // 0 or 1 + __shared__ float ww[MAXTK]; // z weight + + __shared__ uint32_t nq; // number of track for this vertex + nq = 0; + __syncthreads(); + + // copy to local + for (auto k = threadIdx.x; k < nt; k += blockDim.x) { + if (iv[k] == int(kv)) { + auto old = atomicInc(&nq, MAXTK); + zz[old] = zt[k] - zv[kv]; + newV[old] = zz[old] < 0 ? 0 : 1; + ww[old] = 1.f / ezt2[k]; + it[old] = k; + } + } + + __shared__ float znew[2], wnew[2]; // the new vertices + + __syncthreads(); + assert(int(nq) == nn[kv] + 1); + + int maxiter = 20; + // 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 < nq; k += blockDim.x) { + auto i = newV[k]; + atomicAdd(&znew[i], zz[k] * ww[k]); + atomicAdd(&wnew[i], ww[k]); + } + __syncthreads(); + if (0 == threadIdx.x) { + znew[0] /= wnew[0]; + znew[1] /= wnew[1]; + } + __syncthreads(); + for (auto k = threadIdx.x; k < nq; k += blockDim.x) { + auto d0 = fabs(zz[k] - znew[0]); + auto d1 = fabs(zz[k] - znew[1]); + auto newer = d0 < d1 ? 0 : 1; + more |= newer != newV[k]; + newV[k] = newer; + } + --maxiter; + if (maxiter <= 0) + more = false; + } + + // avoid empty vertices + if (0 == wnew[0] || 0 == wnew[1]) + continue; + + // quality cut + auto dist2 = (znew[0] - znew[1]) * (znew[0] - znew[1]); + + 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 < 4) + continue; + + // get a new global vertex + __shared__ uint32_t igv; + if (0 == threadIdx.x) + igv = atomicAdd(&ws.nvIntermediate, 1); + __syncthreads(); + for (auto k = threadIdx.x; k < nq; k += blockDim.x) { + if (1 == newV[k]) + iv[it[k]] = igv; + } + + } // loop on vertices + } + + __global__ void splitVerticesKernel(ZVertices* pdata, WorkSpace* pws, float maxChi2) { + splitVertices(pdata, pws, maxChi2); + } + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuSplitVertices_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.cc b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.cc new file mode 100644 index 0000000000000..084763385bdb4 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.cc @@ -0,0 +1 @@ +#include "gpuVertexFinderImpl.h" diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.cu b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.cu new file mode 100644 index 0000000000000..084763385bdb4 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.cu @@ -0,0 +1 @@ +#include "gpuVertexFinderImpl.h" diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.h new file mode 100644 index 0000000000000..6cd86c93a6737 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinder.h @@ -0,0 +1,83 @@ +#ifndef RecoPixelVertexing_PixelVertexFinding_src_gpuVertexFinder_h +#define RecoPixelVertexing_PixelVertexFinding_src_gpuVertexFinder_h + +#include +#include + +#include "CUDADataFormats/Vertex/interface/ZVertexHeterogeneous.h" + +namespace gpuVertexFinder { + + using ZVertices = ZVertexSoA; + using TkSoA = pixelTrack::TrackSoA; + + // workspace used in the vertex reco algos + struct WorkSpace { + static constexpr uint32_t MAXTRACKS = ZVertexSoA::MAXTRACKS; + static constexpr uint32_t MAXVTX = ZVertexSoA::MAXVTX; + + uint32_t ntrks; // number of "selected tracks" + uint16_t itrk[MAXTRACKS]; // index of original track + float zt[MAXTRACKS]; // input track z at bs + float ezt2[MAXTRACKS]; // input error^2 on the above + float ptt2[MAXTRACKS]; // input pt^2 on the above + uint8_t izt[MAXTRACKS]; // interized z-position of input tracks + int32_t iv[MAXTRACKS]; // vertex index for each associated track + + uint32_t nvIntermediate; // the number of vertices after splitting pruning etc. + + __host__ __device__ void init() { + ntrks = 0; + nvIntermediate = 0; + } + }; + + __global__ void init(ZVertexSoA* pdata, WorkSpace* pws) { + pdata->init(); + pws->init(); + } + + class Producer { + public: + using ZVertices = ZVertexSoA; + using WorkSpace = gpuVertexFinder::WorkSpace; + using TkSoA = pixelTrack::TrackSoA; + + Producer(bool oneKernel, + bool useDensity, + bool useDBSCAN, + bool useIterative, + 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 + ) + : oneKernel_(oneKernel && !(useDBSCAN || useIterative)), + useDensity_(useDensity), + useDBSCAN_(useDBSCAN), + useIterative_(useIterative), + minT(iminT), + eps(ieps), + errmax(ierrmax), + chi2max(ichi2max) {} + + ~Producer() = default; + + ZVertexHeterogeneous makeAsync(cudaStream_t stream, TkSoA const* tksoa, float ptMin) const; + ZVertexHeterogeneous make(TkSoA const* tksoa, float ptMin) const; + + private: + const bool oneKernel_; + const bool useDensity_; + const bool useDBSCAN_; + const bool useIterative_; + + int minT; // min number of neighbours to be "core" + float eps; // max absolute distance to cluster + float errmax; // max error to be "seed" + float chi2max; // max normalized distance to cluster + }; + +} // namespace gpuVertexFinder + +#endif // RecoPixelVertexing_PixelVertexFinding_src_gpuVertexFinder_h diff --git a/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinderImpl.h b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinderImpl.h new file mode 100644 index 0000000000000..cf34d9075b70d --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/plugins/gpuVertexFinderImpl.h @@ -0,0 +1,169 @@ +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +#include "gpuClusterTracksByDensity.h" +#include "gpuClusterTracksDBSCAN.h" +#include "gpuClusterTracksIterative.h" +#include "gpuFitVertices.h" +#include "gpuSortByPt2.h" +#include "gpuSplitVertices.h" + +namespace gpuVertexFinder { + + __global__ void loadTracks(TkSoA const* ptracks, ZVertexSoA* soa, WorkSpace* pws, float ptMin) { + assert(ptracks); + assert(soa); + auto const& tracks = *ptracks; + auto const& fit = tracks.stateAtBS; + auto const* quality = tracks.qualityData(); + + auto first = blockIdx.x * blockDim.x + threadIdx.x; + for (int idx = first, nt = TkSoA::stride(); idx < nt; idx += gridDim.x * blockDim.x) { + auto nHits = tracks.nHits(idx); + if (nHits == 0) + break; // this is a guard: maybe we need to move to nTracks... + + // initialize soa... + soa->idv[idx] = -1; + + if (nHits < 4) + continue; // no triplets + if (quality[idx] != trackQuality::loose) + continue; + + auto pt = tracks.pt(idx); + + if (pt < ptMin) + continue; + + auto& data = *pws; + auto it = atomicAdd(&data.ntrks, 1); + data.itrk[it] = idx; + data.zt[it] = tracks.zip(idx); + data.ezt2[it] = fit.covariance(idx)(14); + data.ptt2[it] = pt * pt; + } + } + +// #define THREE_KERNELS +#ifndef THREE_KERNELS + __global__ void vertexFinderOneKernel(gpuVertexFinder::ZVertices* pdata, + gpuVertexFinder::WorkSpace* pws, + int minT, // min number of neighbours to be "seed" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster, + ) { + clusterTracksByDensity(pdata, pws, minT, eps, errmax, chi2max); + __syncthreads(); + fitVertices(pdata, pws, 50.); + __syncthreads(); + splitVertices(pdata, pws, 9.f); + __syncthreads(); + fitVertices(pdata, pws, 5000.); + __syncthreads(); + sortByPt2(pdata, pws); + } +#else + __global__ void vertexFinderKernel1(gpuVertexFinder::ZVertices* pdata, + gpuVertexFinder::WorkSpace* pws, + int minT, // min number of neighbours to be "seed" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster, + ) { + clusterTracksByDensity(pdata, pws, minT, eps, errmax, chi2max); + __syncthreads(); + fitVertices(pdata, pws, 50.); + } + + __global__ void vertexFinderKernel2(gpuVertexFinder::ZVertices* pdata, gpuVertexFinder::WorkSpace* pws) { + fitVertices(pdata, pws, 5000.); + __syncthreads(); + sortByPt2(pdata, pws); + } +#endif + +#ifdef __CUDACC__ + ZVertexHeterogeneous Producer::makeAsync(cudaStream_t stream, TkSoA const* tksoa, float ptMin) const { + // std::cout << "producing Vertices on GPU" << std::endl; + ZVertexHeterogeneous vertices(cms::cuda::make_device_unique(stream)); +#else + ZVertexHeterogeneous Producer::make(TkSoA const* tksoa, float ptMin) const { + // std::cout << "producing Vertices on CPU" << std::endl; + ZVertexHeterogeneous vertices(std::make_unique()); +#endif + assert(tksoa); + auto* soa = vertices.get(); + assert(soa); + +#ifdef __CUDACC__ + auto ws_d = cms::cuda::make_device_unique(stream); +#else + auto ws_d = std::make_unique(); +#endif + +#ifdef __CUDACC__ + init<<<1, 1, 0, stream>>>(soa, ws_d.get()); + auto blockSize = 128; + auto numberOfBlocks = (TkSoA::stride() + blockSize - 1) / blockSize; + loadTracks<<>>(tksoa, soa, ws_d.get(), ptMin); + cudaCheck(cudaGetLastError()); +#else + init(soa, ws_d.get()); + loadTracks(tksoa, soa, ws_d.get(), ptMin); +#endif + +#ifdef __CUDACC__ + if (oneKernel_) { + // implemented only for density clustesrs +#ifndef THREE_KERNELS + vertexFinderOneKernel<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), minT, eps, errmax, chi2max); +#else + vertexFinderKernel1<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), minT, eps, errmax, chi2max); + cudaCheck(cudaGetLastError()); + // one block per vertex... + splitVerticesKernel<<<1024, 128, 0, stream>>>(soa, ws_d.get(), 9.f); + cudaCheck(cudaGetLastError()); + vertexFinderKernel2<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get()); +#endif + } else { // five kernels + if (useDensity_) { + clusterTracksByDensityKernel<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), minT, eps, errmax, chi2max); + } else if (useDBSCAN_) { + clusterTracksDBSCAN<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), minT, eps, errmax, chi2max); + } else if (useIterative_) { + clusterTracksIterative<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), minT, eps, errmax, chi2max); + } + cudaCheck(cudaGetLastError()); + fitVerticesKernel<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), 50.); + cudaCheck(cudaGetLastError()); + // one block per vertex... + splitVerticesKernel<<<1024, 128, 0, stream>>>(soa, ws_d.get(), 9.f); + cudaCheck(cudaGetLastError()); + fitVerticesKernel<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get(), 5000.); + cudaCheck(cudaGetLastError()); + sortByPt2Kernel<<<1, 1024 - 256, 0, stream>>>(soa, ws_d.get()); + } + cudaCheck(cudaGetLastError()); +#else // __CUDACC__ + if (useDensity_) { + clusterTracksByDensity(soa, ws_d.get(), minT, eps, errmax, chi2max); + } else if (useDBSCAN_) { + clusterTracksDBSCAN(soa, ws_d.get(), minT, eps, errmax, chi2max); + } else if (useIterative_) { + clusterTracksIterative(soa, ws_d.get(), minT, eps, errmax, chi2max); + } + // std::cout << "found " << (*ws_d).nvIntermediate << " vertices " << std::endl; + fitVertices(soa, ws_d.get(), 50.); + // one block per vertex! + splitVertices(soa, ws_d.get(), 9.f); + fitVertices(soa, ws_d.get(), 5000.); + sortByPt2(soa, ws_d.get()); +#endif + + return vertices; + } + +} // namespace gpuVertexFinder + +#undef FROM diff --git a/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py b/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py index 77a9f367b9d9b..903c2a894ff86 100644 --- a/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py +++ b/RecoPixelVertexing/PixelVertexFinding/python/PixelVertexes_cfi.py @@ -18,5 +18,3 @@ refToPSet_ = cms.string('pvClusterComparer') ) ) - - diff --git a/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml b/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml index 0f4f4dee63832..f5c154b298574 100644 --- a/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml +++ b/RecoPixelVertexing/PixelVertexFinding/test/BuildFile.xml @@ -2,8 +2,41 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RecoPixelVertexing/PixelVertexFinding/test/VertexFinder_t.h b/RecoPixelVertexing/PixelVertexFinding/test/VertexFinder_t.h new file mode 100644 index 0000000000000..e3298f8c5761b --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/test/VertexFinder_t.h @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include + +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "HeterogeneousCore/CUDAUtilities/interface/requireDevices.h" +#include "HeterogeneousCore/CUDAUtilities/interface/launch.h" +#ifdef USE_DBSCAN +#include "../plugins/gpuClusterTracksDBSCAN.h" +#define CLUSTERIZE gpuVertexFinder::clusterTracksDBSCAN +#elif USE_ITERATIVE +#include "../plugins/gpuClusterTracksIterative.h" +#define CLUSTERIZE gpuVertexFinder::clusterTracksIterative +#else +#include "../plugins/gpuClusterTracksByDensity.h" +#define CLUSTERIZE gpuVertexFinder::clusterTracksByDensityKernel +#endif +#include "../plugins/gpuFitVertices.h" +#include "../plugins/gpuSortByPt2.h" +#include "../plugins/gpuSplitVertices.h" + +#ifdef ONE_KERNEL +#ifdef __CUDACC__ +__global__ void vertexFinderOneKernel(gpuVertexFinder::ZVertices* pdata, + gpuVertexFinder::WorkSpace* pws, + int minT, // min number of neighbours to be "seed" + float eps, // max absolute distance to cluster + float errmax, // max error to be "seed" + float chi2max // max normalized distance to cluster, +) { + clusterTracksByDensity(pdata, pws, minT, eps, errmax, chi2max); + __syncthreads(); + fitVertices(pdata, pws, 50.); + __syncthreads(); + splitVertices(pdata, pws, 9.f); + __syncthreads(); + fitVertices(pdata, pws, 5000.); + __syncthreads(); + sortByPt2(pdata, pws); +} +#endif +#endif + +struct Event { + std::vector zvert; + std::vector itrack; + std::vector ztrack; + std::vector eztrack; + std::vector pttrack; + std::vector ivert; +}; + +struct ClusterGenerator { + explicit ClusterGenerator(float nvert, float ntrack) + : rgen(-13., 13), errgen(0.005, 0.025), clusGen(nvert), trackGen(ntrack), gauss(0., 1.), ptGen(1.) {} + + void operator()(Event& ev) { + int nclus = clusGen(reng); + ev.zvert.resize(nclus); + ev.itrack.resize(nclus); + for (auto& z : ev.zvert) { + z = 3.5f * gauss(reng); + } + + ev.ztrack.clear(); + ev.eztrack.clear(); + ev.ivert.clear(); + for (int iv = 0; iv < nclus; ++iv) { + auto nt = trackGen(reng); + ev.itrack[nclus] = nt; + for (int it = 0; it < nt; ++it) { + auto err = errgen(reng); // reality is not flat.... + ev.ztrack.push_back(ev.zvert[iv] + err * gauss(reng)); + ev.eztrack.push_back(err * err); + ev.ivert.push_back(iv); + ev.pttrack.push_back((iv == 5 ? 1.f : 0.5f) + ptGen(reng)); + ev.pttrack.back() *= ev.pttrack.back(); + } + } + // add noise + auto nt = 2 * trackGen(reng); + for (int it = 0; it < nt; ++it) { + auto err = 0.03f; + ev.ztrack.push_back(rgen(reng)); + ev.eztrack.push_back(err * err); + ev.ivert.push_back(9999); + ev.pttrack.push_back(0.5f + ptGen(reng)); + ev.pttrack.back() *= ev.pttrack.back(); + } + } + + std::mt19937 reng; + std::uniform_real_distribution rgen; + std::uniform_real_distribution errgen; + std::poisson_distribution clusGen; + std::poisson_distribution trackGen; + std::normal_distribution gauss; + std::exponential_distribution ptGen; +}; + +// a macro SORRY +#define LOC_ONGPU(M) ((char*)(onGPU_d.get()) + offsetof(gpuVertexFinder::ZVertices, M)) +#define LOC_WS(M) ((char*)(ws_d.get()) + offsetof(gpuVertexFinder::WorkSpace, M)) + +__global__ void print(gpuVertexFinder::ZVertices const* pdata, gpuVertexFinder::WorkSpace const* pws) { + auto const& __restrict__ data = *pdata; + auto const& __restrict__ ws = *pws; + printf("nt,nv %d %d,%d\n", ws.ntrks, data.nvFinal, ws.nvIntermediate); +} + +int main() { +#ifdef __CUDACC__ + cms::cudatest::requireDevices(); + + auto onGPU_d = cms::cuda::make_device_unique(1, nullptr); + auto ws_d = cms::cuda::make_device_unique(1, nullptr); +#else + auto onGPU_d = std::make_unique(); + auto ws_d = std::make_unique(); +#endif + + Event ev; + + float eps = 0.1f; + std::array par{{eps, 0.01f, 9.0f}}; + for (int nav = 30; nav < 80; nav += 20) { + ClusterGenerator gen(nav, 10); + + for (int i = 8; i < 20; ++i) { + auto kk = i / 4; // M param + + gen(ev); + +#ifdef __CUDACC__ + init<<<1, 1, 0, 0>>>(onGPU_d.get(), ws_d.get()); +#else + onGPU_d->init(); + ws_d->init(); +#endif + + std::cout << "v,t size " << ev.zvert.size() << ' ' << ev.ztrack.size() << std::endl; + auto nt = ev.ztrack.size(); +#ifdef __CUDACC__ + cudaCheck(cudaMemcpy(LOC_WS(ntrks), &nt, sizeof(uint32_t), cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(LOC_WS(zt), ev.ztrack.data(), sizeof(float) * ev.ztrack.size(), cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(LOC_WS(ezt2), ev.eztrack.data(), sizeof(float) * ev.eztrack.size(), cudaMemcpyHostToDevice)); + cudaCheck(cudaMemcpy(LOC_WS(ptt2), ev.pttrack.data(), sizeof(float) * ev.eztrack.size(), cudaMemcpyHostToDevice)); +#else + ::memcpy(LOC_WS(ntrks), &nt, sizeof(uint32_t)); + ::memcpy(LOC_WS(zt), ev.ztrack.data(), sizeof(float) * ev.ztrack.size()); + ::memcpy(LOC_WS(ezt2), ev.eztrack.data(), sizeof(float) * ev.eztrack.size()); + ::memcpy(LOC_WS(ptt2), ev.pttrack.data(), sizeof(float) * ev.eztrack.size()); +#endif + + std::cout << "M eps, pset " << kk << ' ' << eps << ' ' << (i % 4) << std::endl; + + if ((i % 4) == 0) + par = {{eps, 0.02f, 12.0f}}; + if ((i % 4) == 1) + par = {{eps, 0.02f, 9.0f}}; + if ((i % 4) == 2) + par = {{eps, 0.01f, 9.0f}}; + if ((i % 4) == 3) + par = {{0.7f * eps, 0.01f, 9.0f}}; + + uint32_t nv = 0; +#ifdef __CUDACC__ + print<<<1, 1, 0, 0>>>(onGPU_d.get(), ws_d.get()); + cudaCheck(cudaGetLastError()); + cudaDeviceSynchronize(); + +#ifdef ONE_KERNEL + cms::cuda::launch(vertexFinderOneKernel, {1, 512 + 256}, onGPU_d.get(), ws_d.get(), kk, par[0], par[1], par[2]); +#else + cms::cuda::launch(CLUSTERIZE, {1, 512 + 256}, onGPU_d.get(), ws_d.get(), kk, par[0], par[1], par[2]); +#endif + print<<<1, 1, 0, 0>>>(onGPU_d.get(), ws_d.get()); + + cudaCheck(cudaGetLastError()); + cudaDeviceSynchronize(); + + cms::cuda::launch(gpuVertexFinder::fitVerticesKernel, {1, 1024 - 256}, onGPU_d.get(), ws_d.get(), 50.f); + cudaCheck(cudaGetLastError()); + cudaCheck(cudaMemcpy(&nv, LOC_ONGPU(nvFinal), sizeof(uint32_t), cudaMemcpyDeviceToHost)); + +#else + print(onGPU_d.get(), ws_d.get()); + CLUSTERIZE(onGPU_d.get(), ws_d.get(), kk, par[0], par[1], par[2]); + print(onGPU_d.get(), ws_d.get()); + fitVertices(onGPU_d.get(), ws_d.get(), 50.f); + nv = onGPU_d->nvFinal; +#endif + + if (nv == 0) { + std::cout << "NO VERTICES???" << std::endl; + continue; + } + + float* zv = nullptr; + float* wv = nullptr; + float* ptv2 = nullptr; + int32_t* nn = nullptr; + uint16_t* ind = nullptr; + + // keep chi2 separated... + float chi2[2 * nv]; // make space for splitting... + +#ifdef __CUDACC__ + float hzv[2 * nv]; + float hwv[2 * nv]; + float hptv2[2 * nv]; + int32_t hnn[2 * nv]; + uint16_t hind[2 * nv]; + + zv = hzv; + wv = hwv; + ptv2 = hptv2; + nn = hnn; + ind = hind; +#else + zv = onGPU_d->zv; + wv = onGPU_d->wv; + ptv2 = onGPU_d->ptv2; + nn = onGPU_d->ndof; + ind = onGPU_d->sortInd; +#endif + +#ifdef __CUDACC__ + cudaCheck(cudaMemcpy(nn, LOC_ONGPU(ndof), nv * sizeof(int32_t), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(chi2, LOC_ONGPU(chi2), nv * sizeof(float), cudaMemcpyDeviceToHost)); +#else + memcpy(chi2, LOC_ONGPU(chi2), nv * sizeof(float)); +#endif + + for (auto j = 0U; j < nv; ++j) + if (nn[j] > 0) + chi2[j] /= float(nn[j]); + { + auto mx = std::minmax_element(chi2, chi2 + nv); + std::cout << "after fit nv, min max chi2 " << nv << " " << *mx.first << ' ' << *mx.second << std::endl; + } + +#ifdef __CUDACC__ + cms::cuda::launch(gpuVertexFinder::fitVerticesKernel, {1, 1024 - 256}, onGPU_d.get(), ws_d.get(), 50.f); + cudaCheck(cudaMemcpy(&nv, LOC_ONGPU(nvFinal), sizeof(uint32_t), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(nn, LOC_ONGPU(ndof), nv * sizeof(int32_t), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(chi2, LOC_ONGPU(chi2), nv * sizeof(float), cudaMemcpyDeviceToHost)); +#else + fitVertices(onGPU_d.get(), ws_d.get(), 50.f); + nv = onGPU_d->nvFinal; + memcpy(chi2, LOC_ONGPU(chi2), nv * sizeof(float)); +#endif + + for (auto j = 0U; j < nv; ++j) + if (nn[j] > 0) + chi2[j] /= float(nn[j]); + { + auto mx = std::minmax_element(chi2, chi2 + nv); + std::cout << "before splitting nv, min max chi2 " << nv << " " << *mx.first << ' ' << *mx.second << std::endl; + } + +#ifdef __CUDACC__ + // one vertex per block!!! + cms::cuda::launch(gpuVertexFinder::splitVerticesKernel, {1024, 64}, onGPU_d.get(), ws_d.get(), 9.f); + cudaCheck(cudaMemcpy(&nv, LOC_WS(nvIntermediate), sizeof(uint32_t), cudaMemcpyDeviceToHost)); +#else + splitVertices(onGPU_d.get(), ws_d.get(), 9.f); + nv = ws_d->nvIntermediate; +#endif + std::cout << "after split " << nv << std::endl; + +#ifdef __CUDACC__ + cms::cuda::launch(gpuVertexFinder::fitVerticesKernel, {1, 1024 - 256}, onGPU_d.get(), ws_d.get(), 5000.f); + cudaCheck(cudaGetLastError()); + + cms::cuda::launch(gpuVertexFinder::sortByPt2Kernel, {1, 256}, onGPU_d.get(), ws_d.get()); + cudaCheck(cudaGetLastError()); + cudaCheck(cudaMemcpy(&nv, LOC_ONGPU(nvFinal), sizeof(uint32_t), cudaMemcpyDeviceToHost)); +#else + fitVertices(onGPU_d.get(), ws_d.get(), 5000.f); + sortByPt2(onGPU_d.get(), ws_d.get()); + nv = onGPU_d->nvFinal; + memcpy(chi2, LOC_ONGPU(chi2), nv * sizeof(float)); +#endif + + if (nv == 0) { + std::cout << "NO VERTICES???" << std::endl; + continue; + } + +#ifdef __CUDACC__ + cudaCheck(cudaMemcpy(zv, LOC_ONGPU(zv), nv * sizeof(float), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(wv, LOC_ONGPU(wv), nv * sizeof(float), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(chi2, LOC_ONGPU(chi2), nv * sizeof(float), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(ptv2, LOC_ONGPU(ptv2), nv * sizeof(float), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(nn, LOC_ONGPU(ndof), nv * sizeof(int32_t), cudaMemcpyDeviceToHost)); + cudaCheck(cudaMemcpy(ind, LOC_ONGPU(sortInd), nv * sizeof(uint16_t), cudaMemcpyDeviceToHost)); +#endif + for (auto j = 0U; j < nv; ++j) + if (nn[j] > 0) + chi2[j] /= float(nn[j]); + { + auto mx = std::minmax_element(chi2, chi2 + nv); + std::cout << "nv, min max chi2 " << nv << " " << *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; + std::cout << "min max ptv2 " << ptv2[ind[0]] << ' ' << ptv2[ind[nv - 1]] << " at " << ind[0] << ' ' + << ind[nv - 1] << std::endl; + } + + float dd[nv]; + for (auto kv = 0U; kv < nv; ++kv) { + auto zr = zv[kv]; + auto md = 500.0f; + for (auto zs : ev.ztrack) { + auto d = std::abs(zr - zs); + md = std::min(d, md); + } + dd[kv] = md; + } + if (i == 6) { + for (auto d : dd) + std::cout << d << ' '; + std::cout << std::endl; + } + auto mx = std::minmax_element(dd, dd + nv); + float rms = 0; + for (auto d : dd) + rms += d * d; + rms = std::sqrt(rms) / (nv - 1); + std::cout << "min max rms " << *mx.first << ' ' << *mx.second << ' ' << rms << std::endl; + + } // loop on events + } // lopp on ave vert + + return 0; +} diff --git a/RecoPixelVertexing/PixelVertexFinding/test/cpuVertexFinder_t.cpp b/RecoPixelVertexing/PixelVertexFinding/test/cpuVertexFinder_t.cpp new file mode 100644 index 0000000000000..a7906fe0d03f5 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/test/cpuVertexFinder_t.cpp @@ -0,0 +1 @@ +#include "VertexFinder_t.h" diff --git a/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu new file mode 100644 index 0000000000000..a7906fe0d03f5 --- /dev/null +++ b/RecoPixelVertexing/PixelVertexFinding/test/gpuVertexFinder_t.cu @@ -0,0 +1 @@ +#include "VertexFinder_t.h" diff --git a/RecoTracker/Configuration/python/customizePixelOnlyForProfiling.py b/RecoTracker/Configuration/python/customizePixelOnlyForProfiling.py new file mode 100644 index 0000000000000..24774bbda649c --- /dev/null +++ b/RecoTracker/Configuration/python/customizePixelOnlyForProfiling.py @@ -0,0 +1,59 @@ +import FWCore.ParameterSet.Config as cms + +# Customise the Pixel-only reconstruction to run on GPU +# +# Run the unpacker, clustering, ntuplets, track fit and vertex reconstruction on GPU. +def customizePixelOnlyForProfilingGPUOnly(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('caHitNtupletCUDA', 'pixelVertexCUDA') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process + + +# Customise the Pixel-only reconstruction to run on GPU, and copy the data to the host +# +# Run the unpacker, clustering, ntuplets, track fit and vertex reconstruction on GPU, +# and copy all the products to the host in SoA format. +# +# The same customisation can be also used on the SoA CPU workflow, running up to the +# tracks and vertices on the CPU in SoA format, without conversion to legacy format. +def customizePixelOnlyForProfilingGPUWithHostCopy(process): + + #? process.siPixelRecHitSoAFromLegacy.convertToLegacy = False + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('pixelTrackSoA', 'pixelVertexSoA') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process + + +# Customise the Pixel-only reconstruction to run on GPU, copy the data to the host, +# and convert to legacy format +# +# Run the unpacker, clustering, ntuplets, track fit and vertex reconstruction on GPU; +# copy all the products to the host in SoA format; and convert them to legacy format. +# +# The same customisation can be also used on the CPU workflow, running up to the +# tracks and vertices on the CPU. +def customizePixelOnlyForProfiling(process): + + process.consumer = cms.EDAnalyzer("GenericConsumer", + eventProducts = cms.untracked.vstring('pixelTracks', 'pixelVertices') + ) + + process.consume_step = cms.EndPath(process.consumer) + + process.schedule = cms.Schedule(process.raw2digi_step, process.reconstruction_step, process.consume_step) + + return process diff --git a/RecoTracker/TkSeedGenerator/plugins/BuildFile.xml b/RecoTracker/TkSeedGenerator/plugins/BuildFile.xml index abaa5754e68c7..a743aeca5631f 100644 --- a/RecoTracker/TkSeedGenerator/plugins/BuildFile.xml +++ b/RecoTracker/TkSeedGenerator/plugins/BuildFile.xml @@ -1,18 +1,15 @@ + - - - - - - + + @@ -25,14 +22,20 @@ + + + + + + @@ -43,5 +46,4 @@ - diff --git a/RecoTracker/TkSeedGenerator/plugins/SeedProducerFromSoA.cc b/RecoTracker/TkSeedGenerator/plugins/SeedProducerFromSoA.cc new file mode 100644 index 0000000000000..5c5e5fecf41cf --- /dev/null +++ b/RecoTracker/TkSeedGenerator/plugins/SeedProducerFromSoA.cc @@ -0,0 +1,170 @@ +#include "CUDADataFormats/Track/interface/PixelTrackHeterogeneous.h" +#include "DataFormats/BeamSpot/interface/BeamSpot.h" +#include "DataFormats/GeometrySurface/interface/Plane.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "DataFormats/TrackingRecHit/interface/InvalidTrackingRecHit.h" +#include "DataFormats/TrajectorySeed/interface/TrajectorySeedCollection.h" +#include "DataFormats/TrajectoryState/interface/LocalTrajectoryParameters.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "Geometry/CommonDetUnit/interface/GeomDet.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/Records/interface/TrackerTopologyRcd.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" +#include "RecoPixelVertexing/PixelTrackFitting/interface/FitUtils.h" +#include "TrackingTools/AnalyticalJacobians/interface/JacobianLocalToCurvilinear.h" +#include "TrackingTools/MaterialEffects/interface/PropagatorWithMaterial.h" +#include "TrackingTools/Records/interface/TrackingComponentsRecord.h" +#include "TrackingTools/TrajectoryParametrization/interface/CurvilinearTrajectoryError.h" +#include "TrackingTools/TrajectoryParametrization/interface/GlobalTrajectoryParameters.h" +#include "TrackingTools/TrajectoryState/interface/TrajectoryStateTransform.h" + +/* + produces seeds directly from cuda produced tuples +*/ +class SeedProducerFromSoA : public edm::global::EDProducer<> { +public: + explicit SeedProducerFromSoA(const edm::ParameterSet& iConfig); + ~SeedProducerFromSoA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const override; + + edm::EDGetTokenT tBeamSpot_; + edm::EDGetTokenT tokenTrack_; + + int32_t minNumberOfHits_; +}; + +SeedProducerFromSoA::SeedProducerFromSoA(const edm::ParameterSet& iConfig) + : tBeamSpot_(consumes(iConfig.getParameter("beamSpot"))), + tokenTrack_(consumes(iConfig.getParameter("src"))), + minNumberOfHits_(iConfig.getParameter("minNumberOfHits")) + +{ + produces(); +} + +void SeedProducerFromSoA::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("beamSpot", edm::InputTag("offlineBeamSpot")); + desc.add("src", edm::InputTag("pixelTrackSoA")); + desc.add("minNumberOfHits", 0); + + descriptions.addWithDefaultLabel(desc); +} + +void SeedProducerFromSoA::produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const { + // std::cout << "Converting gpu helix to trajectory seed" << std::endl; + auto result = std::make_unique(); + + edm::ESHandle fieldESH; + iSetup.get().get(fieldESH); + + edm::ESHandle tracker; + iSetup.get().get(tracker); + auto const& dus = tracker->detUnits(); + + edm::ESHandle propagatorHandle; + iSetup.get().get("PropagatorWithMaterial", propagatorHandle); + const Propagator* propagator = &(*propagatorHandle); + + edm::ESHandle httopo; + iSetup.get().get(httopo); + + const auto& bsh = iEvent.get(tBeamSpot_); + // std::cout << "beamspot " << bsh.x0() << ' ' << bsh.y0() << ' ' << bsh.z0() << std::endl; + GlobalPoint bs(bsh.x0(), bsh.y0(), bsh.z0()); + + const auto& tsoa = *(iEvent.get(tokenTrack_)); + + auto const* quality = tsoa.qualityData(); + auto const& fit = tsoa.stateAtBS; + auto const& detIndices = tsoa.detIndices; + auto maxTracks = tsoa.stride(); + + int32_t nt = 0; + for (int32_t it = 0; it < maxTracks; ++it) { + auto nHits = tsoa.nHits(it); + if (nHits == 0) + break; // this is a guard: maybe we need to move to nTracks... + + auto q = quality[it]; + if (q != trackQuality::loose) + continue; // FIXME + if (nHits < minNumberOfHits_) + continue; + ++nt; + + // fill hits with invalid just to hold the detId + auto b = detIndices.begin(it); + edm::OwnVector hits; + for (int iHit = 0; iHit < nHits; ++iHit) { + auto const* det = dus[*(b + iHit)]; + // FIXME at some point get a proper type ... + hits.push_back(new InvalidTrackingRecHit(*det, TrackingRecHit::bad)); + } + + // mind: this values are respect the beamspot! + + float phi = tsoa.phi(it); + + Rfit::Vector5d ipar, opar; + Rfit::Matrix5d icov, ocov; + fit.copyToDense(ipar, icov, it); + Rfit::transformToPerigeePlane(ipar, icov, opar, ocov); + + LocalTrajectoryParameters lpar(opar(0), opar(1), opar(2), opar(3), opar(4), 1.); + AlgebraicSymMatrix55 m; + for (int i = 0; i < 5; ++i) + for (int j = i; j < 5; ++j) + m(i, j) = ocov(i, j); + + float sp = std::sin(phi); + float cp = std::cos(phi); + Surface::RotationType rot(sp, -cp, 0, 0, 0, -1.f, cp, sp, 0); + + Plane impPointPlane(bs, rot); + GlobalTrajectoryParameters gp(impPointPlane.toGlobal(lpar.position()), + impPointPlane.toGlobal(lpar.momentum()), + lpar.charge(), + fieldESH.product()); + + JacobianLocalToCurvilinear jl2c(impPointPlane, lpar, *fieldESH.product()); + + AlgebraicSymMatrix55 mo = ROOT::Math::Similarity(jl2c.jacobian(), m); + + FreeTrajectoryState fts(gp, CurvilinearTrajectoryError(mo)); + + auto const& lastHit = hits.back(); + + TrajectoryStateOnSurface outerState = propagator->propagate(fts, *lastHit.surface()); + + if (!outerState.isValid()) { + edm::LogError("SeedFromGPU") << " was trying to create a seed from:\n" + << fts << "\n propagating to: " << lastHit.geographicalId().rawId(); + continue; + } + + auto const& pTraj = trajectoryStateTransform::persistentState(outerState, lastHit.geographicalId().rawId()); + + result->emplace_back(pTraj, hits, alongMomentum); + } + + iEvent.put(std::move(result)); +} + +DEFINE_FWK_MODULE(SeedProducerFromSoA); diff --git a/SLHCUpgradeSimulations/Geometry/test/phase2_digi_reco_pixelntuple_cfg.py b/SLHCUpgradeSimulations/Geometry/test/phase2_digi_reco_pixelntuple_cfg.py index 713d424998b18..f2ccf76520a18 100644 --- a/SLHCUpgradeSimulations/Geometry/test/phase2_digi_reco_pixelntuple_cfg.py +++ b/SLHCUpgradeSimulations/Geometry/test/phase2_digi_reco_pixelntuple_cfg.py @@ -99,7 +99,7 @@ # clusterizer process.siPixelClusters.ElectronPerADCGain = cms.double(135.) -process.siPixelClustersPreSplitting.ElectronPerADCGain = cms.double(135.) +process.siPixelClustersPreSplitting.cpu.ElectronPerADCGain = cms.double(135.) from Configuration.AlCa.GlobalTag import GlobalTag process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:phase2_realistic_T15', '') diff --git a/SimTracker/TrackerHitAssociation/BuildFile.xml b/SimTracker/TrackerHitAssociation/BuildFile.xml index aa66f443cabb9..5ea8794eda917 100644 --- a/SimTracker/TrackerHitAssociation/BuildFile.xml +++ b/SimTracker/TrackerHitAssociation/BuildFile.xml @@ -5,6 +5,7 @@ + @@ -18,6 +19,7 @@ + diff --git a/SimTracker/TrackerHitAssociation/interface/trackerHitAssociationHeterogeneous.h b/SimTracker/TrackerHitAssociation/interface/trackerHitAssociationHeterogeneous.h new file mode 100644 index 0000000000000..86fe89f05b7d2 --- /dev/null +++ b/SimTracker/TrackerHitAssociation/interface/trackerHitAssociationHeterogeneous.h @@ -0,0 +1,69 @@ +#ifndef SimTracker_TrackerHitAssociation_plugins_trackerHitAssociationHeterogeneousProduct_h +#define SimTracker_TrackerHitAssociation_plugins_trackerHitAssociationHeterogeneousProduct_h + +#include "CUDADataFormats/Common/interface/HeterogeneousSoA.h" +#include "HeterogeneousCore/CUDAUtilities/interface/copyAsync.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" + +namespace trackerHitAssociationHeterogeneous { + + struct ClusterSLView { + using Clus2TP = std::array; + + Clus2TP* links_d; + uint32_t* tkId_d; + uint32_t* tkId2_d; + uint32_t* n1_d; + uint32_t* n2_d; + }; + + template + class Product { + public: + template + using unique_ptr = typename Traits::template unique_ptr; + + Product() = default; + ~Product() = default; + Product(Product const&) = delete; + Product(Product&&) = default; + + Product(int nlinks, int nhits, cudaStream_t stream); + + ClusterSLView& view() { return m_view; } + ClusterSLView const& view() const { return m_view; } + + int nLinks() const { return m_nLinks; } + int nHits() const { return m_nHits; } + + private: + static constexpr uint32_t n32 = 4; + + unique_ptr m_storeTP; //! + unique_ptr m_store32; //! + + ClusterSLView m_view; //! + + int m_nLinks; + int m_nHits; + }; + + template + Product::Product(int nlinks, int nhits, cudaStream_t stream) : m_nLinks(nlinks), m_nHits(nhits) { + m_storeTP = Traits::template make_device_unique(m_nLinks * 7, stream); + m_store32 = Traits::template make_device_unique(m_nHits * n32, stream); + + auto get32 = [&](int i) { return m_store32.get() + i * m_nHits; }; + + m_view.links_d = (ClusterSLView::Clus2TP*)(m_storeTP.get()); + m_view.tkId_d = get32(0); + m_view.tkId2_d = get32(1); + m_view.n1_d = get32(2); + m_view.n2_d = get32(3); + } + + using ProductCUDA = Product; + +} // namespace trackerHitAssociationHeterogeneous + +#endif // SimTracker_TrackerHitAssociation_plugins_trackerHitAssociationHeterogeneousProduct_h diff --git a/SimTracker/TrackerHitAssociation/plugins/BuildFile.xml b/SimTracker/TrackerHitAssociation/plugins/BuildFile.xml index ecda84011006b..186f04cbd611d 100644 --- a/SimTracker/TrackerHitAssociation/plugins/BuildFile.xml +++ b/SimTracker/TrackerHitAssociation/plugins/BuildFile.xml @@ -1,5 +1,10 @@ + + + + + - + diff --git a/SimTracker/TrackerHitAssociation/plugins/ClusterSLOnGPU.cu b/SimTracker/TrackerHitAssociation/plugins/ClusterSLOnGPU.cu new file mode 100644 index 0000000000000..0aab26d9cc091 --- /dev/null +++ b/SimTracker/TrackerHitAssociation/plugins/ClusterSLOnGPU.cu @@ -0,0 +1,224 @@ +#include +#include +#include + +#include "CUDADataFormats/SiPixelCluster/interface/gpuClusteringConstants.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cuda_assert.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudastdAlgorithm.h" +#include "RecoLocalTracker/SiPixelClusterizer/plugins/SiPixelRawToClusterGPUKernel.h" +#include "RecoLocalTracker/SiPixelRecHits/interface/pixelCPEforGPU.h" + +#include "ClusterSLOnGPU.h" + +using ClusterSLView = trackerHitAssociationHeterogeneous::ClusterSLView; +using Clus2TP = ClusterSLView::Clus2TP; + +// #define DUMP_TK2 + +__global__ void simLink(const SiPixelDigisCUDA::DeviceConstView* dd, + uint32_t ndigis, + TrackingRecHit2DSOAView const* hhp, + ClusterSLView sl, + uint32_t n) { + constexpr uint32_t invTK = 0; // std::numeric_limits::max(); + using gpuClustering::invalidModuleId; + using gpuClustering::maxNumModules; + + auto const& hh = *hhp; + auto i = blockIdx.x * blockDim.x + threadIdx.x; + + if (i >= ndigis) + return; + + auto id = dd->moduleInd(i); + if (invalidModuleId == id) + return; + assert(id < maxNumModules); + + auto ch = pixelgpudetails::pixelToChannel(dd->xx(i), dd->yy(i)); + auto first = hh.hitsModuleStart(id); + auto cl = first + dd->clus(i); + assert(cl < maxNumModules * blockDim.x); + + const Clus2TP me{{id, ch, 0, 0, 0, 0, 0}}; + + auto less = [] __host__ __device__(Clus2TP const& a, Clus2TP const& b) -> bool { + // in this context we do not care of [2] + return a[0] < b[0] or ((not(b[0] < a[0])) and (a[1] < b[1])); + }; + + auto equal = [] __host__ __device__(Clus2TP const& a, Clus2TP const& b) -> bool { + // in this context we do not care of [2] + return a[0] == b[0] and a[1] == b[1]; + }; + + auto const* b = sl.links_d; + auto const* e = b + n; + + auto p = cuda_std::lower_bound(b, e, me, less); + int32_t j = p - sl.links_d; + assert(j >= 0); + + auto getTK = [&](int i) { + auto const& l = sl.links_d[i]; + return l[2]; + }; + + j = std::min(int(j), int(n - 1)); + if (equal(me, sl.links_d[j])) { + auto const itk = j; + auto const tk = getTK(j); + auto old = atomicCAS(&sl.tkId_d[cl], invTK, itk); + if (invTK == old or tk == getTK(old)) { + atomicAdd(&sl.n1_d[cl], 1); + } else { + auto old = atomicCAS(&sl.tkId2_d[cl], invTK, itk); + if (invTK == old or tk == getTK(old)) + atomicAdd(&sl.n2_d[cl], 1); + } + } +} + +__global__ void doZero(uint32_t nhits, ClusterSLView sl) { + auto i = blockIdx.x * blockDim.x + threadIdx.x; + if (i > nhits) + return; + + sl.tkId_d[i] = 0; + sl.n1_d[i] = 0; + sl.tkId2_d[i] = 0; + sl.n2_d[i] = 0; +} + +__global__ void dumpLink(int first, int ev, TrackingRecHit2DSOAView const* hhp, uint32_t nhits, ClusterSLView sl) { + auto i = first + blockIdx.x * blockDim.x + threadIdx.x; + if (i > nhits) + return; + + auto const& hh = *hhp; + + auto const& tk1 = sl.links_d[sl.tkId_d[i]]; + +#ifdef DUMP_TK2 + auto const& tk2 = sl.links_d[sl.tkId2_d[i]]; + + printf("HIT: %d %d %d %d %.4f %.4f %.4f %.4f %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", +#else + printf("HIT: %d %d %d %d %.4f %.4f %.4f %.4f %d %d %d %d %d %d %d %d %d\n", +#endif + ev, + i, + hh.detectorIndex(i), + hh.charge(i), + hh.xGlobal(i), + hh.yGlobal(i), + hh.zGlobal(i), + hh.rGlobal(i), + hh.iphi(i), + hh.clusterSizeX(i), + hh.clusterSizeY(i), + tk1[2], + tk1[3], + tk1[4], + tk1[5], + tk1[6], + sl.n1_d[i] +#ifdef DUMP_TK2 + , + tk2[2], + tk2[3], + tk2[4], + tk2[5], + tk2[6], + sl.n2_d[i] +#endif + ); +} + +namespace clusterSLOnGPU { + + void printCSVHeader() { +#ifdef DUMP_TK2 + printf("HIT: %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n", +#else + printf("HIT: %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n", +#endif + "ev", + "ind", + "det", + "charge", + "xg", + "yg", + "zg", + "rg", + "iphi", + "xsize", + "ysize", + "tkId", + "pt", + "eta", + "z0", + "r0", + "n1" +#ifdef DUMP_TK2 + , + "tkId2", + "pt2", + "eta", + "z02", + "r02", + "n2" +#endif + ); + } + + std::atomic evId(0); + std::once_flag doneCSVHeader; + + Kernel::Kernel(bool dump) : doDump(dump) { + if (doDump) + std::call_once(doneCSVHeader, printCSVHeader); + } + + trackerHitAssociationHeterogeneous::ProductCUDA Kernel::makeAsync(SiPixelDigisCUDA const& dd, + uint32_t ndigis, + HitsOnCPU const& hh, + Clus2TP const* digi2tp, + uint32_t nhits, + uint32_t nlinks, + cudaStream_t stream) const { + trackerHitAssociationHeterogeneous::ProductCUDA product(nlinks, nhits, stream); + auto& csl = product.view(); + + cudaCheck(cudaMemcpyAsync(csl.links_d, digi2tp, sizeof(Clus2TP) * nlinks, cudaMemcpyDefault, stream)); + + if (0 == nhits) + return product; + + int ev = ++evId; + int threadsPerBlock = 256; + + int blocks = (nhits + threadsPerBlock - 1) / threadsPerBlock; + doZero<<>>(nhits, csl); + cudaCheck(cudaGetLastError()); + + blocks = (ndigis + threadsPerBlock - 1) / threadsPerBlock; + simLink<<>>(dd.view(), ndigis, hh.view(), csl, nlinks); + cudaCheck(cudaGetLastError()); + + if (doDump) { + cudaStreamSynchronize(stream); // flush previous printf + // one line == 200B so each kernel can print only 5K lines.... + blocks = 16; + for (int first = 0; first < int(nhits); first += blocks * threadsPerBlock) { + dumpLink<<>>(first, ev, hh.view(), nhits, csl); + cudaCheck(cudaGetLastError()); + cudaStreamSynchronize(stream); + } + } + cudaCheck(cudaGetLastError()); + + return product; + } + +} // namespace clusterSLOnGPU diff --git a/SimTracker/TrackerHitAssociation/plugins/ClusterSLOnGPU.h b/SimTracker/TrackerHitAssociation/plugins/ClusterSLOnGPU.h new file mode 100644 index 0000000000000..3109e6ed45a76 --- /dev/null +++ b/SimTracker/TrackerHitAssociation/plugins/ClusterSLOnGPU.h @@ -0,0 +1,36 @@ +#ifndef SimTracker_TrackerHitAssociation_plugins_ClusterSLOnGPU_h +#define SimTracker_TrackerHitAssociation_plugins_ClusterSLOnGPU_h + +#include + +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "SimTracker/TrackerHitAssociation/interface/trackerHitAssociationHeterogeneous.h" + +namespace clusterSLOnGPU { + + using ClusterSLView = trackerHitAssociationHeterogeneous::ClusterSLView; + using Clus2TP = ClusterSLView::Clus2TP; + using HitsOnGPU = TrackingRecHit2DSOAView; + using HitsOnCPU = TrackingRecHit2DCUDA; + + class Kernel { + public: + explicit Kernel(bool dump); + ~Kernel() {} + trackerHitAssociationHeterogeneous::ProductCUDA makeAsync(SiPixelDigisCUDA const& dd, + uint32_t ndigis, + HitsOnCPU const& hh, + Clus2TP const* digi2tp, + uint32_t nhits, + uint32_t nlinks, + cudaStream_t stream) const; + + private: + public: + bool doDump; + }; +} // namespace clusterSLOnGPU + +#endif // SimTracker_TrackerHitAssociation_plugins_ClusterSLOnGPU_h diff --git a/SimTracker/TrackerHitAssociation/plugins/ClusterTPAssociationProducerCUDA.cc b/SimTracker/TrackerHitAssociation/plugins/ClusterTPAssociationProducerCUDA.cc new file mode 100644 index 0000000000000..35337151eda91 --- /dev/null +++ b/SimTracker/TrackerHitAssociation/plugins/ClusterTPAssociationProducerCUDA.cc @@ -0,0 +1,227 @@ +#include +#include +#include + +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "CUDADataFormats/SiPixelDigi/interface/SiPixelDigisCUDA.h" +#include "CUDADataFormats/TrackingRecHit/interface/TrackingRecHit2DHeterogeneous.h" +#include "DataFormats/Common/interface/DetSetVector.h" +#include "DataFormats/Common/interface/DetSetVectorNew.h" +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/DetId/interface/DetId.h" +#include "DataFormats/Phase2TrackerCluster/interface/Phase2TrackerCluster1D.h" +#include "DataFormats/Phase2TrackerDigi/interface/Phase2TrackerDigi.h" +#include "DataFormats/SiPixelCluster/interface/SiPixelCluster.h" +#include "DataFormats/SiPixelDetId/interface/PixelChannelIdentifier.h" +#include "DataFormats/SiStripCluster/interface/SiStripCluster.h" +#include "DataFormats/TrackerRecHit2D/interface/OmniClusterRef.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAUtilities/interface/cudaCheck.h" +#include "SimDataFormats/Track/interface/SimTrackContainer.h" +#include "SimDataFormats/TrackerDigiSimLink/interface/PixelDigiSimLink.h" +#include "SimDataFormats/TrackerDigiSimLink/interface/StripDigiSimLink.h" +#include "SimDataFormats/TrackingAnalysis/interface/TrackingParticle.h" +#include "SimDataFormats/TrackingAnalysis/interface/TrackingParticleFwd.h" +#include "SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h" + +#include "ClusterSLOnGPU.h" + +class ClusterTPAssociationProducerCUDA : public edm::global::EDProducer<> { +public: + typedef std::vector OmniClusterCollection; + + using ClusterSLGPU = trackerHitAssociationHeterogeneous::ClusterSLView; + using Clus2TP = ClusterSLGPU::Clus2TP; + using ProductCUDA = trackerHitAssociationHeterogeneous::ProductCUDA; + + explicit ClusterTPAssociationProducerCUDA(const edm::ParameterSet &); + ~ClusterTPAssociationProducerCUDA() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions &descriptions); + +private: + void produce(edm::StreamID streamID, edm::Event &iEvent, const edm::EventSetup &iSetup) const override; + + std::map, TrackingParticleRef> makeMap(const edm::Event &iEvent) const; + + template + std::vector> getSimTrackId(const edm::Handle> &simLinks, + const DetId &detId, + uint32_t channel) const; + + edm::EDGetTokenT> sipixelSimLinksToken_; + edm::EDGetTokenT> sistripSimLinksToken_; + edm::EDGetTokenT> siphase2OTSimLinksToken_; + edm::EDGetTokenT> pixelClustersToken_; + edm::EDGetTokenT> stripClustersToken_; + edm::EDGetTokenT> phase2OTClustersToken_; + edm::EDGetTokenT trackingParticleToken_; + + edm::EDGetTokenT> tGpuDigis; + edm::EDGetTokenT> tGpuHits; + + edm::EDPutTokenT> tokenGPUProd_; + + clusterSLOnGPU::Kernel m_gpuAlgo; +}; + +ClusterTPAssociationProducerCUDA::ClusterTPAssociationProducerCUDA(const edm::ParameterSet &cfg) + : sipixelSimLinksToken_( + consumes>(cfg.getParameter("pixelSimLinkSrc"))), + sistripSimLinksToken_( + consumes>(cfg.getParameter("stripSimLinkSrc"))), + siphase2OTSimLinksToken_( + consumes>(cfg.getParameter("phase2OTSimLinkSrc"))), + pixelClustersToken_( + consumes>(cfg.getParameter("pixelClusterSrc"))), + stripClustersToken_( + consumes>(cfg.getParameter("stripClusterSrc"))), + phase2OTClustersToken_(consumes>( + cfg.getParameter("phase2OTClusterSrc"))), + trackingParticleToken_( + consumes(cfg.getParameter("trackingParticleSrc"))), + tGpuDigis(consumes>( + cfg.getParameter("heterogeneousPixelDigiClusterSrc"))), + tGpuHits(consumes>( + cfg.getParameter("heterogeneousPixelRecHitSrc"))), + m_gpuAlgo(cfg.getParameter("dumpCSV")) { + tokenGPUProd_ = produces>(); +} + +void ClusterTPAssociationProducerCUDA::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { + edm::ParameterSetDescription desc; + desc.add("simTrackSrc", edm::InputTag("g4SimHits")); + desc.add("pixelSimLinkSrc", edm::InputTag("simSiPixelDigis")); + desc.add("stripSimLinkSrc", edm::InputTag("simSiStripDigis")); + desc.add("phase2OTSimLinkSrc", edm::InputTag("simSiPixelDigis", "Tracker")); + desc.add("pixelClusterSrc", edm::InputTag("siPixelClusters")); + desc.add("stripClusterSrc", edm::InputTag("siStripClusters")); + desc.add("phase2OTClusterSrc", edm::InputTag("siPhase2Clusters")); + desc.add("trackingParticleSrc", edm::InputTag("mix", "MergedTrackTruth")); + desc.add("heterogeneousPixelDigiClusterSrc", edm::InputTag("siPixelClustersPreSplittingCUDA")); + desc.add("heterogeneousPixelRecHitSrc", edm::InputTag("siPixelRecHitsPreSplittingCUDA")); + + desc.add("dumpCSV", false); + + descriptions.add("tpClusterProducerCUDADefault", desc); +} + +std::map, TrackingParticleRef> ClusterTPAssociationProducerCUDA::makeMap( + const edm::Event &iEvent) const { + // TrackingParticle + edm::Handle TPCollectionH; + iEvent.getByToken(trackingParticleToken_, TPCollectionH); + + // prepare temporary map between SimTrackId and TrackingParticle index + std::map, TrackingParticleRef> mapping; + for (TrackingParticleCollection::size_type itp = 0; itp < TPCollectionH.product()->size(); ++itp) { + TrackingParticleRef trackingParticle(TPCollectionH, itp); + + // SimTracks inside TrackingParticle + EncodedEventId eid(trackingParticle->eventId()); + for (auto itrk = trackingParticle->g4Track_begin(); itrk != trackingParticle->g4Track_end(); ++itrk) { + std::pair trkid(itrk->trackId(), eid); + //std::cout << "creating map for id: " << trkid.first << " with tp: " << trackingParticle.key() << std::endl; + mapping.insert(std::make_pair(trkid, trackingParticle)); + } + } + return mapping; +} + +void ClusterTPAssociationProducerCUDA::produce(edm::StreamID streamID, + edm::Event &iEvent, + const edm::EventSetup &iSetup) const { + edm::ESHandle geom; + iSetup.get().get(geom); + + // Pixel DigiSimLink + edm::Handle> sipixelSimLinks; + // iEvent.getByLabel(_pixelSimLinkSrc, sipixelSimLinks); + iEvent.getByToken(sipixelSimLinksToken_, sipixelSimLinks); + + // TrackingParticle + edm::Handle TPCollectionH; + iEvent.getByToken(trackingParticleToken_, TPCollectionH); + + auto mapping = makeMap(iEvent); + + edm::Handle> gd; + iEvent.getByToken(tGpuDigis, gd); + edm::Handle> gh; + iEvent.getByToken(tGpuHits, gh); + + cms::cuda::ScopedContextProduce ctx{*gd}; + auto const &gDigis = ctx.get(*gd); + auto const &gHits = ctx.get(*gh); + auto ndigis = gDigis.nDigis(); + auto nhits = gHits.nHits(); + + std::vector digi2tp; + digi2tp.push_back({{0, 0, 0, 0, 0, 0, 0}}); // put at 0 0 + for (auto const &links : *sipixelSimLinks) { + DetId detId(links.detId()); + const GeomDetUnit *genericDet = geom->idToDetUnit(detId); + uint32_t gind = genericDet->index(); + for (auto const &link : links) { + if (link.fraction() < 0.5f) { + continue; + } + auto tkid = std::make_pair(link.SimTrackId(), link.eventId()); + auto ipos = mapping.find(tkid); + if (ipos != mapping.end()) { + uint32_t pt = 1000 * (*ipos).second->pt(); + uint32_t eta = 10000 * (*ipos).second->eta(); + uint32_t z0 = 10000 * (*ipos).second->vz(); // in um + uint32_t r0 = 10000 * std::sqrt((*ipos).second->vx() * (*ipos).second->vx() + + (*ipos).second->vy() * (*ipos).second->vy()); // in um + digi2tp.push_back({{gind, uint32_t(link.channel()), (*ipos).second.key(), pt, eta, z0, r0}}); + } + } + } + + std::sort(digi2tp.begin(), digi2tp.end()); + + ctx.emplace(iEvent, + tokenGPUProd_, + m_gpuAlgo.makeAsync(gDigis, ndigis, gHits, digi2tp.data(), nhits, digi2tp.size(), ctx.stream())); +} + +template +std::vector> +//std::pair +ClusterTPAssociationProducerCUDA::getSimTrackId(const edm::Handle> &simLinks, + const DetId &detId, + uint32_t channel) const { + //std::pair simTrkId; + std::vector> simTrkId; + auto isearch = simLinks->find(detId); + if (isearch != simLinks->end()) { + // Loop over DigiSimLink in this det unit + edm::DetSet link_detset = (*isearch); + for (typename edm::DetSet::const_iterator it = link_detset.data.begin(); it != link_detset.data.end(); ++it) { + if (channel == it->channel()) { + simTrkId.push_back(std::make_pair(it->SimTrackId(), it->eventId())); + } + } + } + return simTrkId; +} + +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/Framework/interface/MakerMacros.h" + +DEFINE_FWK_MODULE(ClusterTPAssociationProducerCUDA); diff --git a/SimTracker/TrackerHitAssociation/python/tpClusterProducer_cfi.py b/SimTracker/TrackerHitAssociation/python/tpClusterProducer_cfi.py index 8757a67226fb8..890d05c4fc093 100644 --- a/SimTracker/TrackerHitAssociation/python/tpClusterProducer_cfi.py +++ b/SimTracker/TrackerHitAssociation/python/tpClusterProducer_cfi.py @@ -18,3 +18,6 @@ stripSimLinkSrc = "mixData:StripDigiSimLink", phase2OTSimLinkSrc = "mixData:Phase2OTDigiSimLink", ) + +from SimTracker.TrackerHitAssociation.tpClusterProducerCUDADefault_cfi import tpClusterProducerCUDADefault as _tpClusterProducerCUDA +tpClusterProducerCUDA = _tpClusterProducerCUDA.clone() diff --git a/SimTracker/TrackerHitAssociation/src/classes.h b/SimTracker/TrackerHitAssociation/src/classes.h index 457b6683d5cea..c8f98cd38ca81 100644 --- a/SimTracker/TrackerHitAssociation/src/classes.h +++ b/SimTracker/TrackerHitAssociation/src/classes.h @@ -5,6 +5,8 @@ #include "DataFormats/Common/interface/AssociationMap.h" #include "DataFormats/TrackerRecHit2D/interface/OmniClusterRef.h" #include "SimTracker/TrackerHitAssociation/interface/ClusterTPAssociation.h" +#include "CUDADataFormats/Common/interface/Product.h" +#include "SimTracker/TrackerHitAssociation/interface/trackerHitAssociationHeterogeneous.h" #include "DataFormats/Common/interface/AssociationMap.h" namespace SimTracker_TrackerHitAssociation { diff --git a/SimTracker/TrackerHitAssociation/src/classes_def.xml b/SimTracker/TrackerHitAssociation/src/classes_def.xml index f801d25b176e0..e9701e768fe75 100644 --- a/SimTracker/TrackerHitAssociation/src/classes_def.xml +++ b/SimTracker/TrackerHitAssociation/src/classes_def.xml @@ -20,4 +20,9 @@ + + + + + diff --git a/SimTracker/TrackerHitAssociation/test/BuildFile.xml b/SimTracker/TrackerHitAssociation/test/BuildFile.xml index a0dc6b61844a0..df2be2331d810 100644 --- a/SimTracker/TrackerHitAssociation/test/BuildFile.xml +++ b/SimTracker/TrackerHitAssociation/test/BuildFile.xml @@ -2,12 +2,14 @@ + + diff --git a/SimTracker/TrackerHitAssociation/test/ClusterTPCUDAdump.cc b/SimTracker/TrackerHitAssociation/test/ClusterTPCUDAdump.cc new file mode 100644 index 0000000000000..9c7a2e3e4828b --- /dev/null +++ b/SimTracker/TrackerHitAssociation/test/ClusterTPCUDAdump.cc @@ -0,0 +1,66 @@ +#include + +#include "CUDADataFormats/Common/interface/Product.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/PluginManager/interface/ModuleDef.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/Utilities/interface/RunningAverage.h" +#include "HeterogeneousCore/CUDACore/interface/ScopedContext.h" +#include "HeterogeneousCore/CUDAServices/interface/CUDAService.h" +#include "SimTracker/TrackerHitAssociation/interface/trackerHitAssociationHeterogeneous.h" + +class ClusterTPCUDAdump : public edm::global::EDAnalyzer<> { +public: + using ClusterSLGPU = trackerHitAssociationHeterogeneous::ClusterSLView; + using Clus2TP = ClusterSLGPU::Clus2TP; + using ProductCUDA = trackerHitAssociationHeterogeneous::ProductCUDA; + + explicit ClusterTPCUDAdump(const edm::ParameterSet& iConfig); + ~ClusterTPCUDAdump() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void analyze(edm::StreamID streamID, edm::Event const& iEvent, const edm::EventSetup& iSetup) const override; + const bool m_onGPU; + edm::EDGetTokenT> tokenGPU_; +}; + +ClusterTPCUDAdump::ClusterTPCUDAdump(const edm::ParameterSet& iConfig) : m_onGPU(iConfig.getParameter("onGPU")) { + if (m_onGPU) { + tokenGPU_ = consumes>(iConfig.getParameter("clusterTP")); + } else { + } +} + +void ClusterTPCUDAdump::analyze(edm::StreamID streamID, edm::Event const& iEvent, const edm::EventSetup& iSetup) const { + if (m_onGPU) { + auto const& hctp = iEvent.get(tokenGPU_); + cms::cuda::ScopedContextProduce ctx{hctp}; + + auto const& ctp = ctx.get(hctp); + auto const& soa = ctp.view(); + assert(soa.links_d); + } else { + } +} + +void ClusterTPCUDAdump::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("onGPU", true); + desc.add("clusterTP", edm::InputTag("tpClusterProducerCUDAPreSplitting")); + descriptions.add("clusterTPCUDAdump", desc); +} + +DEFINE_FWK_MODULE(ClusterTPCUDAdump); diff --git a/Validation/Configuration/python/globalValidation_cff.py b/Validation/Configuration/python/globalValidation_cff.py index 0a98acf89ad69..b43f8ee22e3a4 100644 --- a/Validation/Configuration/python/globalValidation_cff.py +++ b/Validation/Configuration/python/globalValidation_cff.py @@ -219,8 +219,13 @@ _phase_1_globalValidation = globalValidation.copy() _phase_1_globalValidation += siPixelPhase1OfflineDQM_sourceV + +_phase_1_globalValidationPixelTrackingOnly = globalValidationPixelTrackingOnly.copy() +_phase_1_globalValidationPixelTrackingOnly += siPixelPhase1ValidationPixelTrackingOnly_sourceV + from Configuration.Eras.Modifier_phase1Pixel_cff import phase1Pixel (phase1Pixel & ~fastSim).toReplaceWith( globalValidation, _phase_1_globalValidation ) #module siPixelPhase1OfflineDQM_sourceV can't run in FastSim since siPixelClusters of type edmNew::DetSetVector are not produced +(phase1Pixel & ~fastSim).toReplaceWith( globalValidationPixelTrackingOnly, _phase_1_globalValidationPixelTrackingOnly ) #module siPixelPhase1OfflineDQM_sourceV can't run in FastSim since siPixelClusters of type edmNew::DetSetVector are not produced _run3_globalValidation = globalValidation.copy() _run3_globalValidation += gemSimValid diff --git a/Validation/Configuration/python/postValidation_cff.py b/Validation/Configuration/python/postValidation_cff.py index 8f310eacfce2e..8468943e81b04 100644 --- a/Validation/Configuration/python/postValidation_cff.py +++ b/Validation/Configuration/python/postValidation_cff.py @@ -115,8 +115,13 @@ _phase1_postValidation = postValidation.copy() _phase1_postValidation += siPixelPhase1OfflineDQM_harvestingV + +_phase1_postValidation_trackingOnly = postValidation_trackingOnly.copy() +_phase1_postValidation_trackingOnly += siPixelPhase1OfflineDQM_harvestingV + from Configuration.Eras.Modifier_phase1Pixel_cff import phase1Pixel phase1Pixel.toReplaceWith( postValidation, _phase1_postValidation ) +phase1Pixel.toReplaceWith( postValidation_trackingOnly, _phase1_postValidation_trackingOnly) _run3_postValidation = postValidation.copy() _run3_postValidation += MuonGEMHitsPostProcessors diff --git a/Validation/RecoTrack/python/PostProcessorTracker_cfi.py b/Validation/RecoTrack/python/PostProcessorTracker_cfi.py index cec8d3cd3cff9..88a1e21e4bfb5 100644 --- a/Validation/RecoTrack/python/PostProcessorTracker_cfi.py +++ b/Validation/RecoTrack/python/PostProcessorTracker_cfi.py @@ -348,9 +348,9 @@ def _addNoFlow(module): phase2_tracker.toReplaceWith(postProcessorTrackSummary,postProcessorTrackSummaryPhase2) postProcessorTrackTrackingOnly = postProcessorTrack.clone() -postProcessorTrackTrackingOnly.subDirs.extend(["Tracking/TrackBHadron/*","Tracking/TrackSeeding/*", "Tracking/PixelTrack/*"]) +postProcessorTrackTrackingOnly.subDirs.extend(["Tracking/TrackBHadron/*", "Tracking/TrackSeeding/*", "Tracking/PixelTrack/*", "Tracking/PixelTrackFromPV/*", "Tracking/PixelTrackFromPVAllTP/*", "Tracking/PixelTrackBHadron/*"]) postProcessorTrackSummaryTrackingOnly = postProcessorTrackSummary.clone() -postProcessorTrackSummaryTrackingOnly.subDirs.extend(["Tracking/TrackBHadron","Tracking/TrackSeeding", "Tracking/PixelTrack"]) +postProcessorTrackSummaryTrackingOnly.subDirs.extend(["Tracking/TrackBHadron", "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 a90f3eac8a605..fc45589bd1569 100644 --- a/Validation/RecoTrack/python/TrackValidation_cff.py +++ b/Validation/RecoTrack/python/TrackValidation_cff.py @@ -528,6 +528,11 @@ def _getMVASelectors(postfix): # Built tracks, in the standard sequence mainly for monitoring the track selection MVA tpClusterProducerPreSplitting = tpClusterProducer.clone(pixelClusterSrc = "siPixelClustersPreSplitting") quickTrackAssociatorByHitsPreSplitting = quickTrackAssociatorByHits.clone(cluster2TPSrc = "tpClusterProducerPreSplitting") + +tpClusterProducerCUDAPreSplitting = tpClusterProducerCUDA.clone( + pixelClusterSrc = "siPixelClustersPreSplitting" +) + _trackValidatorSeedingBuilding = trackValidator.clone( # common for built tracks and seeds (in trackingOnly) associators = ["quickTrackAssociatorByHits"], UseAssociators = True, @@ -666,6 +671,16 @@ def _uniqueFirstLayers(layerList): VertexAssociatorByPositionAndTracks, trackingParticleNumberOfLayersProducer ) + +#gpu tp ??? +from Configuration.ProcessModifiers.gpu_cff import gpu +tpClusterProducerPreSplittingCUDA = cms.Task( + tpClusterProducerCUDAPreSplitting +) +_tracksValidationTruth_gpu = tracksValidationTruth.copy() +_tracksValidationTruth_gpu.add(tpClusterProducerPreSplittingCUDA) +gpu.toReplaceWith(tracksValidationTruth,_tracksValidationTruth_gpu) + fastSim.toModify(tracksValidationTruth, lambda x: x.remove(tpClusterProducer)) tracksPreValidation = cms.Task( @@ -905,9 +920,17 @@ def _uniqueFirstLayers(layerList): trackAssociation = "trackingParticlePixelTrackAsssociation" ) +_pixelTracksCustom = dict( + src = "pixelTracks", + vertexTag = "pixelVertices", +) +pixelTracksPt09 = generalTracksPt09.clone(quality = ["undefQuality"], **_pixelTracksCustom) +pixelTracksFromPV = generalTracksFromPV.clone(quality = "undefQuality", **_pixelTracksCustom) +pixelTracksFromPVPt09 = pixelTracksPt09.clone(src = "pixelTracksFromPV") + trackValidatorPixelTrackingOnly = trackValidator.clone( dirName = "Tracking/PixelTrack/", - label = ["pixelTracks"], + label = ["pixelTracks", "pixelTracksPt09"], doResolutionPlotsForLabels = [], trackCollectionForDrCalculation = "pixelTracks", associators = ["trackingParticlePixelTrackAsssociation"], @@ -916,16 +939,59 @@ def _uniqueFirstLayers(layerList): dodEdxPlots = False, cores = cms.InputTag(""), ) +trackValidatorFromPVPixelTrackingOnly = trackValidatorPixelTrackingOnly.clone( + dirName = "Tracking/PixelTrackFromPV/", + label = ["pixelTracksFromPV", "pixelTracksFromPVPt09"], + label_tp_effic = "trackingParticlesSignal", + label_tp_fake = "trackingParticlesSignal", + label_tp_effic_refvector = True, + label_tp_fake_refvector = True, + trackCollectionForDrCalculation = "pixelTracksFromPV", + doPlotsOnlyForTruePV = True, + doPVAssociationPlots = False, + doResolutionPlotsForLabels = ["disabled"], +) +trackValidatorFromPVAllTPPixelTrackingOnly = trackValidatorFromPVPixelTrackingOnly.clone( + dirName = "Tracking/PixelTrackFromPVAllTP/", + label_tp_effic = trackValidatorPixelTrackingOnly.label_tp_effic.value(), + label_tp_fake = trackValidatorPixelTrackingOnly.label_tp_fake.value(), + label_tp_effic_refvector = False, + label_tp_fake_refvector = False, + 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(trackingParticleRecoTrackAsssociation, trackingParticlePixelTrackAsssociation) tracksValidationTruthPixelTrackingOnly.replace(VertexAssociatorByPositionAndTracks, PixelVertexAssociatorByPositionAndTracks) +tracksValidationTruthPixelTrackingOnly.add(trackingParticlesBHadron) + +tracksPreValidationPixelTrackingOnly = cms.Task( + tracksValidationTruthPixelTrackingOnly, + trackingParticlesSignal, + pixelTracksPt09, + pixelTracksFromPV, + pixelTracksFromPVPt09, +) tracksValidationPixelTrackingOnly = cms.Sequence( - trackValidatorPixelTrackingOnly, - tracksValidationTruthPixelTrackingOnly + trackValidatorPixelTrackingOnly + + trackValidatorFromPVPixelTrackingOnly + + trackValidatorFromPVAllTPPixelTrackingOnly + + trackValidatorBHadronPixelTrackingOnly, + tracksPreValidationPixelTrackingOnly ) + ### Lite mode (only generalTracks and HP) trackValidatorLite = trackValidator.clone( label = ["generalTracks", "cutsRecoTracksHp"] diff --git a/Validation/RecoTrack/python/TrackingParticleSelectionsForEfficiency_cff.py b/Validation/RecoTrack/python/TrackingParticleSelectionsForEfficiency_cff.py index c020d894c8d4b..04f9e52ee18a7 100644 --- a/Validation/RecoTrack/python/TrackingParticleSelectionsForEfficiency_cff.py +++ b/Validation/RecoTrack/python/TrackingParticleSelectionsForEfficiency_cff.py @@ -12,7 +12,7 @@ ptMin = cms.double(0.9), ptMax = cms.double(1e100), maxRapidity = cms.double(2.5), - tip = cms.double(3.5), + tip = cms.double(2.0), minPhi = cms.double(-3.2), maxPhi = cms.double(3.2), invertRapidityCut = cms.bool(False) diff --git a/Validation/RecoTrack/python/plotting/html.py b/Validation/RecoTrack/python/plotting/html.py index a76601813fc3f..4e3bc59fed8fc 100644 --- a/Validation/RecoTrack/python/plotting/html.py +++ b/Validation/RecoTrack/python/plotting/html.py @@ -62,8 +62,14 @@ def _allToHP(s): return s.replace("All", "High purity") def _allToBTV(s): return s.replace("All", "BTV-like") +def _allPtCut(s): + return s.replace("All tracks", "Tracks pT > 0.9 GeV") 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([ ("seeding_seeds", "Seeds"), ("seeding_seedsa", "Seeds A"), @@ -74,8 +80,8 @@ def _ptCut(s): ("building_", "Built tracks"), ("", _allName), ("highPurity", _allToHP(_allName)), - ("Pt09", "Tracks pT > 0.9 GeV"), - ("highPurityPt09", "High purity tracks pT > 0.9 GeV"), + ("Pt09", _allPtCut(_allName)), + ("highPurityPt09", _ptCut(_allToHP(_allName))), ("ByOriginalAlgo", _toOriAlgo(_allName)), ("highPurityByOriginalAlgo", _toOriAlgo(_toHP(_allName))), ("ByAlgoMask", _toAlgoMask(_allName)), @@ -113,6 +119,15 @@ def _ptCut(s): ("bhadron_ByAlgoMask", _toAlgoMask(_bhadronName)), ("bhadron_highPurityByAlgoMask", _toAlgoMask(_allToHP(_bhadronName))), ("bhadron_btvLike", _allToBTV(_bhadronName)), + # Pixel tracks + ("pixel_", _allToPixel(_allName)), + ("pixel_Pt09", _ptCut(_allToPixel(_allName))), + ("pixelFromPV_", _toPixel(_fromPVName)), + ("pixelFromPV_Pt09", _ptCut(_toPixel(_fromPVName))), + ("pixelFromPVAllTP_", _toPixel(_fromPVAllTPName)), + ("pixelFromPVAllTP_Pt09", _ptCut(_toPixel(_fromPVAllTPName))), + ("pixelbhadron_", _allToPixel(_bhadronName)), + ("pixelbhadron_Pt09", _ptCut(_allToPixel(_bhadronName))), ]) _trackAlgoName = { @@ -127,6 +142,7 @@ def _ptCut(s): "iter7" : "Iterative Step 7", "iter9" : "Iterative Step 9", "iter10": "Iterative Step 10", + "pixel": "Pixel tracks", } _trackAlgoOrder = [ @@ -161,6 +177,7 @@ def _ptCut(s): 'iter7', 'iter9', 'iter10', + "pixel", ] _pageNameMap = { @@ -178,10 +195,10 @@ def _ptCut(s): # These are for the summary page ("seeding_seeds", "Seeds"), ("building", "Built tracks"), - ("", "All tracks"), - ("Pt09", "All tracks (pT>0.9 GeV)"), - ("highPurity", "High purity tracks"), - ("highPurityPt09", "High purity tracks (pT>0.9 GeV)"), + ("", _allName), + ("Pt09", _allPtCut(_allName)), + ("highPurity", _allToHP(_allName)), + ("highPurityPt09", _ptCut(_allToHP(_allName))), ("tpPtLess09", _tpPtLess09Name), ("tpPtLess09_highPurity", _allToHP(_tpPtLess09Name)), ("tpEtaGreater2p7", _tpEtaGreater2p7Name), @@ -199,7 +216,14 @@ def _ptCut(s): ("bhadron", _bhadronName), ("bhadron_highPurity", _allToHP(_bhadronName)), # Pixel tracks - ("pixel", "Pixel tracks"), + ("pixel", _allToPixel(_allName)), + ("pixelPt09", _ptCut(_allToPixel(_allName))), + ("pixelFromPV", _toPixel(_fromPVName)), + ("pixelFromPVPt09", _ptCut(_toPixel(_fromPVName))), + ("pixelFromPVAllTP", _toPixel(_fromPVAllTPName)), + ("pixelFromPVAllTPPt09", _ptCut(_toPixel(_fromPVAllTPName))), + ("pixelbhadron", _allToPixel(_bhadronName)), + ("pixelbhadronPt09", _ptCut(_allToPixel(_bhadronName))), # These are for vertices ("genvertex", "Gen vertices"), ("pixelVertices", "Pixel vertices"), @@ -223,6 +247,7 @@ def _ptCut(s): _fromPVAllTP2Legend = "Tracks from reco PV (another method), fake rate numerator contains all TrackingParticles (separates fake tracks from pileup tracks)" _fromPVAllTPPt2Legend = "Tracks (pT > 0.9 GeV) from reco PV (another method), fake rate numerator contains all TrackingParticles (separates fake tracks from pileup tracks)" _bhadronLegend = "All tracks, efficiency denominator contains only TrackingParticles from B-hadron decays" +_bhadronPtLegend = "Tracks (pT > 0.9 GeV), efficiency denominator contains only TrackingParticles from B-hadron decays" def _sectionNameLegend(): return { @@ -248,6 +273,12 @@ def _sectionNameLegend(): "bhadron_": _bhadronLegend, "bhadron_highPurity": _allToHP(_bhadronLegend), "bhadron_btvLike": _bhadronLegend.replace("All tracks", _btvLegend), + "pixelFromPV_": _fromPVLegend, + "pixelFromPV_Pt09": _fromPVPtLegend, + "pixelFromPVAllTP_": _fromPVAllTPLegend, + "pixelFromPVAllTP_Pt09": _fromPVAllTPPtLegend, + "pixelbhadron_": _bhadronLegend, + "pixelbhadron_Pt09": _bhadronPtLegend, } class Table: @@ -691,7 +722,7 @@ def __init__(self, sample, title, fastVsFull, pileupComparison): self._timingPage = PageSet(*params) self._pfPages = PageSet(*params) self._hltPages = PageSet(*params, dqmSubFolderTranslatedToSectionName=lambda algoQuality: algoQuality[0]) - self._pixelPages = PageSet(*params, dqmSubFolderTranslatedToSectionName=lambda algoQuality: algoQuality[0]) + self._pixelPages = TrackingPageSet(*params) self._otherPages = PageSet(*params) self._purposePageMap = { diff --git a/Validation/RecoTrack/python/plotting/trackingPlots.py b/Validation/RecoTrack/python/plotting/trackingPlots.py index 374c83cf50ac8..9d8f81c64b35f 100644 --- a/Validation/RecoTrack/python/plotting/trackingPlots.py +++ b/Validation/RecoTrack/python/plotting/trackingPlots.py @@ -637,6 +637,8 @@ def _mapCollectionToAlgoQuality(collName): prefixes = ["cutsreco", "cutsrecofrompv", "cutsrecofrompv2", "cutsrecofrompvalltp", "cutsrecoetagreater2p7"] if collNameLow in ["general", "generalfrompv", "generaletagreater2p7"]+prefixes: algo = "ootb" + elif collNameLow in ["pixel", "pixelfrompv", "pixelfrompvalltp"]: + algo = "pixel" else: def testColl(coll): for pfx in prefixes: @@ -961,6 +963,7 @@ class HighPurityPt09: pass class BTVLike: pass class AK4PFJets: pass class Pixel: pass + class PixelPt09: pass def __init__(self, section, collection=GeneralTracks): self._collection = collection @@ -1003,6 +1006,8 @@ def _getN(hname): return _getAlgoQuality(data, "ak4PFJets", "") elif self._collection == TrackingSummaryTable.Pixel: return _getAlgoQuality(data, "pixel", "") + elif self._collection == TrackingSummaryTable.PixelPt09: + return _getAlgoQuality(data, "pixel", "Pt09") else: raise Exception("Collection not recognized, %s" % str(self._collection)) def _formatOrNone(num, func): @@ -1376,11 +1381,21 @@ def _appendTrackingPlots(lastDirName, name, algoPlots, onlyForPileup=False, only _appendTrackingPlots("TrackGsf", "gsf", _simBasedPlots+_recoBasedPlots, onlyForElectron=True, rawSummary=True, highPuritySummary=False) _appendTrackingPlots("TrackBHadron", "bhadron", _simBasedPlots+_recoBasedPlots, onlyForBHadron=True) # Pixel tracks -_common = dict(purpose=PlotPurpose.Pixel, page="pixel") -plotter.append("pixelTrack", _trackingFolders("PixelTrack"), TrackingPlotFolder(*(_simBasedPlots+_recoBasedPlots), **_common)) -plotterExt.append("pixelTrack", _trackingFolders("PixelTrack"), TrackingPlotFolder(*_extendedPlots, **_common)) -plotter.append("pixelTrack_summary", _trackingFolders("PixelTrack"), PlotFolder(_summaryRaw, _summaryRawN, loopSubFolders=False, purpose=PlotPurpose.TrackingSummary, page="summary", section="pixel")) -plotter.appendTable("pixelTrack_summary", _trackingFolders("PixelTrack"), TrackingSummaryTable(section="pixel", collection=TrackingSummaryTable.Pixel)) +def _appendPixelTrackingPlots(lastDirName, name): + _common = dict(purpose=PlotPurpose.Pixel, page="pixel") + _folders = _trackingFolders(lastDirName) + + plotter.append(name, _folders, TrackingPlotFolder(*(_simBasedPlots+_recoBasedPlots), **_common)) + plotterExt.append(name, _folders, TrackingPlotFolder(*_extendedPlots, **_common)) + + plotter.append(name+"_summary", _folders, PlotFolder(_summaryRaw, _summaryRawN, loopSubFolders=False, purpose=PlotPurpose.TrackingSummary, page="summary", section=name)) + plotter.append(name+"_summary", _folders, PlotFolder(_summaryRaw, _summaryRawN, loopSubFolders=False, purpose=PlotPurpose.TrackingSummary, page="summary", section=name+"Pt09")) + plotter.appendTable(name+"_summary", _folders, TrackingSummaryTable(section=name, collection=TrackingSummaryTable.Pixel)) + plotter.appendTable(name+"_summary", _folders, TrackingSummaryTable(section=name+"Pt09", collection=TrackingSummaryTable.PixelPt09)) +_appendPixelTrackingPlots("PixelTrack", "pixel") +_appendPixelTrackingPlots("PixelTrackFromPV", "pixelFromPV") +_appendPixelTrackingPlots("PixelTrackFromPVAllTP", "pixelFromPVAllTP") +_appendPixelTrackingPlots("PixelTrackBHadron", "pixelbhadron") # MiniAOD diff --git a/Validation/SiPixelPhase1ConfigV/python/SiPixelPhase1OfflineDQM_sourceV_cff.py b/Validation/SiPixelPhase1ConfigV/python/SiPixelPhase1OfflineDQM_sourceV_cff.py index 1a5692bf6677b..d0d98251ea9d4 100644 --- a/Validation/SiPixelPhase1ConfigV/python/SiPixelPhase1OfflineDQM_sourceV_cff.py +++ b/Validation/SiPixelPhase1ConfigV/python/SiPixelPhase1OfflineDQM_sourceV_cff.py @@ -20,3 +20,38 @@ + SiPixelPhase1TrackingParticleAnalyzerV ) +### Pixel Tracking-only configurations for the GPU workflow + +# Pixel digis +pixelOnlyDigisAnalyzerV = SiPixelPhase1DigisAnalyzerV.clone() + +# Pixel clusters +pixelOnlyTrackClustersAnalyzerV = SiPixelPhase1TrackClustersAnalyzerV.clone( + clusters = 'siPixelClustersPreSplitting', + tracks = 'pixelTracks' +) + +# Pixel rechit analyzer +pixelOnlyRecHitsAnalyzerV = SiPixelPhase1RecHitsAnalyzerV.clone( + src = 'siPixelRecHitsPreSplitting', + pixelSimLinkSrc = 'simSiPixelDigis', + ROUList = ('TrackerHitsPixelBarrelLowTof', + 'TrackerHitsPixelBarrelHighTof', + 'TrackerHitsPixelEndcapLowTof', + 'TrackerHitsPixelEndcapHighTof') +) + +# Pixel hits +pixelOnlyHitsAnalyzerV = SiPixelPhase1HitsAnalyzerV.clone( + tracksTag = 'pixelTracks' +) + +# Tracking particles +pixelOnlyTrackingParticleAnalyzerV = SiPixelPhase1TrackingParticleAnalyzerV.clone() + +siPixelPhase1ValidationPixelTrackingOnly_sourceV = cms.Sequence(pixelOnlyDigisAnalyzerV + + pixelOnlyTrackClustersAnalyzerV + + pixelOnlyHitsAnalyzerV + + pixelOnlyRecHitsAnalyzerV + + pixelOnlyTrackingParticleAnalyzerV +)