177{
178
181 ATH_MSG_WARNING(
"Model not loaded or Egamma pointer is null, returning cluster energy");
183 } else {
184 ATH_MSG_FATAL(
"Model not loaded or Egamma pointer is null, and useClusterIf0 is false, cannot proceed");
185 return 0.0f;
186 }
187 }
188
189
190 IegammaCellRecoveryTool::Info recoveryInfo;
191 bool recoverySucceeded = true;
193 egammaCellUtils::MaxECell maxECell(&clus);
194 if (maxECell.sc == StatusCode::FAILURE) {
196 recoverySucceeded = false;
197 } else {
198 recoveryInfo.
etamax = maxECell.etaCell;
199 recoveryInfo.
phimax = maxECell.phiCell;
201 ATH_MSG_WARNING(
"Cell Recovery Tool failed. Proceeding without recovered cells.");
202 recoverySucceeded = false;
203 }
204 }
205 }
208
209
211 auto array_layer_scales = std::array<double, 4>{1.0, 1.0, 1.0, 1.0};
212
214 ATH_MSG_DEBUG(
"Applying layer recalibration for GNN on data.");
215
217 if (!eventInfo) {
218 ATH_MSG_ERROR(
"EventInfo is required to apply data layer corrections; using configured fallback");
220 }
222 }
224 << array_layer_scales[0] << " " << array_layer_scales[1] << " "
225 << array_layer_scales[2] << " " << array_layer_scales[3]);
226
228 ATH_MSG_DEBUG(
"Applying extra layer scales for systematic studies, normally this is for MC events.");
230 ATH_MSG_WARNING(
"You are applying extra layer scales but the input is not MC! Are you sure this is intended?");
231 }
232
233 for (std::size_t i = 0;
i < 4; ++
i)
234 array_layer_scales[i] *= gei.
scaleEs[i];
235 }
236
237
238
239
240
244 ATH_MSG_DEBUG(
"raw Es " << raw_Es1 <<
" " << raw_Es2 <<
" " << raw_Es3);
245
246
247 std::vector<float> cells_E, cells_eta, cells_phi, cells_x, cells_y, cells_z;
248 std::vector<int> cells_layer;
249 std::vector<Identifier> included_cells;
250
251
252 double sum_cell_E_L0 = 0.0, sum_cell_E_L1 = 0.0, sum_cell_E_L2 = 0.0, sum_cell_E_L3 = 0.0, sum_cell_E_Gap = 0.0;
253
254
255 const CaloClusterCellLink* cellLinks = clus.
getCellLinks();
256 if (cellLinks) {
257 for (const CaloCell* cell : *cellLinks) {
258 if (!cell || !
cell->caloDDE()) {
260 continue;
261 }
262
263 int sampling =
cell->caloDDE()->getSampling();
264 double scale_factor = 1.0;
266
267 switch (sampling) {
268 case CaloCell_ID::PreSamplerB: case CaloCell_ID::PreSamplerE:
269 scale_factor = array_layer_scales[0];
layer_idx = 0;
break;
270 case CaloCell_ID::EMB1: case CaloCell_ID::EME1:
271 scale_factor = array_layer_scales[1];
layer_idx = 1;
break;
272 case CaloCell_ID::EMB2: case CaloCell_ID::EME2:
273 scale_factor = array_layer_scales[2];
layer_idx = 2;
274
276 included_cells.push_back(
cell->ID());
277 }
278 break;
279 case CaloCell_ID::EMB3: case CaloCell_ID::EME3:
280 scale_factor = array_layer_scales[3];
layer_idx = 3;
282 included_cells.push_back(
cell->ID());
283 }
284 break;
285 case CaloCell_ID::TileGap3:
286 scale_factor = 1.0;
layer_idx = 4;
break;
287 default: continue;
288 }
289
290 double final_E =
cell->e() * scale_factor;
291
292 cells_E.push_back(final_E);
293 cells_eta.push_back(
cell->eta());
294 cells_phi.push_back(
cell->phi());
295 cells_x.push_back(
cell->x());
296 cells_y.push_back(
cell->y());
297 cells_z.push_back(
cell->z());
298 cells_layer.push_back(layer_idx);
299
300
301 switch(layer_idx) {
302 case 0: sum_cell_E_L0 += final_E; break;
303 case 1: sum_cell_E_L1 += final_E; break;
304 case 2: sum_cell_E_L2 += final_E; break;
305 case 3: sum_cell_E_L3 += final_E; break;
306 case 4: sum_cell_E_Gap += final_E; break;
307 }
308 }
309 }
310
311
312
313 if (recoverySucceeded) {
314 for (
const CaloCell* cell : recoveryInfo.
addedCells) {
315 if (!cell || !
cell->caloDDE())
continue;
316
317
318 if (std::find(included_cells.begin(), included_cells.end(),
cell->ID()) != included_cells.end()) {
319 ATH_MSG_WARNING(
"Recovered cell " <<
cell->ID() <<
" already included in cluster. Skipping to avoid double counting.");
320 continue;
321 }
322 else {
324 }
325
326 int sampling =
cell->caloDDE()->getSampling();
327 double scale_factor = 1.0;
329
330 if (sampling == CaloCell_ID::EMB2 || sampling == CaloCell_ID::EME2) {
331 scale_factor = array_layer_scales[2];
layer_idx = 2;
332 } else if (sampling == CaloCell_ID::EMB3 || sampling == CaloCell_ID::EME3) {
333 scale_factor = array_layer_scales[3];
layer_idx = 3;
334 } else {
335
336 if (sampling == CaloCell_ID::PreSamplerB || sampling == CaloCell_ID::PreSamplerE) {
337 scale_factor = array_layer_scales[0];
layer_idx = 0;
338 } else if (sampling == CaloCell_ID::EMB1 || sampling == CaloCell_ID::EME1) {
339 scale_factor = array_layer_scales[1];
layer_idx = 1;
340 } else {
341 continue;
342 }
343 }
344
345 double final_E =
cell->e() * scale_factor;
346
347 cells_E.push_back(final_E);
348 cells_eta.push_back(
cell->eta());
349 cells_phi.push_back(
cell->phi());
350 cells_x.push_back(
cell->x());
351 cells_y.push_back(
cell->y());
352 cells_z.push_back(
cell->z());
353 cells_layer.push_back(layer_idx);
354
355 switch(layer_idx) {
356 case 0: sum_cell_E_L0 += final_E; break;
357 case 1: sum_cell_E_L1 += final_E; break;
358 case 2: sum_cell_E_L2 += final_E; break;
359 case 3: sum_cell_E_L3 += final_E; break;
360
361
362
363 }
364 }
365 }
366
367
368 const size_t nCells = cells_E.size();
370 if (nCells == 0) {
371 ATH_MSG_WARNING(
"No supported calorimeter cells; using configured fallback");
373 }
374
375 double sum_cell_E_total = sum_cell_E_L0 + sum_cell_E_L1 + sum_cell_E_L2 + sum_cell_E_L3;
376 const double cluster_eta = clus.
eta();
377 const double cluster_phi = clus.
phi();
378
379 std::vector<float> cells_deta, cells_dphi, cells_eFrac;
380 cells_deta.reserve(nCells);
381 cells_dphi.reserve(nCells);
382 cells_eFrac.reserve(nCells);
383
384 for (
size_t i = 0;
i <
nCells; ++
i) {
385 float deta = cells_eta[
i] - cluster_eta;
386 float dphi = cells_phi[
i] - cluster_phi;
387 dphi = std::fmod(dphi + 3.0f *
M_PI, 2.0f *
M_PI) -
M_PI;
388
389 cells_deta.push_back(deta);
390 cells_dphi.push_back(dphi);
391
392 float eFrac_layer = 0.0f;
393 switch (cells_layer[i]) {
394 case 0: eFrac_layer = (sum_cell_E_L0 != 0) ? (cells_E[i] / sum_cell_E_L0) : 0.0f; break;
395 case 1: eFrac_layer = (sum_cell_E_L1 != 0) ? (cells_E[i] / sum_cell_E_L1) : 0.0f; break;
396 case 2: eFrac_layer = (sum_cell_E_L2 != 0) ? (cells_E[i] / sum_cell_E_L2) : 0.0f; break;
397 case 3: eFrac_layer = (sum_cell_E_L3 != 0) ? (cells_E[i] / sum_cell_E_L3) : 0.0f; break;
398 case 4: eFrac_layer = (sum_cell_E_Gap != 0) ? (cells_E[i] / sum_cell_E_Gap) : 0.0f; break;
399 }
400 cells_eFrac.push_back(eFrac_layer);
401 }
402
403
404 double ratio_L1_L2 = (sum_cell_E_L2 != 0) ? (sum_cell_E_L1 / sum_cell_E_L2) : 0.0;
405 double main_layers_sum = sum_cell_E_L1 + sum_cell_E_L2 + sum_cell_E_L3;
406 double ratio_L0_total = (main_layers_sum != 0) ? (sum_cell_E_L0 / main_layers_sum) : 0.0;
407 double ratio_Tile_total = (main_layers_sum != 0) ? (sum_cell_E_Gap / main_layers_sum) : 0.0;
408
410
411 static const std::vector<std::string> featN = {
412 "Etot", "E0", "E1", "E2", "E3", "Egap", "cleta", "clphi", "E1/E2", "E0/E123", "Egap/E123",
413 "convR", "convEoP", "convPt1OPt2", "convT" };
414
415
416 std::vector<float> cluster_feats = {
417 static_cast<float>(sum_cell_E_total),
418 static_cast<float>(sum_cell_E_L0),
419 static_cast<float>(sum_cell_E_L1),
420 static_cast<float>(sum_cell_E_L2),
421 static_cast<float>(sum_cell_E_L3),
422 static_cast<float>(sum_cell_E_Gap),
423 static_cast<float>(cluster_eta),
424 static_cast<float>(cluster_phi),
425 static_cast<float>(ratio_L1_L2),
426 static_cast<float>(ratio_L0_total),
427 static_cast<float>(ratio_Tile_total)
428 };
429
430
433 if (photon) {
434
435 float convR = 799.0f;
438 }
439
440
441 float convEtOverPt = 0.0f;
445 (raw_Es1 * array_layer_scales[1] + raw_Es2 * array_layer_scales[2] + raw_Es3 * array_layer_scales[3]) :
446 (raw_Es1 + raw_Es2 + raw_Es3));
448 convEtOverPt = std::max(0.0f, eacc / (std::cosh(cl_eta) * ptconv));
449 }
450 convEtOverPt = std::min(convEtOverPt, 2.0f);
451
452
453 float convPtRatio = 1.0f;
457 if ((pt1 + pt2) > 0.0f) {
458 convPtRatio = std::max(pt1, pt2) / (pt1 + pt2);
459 }
460 }
461
462
464
465 cluster_feats.push_back(convR);
466 cluster_feats.push_back(convEtOverPt);
467 cluster_feats.push_back(convPtRatio);
468 cluster_feats.push_back(conversionType);
469 } else {
470 cluster_feats.push_back(0.0f);
471 cluster_feats.push_back(0.0f);
472 cluster_feats.push_back(0.0f);
473 cluster_feats.push_back(0.0f);
474 }
475 }
477 for (
int ifeat = 0;
auto f : cluster_feats) {
478 ATH_MSG_DEBUG(
"Cluster feature " << ifeat <<
" " << featN[ifeat] <<
" = " << f);
479 ifeat++;
480 }
481
482 gnn_input["cluster_features"] = FlavorTagInference::Inputs(cluster_feats, {1, (int64_t)cluster_feats.size()});
483
484
485 std::vector<float> cell_feats_flat;
487 for (
size_t i = 0;
i <
nCells; ++
i) {
488 cell_feats_flat.push_back(cells_eFrac[i]);
489 cell_feats_flat.push_back(cells_deta[i]);
490 cell_feats_flat.push_back(cells_dphi[i]);
491 cell_feats_flat.push_back(cells_x[i]);
492 cell_feats_flat.push_back(cells_y[i]);
493 cell_feats_flat.push_back(cells_z[i]);
494 cell_feats_flat.push_back(static_cast<float>(cells_layer[i]));
496 << "Layer " << cells_layer[i] << " deta = " << cells_deta[i] << " dphi = " << cells_dphi[i]
497 << " x, y, z = " << cells_x[i] << " " << cells_y[i] << " " << cells_z[i]
498 << " eFrac = " << cells_eFrac[i]);
499 }
500 gnn_input[
"cell_features"] = FlavorTagInference::Inputs(cell_feats_flat, {(int64_t)nCells,
m_num_cell_features});
501
502
503 auto [out_f, out_vc, out_vf] =
m_saltModel->runInference(gnn_input);
504
505 float el_gnn_score = 0.0f;
507 if (output == out_vf.end() ||
output->second.empty()) {
509 } else {
510 el_gnn_score =
output->second.front();
511 }
512
513
514 if (el_gnn_score == 0.0f) {
516 }
517
518 return el_gnn_score * static_cast<float>(sum_cell_E_total);
519}
#define ATH_MSG_WARNING(x)
const CaloClusterCellLink * getCellLinks() const
Get a pointer to the CaloClusterCellLink object (const version).
virtual double eta() const
The pseudorapidity ( ) of the particle.
virtual double e() const
The total energy of the particle.
float energyBE(const unsigned layer) const
Get the energy in one layer of the EM Calo.
virtual double phi() const
The azimuthal angle ( ) of the particle.
const xAOD::CaloCluster * caloCluster(size_t index=0) const
Pointer to the xAOD::CaloCluster/s that define the electron candidate.
std::map< std::string, Inputs, std::less<> > InputMap
float compute_ptconv(const xAOD::Photon *ph)
This ptconv is the old one used by MVACalib.
float compute_pt2conv(const xAOD::Photon *ph)
float compute_pt1conv(const xAOD::Photon *ph)
ConversionType conversionType(const bool hasTrk1, const bool hasTrk2, const std::uint8_t nSiHits1, const std::uint8_t nSiHits2)
return the photon conversion type (see EgammaEnums)
std::size_t numberOfSiTracks(const xAOD::Photon *eg)
return the number of Si tracks in the conversion
float conversionRadius(const xAOD::Vertex *vx)
return the conversion radius or 9999.
EventInfo_v1 EventInfo
Definition of the latest event info version.
Photon_v1 Photon
Definition of the current "egamma version".
setRawEt setRawPhi nCells
std::array< float, 4 > scaleEs
const xAOD::EventInfo * eventInfo