/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
SimTracker/SiPhase2Digitizer/plugins/Phase2TrackerDigitizerAlgorithm.cc
1 033 строки
48 KB
Suchandra
removing comment line
16 июн 2026, 19:04
16 июн 2026, 19:04
8f9c9fe
Код
Авторство
О чём код?
#include <cmath> #include <iostream> #include <typeinfo> #include "CLHEP/Random/RandGaussQ.h" #include "CalibTracker/SiPixelESProducers/interface/SiPixelGainCalibrationOfflineSimService.h" #include "CondFormats/SiPhase2TrackerObjects/interface/SiPhase2OuterTrackerLorentzAngle.h" #include "CondFormats/SiPixelObjects/interface/GlobalPixel.h" #include "CondFormats/SiPixelObjects/interface/LocalPixel.h" #include "CondFormats/SiPixelObjects/interface/SiPixelLorentzAngle.h" #include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/GeometryVector/interface/LocalPoint.h" #include "DataFormats/SiPixelDetId/interface/PixelSubdetector.h" #include "DataFormats/SiStripDetId/interface/StripSubdetector.h" #include "DataFormats/TrackerCommon/interface/TrackerTopology.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "FWCore/ParameterSet/interface/ParameterSet.h" #include "FWCore/ServiceRegistry/interface/Service.h" #include "FWCore/Utilities/interface/Exception.h" #include "Geometry/CommonTopologies/interface/PixelGeomDetUnit.h" #include "Geometry/CommonTopologies/interface/PixelTopology.h" #include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" #include "SimGeneral/NoiseGenerators/interface/GaussianTailNoiseGenerator.h" #include "SimTracker/Common/interface/SiG4UniversalFluctuation.h" #include "SimTracker/SiPhase2Digitizer/plugins/Phase2TrackerDigitizerAlgorithm.h" using namespace edm; namespace ph2tkdigialgo { // Mass in MeV constexpr double m_pion = 139.571; constexpr double m_kaon = 493.677; constexpr double m_electron = 0.511; constexpr double m_muon = 105.658; constexpr double m_proton = 938.272; } // namespace ph2tkdigialgo Phase2TrackerDigitizerAlgorithm::Phase2TrackerDigitizerAlgorithm(const edm::ParameterSet& conf_common, const edm::ParameterSet& conf_specific, edm::ConsumesCollector iC) : _signal(), makeDigiSimLinks_(conf_common.getUntrackedParameter<bool>("makeDigiSimLinks", true)), use_ineff_from_db_(conf_specific.getParameter<bool>("Inefficiency_DB")), use_module_killing_(conf_specific.getParameter<bool>("KillModules")), // boolean to kill or not modules use_deadmodule_DB_(conf_specific.getParameter<bool>("DeadModules_DB")), // boolean to access dead modules from DB // boolean to access Lorentz angle from DB use_LorentzAngle_DB_(conf_specific.getParameter<bool>("LorentzAngle_DB")), // get dead module from cfg file deadModules_(use_deadmodule_DB_ ? Parameters() : conf_specific.getParameter<Parameters>("DeadModules")), // Common pixel parameters // These are parameters which are not likely to be changed GeVperElectron_(3.61E-09), // 1 electron(3.61eV, 1keV(277e, mod 9/06 d.k. alpha2Order_(conf_specific.getParameter<bool>("Alpha2Order")), // switch on/off of E.B effect addXtalk_(conf_specific.getParameter<bool>("AddXTalk")), // Interstrip Coupling - Not used in PixelDigitizerAlgorithm interstripCoupling_(conf_specific.getParameter<double>("InterstripCoupling")), Sigma0_(conf_specific.getParameter<double>("SigmaZero")), // Charge diffusion constant 7->3.7 SigmaCoeff_(conf_specific.getParameter<double>("SigmaCoeff")), // delta in the diffusion across the strip pitch // (set between 0 to 0.9, 0-->flat Sigma0, 1-->Sigma_min=0 & Sigma_max=2*Sigma0 // D.B.: Dist300 replaced by moduleThickness, may not work with partially depleted sensors but works otherwise // Dist300(0.0300), // normalized to 300micron Silicon // Charge integration spread on the collection plane clusterWidth_(conf_specific.getParameter<double>("ClusterWidth")), // Allowed modes of readout which has following values : // 0 ---> Digital or binary readout // -1 ---> Analog readout, current digitizer (Inner Pixel) (TDR version) with no threshold subtraction // Analog readout with dual slope with the "second" slope being 1/2^(n-1) and threshold subtraction (n = 1, 2, 3,4) thePhase2ReadoutMode_(conf_specific.getParameter<int>("Phase2ReadoutMode")), // ADC calibration 1adc count(135e. // Corresponds to 2adc/kev, 270[e/kev]/135[e/adc](2[adc/kev] // Be careful, this parameter is also used in SiPixelDet.cc to // calculate the noise in adc counts from noise in electrons. // Both defaults should be the same. theElectronPerADC_(conf_specific.getParameter<double>("ElectronPerAdc")), // ADC saturation value, 255(8bit adc. theAdcFullScale_(conf_specific.getParameter<int>("AdcFullScale")), // Noise in electrons: // Pixel cell noise, relevant for generating noisy pixels theNoiseInElectrons_(conf_specific.getParameter<double>("NoiseInElectrons")), // Fill readout noise, including all readout chain, relevant for smearing theReadoutNoise_(conf_specific.getParameter<double>("ReadoutNoiseInElec")), // Threshold in units of noise: // thePixelThreshold(conf.getParameter<double>("ThresholdInNoiseUnits")), // Pixel threshold in electron units. theThresholdInE_Endcap_(conf_specific.getParameter<double>("ThresholdInElectrons_Endcap")), theThresholdInE_Barrel_(conf_specific.getParameter<double>("ThresholdInElectrons_Barrel")), // Add threshold gaussian smearing: theThresholdSmearing_Endcap_(conf_specific.getParameter<double>("ThresholdSmearing_Endcap")), theThresholdSmearing_Barrel_(conf_specific.getParameter<double>("ThresholdSmearing_Barrel")), // Add HIP Threshold in electron units. theHIPThresholdInE_Endcap_(conf_specific.getParameter<double>("HIPThresholdInElectrons_Endcap")), theHIPThresholdInE_Barrel_(conf_specific.getParameter<double>("HIPThresholdInElectrons_Barrel")), // theTofCut 12.5, cut in particle TOD +/- 12.5ns theTofLowerCut_(conf_specific.getParameter<double>("TofLowerCut")), theTofUpperCut_(conf_specific.getParameter<double>("TofUpperCut")), cosmicShift_(conf_common.getParameter<double>("CosmicDelayShift")), // Get the Lorentz angle from the cfg file: tanLorentzAnglePerTesla_Endcap_( use_LorentzAngle_DB_ ? 0.0 : conf_specific.getParameter<double>("TanLorentzAnglePerTesla_Endcap")), tanLorentzAnglePerTesla_Barrel_( use_LorentzAngle_DB_ ? 0.0 : conf_specific.getParameter<double>("TanLorentzAnglePerTesla_Barrel")), // Add noise addNoise_(conf_specific.getParameter<bool>("AddNoise")), // Add noisy pixels addNoisyPixels_(conf_specific.getParameter<bool>("AddNoisyPixels")), // Check ALL modules to find Noisy Cells else only modules with at least one SimHit checkAllModulesForNoisyCells_(conf_specific.getParameter<bool>("CheckAllModulesForNoisyCells")), // Fluctuate charge in track subsegments fluctuateCharge_(conf_specific.getUntrackedParameter<bool>("FluctuateCharge", true)), // Control the pixel inefficiency addPixelInefficiency_(conf_specific.getParameter<bool>("AddInefficiency")), // Add threshold gaussian smearing: addThresholdSmearing_(conf_specific.getParameter<bool>("AddThresholdSmearing")), // Add some pseudo-red damage pseudoRadDamage_(conf_specific.exists("PseudoRadDamage") ? conf_specific.getParameter<double>("PseudoRadDamage") : double(0.0)), pseudoRadDamageRadius_(conf_specific.exists("PseudoRadDamageRadius") ? conf_specific.getParameter<double>("PseudoRadDamageRadius") : double(0.0)), // Add pixel radiation damage useChargeReweighting_(conf_specific.getParameter<bool>("UseReweighting")), theSiPixelChargeReweightingAlgorithm_( useChargeReweighting_ ? std::make_unique<SiPixelChargeReweightingAlgorithm>(conf_specific, iC) : nullptr), // delta cutoff in MeV, has to be same as in OSCAR(0.030/cmsim=1.0 MeV // tMax(0.030), // In MeV. // tMax(conf.getUntrackedParameter<double>("DeltaProductionCut",0.030)), tMax_(conf_common.getParameter<double>("DeltaProductionCut")), badPixels_(conf_specific.getParameter<Parameters>("CellsToKill")), fluctuate_(fluctuateCharge_ ? std::make_unique<SiG4UniversalFluctuation>() : nullptr), theNoiser_(addNoise_ ? std::make_unique<GaussianTailNoiseGenerator>() : nullptr), theSiPixelGainCalibrationService_( use_ineff_from_db_ ? std::make_unique<SiPixelGainCalibrationOfflineSimService>(conf_specific, iC) : nullptr), subdetEfficiencies_(conf_specific) { LogDebug("Phase2TrackerDigitizerAlgorithm") << "Phase2TrackerDigitizerAlgorithm constructed\n" << "Configuration parameters:\n" << "Threshold/Gain = " << "threshold in electron Endcap = " << theThresholdInE_Endcap_ << "\nthreshold in electron Barrel = " << theThresholdInE_Barrel_ << " ElectronPerADC " << theElectronPerADC_ << " ADC Scale (in bits) " << theAdcFullScale_ << " The delta cut-off is set to " << tMax_ << " pix-inefficiency " << addPixelInefficiency_; } Phase2TrackerDigitizerAlgorithm::~Phase2TrackerDigitizerAlgorithm() { LogDebug("Phase2TrackerDigitizerAlgorithm") << "Phase2TrackerDigitizerAlgorithm deleted"; } Phase2TrackerDigitizerAlgorithm::SubdetEfficiencies::SubdetEfficiencies(const edm::ParameterSet& conf) { barrel_efficiencies = conf.getParameter<std::vector<double> >("EfficiencyFactors_Barrel"); endcap_efficiencies = conf.getParameter<std::vector<double> >("EfficiencyFactors_Endcap"); } void Phase2TrackerDigitizerAlgorithm::accumulateSimHits(std::vector<PSimHit>::const_iterator inputBegin, std::vector<PSimHit>::const_iterator inputEnd, const size_t inputBeginGlobalIndex, const uint32_t tofBin, const Phase2TrackerGeomDetUnit* pixdet, const GlobalVector& bfield) { // produce SignalPoint's for all SimHit's in detector // Loop over hits uint32_t detId = pixdet->geographicalId().rawId(); size_t simHitGlobalIndex = inputBeginGlobalIndex; // This needs to be stored to create the digi-sim link later // find the relevant hits std::vector<PSimHit> matchedSimHits; std::copy_if(inputBegin, inputEnd, std::back_inserter(matchedSimHits), [detId](auto const& hit) -> bool { return hit.detUnitId() == detId; }); // loop over a much reduced set of SimHits for (auto const& hit : matchedSimHits) { LogDebug("Phase2DigitizerAlgorithm") << hit.particleType() << " " << hit.pabs() << " " << hit.energyLoss() << " " << hit.tof() << " " << hit.trackId() << " " << hit.processType() << " " << hit.detUnitId() << hit.entryPoint() << " " << hit.exitPoint(); double signalScale = 1.0; // fill collection_points for this SimHit, indpendent of topology if (select_hit(hit, (pixdet->surface().toGlobal(hit.localPosition()).mag() * c_inv) + cosmicShift_, signalScale)) { const auto& ionization_points = primary_ionization(hit); // fills ionization_points // transforms ionization_points -> collection_points const auto& collection_points = drift(hit, pixdet, bfield, ionization_points); // compute induced signal on readout elements and add to _signal // hit needed only for SimHit<-->Digi link induce_signal(inputBegin, hit, simHitGlobalIndex, inputBeginGlobalIndex, tofBin, pixdet, collection_points); LogDebug("Phase2DigitizerAlgorithm") << "Induced signal was computed"; } ++simHitGlobalIndex; } } // ================================================================= // // Generate primary ionization along the track segment. // Divide the track into small sub-segments // // ================================================================= std::vector<digitizerUtility::EnergyDepositUnit> Phase2TrackerDigitizerAlgorithm::primary_ionization( const PSimHit& hit) const { // Straight line approximation for trajectory inside active media constexpr float SegmentLength = 0.0010; // in cm (10 microns) // Get the 3D segment direction vector LocalVector direction = hit.exitPoint() - hit.entryPoint(); float eLoss = hit.energyLoss(); // Eloss in GeV float length = direction.mag(); // Track length in Silicon int NumberOfSegments = static_cast<int>(length / SegmentLength); // Number of segments if (NumberOfSegments < 1) NumberOfSegments = 1; LogDebug("Phase2TrackerDigitizerAlgorithm") << "enter primary_ionzation " << NumberOfSegments << " shift = " << hit.exitPoint().x() - hit.entryPoint().x() << " " << hit.exitPoint().y() - hit.entryPoint().y() << " " << hit.exitPoint().z() - hit.entryPoint().z() << " " << hit.particleType() << " " << hit.pabs(); std::vector<float> elossVector; elossVector.reserve(NumberOfSegments); if (fluctuateCharge_) { // Generate fluctuated charge points elossVector = fluctuateEloss(hit.particleType(), hit.pabs(), eLoss, length, NumberOfSegments); } else { float averageEloss = eLoss / NumberOfSegments; elossVector.resize(NumberOfSegments, averageEloss); } std::vector<digitizerUtility::EnergyDepositUnit> ionization_points; ionization_points.reserve(NumberOfSegments); // set size // loop over segments for (size_t i = 0; i < elossVector.size(); ++i) { // Divide the segment into equal length subsegments Local3DPoint point = hit.entryPoint() + ((i + 0.5) / NumberOfSegments) * direction; float energy = elossVector[i] / GeVperElectron_; // Convert charge to elec. digitizerUtility::EnergyDepositUnit edu(energy, point); // define position,energy point ionization_points.push_back(edu); // save LogDebug("Phase2TrackerDigitizerAlgorithm") << "For index = " << i << " EnergyDepositUnit-x = " << edu.x() << " EnergyDepositUnit-y = " << edu.y() << " EnergyDepositUnit-z = " << edu.z() << " EnergyDepositUnit-energy = " << edu.energy(); } return ionization_points; } //============================================================================== // // Fluctuate the charge comming from a small (10um) track segment. // Use the G4 routine. For mip pions for the moment. // //============================================================================== std::vector<float> Phase2TrackerDigitizerAlgorithm::fluctuateEloss( int pid, float particleMomentum, float eloss, float length, int NumberOfSegs) const { double particleMass = ph2tkdigialgo::m_pion; // Mass in MeV, assume pion pid = std::abs(pid); if (pid != 211) { // Mass in MeV if (pid == 11) particleMass = ph2tkdigialgo::m_electron; else if (pid == 13) particleMass = ph2tkdigialgo::m_muon; else if (pid == 321) particleMass = ph2tkdigialgo::m_kaon; else if (pid == 2212) particleMass = ph2tkdigialgo::m_proton; } // What is the track segment length. float segmentLength = length / NumberOfSegs; // Generate charge fluctuations. float sum = 0.; double segmentEloss = (1000. * eloss) / NumberOfSegs; //eloss in MeV std::vector<float> elossVector; elossVector.reserve(NumberOfSegs); for (int i = 0; i < NumberOfSegs; ++i) { // The G4 routine needs momentum in MeV, mass in Mev, delta-cut in MeV, // track segment length in mm, segment eloss in MeV // Returns fluctuated eloss in MeV double deltaCutoff = tMax_; // the cutoff is sometimes redefined inside, so fix it. float de = fluctuate_->SampleFluctuations(particleMomentum * 1000., particleMass, deltaCutoff, segmentLength * 10., segmentEloss, rengine_) / 1000.; //convert to GeV elossVector.push_back(de); sum += de; } if (sum > 0.) { // if fluctuations give eloss>0. // Rescale to the same total eloss float ratio = eloss / sum; std::transform( std::begin(elossVector), std::end(elossVector), std::begin(elossVector), [&ratio](auto const& c) -> float { return c * ratio; }); // use a simple lambda expression } else { // if fluctuations gives 0 eloss float averageEloss = eloss / NumberOfSegs; elossVector.resize(NumberOfSegs, averageEloss); } return elossVector; } // ====================================================================== // // Drift the charge segments to the sensor surface (collection plane) // Include the effect of E-field and B-field // // ===================================================================== std::vector<digitizerUtility::SignalPoint> Phase2TrackerDigitizerAlgorithm::drift( const PSimHit& hit, const Phase2TrackerGeomDetUnit* pixdet, const GlobalVector& bfield, const std::vector<digitizerUtility::EnergyDepositUnit>& ionization_points) const { LogDebug("Phase2TrackerDigitizerAlgorithm") << "Enter drift "; std::vector<digitizerUtility::SignalPoint> collection_points; collection_points.reserve(ionization_points.size()); // set size LocalVector driftDir = driftDirection(pixdet, bfield, hit.detUnitId()); // get the charge drift direction if (driftDir.z() == 0.) { LogWarning("Phase2TrackerDigitizerAlgorithm") << " pxlx: drift in z is zero "; return collection_points; } float TanLorenzAngleX = driftDir.x(); // tangent of Lorentz angle float TanLorenzAngleY = 0.; // force to 0, driftDir.y()/driftDir.z(); float dir_z = driftDir.z(); // The z drift direction float CosLorenzAngleX = 1. / std::sqrt(1. + std::pow(TanLorenzAngleX, 2)); // cosine to estimate the path length float CosLorenzAngleY = 1.; if (alpha2Order_) { TanLorenzAngleY = driftDir.y(); CosLorenzAngleY = 1. / std::sqrt(1. + std::pow(TanLorenzAngleY, 2)); // cosine } float moduleThickness = pixdet->specificSurface().bounds().thickness(); float stripPitch = pixdet->specificTopology().pitch().first; LogDebug("Phase2TrackerDigitizerAlgorithm") << " Lorentz Tan-X " << TanLorenzAngleX << " Lorentz Tan-Y " << TanLorenzAngleY << " Lorentz Cos-X " << CosLorenzAngleX << " Lorentz Cos-Y " << CosLorenzAngleY << " ticknes * Lorentz Tan-X = " << moduleThickness * TanLorenzAngleX << " drift direction " << driftDir; for (auto const& val : ionization_points) { // position float SegX = val.x(), SegY = val.y(), SegZ = val.z(); // Distance from the collection plane // DriftDistance = (moduleThickness/2. + SegZ); // Drift to -z // Include explixitely the E drift direction (for CMS dir_z=-1) // Distance between charge generation and collection float driftDistance = moduleThickness / 2. - (dir_z * SegZ); // Drift to -z if (driftDistance < 0.) driftDistance = 0.; else if (driftDistance > moduleThickness) driftDistance = moduleThickness; // Assume full depletion now, partial depletion will come later. float XDriftDueToMagField = driftDistance * TanLorenzAngleX; float YDriftDueToMagField = driftDistance * TanLorenzAngleY; // Shift cloud center float CloudCenterX = SegX + XDriftDueToMagField; float CloudCenterY = SegY + YDriftDueToMagField; // Calculate how long is the charge drift path // Actual Drift Lentgh float driftLength = std::sqrt(std::pow(driftDistance, 2) + std::pow(XDriftDueToMagField, 2) + std::pow(YDriftDueToMagField, 2)); // What is the charge diffusion after this path // Sigma0=0.00037 is for 300um thickness (make sure moduleThickness is in [cm]) float Sigma = std::sqrt(driftLength / moduleThickness) * Sigma0_ * moduleThickness / 0.0300; // D.B.: sigmaCoeff=0 means no modulation if (SigmaCoeff_) Sigma *= (SigmaCoeff_ * std::pow(cos(SegX * M_PI / stripPitch), 2) + 1); // NB: divided by 4 to get a periodicity of stripPitch // Project the diffusion sigma on the collection plane float Sigma_x = Sigma / CosLorenzAngleX; float Sigma_y = Sigma / CosLorenzAngleY; // Insert a charge loss due to Rad Damage here float energyOnCollector = val.energy(); // The energy that reaches the collector // pseudoRadDamage if (pseudoRadDamage_) { float moduleRadius = pixdet->surface().position().perp(); if (moduleRadius <= pseudoRadDamageRadius_) { float kValue = pseudoRadDamage_ / std::pow(moduleRadius, 2); energyOnCollector *= exp(-1 * kValue * driftDistance / moduleThickness); } } LogDebug("Phase2TrackerDigitizerAlgorithm") << "Dift DistanceZ = " << driftDistance << " module thickness = " << moduleThickness << " Start Energy = " << val.energy() << " Energy after loss= " << energyOnCollector; digitizerUtility::SignalPoint sp(CloudCenterX, CloudCenterY, Sigma_x, Sigma_y, hit.tof(), energyOnCollector); // Load the Charge distribution parameters collection_points.push_back(sp); } return collection_points; } // ==================================================================== // // Induce the signal on the collection plane of the active sensor area. void Phase2TrackerDigitizerAlgorithm::induce_signal( std::vector<PSimHit>::const_iterator inputBegin, const PSimHit& hit, const size_t hitIndex, const size_t firstHitIndex, const uint32_t tofBin, const Phase2TrackerGeomDetUnit* pixdet, const std::vector<digitizerUtility::SignalPoint>& collection_points) { // X - Rows, Left-Right, 160, (1.6cm) for barrel // Y - Columns, Down-Up, 416, (6.4cm) const Phase2TrackerTopology* topol = &pixdet->specificTopology(); uint32_t detID = pixdet->geographicalId().rawId(); signal_map_type& theSignal = _signal[detID]; LogDebug("Phase2TrackerDigitizerAlgorithm") << "Enter induce_signal, Pitch size is " << topol->pitch().first << " cm vs " << topol->pitch().second << " cm"; // local map to store pixels hit by 1 Hit. using hit_map_type = std::map<int, float, std::less<int> >; hit_map_type hit_signal; // Assign signals to readout channels and store sorted by channel number // Iterate over collection points on the collection plane for (auto const& v : collection_points) { float CloudCenterX = v.position().x(); // Charge position in x float CloudCenterY = v.position().y(); // in y float SigmaX = v.sigma_x(); // Charge spread in x float SigmaY = v.sigma_y(); // in y float Charge = v.amplitude(); // Charge amplitude LogDebug("Phase2TrackerDigitizerAlgorithm") << " cloud " << v.position().x() << " " << v.position().y() << " " << v.sigma_x() << " " << v.sigma_y() << " " << v.amplitude(); // Find the maximum cloud spread in 2D plane , assume 3*sigma float CloudRight = CloudCenterX + clusterWidth_ * SigmaX; float CloudLeft = CloudCenterX - clusterWidth_ * SigmaX; float CloudUp = CloudCenterY + clusterWidth_ * SigmaY; float CloudDown = CloudCenterY - clusterWidth_ * SigmaY; // Define 2D cloud limit points LocalPoint PointRightUp = LocalPoint(CloudRight, CloudUp); LocalPoint PointLeftDown = LocalPoint(CloudLeft, CloudDown); // This points can be located outside the sensor area. // The conversion to measurement point does not check for that // so the returned pixel index might be wrong (outside range). // We rely on the limits check below to fix this. // But remember whatever we do here THE CHARGE OUTSIDE THE ACTIVE // PIXEL ARE IS LOST, it should not be collected. // Convert the 2D points to pixel indices MeasurementPoint mp = topol->measurementPosition(PointRightUp); int IPixRightUpX = static_cast<int>(std::floor(mp.x())); // cast reqd. int IPixRightUpY = static_cast<int>(std::floor(mp.y())); LogDebug("Phase2TrackerDigitizerAlgorithm") << " right-up " << PointRightUp << " " << mp.x() << " " << mp.y() << " " << IPixRightUpX << " " << IPixRightUpY; mp = topol->measurementPosition(PointLeftDown); int IPixLeftDownX = static_cast<int>(std::floor(mp.x())); int IPixLeftDownY = static_cast<int>(std::floor(mp.y())); LogDebug("Phase2TrackerDigitizerAlgorithm") << " left-down " << PointLeftDown << " " << mp.x() << " " << mp.y() << " " << IPixLeftDownX << " " << IPixLeftDownY; // Check detector limits to correct for pixels outside range. int numColumns = topol->ncolumns(); // det module number of cols&rows int numRows = topol->nrows(); IPixRightUpX = numRows > IPixRightUpX ? IPixRightUpX : numRows - 1; IPixRightUpY = numColumns > IPixRightUpY ? IPixRightUpY : numColumns - 1; IPixLeftDownX = 0 < IPixLeftDownX ? IPixLeftDownX : 0; IPixLeftDownY = 0 < IPixLeftDownY ? IPixLeftDownY : 0; // First integrate charge strips in x hit_map_type x; for (int ix = IPixLeftDownX; ix <= IPixRightUpX; ++ix) { // loop over x index float xLB, LowerBound; // Why is set to 0 if ix=0, does it meen that we accept charge // outside the sensor? if (ix == 0 || SigmaX == 0.) { // skip for surface segemnts LowerBound = 0.; } else { mp = MeasurementPoint(ix, 0.0); xLB = topol->localPosition(mp).x(); LowerBound = 1 - calcQ((xLB - CloudCenterX) / SigmaX); } float xUB, UpperBound; if (ix == numRows - 1 || SigmaX == 0.) { UpperBound = 1.; } else { mp = MeasurementPoint(ix + 1, 0.0); xUB = topol->localPosition(mp).x(); UpperBound = 1. - calcQ((xUB - CloudCenterX) / SigmaX); } float TotalIntegrationRange = UpperBound - LowerBound; // get strip x.emplace(ix, TotalIntegrationRange); // save strip integral } // Now integrate strips in y hit_map_type y; for (int iy = IPixLeftDownY; iy <= IPixRightUpY; ++iy) { // loop over y index float yLB, LowerBound; if (iy == 0 || SigmaY == 0.) { LowerBound = 0.; } else { mp = MeasurementPoint(0.0, iy); yLB = topol->localPosition(mp).y(); LowerBound = 1. - calcQ((yLB - CloudCenterY) / SigmaY); } float yUB, UpperBound; if (iy == numColumns - 1 || SigmaY == 0.) { UpperBound = 1.; } else { mp = MeasurementPoint(0.0, iy + 1); yUB = topol->localPosition(mp).y(); UpperBound = 1. - calcQ((yUB - CloudCenterY) / SigmaY); } float TotalIntegrationRange = UpperBound - LowerBound; y.emplace(iy, TotalIntegrationRange); // save strip integral } // Get the 2D charge integrals by folding x and y strips for (int ix = IPixLeftDownX; ix <= IPixRightUpX; ++ix) { // loop over x index for (int iy = IPixLeftDownY; iy <= IPixRightUpY; ++iy) { // loop over y index float ChargeFraction = Charge * x[ix] * y[iy]; int chanFired = -1; if (ChargeFraction > 0.) { chanFired = pixelFlag_ ? PixelDigi::pixelToChannel(ix, iy) : Phase2TrackerDigi::pixelToChannel(ix, iy); // Get index // Load the amplitude hit_signal[chanFired] += ChargeFraction; } mp = MeasurementPoint(ix, iy); LocalPoint lp = topol->localPosition(mp); int chan = topol->channel(lp); LogDebug("Phase2TrackerDigitizerAlgorithm") << " pixel " << ix << " " << iy << " - " << " " << chanFired << " " << ChargeFraction << " " << mp.x() << " " << mp.y() << " " << lp.x() << " " << lp.y() << " " // givex edge position << chan; // edge belongs to previous ? } } } // Fill the global map with all hit pixels from this event bool reweighted = false; size_t referenceIndex4CR = 0; if (useChargeReweighting_) { if (hit.processType() == 0) { referenceIndex4CR = hitIndex; reweighted = theSiPixelChargeReweightingAlgorithm_->hitSignalReweight<digitizerUtility::Ph2Amplitude>(hit, hit_signal, hitIndex, referenceIndex4CR, tofBin, topol, detID, theSignal, hit.processType(), makeDigiSimLinks_); } else { // If it's not the primary particle, use the first hit in the collection as SimHit, which should be the corresponding primary. referenceIndex4CR = firstHitIndex; reweighted = theSiPixelChargeReweightingAlgorithm_->hitSignalReweight<digitizerUtility::Ph2Amplitude>((*inputBegin), hit_signal, hitIndex, referenceIndex4CR, tofBin, topol, detID, theSignal, hit.processType(), makeDigiSimLinks_); } } float corr_time = hit.tof() - pixdet->surface().toGlobal(hit.localPosition()).mag() * c_inv; if (!reweighted) { for (auto const& hit_s : hit_signal) { int chan = hit_s.first; theSignal[chan] += (makeDigiSimLinks_ ? digitizerUtility::Ph2Amplitude( hit_s.second, &hit, hit_s.second, corr_time, hitIndex, tofBin) : digitizerUtility::Ph2Amplitude(hit_s.second, nullptr, hit_s.second)); } } } // end of induce_signal function // ====================================================================== // // Add electronic noise to pixel charge // // ====================================================================== void Phase2TrackerDigitizerAlgorithm::add_noise(const Phase2TrackerGeomDetUnit* pixdet) { uint32_t detID = pixdet->geographicalId().rawId(); signal_map_type& theSignal = _signal[detID]; for (auto& s : theSignal) { float noise = gaussDistribution_->fire(); if ((s.second.ampl() + noise) < 0.) s.second.set(0); else s.second += noise; } } // ====================================================================== // // Add Cross-talk contribution // // ====================================================================== void Phase2TrackerDigitizerAlgorithm::add_cross_talk(const Phase2TrackerGeomDetUnit* pixdet) { uint32_t detID = pixdet->geographicalId().rawId(); signal_map_type& theSignal = _signal[detID]; signal_map_type signalNew; const Phase2TrackerTopology* topol = &pixdet->specificTopology(); int numRows = topol->nrows(); for (auto& s : theSignal) { float signalInElectrons = s.second.ampl(); // signal in electrons std::pair<int, int> hitChan; if (pixelFlag_) hitChan = PixelDigi::channelToPixel(s.first); else hitChan = Phase2TrackerDigi::channelToPixel(s.first); float signalInElectrons_Xtalk = signalInElectrons * interstripCoupling_; // subtract the charge which will be shared s.second.set(signalInElectrons - signalInElectrons_Xtalk); if (hitChan.first != 0) { auto XtalkPrev = std::make_pair(hitChan.first - 1, hitChan.second); int chanXtalkPrev = pixelFlag_ ? PixelDigi::pixelToChannel(XtalkPrev.first, XtalkPrev.second) : Phase2TrackerDigi::pixelToChannel(XtalkPrev.first, XtalkPrev.second); signalNew.emplace(chanXtalkPrev, digitizerUtility::Ph2Amplitude(signalInElectrons_Xtalk * 0.5, nullptr, -1.0)); } if (hitChan.first < numRows - 1) { auto XtalkNext = std::make_pair(hitChan.first + 1, hitChan.second); int chanXtalkNext = pixelFlag_ ? PixelDigi::pixelToChannel(XtalkNext.first, XtalkNext.second) : Phase2TrackerDigi::pixelToChannel(XtalkNext.first, XtalkNext.second); signalNew.emplace(chanXtalkNext, digitizerUtility::Ph2Amplitude(signalInElectrons_Xtalk * 0.5, nullptr, -1.0)); } } for (auto const& l : signalNew) { int chan = l.first; auto iter = theSignal.find(chan); if (iter != theSignal.end()) { theSignal[chan] += l.second.ampl(); } else { theSignal.emplace(chan, digitizerUtility::Ph2Amplitude(l.second.ampl(), nullptr, -1.0)); } } } // ====================================================================== // // Add noise on non-hit cells // // ====================================================================== void Phase2TrackerDigitizerAlgorithm::add_noisy_cells(const Phase2TrackerGeomDetUnit* pixdet, float thePixelThreshold) { uint32_t detID = pixdet->geographicalId().rawId(); signal_map_type& theSignal = _signal[detID]; const Phase2TrackerTopology* topol = &pixdet->specificTopology(); int numColumns = topol->ncolumns(); // det module number of cols&rows int numRows = topol->nrows(); int numberOfPixels = numRows * numColumns; std::map<int, float, std::less<int> > otherPixels; theNoiser_->generate(numberOfPixels, thePixelThreshold, //thr. in un. of nois theNoiseInElectrons_, // noise in elec. otherPixels, rengine_); LogDebug("Phase2TrackerDigitizerAlgorithm") << " Add noisy pixels " << numRows << " " << numColumns << " " << theNoiseInElectrons_ << " " << theThresholdInE_Endcap_ << " " << theThresholdInE_Barrel_ << " " << numberOfPixels << " " << otherPixels.size(); // Add noisy pixels for (auto const& el : otherPixels) { int iy = el.first / numRows; if (iy < 0 || iy > numColumns - 1) LogWarning("Phase2TrackerDigitizerAlgorithm") << " error in iy " << iy; int ix = el.first - iy * numRows; if (ix < 0 || ix > numRows - 1) LogWarning("Phase2TrackerDigitizerAlgorithm") << " error in ix " << ix; int chan = pixelFlag_ ? PixelDigi::pixelToChannel(ix, iy) : Phase2TrackerDigi::pixelToChannel(ix, iy); LogDebug("Phase2TrackerDigitizerAlgorithm") << " Storing noise = " << el.first << " " << el.second << " " << ix << " " << iy << " " << chan; if (theSignal[chan] == 0) theSignal[chan] = digitizerUtility::Ph2Amplitude(el.second, nullptr, -1.); } } // ============================================================================ // // Simulate the readout inefficiencies. // Delete a selected number of single pixels, dcols and rocs. void Phase2TrackerDigitizerAlgorithm::pixel_inefficiency(const SubdetEfficiencies& eff, const Phase2TrackerGeomDetUnit* pixdet, const TrackerTopology* tTopo) { uint32_t detID = pixdet->geographicalId().rawId(); signal_map_type& theSignal = _signal[detID]; // check validity // Predefined efficiencies float subdetEfficiency = 1.0; // setup the chip indices conversion uint32_t Subid = DetId(detID).subdetId(); if (Subid == PixelSubdetector::PixelBarrel || Subid == StripSubdetector::TOB) { // barrel layers uint32_t layerIndex = tTopo->pxbLayer(detID); if (layerIndex - 1 < eff.barrel_efficiencies.size()) subdetEfficiency = eff.barrel_efficiencies[layerIndex - 1]; } else { // forward disks uint32_t diskIndex = 2 * tTopo->pxfDisk(detID) - tTopo->pxfSide(detID); if (diskIndex - 1 < eff.endcap_efficiencies.size()) subdetEfficiency = eff.endcap_efficiencies[diskIndex - 1]; } LogDebug("Phase2TrackerDigitizerAlgorithm") << " enter pixel_inefficiency " << subdetEfficiency; // Now loop again over pixels to kill some of them. // Loop over hits, amplitude in electrons, channel = coded row,col for (auto& s : theSignal) { float rand = rengine_->flat(); if (rand > subdetEfficiency) { // make amplitude =0 s.second.set(0.); // reset amplitude } } } void Phase2TrackerDigitizerAlgorithm::initializeEvent(CLHEP::HepRandomEngine& eng) { if (addNoise_ || addPixelInefficiency_ || fluctuateCharge_ || addThresholdSmearing_) { gaussDistribution_ = std::make_unique<CLHEP::RandGaussQ>(eng, 0., theNoiseInElectrons_); } // Threshold smearing with gaussian distribution: if (addThresholdSmearing_) { smearedThreshold_Endcap_ = std::make_unique<CLHEP::RandGaussQ>(eng, theThresholdInE_Endcap_, theThresholdSmearing_Endcap_); smearedThreshold_Barrel_ = std::make_unique<CLHEP::RandGaussQ>(eng, theThresholdInE_Barrel_, theThresholdSmearing_Barrel_); } rengine_ = ŋ _signal.clear(); } // ======================================================================================= // // Set the drift direction accoring to the Bfield in local det-unit frame // Works for both barrel and forward pixels. // Replace the sign convention to fit M.Swartz's formulaes. // Configurations for barrel and foward pixels possess different tanLorentzAngleperTesla // parameter value LocalVector Phase2TrackerDigitizerAlgorithm::driftDirection(const Phase2TrackerGeomDetUnit* pixdet, const GlobalVector& bfield, const DetId& detId) const { Frame detFrame(pixdet->surface().position(), pixdet->surface().rotation()); LocalVector Bfield = detFrame.toLocal(bfield); float dir_x = 0.0; float dir_y = 0.0; float dir_z = 0.0; float scale = 1.0; uint32_t detID = pixdet->geographicalId().rawId(); uint32_t Sub_detid = DetId(detID).subdetId(); // Read Lorentz angle from DB: if (use_LorentzAngle_DB_) { bool isPixel = (Sub_detid == PixelSubdetector::PixelBarrel || Sub_detid == PixelSubdetector::PixelEndcap); if (isPixel) { LogDebug("Phase2TrackerDigitizerAlgorithm") << "Read Lorentz angle from DB for Phase-2 IT"; } else { LogDebug("Phase2TrackerDigitizerAlgorithm") << "Read Lorentz angle from DB for Phase-2 OT"; } float lorentzAngle = isPixel ? siPixelLorentzAngle_->getLorentzAngle(detId) : siPhase2OTLorentzAngle_->getLorentzAngle(detId); float alpha2 = std::pow(lorentzAngle, 2); dir_x = -(lorentzAngle * Bfield.y() + alpha2 * Bfield.z() * Bfield.x()); dir_y = +(lorentzAngle * Bfield.x() - alpha2 * Bfield.z() * Bfield.y()); dir_z = -(1 + alpha2 * std::pow(Bfield.z(), 2)); scale = (1 + alpha2 * std::pow(Bfield.z(), 2)); } else { // Read Lorentz angle from cfg file: LogDebug("Phase2TrackerDigitizerAlgorithm") << "Read Lorentz angle from cfg file"; float alpha2_Endcap = 0.0; float alpha2_Barrel = 0.0; if (alpha2Order_) { alpha2_Endcap = std::pow(tanLorentzAnglePerTesla_Endcap_, 2); alpha2_Barrel = std::pow(tanLorentzAnglePerTesla_Barrel_, 2); } if (Sub_detid == PixelSubdetector::PixelBarrel || Sub_detid == StripSubdetector::TOB) { // barrel layers dir_x = -(tanLorentzAnglePerTesla_Barrel_ * Bfield.y() + alpha2_Barrel * Bfield.z() * Bfield.x()); dir_y = +(tanLorentzAnglePerTesla_Barrel_ * Bfield.x() - alpha2_Barrel * Bfield.z() * Bfield.y()); dir_z = -(1 + alpha2_Barrel * std::pow(Bfield.z(), 2)); scale = (1 + alpha2_Barrel * std::pow(Bfield.z(), 2)); } else { // forward disks dir_x = -(tanLorentzAnglePerTesla_Endcap_ * Bfield.y() + alpha2_Endcap * Bfield.z() * Bfield.x()); dir_y = +(tanLorentzAnglePerTesla_Endcap_ * Bfield.x() - alpha2_Endcap * Bfield.z() * Bfield.y()); dir_z = -(1 + alpha2_Endcap * std::pow(Bfield.z(), 2)); scale = (1 + alpha2_Endcap * std::pow(Bfield.z(), 2)); } } LocalVector theDriftDirection = LocalVector(dir_x / scale, dir_y / scale, dir_z / scale); LogDebug("Phase2TrackerDigitizerAlgorithm") << " The drift direction in local coordinate is " << theDriftDirection; return theDriftDirection; } // ============================================================================= void Phase2TrackerDigitizerAlgorithm::pixel_inefficiency_db(uint32_t detID) { signal_map_type& theSignal = _signal[detID]; // check validity // Loop over hit pixels, amplitude in electrons, channel = coded row,col for (auto& s : theSignal) { std::pair<int, int> ip; if (pixelFlag_) ip = PixelDigi::channelToPixel(s.first); //get pixel pos else ip = Phase2TrackerDigi::channelToPixel(s.first); //get pixel pos int row = ip.first; // X in row int col = ip.second; // Y is in col // transform to ROC index coordinates if (theSiPixelGainCalibrationService_->isDead(detID, col, row)) s.second.set(0.); // reset amplitude } } // ========================================================================== void Phase2TrackerDigitizerAlgorithm::module_killing_conf(uint32_t detID) { bool isbad = false; int detid = detID; std::string Module; for (auto const& det_m : deadModules_) { int Dead_detID = det_m.getParameter<int>("Dead_detID"); Module = det_m.getParameter<std::string>("Module"); if (detid == Dead_detID) { isbad = true; break; } } if (!isbad) return; signal_map_type& theSignal = _signal[detID]; // check validity for (auto& s : theSignal) { std::pair<int, int> ip; if (pixelFlag_) ip = PixelDigi::channelToPixel(s.first); else ip = Phase2TrackerDigi::channelToPixel(s.first); //get pixel pos if (Module == "whole" || (Module == "tbmA" && ip.first >= 80 && ip.first <= 159) || (Module == "tbmB" && ip.first <= 79)) s.second.set(0.); } } // For premixing void Phase2TrackerDigitizerAlgorithm::loadAccumulator(uint32_t detId, const std::map<int, float>& accumulator) { auto& theSignal = _signal[detId]; // the input channel is always with PixelDigi definition // if needed, that has to be converted to Phase2TrackerDigi convention for (const auto& elem : accumulator) { auto inserted = theSignal.emplace(elem.first, digitizerUtility::Ph2Amplitude(elem.second, nullptr)); if (!inserted.second) { throw cms::Exception("LogicError") << "Signal was already set for DetId " << detId; } } } void Phase2TrackerDigitizerAlgorithm::digitize(const Phase2TrackerGeomDetUnit* pixdet, std::map<int, digitizerUtility::DigiSimInfo>& digi_map, const TrackerTopology* tTopo) { uint32_t detID = pixdet->geographicalId().rawId(); auto it = _signal.find(detID); if (!checkAllModulesForNoisyCells_ && (it == _signal.end())) return; const signal_map_type& theSignal = _signal[detID]; uint32_t Sub_detid = DetId(detID).subdetId(); float theThresholdInE = 0.; float theHIPThresholdInE = 0.; // Define Threshold if (Sub_detid == PixelSubdetector::PixelBarrel || Sub_detid == StripSubdetector::TOB) { // Barrel modules theThresholdInE = addThresholdSmearing_ ? smearedThreshold_Barrel_->fire() // gaussian smearing : theThresholdInE_Barrel_; // no smearing theHIPThresholdInE = theHIPThresholdInE_Barrel_; } else { // Forward disks modules theThresholdInE = addThresholdSmearing_ ? smearedThreshold_Endcap_->fire() // gaussian smearing : theThresholdInE_Endcap_; // no smearing theHIPThresholdInE = theHIPThresholdInE_Endcap_; } // if (addNoise) add_noise(pixdet, theThresholdInE/theNoiseInElectrons_); // generate noise if (addNoise_) add_noise(pixdet); // generate noise if (addXtalk_) add_cross_talk(pixdet); if (addNoisyPixels_) { float thresholdInNoiseUnits = 99.9; if (theNoiseInElectrons_) thresholdInNoiseUnits = theThresholdInE / theNoiseInElectrons_; add_noisy_cells(pixdet, thresholdInNoiseUnits); } // Do only if needed if (addPixelInefficiency_ && !theSignal.empty()) { if (use_ineff_from_db_) pixel_inefficiency_db(detID); else pixel_inefficiency(subdetEfficiencies_, pixdet, tTopo); } if (use_module_killing_) { if (use_deadmodule_DB_) // remove dead modules using DB module_killing_DB(pixdet); else // remove dead modules using the list in cfg file module_killing_conf(detID); } // Digitize if the signal is greater than threshold for (auto const& s : theSignal) { const digitizerUtility::Ph2Amplitude& sig_data = s.second; float signalInElectrons = sig_data.ampl(); const auto& info_list = sig_data.simInfoList(); const digitizerUtility::SimHitInfo* hitInfo = nullptr; if (!info_list.empty()) hitInfo = std::max_element(info_list.begin(), info_list.end())->second.get(); if (isAboveThreshold(hitInfo, signalInElectrons, theThresholdInE)) { // check threshold digitizerUtility::DigiSimInfo info; info.sig_tot = convertSignalToAdc(detID, signalInElectrons, theThresholdInE); // adc info.ot_bit = signalInElectrons > theHIPThresholdInE ? true : false; if (makeDigiSimLinks_) { for (auto const& l : sig_data.simInfoList()) { float charge_frac = l.first / signalInElectrons; if (l.first > -5.0) info.simInfoList.push_back({charge_frac, l.second.get()}); } } digi_map.insert({s.first, info}); } } } // // Scale the Signal using Dual Slope option // int Phase2TrackerDigitizerAlgorithm::convertSignalToAdc(uint32_t detID, float signal_in_elec, float threshold) { int signal_in_adc; int temp_signal; const int max_limit = 10; if (thePhase2ReadoutMode_ == 0) { signal_in_adc = theAdcFullScale_; } else { if (thePhase2ReadoutMode_ == -1) { temp_signal = std::min(static_cast<int>(std::round(signal_in_elec / theElectronPerADC_)), theAdcFullScale_); } else { // calculate the kink point and the slope int dualslope_param = std::min(std::abs(thePhase2ReadoutMode_), max_limit); int kink_point = static_cast<int>(theAdcFullScale_ / 2) + 1; // C-ROC: first valid ToT code above threshold is 0b0000 temp_signal = std::floor((signal_in_elec - threshold) / theElectronPerADC_); if (temp_signal > kink_point) temp_signal = static_cast<int>(std::floor((temp_signal - kink_point) / (pow(2, dualslope_param - 1)))) + kink_point; } signal_in_adc = std::min(temp_signal, theAdcFullScale_); LogTrace("Phase2TrackerDigitizerAlgorithm") << " DetId " << detID << " signal_in_elec " << signal_in_elec << " threshold " << threshold << " signal_above_thr " << signal_in_elec - threshold << " temp conversion " << std::floor((signal_in_elec - threshold) / theElectronPerADC_) + 1 << " signal after slope correction " << temp_signal << " signal_in_adc " << signal_in_adc; } return signal_in_adc; } float Phase2TrackerDigitizerAlgorithm::calcQ(float x) { constexpr float p1 = 12.5f; constexpr float p2 = 0.2733f; constexpr float p3 = 0.147f; auto xx = std::min(0.5f * x * x, p1); return 0.5f * (1.f - std::copysign(std::sqrt(1.f - unsafe_expf<4>(-xx * (1.f + p2 / (1.f + p3 * xx)))), x)); }