ATLAS Offline Software
Loading...
Searching...
No Matches
TFCSPredictExtrapWeights.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
11
12#include "TFile.h"
13#include "TClass.h"
14
15#include "CLHEP/Random/RandGauss.h"
16#include "CLHEP/Random/RandFlat.h"
17
18#if defined(__FastCaloSimStandAlone__)
19#include "CLHEP/Random/TRandomEngine.h"
20#else
21#include <CLHEP/Random/RanluxEngine.h>
22#endif
23
24#include <iostream>
25#include <fstream>
26
27// LWTNN
28#include "lwtnn/LightweightGraph.hh"
29#include "lwtnn/LightweightNeuralNetwork.hh"
30#include "lwtnn/parse_json.hh"
31
32//=============================================
33//======= TFCSPredictExtrapWeights =========
34//=============================================
35
42
43// Destructor
45 if (m_input != nullptr) {
46 delete m_input;
47 }
48 if (m_relevantLayers != nullptr) {
49 delete m_relevantLayers;
50 }
51 if (m_normLayers != nullptr) {
52 delete m_normLayers;
53 }
54 if (m_normMeans != nullptr) {
55 delete m_normMeans;
56 }
57 if (m_normStdDevs != nullptr) {
58 delete m_normStdDevs;
59 }
60 if (m_nn != nullptr) {
61 delete m_nn;
62 }
63}
64
66 const TFCSParametrizationBase &ref) const {
67 if (IsA() != ref.IsA()) {
68 ATH_MSG_DEBUG("operator==: different class types "
69 << IsA()->GetName() << " != " << ref.IsA()->GetName());
70 return false;
71 }
72 const TFCSPredictExtrapWeights &ref_typed =
73 static_cast<const TFCSPredictExtrapWeights &>(ref);
74
76 return true;
78 return false;
80 return false;
81
82 return (m_input->compare(*ref_typed.m_input) == 0);
83}
84
85// getNormInputs()
86// Get values needed to normalize inputs
88 const std::string &etaBin, const std::string &FastCaloTXTInputFolderName) {
89 ATH_MSG_DEBUG(" Getting normalization inputs... ");
90
91 // Open corresponding TXT file and extract mean/std dev for each variable
92 if (m_normLayers != nullptr) {
93 m_normLayers->clear();
94 } else {
95 m_normLayers = new std::vector<int>();
96 }
97 if (m_normMeans != nullptr) {
98 m_normMeans->clear();
99 } else {
100 m_normMeans = new std::vector<float>();
101 }
102 if (m_normStdDevs != nullptr) {
103 m_normStdDevs->clear();
104 } else {
105 m_normStdDevs = new std::vector<float>();
106 }
107 std::string inputFileName = FastCaloTXTInputFolderName +
108 "MeanStdDevEnergyFractions_eta_" + etaBin +
109 ".txt";
110 ATH_MSG_DEBUG(" Opening " << inputFileName);
111 std::ifstream inputTXT(inputFileName);
112 if (inputTXT.is_open()) {
113 std::string line;
114 while (getline(inputTXT, line)) {
115 std::stringstream ss(line);
116 unsigned int counter = 0;
117 while (ss.good()) {
118 std::string substr;
119 getline(ss, substr, ' ');
120 if (counter == 0) { // Get index (#layer or -1 if var == etrue)
121 if (substr != "etrue") {
122 int index = std::stoi(substr.substr(substr.find('_') + 1));
123 m_normLayers->push_back(index);
124 } else { // etrue
125 m_normLayers->push_back(-1);
126 }
127 } else if (counter == 1) {
128 m_normMeans->push_back(std::stof(substr));
129 } else if (counter == 2) {
130 m_normStdDevs->push_back(std::stof(substr));
131 }
132 counter++;
133 }
134 }
135 inputTXT.close();
136 } else {
137 ATH_MSG_ERROR(" Unable to open file " << inputFileName);
138 return false;
139 }
140
141 return true;
142}
143
144// prepareInputs()
145// Prepare input variables to the Neural Network
146std::map<std::string, double>
148 const float truthE) const {
149 std::map<std::string, double> inputVariables;
150 for (int ilayer = 0; ilayer < CaloCell_ID_FCS::MaxSample; ++ilayer) {
151 if (std::find(m_relevantLayers->cbegin(), m_relevantLayers->cend(),
152 ilayer) != m_relevantLayers->cend()) {
153 const std::string layer = std::to_string(ilayer);
154 // Find index
155 auto itr =
156 std::find(m_normLayers->cbegin(), m_normLayers->cend(), ilayer);
157 if (itr != m_normLayers->cend()) {
158 const int index = std::distance(m_normLayers->cbegin(), itr);
159 inputVariables["ef_" + layer] =
160 (simulstate.Efrac(ilayer) - std::as_const(m_normMeans)->at(index)) /
161 std::as_const(m_normStdDevs)->at(index);
162 } else {
163 ATH_MSG_ERROR("Normalization information not found for layer "
164 << ilayer);
165 }
166 }
167 }
168 // Find index for truth energy
169 auto itr = std::find(m_normLayers->cbegin(), m_normLayers->cend(), -1);
170 int index = std::distance(m_normLayers->cbegin(), itr);
171 inputVariables["etrue"] = (truthE - std::as_const(m_normMeans)->at(index)) /
172 std::as_const(m_normStdDevs)->at(index);
173 if (is_match_pdgid(22)) {
174 inputVariables["pdgId"] = 1; // one hot enconding
175 } else if (is_match_pdgid(11) || is_match_pdgid(-11)) {
176 inputVariables["pdgId"] = 0; // one hot enconding
177 }
178 return inputVariables;
179}
180
181// simulate()
182// get predicted extrapolation weights and save them as AuxInfo in simulstate
184 TFCSSimulationState &simulstate, const TFCSTruthState *truth,
185 const TFCSExtrapolationState * /*extrapol*/) const {
186
187 // Get inputs to Neural Network
188 std::map<std::string, double> inputVariables =
189 prepareInputs(simulstate, truth->E() * 0.001);
190
191 // Get predicted extrapolation weights
192 auto outputs = m_nn->compute(inputVariables);
193 for (int ilayer = 0; ilayer < CaloCell_ID_FCS::MaxSample; ++ilayer) {
194 if (std::find(m_relevantLayers->cbegin(), m_relevantLayers->cend(),
195 ilayer) != m_relevantLayers->cend()) {
196 ATH_MSG_DEBUG("TFCSPredictExtrapWeights::simulate: layer: "
197 << ilayer << " weight: "
198 << outputs["extrapWeight_" + std::to_string(ilayer)]);
199 float weight = outputs["extrapWeight_" + std::to_string(ilayer)];
200 // Protections
201 if (weight < 0) {
202 weight = 0;
203 } else if (weight > 1) {
204 weight = 1;
205 }
206 simulstate.setAuxInfo<float>(ilayer, weight);
207 } else { // use weight=0.5 for non-relevant layers
209 "Setting weight=0.5 for layer = " << std::to_string(ilayer));
210 simulstate.setAuxInfo<float>(ilayer, float(0.5));
211 }
212 }
213 return FCSSuccess;
214}
215
216// simulate_hit()
218 Hit &hit, TFCSSimulationState &simulstate, const TFCSTruthState * /*truth*/,
219 const TFCSExtrapolationState *extrapol) {
220
221 const int cs = calosample();
222
223 // Get corresponding predicted extrapolation weight from simulstate
224 float extrapWeight;
225 if (simulstate.hasAuxInfo(cs)) {
226 extrapWeight = simulstate.getAuxInfo<float>(cs);
227 } else { // missing AuxInfo
229 "Simulstate is not decorated with extrapolation weights for cs = "
230 << std::to_string(cs));
231 return FCSFatal;
232 }
233
234 double eta = (1. - extrapWeight) * extrapol->eta(cs, SUBPOS_ENT) +
235 extrapWeight * extrapol->eta(cs, SUBPOS_EXT);
236 double phi = (1. - extrapWeight) * extrapol->phi(cs, SUBPOS_ENT) +
237 extrapWeight * extrapol->phi(cs, SUBPOS_EXT);
238 float extrapWeight_for_r_z = extrapWeight;
239 if (UseHardcodedWeight()) {
240 extrapWeight_for_r_z = 0.5;
242 "Will use extrapWeight=0.5 for r and z when constructing a hit");
243 } else {
244 ATH_MSG_DEBUG("Will use predicted extrapWeight also for r and z when "
245 "constructing a hit");
246 }
247 double r = (1. - extrapWeight_for_r_z) * extrapol->r(cs, SUBPOS_ENT) +
248 extrapWeight_for_r_z * extrapol->r(cs, SUBPOS_EXT);
249 double z = (1. - extrapWeight_for_r_z) * extrapol->z(cs, SUBPOS_ENT) +
250 extrapWeight_for_r_z * extrapol->z(cs, SUBPOS_EXT);
251
252 if (!std::isfinite(r) || !std::isfinite(z) || !std::isfinite(eta) ||
253 !std::isfinite(phi)) {
254 ATH_MSG_WARNING("Extrapolator contains NaN or infinite number.\nSetting "
255 "center position to calo boundary.");
256 ATH_MSG_WARNING("Before fix: center_r: "
257 << r << " center_z: " << z << " center_phi: " << phi
258 << " center_eta: " << eta << " weight: " << extrapWeight
259 << " cs: " << cs);
260 // If extrapolator fails we can set position to calo boundary
261 r = extrapol->IDCaloBoundary_r();
262 z = extrapol->IDCaloBoundary_z();
263 eta = extrapol->IDCaloBoundary_eta();
264 phi = extrapol->IDCaloBoundary_phi();
265 ATH_MSG_WARNING("After fix: center_r: "
266 << r << " center_z: " << z << " center_phi: " << phi
267 << " center_eta: " << eta << " weight: " << extrapWeight
268 << " cs: " << cs);
269 }
270
271 hit.setCenter_r(r);
272 hit.setCenter_z(z);
273 hit.setCenter_eta(eta);
274 hit.setCenter_phi(phi);
275
276 ATH_MSG_DEBUG("TFCSPredictExtrapWeights: center_r: "
277 << hit.center_r() << " center_z: " << hit.center_z()
278 << " center_phi: " << hit.center_phi()
279 << " center_eta: " << hit.center_eta()
280 << " weight: " << extrapWeight << " cs: " << cs);
281
282 return FCSSuccess;
283}
284
285// initializeNetwork()
286// Initialize lwtnn network
288 int pid, const std::string &etaBin,
289 const std::string &FastCaloNNInputFolderName) {
290
292 "Using FastCaloNNInputFolderName: " << FastCaloNNInputFolderName);
293 set_pdgid(pid);
294
295 std::string inputFileName =
296 FastCaloNNInputFolderName + "NN_" + etaBin + ".json";
297 ATH_MSG_DEBUG("Will read JSON file: " << inputFileName);
298 if (inputFileName.empty()) {
299 ATH_MSG_ERROR("Could not find json file " << inputFileName);
300 return false;
301 } else {
302 ATH_MSG_INFO("For pid: " << pid << " and etaBin" << etaBin
303 << ", loading json file " << inputFileName);
304 std::ifstream input(inputFileName);
305 std::stringstream sin;
306 sin << input.rdbuf();
307 input.close();
308 auto config = lwt::parse_json(sin);
309 m_nn = new lwt::LightweightNeuralNetwork(config.inputs, config.layers,
310 config.outputs);
311 if (m_nn == nullptr) {
312 ATH_MSG_ERROR("Could not create LightWeightNeuralNetwork from "
313 << inputFileName);
314 return false;
315 }
316 if (m_input != nullptr) {
317 delete m_input;
318 }
319 m_input = new std::string(sin.str());
320 // Extract relevant layers from the outputs
321 m_relevantLayers = new std::vector<int>();
322 for (auto name : config.outputs) {
323 int layer = std::stoi(
324 name.erase(0, 13)); // remove "extrapWeight_" and convert to int
325 m_relevantLayers->push_back(layer);
326 }
327 }
328 return true;
329}
330
331// Streamer()
332void TFCSPredictExtrapWeights::Streamer(TBuffer &R__b) {
333 // Stream an object of class TFCSPredictExtrapWeights
334
335 if (R__b.IsReading()) {
336 R__b.ReadClassBuffer(TFCSPredictExtrapWeights::Class(), this);
337 if (m_nn != nullptr) {
338 delete m_nn;
339 m_nn = nullptr;
340 }
341 if (m_input && !m_input->empty()) {
342 std::stringstream sin;
343 sin.str(*m_input);
344 auto config = lwt::parse_json(sin);
345 m_nn = new lwt::LightweightNeuralNetwork(config.inputs, config.layers,
346 config.outputs);
347 }
348#ifndef __FastCaloSimStandAlone__
349 // When running inside Athena, delete input/config/normInputs to free the
350 // memory
351 if (freemem()) {
352 delete m_input;
353 m_input = nullptr;
354 }
355#endif
356 } else {
357 R__b.WriteClassBuffer(TFCSPredictExtrapWeights::Class(), this);
358 }
359}
360
361// unit_test()
362// Function for testing
364 TFCSSimulationState *simulstate, const TFCSTruthState *truth,
365 const TFCSExtrapolationState *extrapol) {
366 const std::string this_file = __FILE__;
367 const std::string parent_dir = this_file.substr(0, this_file.find("/src/"));
368 const std::string norm_path = parent_dir + "/share/NormPredExtrapSample/";
369 std::string net_path = "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/"
370 "FastCaloSim/LWTNNPredExtrapSample/";
371 test_path(net_path, norm_path, simulstate, truth, extrapol);
372 //net_path = "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/FastCaloSim/"
373 // "ONNXPredExtrapSample/";
374 //test_path(net_path, norm_path, simulstate, truth, extrapol);
375}
376
377// test_path()
378// Function for testing
380 std::string &net_path, std::string const &norm_path,
381 TFCSSimulationState *simulstate, const TFCSTruthState *truth,
382 const TFCSExtrapolationState *extrapol) {
384 ATH_MSG_NOCLASS(logger, "Testing net path ..."
385 << net_path.substr(net_path.length() - 20)
386 << " and norm path ..."
387 << norm_path.substr(norm_path.length() - 20));
388 if (!simulstate) {
389 simulstate = new TFCSSimulationState();
390#if defined(__FastCaloSimStandAlone__)
391 simulstate->setRandomEngine(new CLHEP::TRandomEngine());
392#else
393 simulstate->setRandomEngine(new CLHEP::RanluxEngine());
394#endif
395 }
396 if (!truth) {
398 t->SetPtEtaPhiM(524288000, 0, 0, 130); // 524288 GeV
399 t->set_pdgid(22); // photon
400 truth = t;
401 }
402 if (!extrapol) {
404 e->set_IDCaloBoundary_eta(truth->Eta());
405 for (int i = 0; i < 24; ++i) {
406 e->set_eta(i, TFCSExtrapolationState::SUBPOS_ENT, truth->Eta());
407 e->set_eta(i, TFCSExtrapolationState::SUBPOS_EXT, truth->Eta());
408 e->set_eta(i, TFCSExtrapolationState::SUBPOS_MID, truth->Eta());
409 e->set_phi(i, TFCSExtrapolationState::SUBPOS_ENT, 0);
410 e->set_phi(i, TFCSExtrapolationState::SUBPOS_EXT, 0);
411 e->set_phi(i, TFCSExtrapolationState::SUBPOS_MID, 0);
412 e->set_r(i, TFCSExtrapolationState::SUBPOS_ENT, 1500 + i * 10);
413 e->set_r(i, TFCSExtrapolationState::SUBPOS_EXT, 1510 + i * 10);
414 e->set_r(i, TFCSExtrapolationState::SUBPOS_MID, 1505 + i * 10);
415 e->set_z(i, TFCSExtrapolationState::SUBPOS_ENT, 3500 + i * 10);
416 e->set_z(i, TFCSExtrapolationState::SUBPOS_EXT, 3510 + i * 10);
417 e->set_z(i, TFCSExtrapolationState::SUBPOS_MID, 3505 + i * 10);
418 }
419 extrapol = e;
420 }
421
422 // Set energy in layers which then will be retrieved in simulate_hit()
423 simulstate->set_E(0, 1028.77124023);
424 simulstate->set_E(1, 68199.0625);
425 simulstate->set_E(2, 438270.78125);
426 simulstate->set_E(3, 3024.02929688);
427 simulstate->set_E(12, 1330.10131836);
428 simulstate->set_E(1028.77124023 + 68199.0625 + 438270.78125 + 3024.02929688 +
429 1330.10131836);
430 simulstate->set_Efrac(0, simulstate->E(0) / simulstate->E());
431 simulstate->set_Efrac(1, simulstate->E(1) / simulstate->E());
432 simulstate->set_Efrac(2, simulstate->E(2) / simulstate->E());
433 simulstate->set_Efrac(3, simulstate->E(3) / simulstate->E());
434 simulstate->set_Efrac(12, simulstate->E(12) / simulstate->E());
435
436 const int pdgId = truth->pdgid();
437 const float Ekin = truth->Ekin();
438 const float eta = truth->Eta();
439
440 ATH_MSG_NOCLASS(logger, "True energy " << Ekin << " pdgId " << pdgId
441 << " eta " << eta);
442
443 // Find eta bin
444 const int Eta = eta * 10;
445 std::string etaBin = "";
446 for (int i = 0; i <= 25; ++i) {
447 int etaTmp = i * 5;
448 if (Eta >= etaTmp && Eta < (etaTmp + 5)) {
449 etaBin = std::to_string(i * 5) + "_" + std::to_string((i + 1) * 5);
450 }
451 }
452
453 ATH_MSG_NOCLASS(logger, "etaBin = " << etaBin);
454
455 TFCSPredictExtrapWeights NN("NN", "NN");
456 NN.setLevel(MSG::INFO);
457 const int pid = truth->pdgid();
458 NN.initializeNetwork(pid, etaBin, net_path);
459 NN.getNormInputs(etaBin, norm_path);
460
461 // Get extrapWeights and save them as AuxInfo in simulstate
462
463 // Get inputs to Neural Network
464 std::map<std::string, double> inputVariables =
465 NN.prepareInputs(*simulstate, truth->E() * 0.001);
466
467 // Get predicted extrapolation weights
468 ATH_MSG_NOCLASS(logger, "computing with m_nn");
469 auto outputs = NN.m_nn->compute(inputVariables);
470 const std::vector<int> layers = {0, 1, 2, 3, 12};
471 for (int ilayer : layers) {
472 simulstate->setAuxInfo<float>(
473 ilayer, outputs["extrapWeight_" + std::to_string(ilayer)]);
474 }
475
476 // Simulate
477 const int layer = 0;
478 NN.set_calosample(layer);
480 NN.simulate_hit(hit, *simulstate, truth, extrapol);
481
482 // Write
483 TFile *fNN = new TFile("FCSNNtest.root", "RECREATE");
484 NN.Write();
485 fNN->ls();
486 fNN->Close();
487 delete fNN;
488
489 // Open
490 fNN = TFile::Open("FCSNNtest.root");
491 TFCSPredictExtrapWeights *NN2 = (TFCSPredictExtrapWeights *)(fNN->Get("NN"));
492
493 NN2->setLevel(MSG::INFO);
494 NN2->simulate_hit(hit, *simulstate, truth, extrapol);
495 //simulstate->Print();
496
497 return;
498}
499
500void TFCSPredictExtrapWeights::Print(Option_t *option) const {
501 TString opt(option);
502 bool shortprint = opt.Index("short") >= 0;
503 bool longprint = msgLvl(MSG::DEBUG) || (msgLvl(MSG::INFO) && !shortprint);
504 TString optprint = opt;
505 optprint.ReplaceAll("short", "");
507
508 if (longprint)
509 ATH_MSG_INFO(optprint << " m_input (TFCSPredictExtrapWeights): "
511 if (longprint)
512 ATH_MSG_INFO(optprint << " Address of m_nn: " << (void *)m_nn);
513}
const std::regex ref(r_ef)
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
static Double_t ss
#define ATH_MSG_NOCLASS(logger_name, x)
Definition MLogging.h:52
FCSReturnCode
Base class for all FastCaloSim parametrizations Functionality in derivde classes is provided through ...
#define z
Helper for getting a const version of a pointer.
Cut down AthMessaging.
Definition MLogging.h:176
bool msgLvl(const MSG::Level lvl) const
Check whether the logging system is active at the provided verbosity level.
Definition MLogging.h:222
virtual void setLevel(MSG::Level lvl)
Update outputlevel.
Definition MLogging.cxx:105
double phi(int layer, int subpos) const
double z(int layer, int subpos) const
double r(int layer, int subpos) const
double eta(int layer, int subpos) const
TFCSLateralShapeParametrizationHitBase(const char *name=nullptr, const char *title=nullptr)
void Print(Option_t *option="") const override
bool compare(const TFCSParametrizationBase &ref) const
bool compare(const TFCSParametrizationBase &ref) const
Do not persistify!
virtual bool is_match_pdgid(int id) const override
virtual void set_pdgid(int id)
bool compare(const TFCSParametrizationBase &ref) const
virtual FCSReturnCode simulate(TFCSSimulationState &simulstate, const TFCSTruthState *truth, const TFCSExtrapolationState *extrapol) const override
Method in all derived classes to do some simulation.
bool getNormInputs(const std::string &etaBin, const std::string &FastCaloTXTInputFolderName)
std::vector< float > * m_normMeans
lwt::LightweightNeuralNetwork * m_nn
virtual bool operator==(const TFCSParametrizationBase &ref) const override
The == operator compares the content of instances.
TFCSPredictExtrapWeights(const char *name=nullptr, const char *title=nullptr)
std::vector< float > * m_normStdDevs
std::vector< int > * m_relevantLayers
void Print(Option_t *option="") const override
std::vector< int > * m_normLayers
Do not persistify.
std::map< std::string, double > prepareInputs(TFCSSimulationState &simulstate, const float truthE) const
virtual FCSReturnCode simulate_hit(Hit &hit, TFCSSimulationState &simulstate, const TFCSTruthState *truth, const TFCSExtrapolationState *extrapol) override
simulated one hit position with some energy.
static void test_path(std::string &net_path, std::string const &norm_path, TFCSSimulationState *simulstate=nullptr, const TFCSTruthState *truth=nullptr, const TFCSExtrapolationState *extrapol=nullptr)
static void unit_test(TFCSSimulationState *simulstate=nullptr, const TFCSTruthState *truth=nullptr, const TFCSExtrapolationState *extrapol=nullptr)
bool initializeNetwork(int pid, const std::string &etaBin, const std::string &FastCaloNNInputFolderName)
double Efrac(int sample) const
void set_E(int sample, double Esample)
bool hasAuxInfo(std::uint32_t index) const
const T getAuxInfo(std::uint32_t index) const
void set_Efrac(int sample, double Efracsample)
void setAuxInfo(std::uint32_t index, const T &val)
void setRandomEngine(CLHEP::HepRandomEngine *engine)
int pdgid() const
double Ekin() const
int r
Definition globals.cxx:22
static Root::TMsgLogger logger("iLumiCalc")
const T * as_const_ptr(const T *p)
Helper for getting a const version of a pointer.
Definition index.py:1
#define IsA
Declare the TObject style functions.