ATLAS Offline Software
eFEXFPGATowerIdProvider.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
6 #include <iostream>
7 #include <fstream>
8 #include <algorithm>
9 
11 
12 LVL1::eFEXFPGATowerIdProvider::eFEXFPGATowerIdProvider(const std::string &type, const std::string &name, const IInterface *parent):
14 {
15  declareInterface<eFEXFPGATowerIdProvider>(this);
16 }
17 
19 {
20  std::string csvpath = PathResolver::find_file("tower_fpga_efex_map.csv", "DATAPATH");
21  if (setAddress(csvpath) == StatusCode::FAILURE) {
22  ATH_MSG_WARNING("tower_fpga_efex_map.csv missing or invalid. Swiching to hard-coded mapping.");
23  };
24  return StatusCode::SUCCESS;
25 }
26 
28 {
29  m_alltowers.clear();
30  m_towerrankingcache.clear();
31 }
32 
33 StatusCode LVL1::eFEXFPGATowerIdProvider::setAddress(const std::string& inputaddress)
34 {
35  if (inputaddress.empty()) {
36  m_hascsvfile = false;
37  return StatusCode::FAILURE;
38  }
39  m_hascsvfile = true;
40  m_csvaddress = inputaddress;
41  if (loadcsv() == StatusCode::FAILURE) {
42  m_hascsvfile = false;
43  return StatusCode::FAILURE;
44  }
45  m_towerrankingcache.clear();
46  m_towerrankingcache.resize(96);
47  // sort the towers in each FPGA
48  for (int efex{ 0 }; efex < 24; efex++) {
49  for (int fpga{ 0 }; fpga < 4; fpga++) {
50  if (rankTowerinFPGA(getFPGAIndex(efex, fpga)) == StatusCode::FAILURE) {
51  m_hascsvfile = false;
52  return StatusCode::FAILURE;
53  }
54  }
55  }
56  return StatusCode::SUCCESS;
57 }
58 
59 StatusCode LVL1::eFEXFPGATowerIdProvider::getRankedTowerIDinFPGA(int eFEXID, int FPGAID, int(&towerlist)[10][6]) const {
60  int FPGAindex{ getFPGAIndex(eFEXID, FPGAID) };
61  if (FPGAindex<0) return StatusCode::FAILURE;
62  if (m_towerrankingcache[FPGAindex]) {
63  // obtain the tower order from cache
64  int vectorindex{ 0 };
65  for (int i{ 0 }; i < 10; i++) {
66  for (int j{ 0 }; j < 6; j++) {
67  towerlist[i][j] = (*m_towerrankingcache[FPGAindex]).at(vectorindex++);
68  }
69  }
70  }
71  // set tower ids to 0 if the information about the FPGA stored in the .csv file is damaged
72  else {
73  for (int i{ 0 }; i < 10; i++) {
74  for (int j{ 0 }; j < 6; j++) {
75  towerlist[i][j] = 0;
76  }
77  }
78  ATH_MSG_ERROR("FPGA mapping info missing.");
79  return StatusCode::FAILURE;
80  }
81  return StatusCode::SUCCESS;
82 }
83 
84 StatusCode LVL1::eFEXFPGATowerIdProvider::getRankedTowerIDineFEX(int eFEXID, int(&towerlist)[10][18]) const {
85  int FPGA0index{ getFPGAIndex(eFEXID, 0) };
86  int FPGA1index{ getFPGAIndex(eFEXID, 1) };
87  int FPGA2index{ getFPGAIndex(eFEXID, 2) };
88  int FPGA3index{ getFPGAIndex(eFEXID, 3) };
89  //
90  auto is_negative = [](int val){return val<0;};
91  if (std::ranges::any_of(std::initializer_list{FPGA0index, FPGA1index, FPGA2index, FPGA3index}, is_negative)){
92  return StatusCode::FAILURE;
93  }
94 
95 
96  if (!m_towerrankingcache[FPGA0index] || !m_towerrankingcache[FPGA1index] ||
97  !m_towerrankingcache[FPGA2index] || !m_towerrankingcache[FPGA3index]) {
98  for (int i{ 0 }; i < 10; i++) {
99  for (int j{ 0 }; j < 18; j++) {
100  towerlist[i][j] = 0;
101  }
102  }
103  ATH_MSG_ERROR("FPGA mapping info missing.");
104  return StatusCode::FAILURE;
105  }
106 
107  // FPGA0
108  int vectorindex{ 0 };
109  for (int i{ 0 }; i < 10; i++) {
110  for (int j{ 0 }; j < 6; j++) {
111  towerlist[i][j] = (*m_towerrankingcache[FPGA0index]).at(vectorindex++);
112  }
113  }
114 
115  // fPGA1 skipping overlap with FPGA0 and FPGA2
116  vectorindex = 0;
117  for (int i{ 0 }; i < 10; i++) {
118  vectorindex += 2;
119  for (int j{ 2 }; j < 4; j++) {
120  towerlist[i][j + 4] = (*m_towerrankingcache[FPGA1index]).at(vectorindex++);
121  }
122  vectorindex += 2;
123  }
124 
125  // FPGA2
126  vectorindex = 0;
127  for (int i{ 0 }; i < 10; i++) {
128  for (int j{ 0 }; j < 6; j++) {
129  towerlist[i][j + 8] = (*m_towerrankingcache[FPGA2index]).at(vectorindex++);
130  }
131  }
132 
133  // FPGA4 skipping overlap with FPGA2
134  vectorindex = 0;
135  for (int i{ 0 }; i < 10; i++) {
136  vectorindex += 2;
137  for (int j{ 2 }; j < 6; j++) {
138  towerlist[i][j + 12] = (*m_towerrankingcache[FPGA3index]).at(vectorindex++);
139  }
140  }
141  return StatusCode::SUCCESS;
142 }
143 
145  return m_hascsvfile;
146 }
147 
149 {
150  // This function determines the order of towers in an FPGA.
151  // Towers are sorted using eta and phi, and
152  // then store the result in m_towerrankingcache object for future use.
153  if (FPGAindex < 0) return StatusCode::FAILURE;
154  // If the tower order of an FPGA has not been determined yet, start sorting.
155  if (!m_towerrankingcache[FPGAindex]) {
156  std::vector<std::pair<int, int>> rankingmap;
157  // the row order is determined by eta while the column order is determined by phi
158  for (auto each : *m_alltowers[FPGAindex]) {
159  if (each.eTowerPhi < 3 && each.eTowerPhi > -1 && FPGAindex >= getFPGAIndex(21, 0)) {
160  rankingmap.push_back(std::pair<int, int>(-each.eTowerEta - (90 + each.eTowerPhi) * 100, each.eTowerID));
161  } else {
162  rankingmap.push_back(std::pair<int, int>(-each.eTowerEta - each.eTowerPhi * 100, each.eTowerID));
163  }
164  }
165  // There is supposed to be 60 towers in each FPGA
166  if (rankingmap.size() != 60) {
167  return StatusCode::FAILURE;
168  }
169  std::sort(rankingmap.begin(), rankingmap.end(),
170  [](std::pair<int, int> a, std::pair<int, int> b) {
171  // tower around eta == 0 has the same eta.
172  if (a.first == b.first) {
173  return (a.second < b.second);
174  }
175  return (a.first > b.first);
176  });
177  auto output = std::make_unique<std::vector<int>>();
178  output->reserve(60);
179  int vectorindex = 0;
180  for (int i{ 0 }; i < 10; i++) {
181  for (int j{ 0 }; j < 6; j++) {
182  output->push_back(rankingmap[vectorindex++].second);
183  }
184  }
185  m_towerrankingcache[FPGAindex] = std::move(output);
186  }
187  return StatusCode::SUCCESS;
188 }
189 
190 bool LVL1::eFEXFPGATowerIdProvider::hasFPGA(int FPGAindex) const
191 {
192  // check if the info of a speific FPGA has been loaded or not
193  return m_alltowers.contains(FPGAindex);
194 }
195 
197 {
198  // read the .csv file line by line
199  std::string eachline;
200  std::ifstream myfile(m_csvaddress);
201  if (myfile.is_open()) {
202  while (std::getline(myfile, eachline)) {
203  // ignore text after #
204  std::string::size_type ipos = eachline.find("#");
205  if (ipos!=std::string::npos) eachline.resize(ipos);
206 
207  // prevent reading lines with only white spaces
208  if (std::all_of(eachline.begin(), eachline.end(), ::isspace)) {
209  continue;
210  }
211 
212  // ignore lines with non-digit characters
213  auto validchar = [](char ch) { return (::isspace(ch) || ::isdigit(ch) || ch==','); };
214  if (!std::all_of(eachline.begin(), eachline.end(), validchar)) {
215  ATH_MSG_INFO("invalid line '" << eachline << "'");
216  break;
217  }
218 
219  // split lines in the .csv file by comma
220  std::stringstream eachline_stream(eachline);
221  std::vector<int> numbers_in_eachline;
222  while (eachline_stream.good()) {
223  std::string tem_string;
224  std::getline(eachline_stream, tem_string, ',');
225  try {
226  numbers_in_eachline.push_back(std::stoi(tem_string));
227  }
228  catch (...) {
229  ATH_MSG_WARNING( "Invalid input in tower_fpga_efex_map.csv." );
230  return StatusCode::FAILURE;
231  }
232  }
233 
234  // There is supposed to be 5 numbers in each line:
235  // eFEX ID, FPGA ID, eTower ID, eta, phi
236  if (numbers_in_eachline.size() != 5) {
237  ATH_MSG_WARNING( "Invalid input in tower_fpga_efex_map.csv." );
238  return StatusCode::FAILURE;
239  }
240  // eta and phi are supposed to be positive
241  if (numbers_in_eachline[3] < 0 || numbers_in_eachline[4] < 0) {
242  ATH_MSG_WARNING("Invalid input in tower_fpga_efex_map.csv.");
243  // return scodefail
244  return StatusCode::FAILURE;
245  }
246  int FPGAindex = getFPGAIndex(numbers_in_eachline[0], numbers_in_eachline[1]);
247  // check the range of eFEX ID and FPGA ID
248  if (FPGAindex == -1) {
249  ATH_MSG_WARNING("Invalid input in tower_fpga_efex_map.csv.");
250  return StatusCode::FAILURE;
251  }
252  towerinfo tem_towerinfo;
253  tem_towerinfo.eTowerID = numbers_in_eachline[2];
254  // determine the sign of eta using the tower id
255  // positive: 200000-299999, 400000-499999, 600000-699999
256  // negative: 100000-199999, 300000-399999, 500000-599999
257  int etasign;
258  if (int(numbers_in_eachline[2] / 100000) % 2 != 0) {
259  etasign = -1;
260  } else {
261  etasign = 1;
262  }
263  tem_towerinfo.eTowerEta = etasign * (numbers_in_eachline[3]);
264  tem_towerinfo.eTowerPhi = numbers_in_eachline[4];
265 
266  // Create a key for the FPGA if the the FPGA key is not
267  // avaliable in the m_alltowers hash table object yet.
268  // Otherwise append the tower to the existing bucket.
269  if (hasFPGA(FPGAindex)) {
270  m_alltowers[FPGAindex]->push_back(tem_towerinfo);
271  } else {
272  m_alltowers[FPGAindex] = std::make_unique<std::vector<towerinfo>>(std::vector{tem_towerinfo});
273  }
274  }
275  myfile.close();
276  } else {
277  ATH_MSG_WARNING("Unable to open share/tower_fpga_efex_map.csv.");
278  return StatusCode::FAILURE;
279  }
280  return StatusCode::SUCCESS;
281 }
282 
283 int LVL1::eFEXFPGATowerIdProvider::getFPGAIndex(int eFEXid, int FPGAid) const
284 {
285  // check the range of eFEX ID and FPGA ID
286  // There is supposed to be 24 eFex and each eFex
287  // contains 4 FPGAs.
288  if (FPGAid < 0 or FPGAid > 3) {
289  return -1;
290  }
291  if (eFEXid < 0 or FPGAid > 23) {
292  return -1;
293  }
294  return eFEXid * 4 + FPGAid;
295 }
LVL1::eFEXFPGATowerIdProvider::setAddress
StatusCode setAddress(const std::string &inputaddress)
set path to the csv file and load
Definition: eFEXFPGATowerIdProvider.cxx:33
sendEI_SPB.ch
ch
Definition: sendEI_SPB.py:35
LVL1::eFEXFPGATowerIdProvider::rankTowerinFPGA
StatusCode rankTowerinFPGA(int FPGAindex)
rank the tower ids in an FPGA This function determines the order of towers in an FPGA.
Definition: eFEXFPGATowerIdProvider.cxx:148
LVL1::eFEXFPGATowerIdProvider::~eFEXFPGATowerIdProvider
~eFEXFPGATowerIdProvider()
Destructor.
Definition: eFEXFPGATowerIdProvider.cxx:27
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
python.SystemOfUnits.second
float second
Definition: SystemOfUnits.py:135
LVL1::eFEXFPGATowerIdProvider::towerinfo
Sturcture defines the id, eta and phi position of a tower.
Definition: eFEXFPGATowerIdProvider.h:40
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
LVL1::eFEXFPGATowerIdProvider::getRankedTowerIDineFEX
StatusCode getRankedTowerIDineFEX(int eFEXID, int(&towerlist)[10][18]) const
obtain ordered tower IDs in an eFEX
Definition: eFEXFPGATowerIdProvider.cxx:84
LVL1::eFEXFPGATowerIdProvider::hasFPGA
bool hasFPGA(int) const
check if an FPGA exists in the csv file
Definition: eFEXFPGATowerIdProvider.cxx:190
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
LVL1::eFEXFPGATowerIdProvider::towerinfo::eTowerPhi
int eTowerPhi
tower Phi index
Definition: eFEXFPGATowerIdProvider.h:43
lumiFormat.i
int i
Definition: lumiFormat.py:85
LVL1::eFEXFPGATowerIdProvider::towerinfo::eTowerEta
int eTowerEta
tower Eta index
Definition: eFEXFPGATowerIdProvider.h:42
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
LVL1::eFEXFPGATowerIdProvider::ifhaveinputfile
bool ifhaveinputfile() const
Definition: eFEXFPGATowerIdProvider.cxx:144
LVL1::eFEXFPGATowerIdProvider::getRankedTowerIDinFPGA
StatusCode getRankedTowerIDinFPGA(int eFEXID, int FPGAID, int(&towerlist)[10][6]) const
obtain ordered tower IDs in an FPGA
Definition: eFEXFPGATowerIdProvider.cxx:59
test_pyathena.parent
parent
Definition: test_pyathena.py:15
eFEXFPGATowerIdProvider.h
Provide tower-FPGA mapping.
TrigConf::name
Definition: HLTChainList.h:35
PathResolver.h
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:76
LVL1::eFEXFPGATowerIdProvider::towerinfo::eTowerID
int eTowerID
tower ID
Definition: eFEXFPGATowerIdProvider.h:41
a
TList * a
Definition: liststreamerinfos.cxx:10
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
PathResolver::find_file
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
Definition: PathResolver.cxx:183
LVL1::eFEXFPGATowerIdProvider::getFPGAIndex
int getFPGAIndex(int eFEXid, int FPGAid) const
determine the index of an FPGA
Definition: eFEXFPGATowerIdProvider.cxx:283
Pythia8_RapidityOrderMPI.val
val
Definition: Pythia8_RapidityOrderMPI.py:14
LVL1::eFEXFPGATowerIdProvider::eFEXFPGATowerIdProvider
eFEXFPGATowerIdProvider(const std::string &type, const std::string &name, const IInterface *parent)
Constructor.
Definition: eFEXFPGATowerIdProvider.cxx:12
AthAlgTool
Definition: AthAlgTool.h:26
LVL1::eFEXFPGATowerIdProvider::loadcsv
StatusCode loadcsv()
load the mapping info in the csv file
Definition: eFEXFPGATowerIdProvider.cxx:196
LVL1::eFEXFPGATowerIdProvider::initialize
StatusCode initialize()
initialize the tool
Definition: eFEXFPGATowerIdProvider.cxx:18