ATLAS Offline Software
Loading...
Searching...
No Matches
MuonCaloEnergyTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
3*/
4
6// forward declares
7#include <cmath>
8
11#include "GaudiKernel/SystemOfUnits.h"
20
21namespace Rec {
22
23 MuonCaloEnergyTool::MuonCaloEnergyTool(const std::string& t, const std::string& n, const IInterface* p) :
24 AthAlgTool(t, n, p)
25 {
26 declareInterface<IMuonCaloEnergyTool>(this);
27 }
28
30 // RETRIEVE TOOLS
33 ATH_CHECK(m_particleCreator.retrieve());
34
35 ATH_CHECK(m_caloNoiseCDOKey.initialize());
38
39 return StatusCode::SUCCESS;
40 }
41
42 void MuonCaloEnergyTool::calculateMuonEnergies(const Trk::Track* trk, double deltaE, double meanIoni, double sigmaIoni, double& E,
43 double& sigma, double& E_FSR, double& E_expected, double& E_em_meas, double& E_em_exp,
44 double& E_tile_meas, double& E_tile_exp, double& E_HEC_meas, double& E_HEC_exp,
45 double& E_dead_exp, std::vector<Identifier>* crossedCells,
46 std::vector<double>* sigmaNoise_cell, std::vector<double>* E_exp_cell) const {
47 const EventContext& ctx = Gaudi::Hive::currentContext();
48 //
49 // Input parameters trk: (muon) track pointer
50 // deltaE: Mean Energy loss in Calorimeter
51 // meanIoni: Mean ionization Energy loss in Calorimeter
52 // sigmaIoni: sigma of the ionization Energy loss in Calorimeter
53 //
54 // Ouput parameters E: Calorimeter energy measured (corrected for dead material etc.)
55 // sigma: Error on measured energy
56 // E_FSR: Energy of Final State Radiation: e.m. = photon energy (if above ET and F1 cut)
57 // if EFSR>0 the corrected hadronic energy is used for the measured Energy E
58 // E_expected: expected meanIonization Energyloss from the TrackingGeometry
59
60 E = 0.;
61 sigma = 0.;
62 E_FSR = 0.;
63 E_expected = 0.;
64
65 E_em_meas = 0.;
66 E_em_exp = 0.;
67 E_tile_meas = 0.;
68 E_tile_exp = 0.;
69 E_HEC_meas = 0.;
70 E_HEC_exp = 0.;
71 E_dead_exp = 0.;
72
73 bool storeCells = false;
74 if (crossedCells != nullptr && sigmaNoise_cell != nullptr && E_exp_cell != nullptr) {
75 storeCells = true;
76 crossedCells->clear();
77 sigmaNoise_cell->clear();
78 E_exp_cell->clear();
79 }
80 //
81 // For the expected Eloss in the dead or not instrumented material we will use the meanIonization loss
82 // this meanIoni is stored on the extended Eloss object
83 // the sigmaIoni corresponds to the Landau width
84 // a factor 3.59524 gives the sigma of the upper tail
85 //
86 // The Eloss derived from the momentum differences on the calo extension correponds to
87 // deltaE on the Eloss object = mean Eloss including ionization and radiation
88 //
89 // To go to the meanIonization loss one needs a scale factor scale_Ionization
90 //
91 if (!trk) return;
92 if (!trk->perigeeParameters()) return;
93 if (trk->perigeeParameters()->parameters()[Trk::qOverP] == 0.) return;
94
95 // int isign = 1;
96 // if(meanIoni<0) isign = -1;
97 // double mopIoni = meanIoni - isign*3.59524*std::abs(sigmaIoni);
98 double mopIoni = meanIoni;
99
100 double scale_Ionization = 0.9;
101 if (std::abs(deltaE) > 0 && std::abs(meanIoni) > 0) scale_Ionization = mopIoni / deltaE;
102 double error_ratio = 0.3;
103 if (std::abs(meanIoni) > 0 && sigmaIoni > 0) error_ratio = 3.59524 * std::abs(sigmaIoni / meanIoni);
104
105 ATH_MSG_DEBUG(" Inputs deltaE " << deltaE << " meanIoni " << meanIoni << " sigmaIoni " << sigmaIoni << " scale_Ionization "
106 << scale_Ionization << " error_ratio " << error_ratio);
107 if (deltaE == 0 || meanIoni == 0 || sigmaIoni < 0)
108 ATH_MSG_WARNING(" Strange Inputs deltaE " << deltaE << " meanIoni " << meanIoni << " sigmaIoni " << sigmaIoni);
109
110 // associate muon to calorimeter
111
112 const xAOD::TrackParticle* tp = nullptr;
113
115 if (indetTrackParticles.isValid()) {
116 // check ID trackparticles
117 for (const auto *it : *indetTrackParticles) {
118 if ((*it).track() == trk) {
119 tp = &(*it);
120 break;
121 }
122 }
123 if (tp) ATH_MSG_DEBUG(" ID xAOD::TrackParticle found " << tp);
124 } else {
125 ATH_MSG_WARNING("Failed to retrieve " << m_indetTrackParticleLocation.key());
126 }
127
128 // look for Muon trackparticles
129
131 if (!tp && muonTrackParticles.isValid()) {
132 for (const auto *it : *muonTrackParticles) {
133 if ((*it).track() == trk) {
134 tp = &(*it);
135 break;
136 }
137 }
138 if (tp) ATH_MSG_DEBUG(" Muon xAOD::TrackParticle found " << tp);
139 } else if (!tp) {
140 ATH_MSG_WARNING("Failed to retrieve " << m_muonTrackParticleLocation.key());
141 }
142
143 std::unique_ptr<const xAOD::TrackParticle> tpholder;
144 if (!tp) {
145 tpholder = std::unique_ptr<const xAOD::TrackParticle>(m_particleCreator->createParticle(*trk, nullptr, nullptr, xAOD::muon));
146
147 tp = tpholder.get();
148 if (tp) ATH_MSG_DEBUG(" xAOD::TrackParticle created from scratch " << tp);
149 }
150
151 if (!tp) return;
152
153 std::unique_ptr<const Rec::ParticleCellAssociation> association =
154 m_caloCellAssociationTool->particleCellAssociation(*tp, 0.2, nullptr);
155
156 if (!association) { return; }
157 ATH_MSG_DEBUG(" particleCellAssociation done " << association.get());
158
159 std::vector<std::pair<const CaloCell*, Rec::ParticleCellIntersection*> > cellIntersections = association->cellIntersections();
160
161 const Trk::CaloExtension& caloExtension = association->caloExtension();
162
163 if (!caloExtension.caloEntryLayerIntersection()) {
164 ATH_MSG_DEBUG("calculateMuonEnergies() - No caloEntryLayerIntersection found");
165 return;
166 }
167
168 ATH_MSG_DEBUG(" nr of cell intersections " << cellIntersections.size());
169 if (cellIntersections.size() < 3) ATH_MSG_DEBUG(" Strange nr of calorimeter cell intersections " << cellIntersections.size());
170
171 double theta = caloExtension.caloEntryLayerIntersection()->position().theta();
172
173 double Eloss = 0.;
174 double scaleTG = 1.0;
175 if (!caloExtension.muonEntryLayerIntersection()) {
176 if (caloExtension.caloEntryLayerIntersection()->momentum().mag() - std::abs(deltaE) > 1000.) {
178 " No muonEntryLayerIntersection found and therefore the expected Eloss is not calculated properly for momentum "
179 << caloExtension.caloEntryLayerIntersection()->momentum().mag());
180 }
181 ATH_MSG_DEBUG(" No muonEntryLayerIntersection found and therefore the expected Eloss is not calculated properly ");
182 } else {
183 Eloss =
184 caloExtension.caloEntryLayerIntersection()->momentum().mag() - caloExtension.muonEntryLayerIntersection()->momentum().mag();
185 ATH_MSG_DEBUG(" Energy loss from CaloExtension "
186 << Eloss << " R muon Entry " << caloExtension.muonEntryLayerIntersection()->position().perp() << " Z muon Entry "
187 << caloExtension.muonEntryLayerIntersection()->position().z() << " R calo entry "
188 << caloExtension.caloEntryLayerIntersection()->position().perp() << " Z calo entry "
189 << caloExtension.caloEntryLayerIntersection()->position().z());
190 if (Eloss > 25000. && Eloss > 0.1 * caloExtension.muonEntryLayerIntersection()->momentum().mag()) {
191 ATH_MSG_WARNING(" Crazy Energy loss from CaloExtension "
192 << Eloss << " p at CaloEntry " << caloExtension.caloEntryLayerIntersection()->momentum().mag()
193 << " p at Muon Entry " << caloExtension.muonEntryLayerIntersection()->momentum().mag());
194 scaleTG = mopIoni / Eloss;
195 }
196 }
197
199 CaloExtensionHelpers::entryExitLayerMap(caloExtension, entryExitLayerMap);
200 ATH_MSG_DEBUG("EntryExitLayerMap " << entryExitLayerMap.size());
201
203 CaloExtensionHelpers::eLossLayerMap(caloExtension, eLossLayerMap);
204 ATH_MSG_DEBUG("eLossLayerMap " << eLossLayerMap.size());
205
206 double scale_em_expected = 0.97; // 0.73
207 double scale_tile_expected = 1.17; // 0.89
208 double scale_HEC_expected = 1.11; // 0.84
209 //
210 // TG expectations
211 //
212 double EE_emB = 0.;
213 double EE_emE = 0.;
214 double EE_HEC = 0.;
215 double EE_tile = 0.;
216 //
217 // const char* sampname[24] = {
218 // "PreSamplerB", "EMB1", "EMB2", "EMB3",
219 // "PreSamplerE", "EME1", "EME2", "EME3",
220 // "HEC0", "HEC1","HEC2", "HEC3",
221 // "TileBar0", "TileBar1", "TileBar2",
222 // "TileExt0", "TileExt1", "TileExt2",
223 // "TileGap1", "TileGap2", "TileGap3",
224 // "FCAL0", "FCAL1", "FCAL2"};
225 //
226 for (int i = CaloSampling::PreSamplerB; i < CaloSampling::CaloSample::Unknown; i++) {
227 CaloSampling::CaloSample sample = static_cast<CaloSampling::CaloSample>(i);
228 auto pos = entryExitLayerMap.find(sample);
229 if (pos == entryExitLayerMap.end()) continue;
230 auto eLossPair = eLossLayerMap.find(sample);
231 double eLoss = 0.;
232 if (eLossPair != eLossLayerMap.end()) {
233 eLoss = eLossPair->second;
234 ATH_MSG_DEBUG(" sample " << i << " eLoss " << scale_Ionization * eLoss);
235 if (i <= 3) {
236 EE_emB += scale_em_expected * scale_Ionization * eLoss;
237 } else if (i <= 7) {
238 EE_emE += scale_em_expected * scale_Ionization * eLoss;
239 } else if (i <= 11) {
240 EE_HEC += scale_HEC_expected * scale_Ionization * eLoss;
241 } else if (i <= 20) {
242 EE_tile += scale_tile_expected * scale_Ionization * eLoss;
243 }
244 }
245 }
246 if (caloExtension.caloEntryLayerIntersection() && caloExtension.muonEntryLayerIntersection()) {
247 double Eloss =
248 caloExtension.caloEntryLayerIntersection()->momentum().mag() - caloExtension.muonEntryLayerIntersection()->momentum().mag();
249 ATH_MSG_DEBUG(" eta " << caloExtension.caloEntryLayerIntersection()->momentum().eta() << " expected energies from TG EE_emB "
250 << EE_emB << " EE_emE " << EE_emE << " EE_tile " << EE_tile << " EE_HEC " << EE_HEC << " total Eloss "
251 << Eloss);
252 }
253 //
254 // do not continue with crazy values for the Eloss from TG
255 //
256 if (scaleTG != 1.0) return;
257
258 // measured and expected energies
259
260 // Get Calo-Noise CDO:
262 const CaloNoise* caloNoise = *caloNoiseHdl;
263
264 double E_em = 0.;
265 double E_em_expected = 0.;
266 double E_em_exptot = 0.;
267 double E_em_expall = 0.;
268 double E_em_threshold = 0.;
269 int nlay_em = 0;
270 double sigma_Noise_em = 0.;
271
272 double E_HEC = 0.;
273 double E_HEC_expected = 0.;
274 double E_HEC_threshold = 0.;
275 double E_HEC_exptot = 0.;
276 double E_HEC_expall = 0.;
277 int nlay_HEC = 0;
278 double sigma_Noise_HEC = 0.;
279
280 double E_tile = 0.;
281 double E_tile_expected = 0.;
282 double E_tile_threshold = 0.;
283 double E_tile_exptot = 0.;
284 double E_tile_expall = 0.;
285 int nlay_tile = 0;
286 double sigma_Noise_tile = 0.;
287
288 E_expected = 0.;
289 const Amg::Vector3D& extension_pos = caloExtension.caloEntryLayerIntersection()->position();
290 const double thetaPos = caloExtension.caloEntryLayerIntersection()->position().theta();
291
292 for (auto it : cellIntersections) {
293 const CaloCell* curr_cell = it.first;
294 const Amg::Vector3D cell_vec(curr_cell->x(),curr_cell->y(), curr_cell->z());
295 const double cell_perp = cell_vec.perp();
296 int cellSampling = curr_cell->caloDDE()->getSampling();
297 bool badCell = curr_cell->badcell();
298 double cellEn = curr_cell->energy();
299 Identifier id = curr_cell->ID();
300
301 double f_exp = (it.second)->pathLength();
302 double E_exp = (it.second)->expectedEnergyLoss();
303
304 if (f_exp > 1.) f_exp = 1.;
305
306 // if(f_exp<0.1) f_exp = 0.1;
307
308 double sigma_Noise = caloNoise->getEffectiveSigma(id, curr_cell->gain(), cellEn);
309
310
311 double dtheta = cell_vec.theta() - thetaPos;
312 double dphi = cell_vec.deltaPhi(extension_pos);
313 double celldr = curr_cell->caloDDE()->dr();
314 double celldz = curr_cell->caloDDE()->dz();
315 double celldtheta = celldr / (cell_perp ? cell_perp : 1. );
316 double celldphi = curr_cell->caloDDE()->dphi();
317
318 ATH_MSG_DEBUG(" cell sampling " << cellSampling << " energy " << cellEn << " position R "
319 << cell_perp << " z "
320 << curr_cell->z() << " f_exp " << f_exp << " E_exp " << E_exp << " dtheta " << dtheta
321 << " dphi " << dphi << " cell dtheta " << celldtheta << " cell dr " << celldr << " cell dz "
322 << celldz << " cell dphi " << celldphi);
323 // ATH_MSG_DEBUG( " sigma_Noise totalNoiseRMS from Alan " << sigma_NoiseA << " sigma_Noise getEffectiveSigma " <<
324 // sigma_Noise);
325
326 // Use expected energy if measured energy below noise level
327 // - this will bias the energy measurement towards too high values
328 // - the bias is corrected in the thresholdCorrection function below
329 //
330 // sum measured, expected energies for crossed cells after noise cuts
331 //
332 if (cellSampling == CaloSampling::PreSamplerB || cellSampling == CaloSampling::PreSamplerE) {
333 if (f_exp > 0 && cellEn > m_sigmasAboveNoise * sigma_Noise && !badCell) {
334 if (storeCells) {
335 crossedCells->push_back(id);
336 sigmaNoise_cell->push_back(sigma_Noise);
337 E_exp_cell->push_back(scale_Ionization * f_exp * E_exp *
338 scale_em_expected); // I don't want sum, but value for each cell
339 }
340 }
341 }
342 if (curr_cell->caloDDE()->getSubCalo() == CaloCell_ID::LAREM) {
343 E_em_exptot += scale_Ionization * f_exp * E_exp * scale_em_expected;
344 if (f_exp > 0) E_em_expall += scale_Ionization * E_exp * scale_em_expected;
345 if (f_exp > 0 && cellEn > m_sigmasAboveNoise * sigma_Noise && !badCell) {
346 // if(f_exp>0&&cellEn>m_sigmasAboveNoise*sigma_Noise&&!badCell) {
347 E_em += cellEn;
348 E_em_expected += scale_Ionization * f_exp * E_exp * scale_em_expected;
349 ATH_MSG_VERBOSE(" EM cell " << cellEn << " sigma_Noise " << sigma_Noise << " f_exp " << f_exp << " E_exp " << E_exp);
350 if (storeCells) {
351 crossedCells->push_back(id);
352 sigmaNoise_cell->push_back(sigma_Noise);
353 E_exp_cell->push_back(scale_Ionization * f_exp * E_exp * scale_em_expected);
354 }
355 }
356 if (f_exp > 0 && !badCell) nlay_em++;
357 if (f_exp > 0 && !badCell) sigma_Noise_em += sigma_Noise;
358 } else if (curr_cell->caloDDE()->getSubCalo() == CaloCell_ID::TILE) {
359 E_tile_exptot += scale_Ionization * f_exp * E_exp * scale_tile_expected;
360 if (f_exp > 0) E_tile_expall += scale_Ionization * E_exp * scale_tile_expected;
361 if (f_exp > 0 && cellEn > m_sigmasAboveNoise * sigma_Noise && !badCell) {
362 // &&f_exp*E_exp>m_sigmasAboveNoise*sigma_Noise/4) {
363 E_tile += cellEn;
364 E_tile_expected += scale_Ionization * f_exp * E_exp * scale_tile_expected;
365 ATH_MSG_VERBOSE(" Tile cell " << cellEn << " sigma_Noise " << sigma_Noise << " f_exp " << f_exp << " E_exp " << E_exp);
366 if (storeCells) {
367 crossedCells->push_back(id);
368 sigmaNoise_cell->push_back(sigma_Noise);
369 E_exp_cell->push_back(scale_Ionization * f_exp * E_exp * scale_tile_expected);
370 }
371 }
372 if (f_exp > 0 && !badCell) nlay_tile++;
373 if (f_exp > 0 && !badCell) sigma_Noise_tile += sigma_Noise;
374 } else if (curr_cell->caloDDE()->getSubCalo() == CaloCell_ID::LARHEC) {
375 E_HEC_exptot += scale_Ionization * f_exp * E_exp * scale_HEC_expected;
376 if (f_exp > 0) E_HEC_expall += scale_Ionization * E_exp * scale_HEC_expected;
377 // lower threshold for HEC
378 if (f_exp > 0 && cellEn > m_sigmasAboveNoise * sigma_Noise / 2. && !badCell) {
379 // &&f_exp*E_exp>m_sigmasAboveNoise*sigma_Noise/4) {
380 E_HEC += cellEn;
381 E_HEC_expected += scale_Ionization * f_exp * E_exp * scale_HEC_expected;
382 ATH_MSG_VERBOSE(" HEC cell " << cellEn << " sigma_Noise " << sigma_Noise << " f_exp " << f_exp << " E_exp " << E_exp);
383 if (storeCells) {
384 crossedCells->push_back(id);
385 sigmaNoise_cell->push_back(sigma_Noise);
386 E_exp_cell->push_back(scale_Ionization * f_exp * E_exp * scale_HEC_expected);
387 }
388 }
389 if (f_exp > 0 && !badCell) nlay_HEC++;
390 if (f_exp > 0 && !badCell) sigma_Noise_HEC += sigma_Noise;
391 }
392 E_expected += scale_Ionization * f_exp * E_exp;
393 }
394 ATH_MSG_DEBUG(" After cuts measured Energies em " << E_em << " tile " << E_tile << " HEC " << E_HEC);
395 ATH_MSG_DEBUG(" After cuts expected Energies em " << E_em_expected << " tile " << E_tile_expected << " HEC " << E_HEC_expected);
396 ATH_MSG_DEBUG(" No cuts with length expected Energies em " << E_em_exptot << " tile " << E_tile_exptot << " HEC " << E_HEC_exptot);
397 ATH_MSG_DEBUG(" No cuts NO length expected Energies em " << E_em_expall << " tile " << E_tile_expall << " HEC " << E_HEC_expall);
398
399 // E resolution of calorimeters
400 // Resolution of Hadronic at low energies is not 100% but more like 50%/sqrt(E)
401
402 double scaleError_tile = 0.20 + 0.80 * E_tile / (10000. + E_tile);
403 double scaleError_HEC = 0.20 + 0.80 * E_HEC / (10000. + E_HEC);
404 double sigma_em = std::sqrt(500. * E_em);
405 double sigma_tile = scaleError_tile * std::sqrt(1000. * E_tile);
406 double sigma_HEC = scaleError_HEC * std::sqrt(1000. * E_HEC);
407
408 // go from e.m. scale to Muon Energy scale
409
410 E_em *= m_emipEM;
411 E_tile *= m_emipTile;
412 E_HEC *= m_emipHEC;
413 ATH_MSG_DEBUG(" e.m. to Muon scale measured Energies em " << E_em << " tile " << E_tile << " HEC " << E_HEC);
414
415 // also for errors
416
417 sigma_em *= m_emipEM;
418 sigma_tile *= m_emipTile;
419 sigma_HEC *= m_emipHEC;
420
421 // Average Noise per layer
422
423 if (nlay_em > 0) sigma_Noise_em /= nlay_em;
424 if (nlay_tile > 0) sigma_Noise_tile /= nlay_tile;
425 if (nlay_HEC > 0) sigma_Noise_HEC /= nlay_HEC;
426 ATH_MSG_DEBUG(" Average Noise em " << sigma_Noise_em << " nlay " << nlay_em << " tile " << sigma_Noise_tile << " nlay "
427 << nlay_tile << " HEC " << sigma_Noise_HEC << " nlay " << nlay_HEC);
428
429 // apply energy correction using the expected Eloss for noise cut
430
431 E_em_threshold += thresholdCorrection(E_em, E_em_expected, m_sigmasAboveNoise * sigma_Noise_em / m_emipEM / 4);
432 E_tile_threshold += thresholdCorrection(E_tile, E_tile_expected, m_sigmasAboveNoise * sigma_Noise_tile / m_emipTile / 4);
433 E_HEC_threshold += thresholdCorrection(E_HEC, E_HEC_expected, m_sigmasAboveNoise * sigma_Noise_HEC / m_emipHEC / 8);
434
435 ATH_MSG_DEBUG(" Threshold correction to Energies em " << E_em_threshold << " tile " << E_tile_threshold << " HEC "
436 << E_HEC_threshold);
437
438 ATH_MSG_DEBUG(" total Energies em " << E_em + E_em_threshold << " tile " << E_tile + E_tile_threshold << " HEC "
439 << E_HEC + E_HEC_threshold);
440
441 // Eloss in dead material (so everything not accounted for in the measurement)
442
443 // if(scale_Ionization*Eloss>E_expected) E_expected = scale_Ionization*Eloss;
444 E_expected = scale_Ionization * Eloss;
445
446 // Corrections for dead material 0.15*E_tile_expected + 0.20*E_HEC_expected;
447
448 double E_dead = E_expected - E_em_expected - E_tile_expected - E_HEC_expected + 0.12 * E_tile_expected + 0.27 * E_HEC_expected;
449
450
451 // treatment of FSR
452
453 E_FSR = 0.;
454 double E_measured = 0.;
455 double E_measured_expected = E_em_expected + E_tile_expected + E_HEC_expected;
456 if (E_em * std::sin(theta) > m_emEtCut) {
457 // large e.m. deposit starting in first e.m. layer
458 E_FSR = E_em;
459 // do not use measured e.m. energy for muons and use expected (tile and HEC are fine)
460 E = E_em_expected + E_tile + E_tile_threshold + E_HEC + E_HEC_threshold + E_dead;
461 double sigma_expdead = error_ratio * (E_em_expected + E_dead);
462 sigma = std::sqrt(sigma_tile * sigma_tile + sigma_HEC * sigma_HEC + sigma_expdead * sigma_expdead);
463 E_measured = E_tile + E_tile_threshold + E_HEC + E_HEC_threshold;
464 } else {
465 // no FSR
466 E = E_em + E_em_threshold + E_tile + E_tile_threshold + E_HEC + E_HEC_threshold + E_dead;
467 double sigma_threshold = error_ratio * E_dead;
468 sigma = std::sqrt(sigma_em * sigma_em + sigma_tile * sigma_tile + sigma_HEC * sigma_HEC + sigma_threshold * sigma_threshold);
469 E_measured = E_em + E_em_threshold + E_tile + E_tile_threshold + E_HEC + E_HEC_threshold;
470 }
471
472 // eta dependent correction
473
474 double etaPos = caloExtension.caloEntryLayerIntersection()->position().eta();
475 E += etaCorr(etaPos) * E_expected;
476
477 ATH_MSG_DEBUG(" Total energy " << E << " sigma " << sigma << " E calo measured in cells " << E_measured
478 << " E calo expected in cells " << E_measured_expected << " E_expected meanIoni from TG "
479 << E_expected);
480
481 if (E_em + E_tile + E_HEC < 0.1 * E && E_em + E_tile + E_HEC > 1000.) {
482 ATH_MSG_DEBUG(" Real Measured Calorimeter energy " << E_em + E_tile + E_HEC << " is much lower than total " << E
483 << " expected energy too large " << E_expected
484 << " put measured energy to zero ");
485 E = 0.;
486 sigma = 0.;
487 }
488 //
489 // add for validation (temporarily)
490 //
491 // E_em_meas,E_em_exp,E_tile_meas,E_tile_exp,E_HEC_meas,E_HEC_exp,E_dead_exp
492 //
493
494 // E_em_meas = E_em + E_em_threshold;
495 // E_em_exp = E_em_expected;
496 // E_tile_meas = E_tile + E_tile_threshold;
497 // E_tile_exp = E_tile_expected;
498 // E_HEC_meas = E_HEC + E_HEC_threshold;
499 // E_HEC_exp = E_HEC_expected;
500 // E_dead_exp = E_dead;
501
502 // double EE_dead = E_expected - EE_emB - EE_emE - EE_tile - EE_HEC + 0.15*EE_tile + 0.20*EE_HEC;
503 double EE_dead = E_expected - EE_emB - EE_emE - EE_tile - EE_HEC + 0.12 * EE_tile + 0.27 * EE_HEC;
504 //
505 // move expected (to match the TG expected Eloss) and measured energies
506 //
507 E_em_meas = E_em + E_em_threshold + EE_emB + EE_emE - E_em_expected;
508 E_em_exp = EE_emB + EE_emE;
509 E_tile_meas = E_tile + E_tile_threshold + EE_tile - E_tile_expected;
510 E_tile_exp = EE_tile;
511 E_HEC_meas = E_HEC + E_HEC_threshold + EE_HEC - E_HEC_expected;
512 E_HEC_exp = EE_HEC;
513 E_dead_exp = EE_dead;
514
515 } // calculateMuonEnergies
516
518 // measured energy* = measured energy + etaCorr(eta) * expected
519
520 int eta_index = int(std::abs(eta) * (60. / 3.));
521 if (eta_index > 59) return 0;
522
523 static constexpr double corr[60] = {0.00368146, 0.0419526, 0.0419322, 0.0392922, 0.030304, 0.0262424, 0.0156346, 0.00590235, 0.00772249,
524 -0.0141775, -0.0152247, -0.0174432, -0.0319056, -0.0670813, -0.128678, -0.0982326, -0.0256406, -0.200244,
525 -0.178975, -0.120156, -0.124606, -0.0961311, -0.0163201, 0.00357829, 0.0292199, 0.0110466, -0.0375598,
526 0.0417912, 0.0386369, -0.0689454, -0.21496, -0.126157, -0.210573, -0.215757, -0.202019, -0.164546,
527 -0.168607, -0.128602, -0.124629, -0.0882631, -0.100869, -0.0460344, -0.0709039, -0.0163041, -0.0521138,
528 -0.0125259, -0.0378681, 0.00396062, -0.0636308, -0.032199, -0.0588335, -0.0470752, -0.0450315, -0.0301302,
529 -0.087378, -0.0115615, -0.0152037, 0, 0, 0}; // corrections
530
531 // additional correction release 21
532
533 static constexpr double cor21[20] = {0.999793, 1.00017, 0.990946, 0.995358, 1.01377, 1.02676, 1.03111, 1.01483, 0.995585, 1.00465,
534 1.05224, 1.05238, 1.03208, 1.02373, 1.02305, 1.03975, 1.06547, 1.0364, 1.0361, 1.0361};
535
536 int i21 = std::abs(eta * 20. / 3.);
537 if (i21 > 19) i21 = 19;
538
539 return (cor21[i21] * corr[eta_index]);
540 } // etaCorr
541
542 double MuonCaloEnergyTool::thresholdCorrection(double E_observed, double E_expected, double sigma_Noise) const {
543 //
544 // Total energy observed and expected in cells of LAr, Tile, or HEC after 4*sigma_Noise cut
545 // for a resolution of dE/E of 20%
546 //
547 // returns a correction to the energy based on the expected energy
548 //
549 // one should so use: Etotal = E_observed + E_dead + ThresholdCorrection(E_observed,E_expected,sigma_Noise)
550
551 static constexpr double par[6] = {1.01431e+00, -2.26317e+01, 1.80901e+02, -2.33105e+01, 1.82930e+02, 1.11356e-02};
552
553 double E = E_expected;
554
555 if (E == 0) return 0.;
556
557 double x = sigma_Noise / E;
558 if (x > 1.) x = 1.;
559
560 // formula is for a 4 sigma Noise cut and obtained by a small simulation programm
561
562 double fraction = (par[0] + par[1] * x + par[2] * x * x) / (1. + par[3] * x + par[4] * x * x) + par[5] * x;
563
564 // for low x values (low thresholds) the fraction is bigger than 1
565 // (1.33) because the observed energy overestimates
566
567 ATH_MSG_DEBUG(" ThresholdCorrection E " << E << " E_observed not used " << E_observed << " sigma_Noise " << sigma_Noise
568 << " x = sigma_Noise/E " << x << " fraction " << fraction << " E correction "
569 << (1 - fraction) * E);
570
571 return (1. - fraction) * E;
572 } // thresholdCorrection
573
574} // namespace Rec
Scalar eta() const
pseudorapidity method
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
#define x
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
Data object for each calorimeter readout cell.
Definition CaloCell.h:57
double energy() const
get energy (data member)
Definition CaloCell.h:327
const CaloDetDescrElement * caloDDE() const
get pointer to CaloDetDescrElement (data member)
Definition CaloCell.h:321
float y() const
get y (through CaloDetDescrElement)
Definition CaloCell.h:436
virtual bool badcell() const
check is cell is dead
Definition CaloCell.cxx:144
CaloGain::CaloGain gain() const
get gain (data member )
Definition CaloCell.h:361
float z() const
get z (through CaloDetDescrElement)
Definition CaloCell.h:443
float x() const
get x (through CaloDetDescrElement)
Definition CaloCell.h:429
Identifier ID() const
get ID (from cached data member) non-virtual and inline for fast access
Definition CaloCell.h:295
CaloCell_ID::CaloSample getSampling() const
cell sampling
float getEffectiveSigma(const Identifier id, const int gain, const float energy) const
Definition CaloNoise.h:55
static double etaCorr(double eta)
virtual StatusCode initialize() override
Gaudi::Property< double > m_sigmasAboveNoise
static constexpr double m_emipEM
static constexpr double m_emipHEC
ToolHandle< Trk::ITrackParticleCreatorTool > m_particleCreator
ToolHandle< Rec::IParticleCaloCellAssociationTool > m_caloCellAssociationTool
static constexpr double m_emipTile
double thresholdCorrection(double E_observed, double E_expected, double sigma_Noise) const
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_muonTrackParticleLocation
ToolHandle< Trk::IParticleCaloExtensionTool > m_caloExtensionTool
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_indetTrackParticleLocation
SG::ReadCondHandleKey< CaloNoise > m_caloNoiseCDOKey
void calculateMuonEnergies(const Trk::Track *trk, double deltaE, double meanIoni, double sigmaIoni, double &E, double &sigma, double &E_FSR, double &E_expected, double &E_em_meas, double &E_em_exp, double &E_tile_meas, double &E_tile_exp, double &E_HEC_meas, double &E_HEC_exp, double &E_dead_exp, std::vector< Identifier > *crossedCells=0, std::vector< double > *sigmaNoise_cell=0, std::vector< double > *E_exp_cell=0) const override
Gaudi::Property< double > m_emEtCut
MuonCaloEnergyTool(const std::string &, const std::string &, const IInterface *)
virtual bool isValid() override final
Can the handle be successfully dereferenced?
Tracking class to hold the extrapolation through calorimeter Layers Both the caloEntryLayerIntersecti...
const TrackParameters * caloEntryLayerIntersection() const
access to intersection with the calorimeter entry layer return nullptr if the intersection failed
const TrackParameters * muonEntryLayerIntersection() const
access to intersection with the muon entry layer return nullptr if the intersection failed
const Amg::Vector3D & momentum() const
Access method for the momentum.
const Amg::Vector3D & position() const
Access method for the position.
const Perigee * perigeeParameters() const
return Perigee.
Eigen::Matrix< double, 3, 1 > Vector3D
void entryExitLayerMap(const Trk::CaloExtension &extension, EntryExitLayerMap &result, const LayersToSelect *selection=nullptr)
std::map< CaloSampling::CaloSample, double > ScalarLayerMap
void eLossLayerMap(const Trk::CaloExtension &extension, ScalarLayerMap &result)
std::map< CaloSampling::CaloSample, std::pair< Amg::Vector3D, Amg::Vector3D > > EntryExitLayerMap
Gaudi Tools.
@ qOverP
perigee
Definition ParamDefs.h:67
TrackParticle_v1 TrackParticle
Reference the current persistent version: