ATLAS Offline Software
MuonRoIByteStreamTool.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 // Local includes
7 
8 // Trigger includes
9 #include "TrigConfData/L1Menu.h"
11 #include "xAODTrigger/MuonRoI.h"
13 // Athena includes
15 
16 // TDAQ includes
17 #include "eformat/SourceIdentifier.h"
18 
19 // get bitsmasks from common definition source:
21 
22 #include <span>
23 
24 using namespace LVL1::MuCTPIBits;
25 
28 
29 // -----------------------------------------------------------------------------
30 // Helpers
31 // -----------------------------------------------------------------------------
32 namespace {
34  constexpr static std::array<size_t,static_cast<size_t>(LVL1::MuCTPIBits::WordType::MAX)> s_wordTypes = {0,1,2,3,4,5};
35  using namespace std::literals::string_view_literals;
37  constexpr static std::array<std::string_view,4> s_sectorNames = {{"Undefined"sv, "Barrel"sv, "Forward"sv, "Endcap"sv}};
39  constexpr static int s_bcidsFullOrbit{3564};
41  constexpr int bcidDiff(int a, int b) {
42  int diff = a - b;
43  while (diff < -s_bcidsFullOrbit/2) {diff += s_bcidsFullOrbit;}
44  while (diff > s_bcidsFullOrbit/2 - 1) {diff -= s_bcidsFullOrbit;}
45  return diff;
46  }
48  constexpr int bcidSum(int a, int b) {
49  int sum = a + b;
50  while (sum < 0) {sum += s_bcidsFullOrbit;}
51  while (sum >= s_bcidsFullOrbit) {sum -= s_bcidsFullOrbit;}
52  return sum;
53  }
55  constexpr uint32_t roiWordAddOfflineRun3Flag (uint32_t candidateWord) {
56  return (candidateWord | 0x1u<<31);
57  }
59  constexpr uint32_t roiWordRemoveOfflineRun3Flag (uint32_t candidateWord) {
60  return (candidateWord & ~(0x1u<<31));
61  }
62 }
63 
64 // -----------------------------------------------------------------------------
65 // Constructor
66 // -----------------------------------------------------------------------------
68  const std::string& name,
69  const IInterface* parent)
70 : base_class(type, name, parent) {}
71 
72 // -----------------------------------------------------------------------------
73 // Initialisation
74 // -----------------------------------------------------------------------------
76  ConversionMode mode = getConversionMode(m_roiReadKeys, m_roiWriteKeys, msg());
79  ATH_CHECK(m_roiReadKeys.initialize(mode==ConversionMode::Encoding));
81 
82  ATH_MSG_DEBUG((mode==ConversionMode::Encoding ? "Encoding" : "Decoding") << " ROB IDs: "
83  << MSG::hex << m_robIds.value() << MSG::dec);
84 
85  if (m_doTopo.value() && mode==ConversionMode::Decoding && m_MuCTPIL1TopoKeys.size() != m_roiWriteKeys.size()) {
86  ATH_MSG_ERROR("Number of Topo TOB output containers (" << m_MuCTPIL1TopoKeys.size() << ") "
87  << "does not match the number of RoI output containers (" << m_roiWriteKeys.size() << ")");
88  return StatusCode::FAILURE;
89  }
90 
92  if (m_readoutWindow!=1 && m_readoutWindow!=3 && m_readoutWindow!=5) {
93  ATH_MSG_ERROR("The expected readout window must be 1, 3 or 5, but it is " << m_readoutWindow);
94  return StatusCode::FAILURE;
95  }
96 
97  if (m_robIds.value().size() != 1) {
98  ATH_MSG_ERROR("This tool implementation assumes there is exactly one MUCTPI ROB, but "
99  << m_robIds.size() << " were configured");
100  return StatusCode::SUCCESS;
101  }
102 
103  CHECK( m_rpcTool.retrieve() );
104  CHECK( m_tgcTool.retrieve() );
105  CHECK( m_thresholdTool.retrieve() );
106  if (!m_monTool.empty()) ATH_CHECK(m_monTool.retrieve());
107 
108  if (m_doTopo.value()) {
109  const std::string barrelFileName = PathResolverFindCalibFile( m_barrelRoIFile );
110  const std::string ecfFileName = PathResolverFindCalibFile( m_ecfRoIFile );
111  const std::string side0LUTFileName = PathResolverFindCalibFile( m_side0LUTFile );
112  const std::string side1LUTFileName = PathResolverFindCalibFile( m_side1LUTFile );
113 
114  //CHECK( m_l1topoLUT.initializeBarrelLUT(side0LUTFileName,
115  // side1LUTFileName) );
116  CHECK( m_l1topoLUT.initializeLUT(barrelFileName,
117  ecfFileName,
118  side0LUTFileName,
119  side1LUTFileName) );
120  }
121  return StatusCode::SUCCESS;
122 }
123 
124 // -----------------------------------------------------------------------------
125 // BS->xAOD conversion
126 // -----------------------------------------------------------------------------
127 StatusCode MuonRoIByteStreamTool::convertFromBS(const std::vector<const ROBF*>& vrobf,
128  const EventContext& eventContext) const {
129  // Create and record the RoI containers
130  std::vector<SG::WriteHandle<xAOD::MuonRoIContainer>> roiHandles = m_roiWriteKeys.makeHandles(eventContext);
131  for (auto& roiHandle : roiHandles) {
132  ATH_CHECK(roiHandle.record(std::make_unique<xAOD::MuonRoIContainer>(),
133  std::make_unique<xAOD::MuonRoIAuxContainer>()));
134  ATH_MSG_DEBUG("Recorded MuonRoIContainer with key " << roiHandle.key());
135  }
136 
137  // Create a WriteHandle for L1Topo output
138  std::vector<SG::WriteHandle<xAOD::MuonRoIContainer>> topoHandles;
139  if (m_doTopo.value()) {
140  topoHandles = m_MuCTPIL1TopoKeys.makeHandles(eventContext);
141  for (auto& topoHandle : topoHandles) {
142  ATH_CHECK(topoHandle.record(std::make_unique<xAOD::MuonRoIContainer>(),
143  std::make_unique<xAOD::MuonRoIAuxContainer>()));
144  ATH_MSG_DEBUG("Recorded MuCTPIL1Topo with key " << topoHandle.key());
145  }
146  }
147 
148  // Find the ROB fragment to decode
149  const eformat::helper::SourceIdentifier sid(m_robIds.value().at(0));
150  auto it = std::find_if(vrobf.begin(), vrobf.end(), [&sid](const ROBF* rob){return rob->rob_source_id() == sid.code();});
151  if (it == vrobf.end()) {
152  ATH_MSG_DEBUG("No MUCTPI ROB fragment with ID 0x" << std::hex << sid.code() << std::dec
153  << " was found, MuonRoIContainer will be empty");
154  return StatusCode::SUCCESS;
155  }
156 
157  // Retrieve the ROD data
158  const ROBF* rob = *it;
159  ATH_MSG_DEBUG("MUCTPI ROB for BCID " << rob->rod_bc_id());
160  const uint32_t ndata = rob->rod_ndata();
161  const uint32_t* const data = rob->rod_data();
162 
163  // Initialise monitoring variables
164  Monitored::Scalar<uint32_t> monNumWords{"NumWordsInROD", ndata};
165  std::array<size_t,static_cast<size_t>(LVL1::MuCTPIBits::WordType::MAX)> wordTypeCounts{}; // zero-initialised
166  auto monWordTypeCount = Monitored::Collection("WordTypeCount", wordTypeCounts);
167  auto monWordType = Monitored::Collection("WordType", s_wordTypes);
168  std::vector<int> bcidOffsetsWrtROB; // diffs between BCID in timeslice header and BCID in ROB header
169  auto monBCIDOffsetsWrtROB = Monitored::Collection("BCIDOffsetsWrtROB", bcidOffsetsWrtROB);
170 
171  // Check for empty data
172  if (ndata==0) {
173  ATH_MSG_ERROR("Empty ROD data in MUCTPI ROB 0x" << std::hex << sid.code() << std::dec);
174  Monitored::Group(m_monTool, monNumWords);
175  return StatusCode::FAILURE;
176  }
177  ATH_MSG_DEBUG("Starting to decode " << ndata << " ROD words");
178 
179  // We don't assume the window size at this point. Instead, we collect the start and size of candidate list for
180  // each time slice and decode them later directly into the right time slice output container.
181  std::vector<std::pair<size_t,size_t>> roiSlices; // v of {start, length}
182  std::vector<std::pair<size_t,size_t>> topoSlices; // v of {start, length}
183 
184  // Iterate over ROD words and decode
185  size_t iWord{0};
186  for (const uint32_t word : std::span{data, ndata}) {
187  ATH_MSG_DEBUG("MUCTPI raw word " << iWord << ": 0x" << std::hex << word << std::dec);
189  ++wordTypeCounts[static_cast<size_t>(wordType)];
190 
191  switch (wordType) {
193  const auto header = LVL1::MuCTPIBits::timesliceHeader(word);
194  ATH_MSG_DEBUG("This is a timeslice header word with BCID=" << header.bcid
195  << ", NTOB=" << header.tobCount << ", NCAND=" << header.candCount);
196  // create new RoI words slice
197  roiSlices.emplace_back(0,0);
198  // create new Topo words slice
199  topoSlices.emplace_back(0,0);
200  // monitor BCID offset
201  bcidOffsetsWrtROB.push_back(bcidDiff(header.bcid, rob->rod_bc_id()));
202  break;
203  }
206  ATH_MSG_DEBUG("This is a multiplicity word #" << tmNum);
207  break;
208  }
210  ATH_MSG_DEBUG("This is a RoI candidate word");
211  if (roiSlices.empty()) {
212  ATH_MSG_ERROR("Unexpected data format - found candidate word before any timeslice header");
213  Monitored::Group(m_monTool, monNumWords, monWordType, monWordTypeCount, monBCIDOffsetsWrtROB);
214  return StatusCode::FAILURE;
215  }
216  // advance slice edges
217  std::pair<size_t,size_t>& slice = roiSlices.back();
218  if (slice.first==0) slice.first = iWord;
219  slice.second = iWord - slice.first + 1;
220  break;
221  }
223  ATH_MSG_DEBUG("This is a Topo TOB word");
224  if (not m_doTopo.value()) {break;}
225  if (topoSlices.empty()) {
226  ATH_MSG_ERROR("Unexpected data format - found Topo TOB word before any timeslice header");
227  return StatusCode::FAILURE;
228  }
229  // advance slice edges
230  std::pair<size_t,size_t>& slice = topoSlices.back();
231  if (slice.first==0) slice.first = iWord;
232  slice.second = iWord - slice.first + 1;
233  break;
234  }
236  ATH_MSG_DEBUG("This is a status word");
237  std::vector<size_t> errorBits = LVL1::MuCTPIBits::getDataStatusWordErrors(word);
238  // TODO: Decide on the action in case of errors, ATR-25069
239  if (!errorBits.empty()) {
240  ATH_MSG_DEBUG("MUCTPI ROD data flagged with errors. The data status word is 0x" << std::hex << word << std::dec);
241  for (size_t bit : errorBits) {
242  ATH_MSG_DEBUG("Error bit " << bit << ": " << LVL1::MuCTPIBits::DataStatusWordErrors.at(bit));
243  }
244  auto monErrorBits = Monitored::Collection("DataStatusWordErrors", errorBits);
245  Monitored::Group(m_monTool, monErrorBits);
246  }
247  break;
248  }
249  default: {
250  ATH_MSG_ERROR("The MUCTPI word 0x" << std::hex << word << std::dec << " does not match any known word type");
251  Monitored::Group(m_monTool, monNumWords, monWordType, monWordTypeCount, monBCIDOffsetsWrtROB);
252  return StatusCode::FAILURE;
253  }
254  }
255  ++iWord;
256  } // Loop over all ROD words
257 
258  // Fill data format monitoring histograms
259  Monitored::Group(m_monTool, monNumWords, monWordType, monWordTypeCount, monBCIDOffsetsWrtROB);
260 
261  // Validate the number of slices and decode the RoI candidate words in each time slice
262  const size_t nSlices{roiSlices.size()};
263  const size_t nOutputSlices{static_cast<size_t>(m_readoutWindow)};
264  if (nSlices > nOutputSlices) {
265  ATH_MSG_ERROR("Found " << nSlices << " time slices, but only " << m_readoutWindow << " outputs are configured");
266  return StatusCode::FAILURE;
267  } else if (nSlices != static_cast<size_t>(rob->rod_detev_type())) {
268  ATH_MSG_ERROR("Found " << nSlices << " time slices, but Detector Event Type word indicates there should be "
269  << rob->rod_detev_type());
270  return StatusCode::FAILURE;
271  } else if (nSlices!=1 && nSlices!=3 && nSlices!=5) {
272  ATH_MSG_ERROR("Expected 1, 3 or 5 time slices but found " << nSlices);
273  return StatusCode::FAILURE;
274  }
275  const size_t outputOffset = nOutputSlices/2 - nSlices/2;
276  ATH_CHECK(decodeRoiSlices(data, roiSlices, roiHandles, outputOffset, eventContext));
277 
278  // Validate the number of slices and decode the Topo TOB words in each time slice
279  if (m_doTopo.value()) {
280  const size_t nTopoSlices{topoSlices.size()};
281  const size_t nTopoOutputSlices{static_cast<size_t>(m_readoutWindow)};
282  if (nTopoSlices > nTopoOutputSlices) {
283  ATH_MSG_ERROR("Found " << nTopoSlices << " TOPO TOB time slices, but only " << m_readoutWindow << " outputs are configured");
284  return StatusCode::FAILURE;
285  } else if (nTopoSlices != static_cast<size_t>(rob->rod_detev_type())) {
286  ATH_MSG_ERROR("Found " << nTopoSlices << " time slices, but Detector Event Type word indicates there should be "
287  << rob->rod_detev_type());
288  return StatusCode::FAILURE;
289  } else if (nTopoSlices!=1 && nTopoSlices!=3 && nTopoSlices!=5) {
290  ATH_MSG_ERROR("Expected 1, 3 or 5 time slices but found " << nTopoSlices);
291  return StatusCode::FAILURE;
292  }
293  const size_t topoOutputOffset = nTopoOutputSlices/2 - nTopoSlices/2;
294  ATH_CHECK(decodeTopoSlices(data, topoSlices, topoHandles, topoOutputOffset, eventContext));
295  }
296 
297  // Output monitoring
298  short bcOffset{static_cast<short>(5/2 - m_readoutWindow/2 - 2)};
299  auto topoHandleIt = topoHandles.begin();
300  for (auto& roiHandle : roiHandles) {
301  auto& topoHandle = *topoHandleIt;
302  Monitored::Scalar<short> monBCOffset{"BCOffset", bcOffset};
303  Monitored::Scalar<size_t> monNumRoIs{"NumOutputRoIs", roiHandle->size()};
304  if (m_doTopo.value()) {
305  Monitored::Scalar<size_t> monNumTopo{"NumOutputTopoTOBs", topoHandle->size()};
306  Monitored::Scalar<int> monNumDiff{"NumOutputDiffRoITopo", static_cast<int>(monNumRoIs)-static_cast<int>(monNumTopo)};
307  ATH_MSG_DEBUG("Decoded " << monNumRoIs << " RoIs into the " << roiHandle.key() << " container "
308  "and " << monNumTopo << " Topo TOBs into the " << topoHandle.key() << " container");
309  Monitored::Group(m_monTool, monBCOffset, monNumRoIs, monNumTopo, monNumDiff);
310  ++topoHandleIt;
311  } else {
312  ATH_MSG_DEBUG("Decoded " << monNumRoIs << " RoIs into the " << roiHandle.key() << " container");
313  Monitored::Group(m_monTool, monBCOffset, monNumRoIs);
314  }
315  ++bcOffset;
316  }
317 
318  return StatusCode::SUCCESS;
319 }
320 
321 // -----------------------------------------------------------------------------
322 // xAOD->BS conversion
323 // -----------------------------------------------------------------------------
325  const EventContext& eventContext) {
326  // Retrieve the RoI containers and determine how many time slices will be encoded
327  std::vector<SG::ReadHandle<xAOD::MuonRoIContainer>> handles = m_roiReadKeys.makeHandles(eventContext);
328  int nSlices{0};
329  int iHandle{0};
330  size_t rodSize{0};
331  for (auto& handle : handles) {
332  ATH_CHECK(handle.isValid());
333  if (!handle->empty()) {
334  rodSize += handle->size();
335  nSlices = std::max(nSlices, 1+2*std::abs(m_readoutWindow/2 - iHandle));
336  }
337  ++iHandle;
338  }
339  if (nSlices==0) {
340  ATH_MSG_DEBUG("There are no muon RoIs to encode in this event");
341  // Force at least the triggered BC slice to be encoded
342  nSlices = 1;
343  }
344 
345  // Calculate the number of words we need to allocate (candidate words already counted above)
346  rodSize += 4*nSlices + 1; // 1 timeslice header per slice, 3 multiplicity words per slice, 1 status word
347  ATH_MSG_DEBUG("Going to encode " << nSlices << " time slices into " << rodSize << " ROD words");
348 
349  // Clear BS data cache and allocate new data array
350  clearCache(eventContext);
351  uint32_t* data = newRodData(eventContext, rodSize); // Owned by the cache
352 
353  // Initialise a few monitoring variables
354  Monitored::Scalar<size_t> monNumWords{"NumWordsInROD", rodSize};
355  std::array<size_t,static_cast<size_t>(LVL1::MuCTPIBits::WordType::MAX)> wordTypeCounts{}; // zero-initialised
356  auto monWordTypeCount = Monitored::Collection("WordTypeCount", wordTypeCounts);
357  auto monWordType = Monitored::Collection("WordType", s_wordTypes);
358  std::vector<int> bcidOffsetsWrtROB; // diffs between BCID in timeslice header and BCID in ROB header
359  auto monBCIDOffsetsWrtROB = Monitored::Collection("BCIDOffsetsWrtROB", bcidOffsetsWrtROB);
360  auto monitorCandidate = [](const auto& monTool, const xAOD::MuonRoI& roi){
361  // Fill per-candidate monitoring histograms
362  const uint32_t word = roi.roiWord();
363  using SubsysID_ut = std::underlying_type_t<LVL1::MuCTPIBits::SubsysID>;
365  Monitored::Scalar<SubsysID_ut> monSubsysID{"SubsysID", static_cast<SubsysID_ut>(subsysID)};
366  std::string sectorName{s_sectorNames[static_cast<size_t>(subsysID)]};
367  Monitored::Scalar<double> monEta{"roiEta_"+sectorName, roi.eta()};
368  Monitored::Scalar<double> monPhi{"roiPhi_"+sectorName, roi.phi()};
369  Monitored::Group(monTool, monSubsysID, monEta, monPhi);
370  };
371 
372  // Fill the data words
373  auto inputIt = handles.begin();
374  std::advance(inputIt, m_readoutWindow/2 - nSlices/2);
375  size_t iWord{0};
376  for (int iSlice=0; iSlice<nSlices; ++iSlice, ++inputIt) {
377  // Timeslice header
378  uint32_t bcid = bcidSum(eventContext.eventID().bunch_crossing_id(), iSlice - nSlices/2);
379  static constexpr uint32_t tobCount = 0; // Filling Topo words not implemented
380  uint32_t candCount = (*inputIt)->size();
381  data[iWord++] = LVL1::MuCTPIBits::timesliceHeader(bcid, tobCount, candCount);
382  ++wordTypeCounts[static_cast<size_t>(LVL1::MuCTPIBits::WordType::Timeslice)];
383  bcidOffsetsWrtROB.push_back(bcidDiff(bcid, eventContext.eventID().bunch_crossing_id()));
384  ATH_MSG_DEBUG("Added timeslice header word with BCID=" << bcid << ", NTOB=" << tobCount << ", NCAND=" << candCount);
385 
386  // Multiplicity words
387  std::array<uint32_t,3> multiplicityWords = LVL1::MuCTPIBits::multiplicityWords(0, 0, false); // Multiplicity words content not implemented
388  for (const uint32_t word : multiplicityWords) {
389  data[iWord++] = word;
390  }
391  wordTypeCounts[static_cast<size_t>(LVL1::MuCTPIBits::WordType::Multiplicity)] += multiplicityWords.size();
392  ATH_MSG_DEBUG("Added " << multiplicityWords.size() << " multiplicity words");
393 
394  // Candidate words
395  for (const xAOD::MuonRoI* roi : **inputIt) {
396  monitorCandidate(m_monTool, *roi);
397  data[iWord++] = roiWordRemoveOfflineRun3Flag(roi->roiWord());
398  ATH_MSG_DEBUG("Added RoI word 0x" << std::hex << roi->roiWord() << std::dec);
399  }
400  wordTypeCounts[static_cast<size_t>(LVL1::MuCTPIBits::WordType::Candidate)] += (*inputIt)->size();
401  ATH_MSG_DEBUG("Added " << (*inputIt)->size() << " candidate words");
402  }
403 
404  // Status word
406  ++wordTypeCounts[static_cast<size_t>(LVL1::MuCTPIBits::WordType::Status)];
407  ATH_MSG_DEBUG("Added the data status word");
408 
409  // Fill data format monitoring histograms
410  Monitored::Group(m_monTool, monNumWords, monWordType, monWordTypeCount, monBCIDOffsetsWrtROB);
411 
412  // Check that we filled all words
413  if (iWord!=rodSize) {
414  ATH_MSG_ERROR("Expected to fill " << rodSize << " ROD words but filled " << iWord);
415  return StatusCode::FAILURE;
416  }
417 
418  // Create a ROBFragment containing the ROD words
419  const eformat::helper::SourceIdentifier sid(m_robIds.value().at(0));
420  vrobf.push_back(newRobFragment(eventContext, sid.code(), rodSize, data, nSlices));
421 
422  return StatusCode::SUCCESS;
423 }
424 
425 // -----------------------------------------------------------------------------
426 // Helper for BS->xAOD conversion of RoI candidate words
427 // -----------------------------------------------------------------------------
429  const std::vector<std::pair<size_t,size_t>>& slices,
430  std::vector<SG::WriteHandle<xAOD::MuonRoIContainer>>& handles,
431  size_t outputOffset,
432  const EventContext& eventContext) const {
433  auto outputIt = handles.begin();
434  std::advance(outputIt, outputOffset);
435  for (const auto& [sliceStart,sliceSize] : slices) {
436  for (const uint32_t word : std::span{data+sliceStart, sliceSize}) {
437  ATH_MSG_DEBUG("Decoding RoI word 0x" << std::hex << word << std::dec << " into the " << outputIt->key() << " container");
438 
439  // Create a new xAOD::MuonRoI object for this candidate in the output container
440  (*outputIt)->push_back(std::make_unique<xAOD::MuonRoI>());
441 
442  // Decode eta/phi information using the right tool for the subsystem
445  switch (subsysID) {
446  case LVL1::MuCTPIBits::SubsysID::Endcap: // same for Endcap and Forward
448  ATH_MSG_DEBUG("This is an Endcap/Forward candidate, calling the " << m_tgcTool.typeAndName());
449  ATH_CHECK( m_tgcTool->roiData(word,roiData) );
450  break;
451  }
453  ATH_MSG_DEBUG("This is a Barrel candidate, calling the " << m_rpcTool.typeAndName());
454  ATH_CHECK( m_rpcTool->roiData(word,roiData) );
455  break;
456  }
457  default: {
458  ATH_MSG_ERROR("Failed to determine Sector ID from RoI word 0x" << std::hex << word << std::dec);
459  return StatusCode::FAILURE;
460  }
461  }
462 
463  // Get the threshold decisions to find the lowest pt threshold passed
464  // This is required by xAOD::MuonRoI::initialize() but not used for HLT seeding (a threshold pattern bit mask is used instead)
465  const std::pair<std::string, double> minThrInfo = m_thresholdTool->getMinThresholdNameAndValue(
466  m_thresholdTool->getThresholdDecisions(word, eventContext),
467  roiData.eta());
468 
469  // Fill the xAOD::MuonRoI object
470  (*outputIt)->back()->initialize(roiWordAddOfflineRun3Flag(word),
471  roiData.eta(),
472  roiData.phi(),
473  minThrInfo.first,
474  minThrInfo.second);
475 
476  // Fill per-candidate monitoring histograms
477  using SubsysID_ut = std::underlying_type_t<LVL1::MuCTPIBits::SubsysID>;
478  Monitored::Scalar<SubsysID_ut> monSubsysID{"SubsysID", static_cast<SubsysID_ut>(subsysID)};
479  std::string sectorName{s_sectorNames[static_cast<size_t>(subsysID)]};
480  Monitored::Scalar<double> monEta{"roiEta_"+sectorName, roiData.eta()};
481  Monitored::Scalar<double> monPhi{"roiPhi_"+sectorName, roiData.phi()};
482  Monitored::Group(m_monTool, monSubsysID, monEta, monPhi);
483  }
484  ++outputIt;
485  } // Loop over RoI candidate time slices
486 
487  return StatusCode::SUCCESS;
488 }
489 
490 // -----------------------------------------------------------------------------
491 // Helper for BS->transient conversion of Topo TOB words
492 // -----------------------------------------------------------------------------
494  const std::vector<std::pair<size_t,size_t>>& slices,
495  std::vector<SG::WriteHandle<xAOD::MuonRoIContainer>>& handles,
496  size_t outputOffset,
497  const EventContext& /*eventContext*/) const {
498  int toposliceiterator = -1;
499  int nomBCID_slice = slices.size() / 2 ;
500  int topobcidOffset = 0;
501  unsigned short subsystem = 0;
502 
503  float eta=0, phi=0;
504  unsigned int et=0;
505  //in case something is found to not be correctly decoded by the L1Topo group - can clean this extra-debug printouts later if wished
506  constexpr static bool local_topo_debug{true};
507 
508  const TrigConf::L1Menu * l1menu = nullptr;
510 
511  const auto & exMU = l1menu->thrExtraInfo().MU();
512  auto tgcPtValues = exMU.knownTgcPtValues();
513 
514  auto outputIt = handles.begin();
515  std::advance(outputIt, outputOffset);
516  // Loop over Topo candidate time slices
517  for (const auto& [sliceStart,sliceSize] : slices) {
518  toposliceiterator++;
519  for (const uint32_t word : std::span{data+sliceStart, sliceSize}) {
520  //the cand usage should be optimised!
521  std::stringstream sectorName;
522  subsystem = 0;
523 
524  topobcidOffset = toposliceiterator - nomBCID_slice;
525 
526  // Create a new xAOD::MuonRoI object for this candidate in the output container
527  (*outputIt)->push_back(std::make_unique<xAOD::MuonRoI>());
528 
529  ATH_MSG_DEBUG("MuCTPIL1Topo: Decoding Topo word 0x" << std::hex << word << std::dec << " into the " << outputIt->key() << " container");
530 
531  // NOTE the convention:
532  // HEMISPHERE 0: C-side (-) / 1: A-side (+)
533  // Det (bits): Barrel: 00 - EC: 1X - FW: 01
534  const auto topoheader = LVL1::MuCTPIBits::topoHeader(word);
535  if (local_topo_debug) {
536  ATH_MSG_DEBUG("MuCTPIL1Topo: TOPOSLICE data: " <<data << " sliceStart: "<<sliceStart<< " sliceSize: " <<sliceSize );
537  ATH_MSG_DEBUG("MuCTPIL1Topo word: 0x" << std::hex << word << std::dec );
538  ATH_MSG_DEBUG("MuCTPIL1Topo word: 0b" << std::bitset<32>(word) );
539  }
540  // Build the sector name
541  // topoheader.det is the direct WORD content, but here later in m_l1topoLUT.getCoordinates the definition is different ... see the subsystem settings below
542  if (topoheader.det == 0) {
543  sectorName<<"B";
544  subsystem = 0;
545  }
546  else if (topoheader.det == 1) {
547  sectorName<<"F";
548  subsystem = 2;
549  }
550  else if (topoheader.det == 2) {
551  sectorName<<"E";
552  subsystem = 1;
553  }
554  if (topoheader.hemi) sectorName << "A";
555  else sectorName<< "C";
556  sectorName << topoheader.sec;
557  // End of: Build the sector name
558 
559  if (local_topo_debug) {
560  ATH_MSG_DEBUG("MuCTPIL1Topo det: " << topoheader.det);
561  ATH_MSG_DEBUG("MuCTPIL1Topo hemi: " << topoheader.hemi );
562  ATH_MSG_DEBUG("MuCTPIL1Topo sector: " << sectorName.str() );
563  ATH_MSG_DEBUG("MuCTPIL1Topo etacode: " << topoheader.etacode );
564  ATH_MSG_DEBUG("MuCTPIL1Topo phicode: " << topoheader.phicode );
565  ATH_MSG_DEBUG("MuCTPIL1Topo sec: " << topoheader.sec );
566  ATH_MSG_DEBUG("MuCTPIL1Topo roi: " << topoheader.roi );
567  ATH_MSG_DEBUG("MuCTPIL1Topo pt: " << topoheader.pt );
568  }
569 
570  if (subsystem == 0) // Barrel
571  {
572  //for barrel topoheader.roi is always 0, so we need to reconstruct it...
573  unsigned short roi = m_l1topoLUT.getBarrelROI(topoheader.hemi, topoheader.sec, topoheader.barrel_eta_lookup, topoheader.barrel_phi_lookup);
574  LVL1MUCTPIPHASE1::L1TopoCoordinates coord = m_l1topoLUT.getCoordinates(topoheader.hemi ,subsystem ,topoheader.sec ,roi);
575  if (local_topo_debug) {
576  ATH_MSG_DEBUG("MuCTPIL1Topo: Barrel decoding");
577  ATH_MSG_DEBUG("MuCTPIL1Topo barrel_eta_lookup: " << topoheader.barrel_eta_lookup );
578  ATH_MSG_DEBUG("MuCTPIL1Topo barrel_phi_lookup: " << topoheader.barrel_phi_lookup );
579  ATH_MSG_DEBUG("MuCTPIL1Topo eta value: " << coord.eta);
580  ATH_MSG_DEBUG("MuCTPIL1Topo phi value: " << coord.phi);
581  }
582 
583  // Documentation / translation for the flag setting below
584  if (local_topo_debug) {
585  ATH_MSG_DEBUG("MuCTPIL1Topo phiOvl(0): " << topoheader.flag0);
586  ATH_MSG_DEBUG("MuCTPIL1Topo is2cand(1):" << topoheader.flag1);
587  }
588 
589  // Create RoI Word
590  uint32_t roiWord = 0;
591  roiWord |= (static_cast<uint32_t>(topoheader.pt/2) & RUN3_CAND_PT_MASK) << RUN3_CAND_PT_SHIFT;
592  roiWord |= (static_cast<uint32_t>(topoheader.flag1) & ROI_OVERFLOW_MASK) << RUN3_ROI_OVERFLOW_SHIFT;
593 
594  // Fill the xAOD::MuonRoI object
595  (*outputIt)->back()->initialize(roiWordAddOfflineRun3Flag(roiWord),
596  coord.eta,
597  coord.phi,
598  "",
599  tgcPtValues[topoheader.pt]);
600  et = topoheader.pt;
601  eta = coord.eta;
602  phi = coord.phi;
603  } // Barrel
604  else { // EC and FWD
605  LVL1MUCTPIPHASE1::L1TopoCoordinates coord = m_l1topoLUT.getCoordinates(topoheader.hemi ,subsystem ,topoheader.sec ,topoheader.roi);
606  if (local_topo_debug) {
607  ATH_MSG_DEBUG("MuCTPIL1Topo: EC / FWD decoding");
608  ATH_MSG_DEBUG("MuCTPIL1Topo coord.eta " << coord.eta);
609  ATH_MSG_DEBUG("MuCTPIL1Topo coord.phi " << coord.phi);
610  ATH_MSG_DEBUG("MuCTPIL1Topo coord.eta_min " << coord.eta_min);
611  ATH_MSG_DEBUG("MuCTPIL1Topo coord.eta_max " << coord.eta_max);
612  ATH_MSG_DEBUG("MuCTPIL1Topo coord.phi_min " << coord.phi_min);
613  ATH_MSG_DEBUG("MuCTPIL1Topo coord.phi_max " << coord.phi_max);
614  ATH_MSG_DEBUG("MuCTPIL1Topo coord.ieta " << coord.ieta);
615  ATH_MSG_DEBUG("MuCTPIL1Topo coord.iphi " << coord.iphi);
616  }
617 
618  // Documentation / translation for the flag setting below
619  if (local_topo_debug) {
620  ATH_MSG_DEBUG("MuCTPIL1Topo charge (0):" << topoheader.flag0);
621  ATH_MSG_DEBUG("MuCTPIL1Topo bw2or3 (1):" << topoheader.flag1);
622  ATH_MSG_DEBUG("MuCTPIL1Topo innerCoin (2):" << topoheader.flag2);
623  ATH_MSG_DEBUG("MuCTPIL1Topo goodMF (3):" << topoheader.flag3);
624  }
625  // Create RoI Word
626  uint32_t roiWord = 0;
627  roiWord |= (static_cast<uint32_t>(topoheader.pt) & RUN3_CAND_PT_MASK) << RUN3_CAND_PT_SHIFT;
628  // Only needed to tell this is TGC
629  roiWord |= (FORWARD_ADDRESS_MASK & CAND_SECTOR_ADDRESS_MASK) << RUN3_CAND_SECTOR_ADDRESS_SHIFT;
630  if (topoheader.flag0) {roiWord |= (0x1) << RUN3_CAND_TGC_CHARGE_SIGN_SHIFT;}
631  if (topoheader.flag1) {roiWord |= (0x1) << RUN3_CAND_TGC_BW2OR3_SHIFT;}
632  if (topoheader.flag2) {roiWord |= (0x1) << RUN3_CAND_TGC_INNERCOIN_SHIFT;}
633  if (topoheader.flag3) {roiWord |= (0x1) << RUN3_CAND_TGC_GOODMF_SHIFT;}
634 
635  // Fill the xAOD::MuonRoI object
636  (*outputIt)->back()->initialize(roiWordAddOfflineRun3Flag(roiWord),
637  coord.eta,
638  coord.phi,
639  "",
640  tgcPtValues[topoheader.pt]);
641 
642  et = topoheader.pt;
643  eta = coord.eta;
644  phi = coord.phi;
645  }// EC and FWD
646 
647  ATH_MSG_DEBUG("MuCTPIL1Topo: L1Topo output recorded to StoreGate with key " << outputIt->key() << " and bcidOffset: " << topobcidOffset);
648 
649  // Fill per-candidate monitoring histograms
650  using SubsysID_t = LVL1::MuCTPIBits::SubsysID;
651  using SubsysID_ut = std::underlying_type_t<SubsysID_t>;
652  SubsysID_t subsysID{SubsysID_t::Undefined};
653  switch (subsystem) {
654  case 0: {subsysID=SubsysID_t::Barrel; break;}
655  case 1: {subsysID=SubsysID_t::Endcap; break;} // Mind the swap in numbering E<->F, see comments above
656  case 2: {subsysID=SubsysID_t::Forward; break;}
657  default: {break;}
658  }
659  Monitored::Scalar<SubsysID_ut> monSubsysID{"topoSubsysID", static_cast<SubsysID_ut>(subsysID)};
660  std::string subsysName{s_sectorNames[static_cast<size_t>(subsysID)]};
661  Monitored::Scalar<float> monEta{"topoEta_"+subsysName, eta};
662  Monitored::Scalar<float> monPhi{"topoPhi_"+subsysName, phi};
663  Monitored::Scalar<unsigned int> monPtThr{"topoPtThreshold_"+subsysName, et};
664  Monitored::Group(m_monTool, monSubsysID, monEta, monPhi, monPtThr);
665  }
666  ++outputIt;
667  } // Loop over Topo candidate time slices
668 
669  return StatusCode::SUCCESS;
670 }
MuonRoIByteStreamTool::m_roiWriteKeys
SG::WriteHandleKeyArray< xAOD::MuonRoIContainer > m_roiWriteKeys
Definition: MuonRoIByteStreamTool.h:59
python.PyKernel.retrieve
def retrieve(aClass, aKey=None)
Definition: PyKernel.py:110
Monitored::Scalar::size
virtual size_t size() const override
gives size of vector representation
Definition: MonitoredScalar.h:128
MuonRoIByteStreamTool::m_side0LUTFile
const std::string m_side0LUTFile
Definition: MuonRoIByteStreamTool.h:78
LVL1::MuCTPIBits::WordType
WordType
Definition: HelpersPhase1.h:16
LVL1::TrigT1MuonRecRoiData
Definition: TrigT1MuonRecRoiData.h:10
data
char data[hepevt_bytes_allocation_ATLAS]
Definition: HepEvt.cxx:11
CxxUtils::span
span(T *ptr, std::size_t sz) -> span< T >
A couple needed deduction guides.
MuonRoIByteStreamTool::decodeRoiSlices
StatusCode decodeRoiSlices(const uint32_t *data, const std::vector< std::pair< size_t, size_t >> &slices, std::vector< SG::WriteHandle< xAOD::MuonRoIContainer >> &handles, size_t outputOffset, const EventContext &eventContext) const
Process raw RoI candidate words in all slices, convert and fill the output EDM.
Definition: MuonRoIByteStreamTool.cxx:428
et
Extra patterns decribing particle interation process.
MuonRoIByteStreamTool::m_roiReadKeys
SG::ReadHandleKeyArray< xAOD::MuonRoIContainer > m_roiReadKeys
Definition: MuonRoIByteStreamTool.h:63
LVL1::MuCTPIBits::WordType::Candidate
@ Candidate
TrigDefs::Group
Group
Properties of a chain group.
Definition: GroupProperties.h:13
Undefined
@ Undefined
Definition: MaterialTypes.h:8
LVL1MUCTPIPHASE1::L1TopoLUT::getBarrelROI
unsigned short getBarrelROI(unsigned short side, unsigned short sector, unsigned short ieta, unsigned short iphi) const
Definition: L1TopoLUT.cxx:232
LVL1::MuCTPIBits::getDataStatusWordErrors
std::vector< size_t > getDataStatusWordErrors(uint32_t word)
Decode the data status word (returns a vector of bit indices for the errors set - empty if no errors)
Definition: HelpersPhase1.h:186
LVL1::MuCTPIBits::getWordType
constexpr WordType getWordType(uint32_t word)
Determine the type of a MUCTPI ROD word.
Definition: HelpersPhase1.h:64
header
Definition: hcg.cxx:526
DetType::Endcap
@ Endcap
Definition: DetType.h:14
max
#define max(a, b)
Definition: cfImp.cxx:41
phi
Scalar phi() const
phi method
Definition: AmgMatrixBasePlugin.h:67
MuonRoIByteStreamTool::m_rpcTool
ToolHandle< LVL1::ITrigT1MuonRecRoiTool > m_rpcTool
Definition: MuonRoIByteStreamTool.h:45
xAOD::uint32_t
setEventNumber uint32_t
Definition: EventInfo_v1.cxx:127
eta
Scalar eta() const
pseudorapidity method
Definition: AmgMatrixBasePlugin.h:83
MuonRoIAuxContainer.h
skel.it
it
Definition: skel.GENtoEVGEN.py:396
MuonRoIByteStreamTool::m_side1LUTFile
const std::string m_side1LUTFile
Definition: MuonRoIByteStreamTool.h:79
mc.diff
diff
Definition: mc.SFGenPy8_MuMu_DD.py:14
MuonRoIByteStreamTool::convertToBS
virtual StatusCode convertToBS(std::vector< OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment * > &vrobf, const EventContext &eventContext) override
xAOD->BS conversion
Definition: MuonRoIByteStreamTool.cxx:324
LVL1::TrigT1MuonRecRoiData::eta
double eta(void) const
Definition: TrigT1MuonRecRoiData.h:22
MuonRoIByteStreamTool::decodeTopoSlices
StatusCode decodeTopoSlices(const uint32_t *data, const std::vector< std::pair< size_t, size_t >> &slices, std::vector< SG::WriteHandle< xAOD::MuonRoIContainer >> &handles, size_t outputOffset, const EventContext &eventContext) const
Process raw Topo TOB words in all slices, convert and fill the output EDM.
Definition: MuonRoIByteStreamTool.cxx:493
TrigConf::L1Menu
L1 menu configuration.
Definition: L1Menu.h:28
PixelByteStreamErrors::Decoding
@ Decoding
Definition: PixelByteStreamErrors.h:14
LVL1MUCTPIPHASE1::L1TopoCoordinates
Definition: L1TopoLUT.h:23
LVL1::MuCTPIBits::WordType::Topo
@ Topo
MuonRoIByteStreamTool::m_MuCTPIL1TopoKeys
SG::WriteHandleKeyArray< xAOD::MuonRoIContainer > m_MuCTPIL1TopoKeys
Definition: MuonRoIByteStreamTool.h:66
DetType::Barrel
@ Barrel
Definition: DetType.h:14
Monitored::Collection
ValuesCollection< T > Collection(std::string name, const T &collection)
Declare a monitored (double-convertible) collection.
Definition: MonitoredCollection.h:38
LVL1::MuCTPIBits::SubsysID::Endcap
@ Endcap
MuonRoIByteStreamTool::m_tgcTool
ToolHandle< LVL1::ITrigT1MuonRecRoiTool > m_tgcTool
Definition: MuonRoIByteStreamTool.h:48
LVL1::MuCTPIBits::multiplicityWordNumber
constexpr uint32_t multiplicityWordNumber(uint32_t word)
Decode the index of the multitpicity word, which is 1, 2, or 3.
Definition: HelpersPhase1.h:154
ROBF
OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment ROBF
Definition: ByteStreamMergeOutputSvc.cxx:16
xAOD::roiWord
roiWord
Definition: TrigMissingET_v1.cxx:36
MuonRoIByteStreamTool::m_l1topoLUT
LVL1MUCTPIPHASE1::L1TopoLUT m_l1topoLUT
Muctpi Topo TOB word lookup table interface: hemi, detector, sector, roi(EC&FWD)/lut-numbers(Barrel) ...
Definition: MuonRoIByteStreamTool.h:85
MuonRoIByteStreamTool::m_monTool
ToolHandle< GenericMonitoringTool > m_monTool
Definition: MuonRoIByteStreamTool.h:54
MuonRoIByteStreamTool::m_barrelRoIFile
const std::string m_barrelRoIFile
Definition: MuonRoIByteStreamTool.h:76
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
convertTimingResiduals.sum
sum
Definition: convertTimingResiduals.py:55
perfmonmt-refit.slice
slice
Definition: perfmonmt-refit.py:52
LVL1MUCTPIPHASE1::L1TopoLUT::getCoordinates
L1TopoCoordinates getCoordinates(const unsigned short &side, const unsigned short &subsystem, const unsigned short &sectorID, const unsigned short &roi) const
Definition: L1TopoLUT.cxx:218
LVL1::MuCTPIBits::SubsysID::Forward
@ Forward
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
LVL1::MuCTPIBits::topoHeader
constexpr auto topoHeader(uint32_t word)
Decode topo word :
Definition: HelpersPhase1.h:105
MuonRoIByteStreamTool::m_thresholdTool
ToolHandle< LVL1::ITrigThresholdDecisionTool > m_thresholdTool
Definition: MuonRoIByteStreamTool.h:51
MuonRoIByteStreamTool::m_ecfRoIFile
const std::string m_ecfRoIFile
Definition: MuonRoIByteStreamTool.h:77
xAOD::MuonRoI_v1
Class describing a LVL1 muon region of interest.
Definition: MuonRoI_v1.h:29
MuonRoIByteStreamTool::convertFromBS
virtual StatusCode convertFromBS(const std::vector< const OFFLINE_FRAGMENTS_NAMESPACE::ROBFragment * > &vrobf, const EventContext &eventContext) const override
BS->xAOD conversion.
Definition: MuonRoIByteStreamTool.cxx:127
test_pyathena.parent
parent
Definition: test_pyathena.py:15
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
Preparation.mode
mode
Definition: Preparation.py:94
CHECK
#define CHECK(...)
Evaluate an expression and check for errors.
Definition: Control/AthenaKernel/AthenaKernel/errorcheck.h:422
LVL1::MuCTPIBits::multiplicityWords
constexpr std::array< uint32_t, 3 > multiplicityWords(uint64_t multiplicity, uint32_t triggerBits, bool overflow)
Encode the multiplicity words.
Definition: HelpersPhase1.h:159
LVL1::MuCTPIBits::dataStatusWord
constexpr uint32_t dataStatusWord(uint16_t status)
Encode the data status word.
Definition: HelpersPhase1.h:199
LVL1::TrigT1MuonRecRoiData::phi
double phi(void) const
Definition: TrigT1MuonRecRoiData.h:23
MuonRoIByteStreamTool::m_readoutWindow
short int m_readoutWindow
Expected readout window size calculated from the size of data handle key arrays, should be 1,...
Definition: MuonRoIByteStreamTool.h:83
LVL1::MuCTPIBits::timesliceHeader
constexpr auto timesliceHeader(uint32_t word)
Decode timeslice word.
Definition: HelpersPhase1.h:81
LVL1::MuCTPIBits::getSubsysID
constexpr SubsysID getSubsysID(uint32_t word)
Decode the subsys ID from RoI candidate word.
Definition: HelpersPhase1.h:174
python.PyKernel.detStore
detStore
Definition: PyKernel.py:41
PathResolver.h
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:221
MuCTPI_Bits.h
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
MuonRoIByteStreamTool::m_robIds
Gaudi::Property< std::vector< uint32_t > > m_robIds
Definition: MuonRoIByteStreamTool.h:71
xAOD::bcid
setEventNumber setTimeStamp bcid
Definition: EventInfo_v1.cxx:133
LVL1::MuCTPIBits::WordType::Multiplicity
@ Multiplicity
OFFLINE_FRAGMENTS_NAMESPACE::ROBFragment
eformat::ROBFragment< PointerType > ROBFragment
Definition: RawEvent.h:27
MuonRoIByteStreamTool::MuonRoIByteStreamTool
MuonRoIByteStreamTool(const std::string &type, const std::string &name, const IInterface *parent)
Definition: MuonRoIByteStreamTool.cxx:67
JetVoronoiDiagramHelpers::coord
double coord
Definition: JetVoronoiDiagramHelpers.h:45
LVL1::MuCTPIBits::WordType::Status
@ Status
PathResolverFindCalibFile
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
Definition: PathResolver.cxx:431
LVL1::MuCTPIBits::WordType::MAX
@ MAX
SG::WriteHandle
Definition: StoreGate/StoreGate/WriteHandle.h:76
a
TList * a
Definition: liststreamerinfos.cxx:10
LVL1::MuCTPIBits::SubsysID
SubsysID
Definition: HelpersPhase1.h:17
OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment
eformat::write::ROBFragment ROBFragment
Definition: RawEvent.h:33
MuonRoIByteStreamTool::m_doTopo
Gaudi::Property< bool > m_doTopo
Definition: MuonRoIByteStreamTool.h:73
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
WROBF
OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment WROBF
Definition: eFexByteStreamTool.cxx:27
MuonRoI.h
LArCellConditions.sv
bool sv
Definition: LArCellConditions.py:45
LVL1MUCTPIPHASE1::L1TopoLUT::initializeLUT
bool initializeLUT(const std::string &barrelFileName, const std::string &ecfFileName, const std::string &side0LUTFileName, const std::string &side1LUTFileName)
Definition: L1TopoLUT.cxx:101
python.XMLReader.l1menu
l1menu
Definition: XMLReader.py:73
LVL1::MuCTPIBits
Definition: HelpersPhase1.h:14
Monitored::Scalar
Declare a monitored scalar variable.
Definition: MonitoredScalar.h:34
LVL1::MuCTPIBits::WordType::Timeslice
@ Timeslice
L1Menu.h
HelpersPhase1.h
L1TopoSimulationConfig.subsystem
subsystem
Definition: L1TopoSimulationConfig.py:263
LVL1::MuCTPIBits::SubsysID::Barrel
@ Barrel
MuonRoIByteStreamTool.h
python.AutoConfigFlags.msg
msg
Definition: AutoConfigFlags.py:7
keylayer_zslicemap.slices
slices
Definition: keylayer_zslicemap.py:112
MuonRoIByteStreamTool::initialize
virtual StatusCode initialize() override
Definition: MuonRoIByteStreamTool.cxx:75