ATLAS Offline Software
Loading...
Searching...
No Matches
BunchCrossingCondAlg.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 "CoolKernel/IObject.h"
8#include "CoralBase/Blob.h"
13#include <nlohmann/json.hpp>
14#include <charconv>
15#include <cstdint>
16
17#include "CoralBase/AttributeListException.h"
18
19
21 if (m_mode == 2) {
22 ATH_CHECK( m_trigConfigSvc.retrieve() );
23 }
24 ATH_CHECK( m_bunchGroupCondDataKey.initialize( m_mode == 2 && !m_bunchGroupCondDataKey.empty() ) );
25 // For MC mode, only initialize fill params key if it's not empty (will be empty for BS input)
26 ATH_CHECK( m_fillParamsFolderKey.initialize( (m_mode == 0 || m_mode == 1) && !m_fillParamsFolderKey.empty() ) );
27 // ByteStream metadata is only used in MC mode (mode==1), and may or may not be present (e.g., in tests)
28 if (m_mode == 1) {
30 }
31 ATH_CHECK( m_lumiCondDataKey.initialize( m_mode == 3 ) );
32 ATH_CHECK( m_outputKey.initialize() );
33 return StatusCode::SUCCESS;
34}
35
36
37StatusCode BunchCrossingCondAlg::execute (const EventContext& ctx) const {
38
40 if (writeHdl.isValid()) {
41 ATH_MSG_DEBUG("Found valid write handle");
42 return StatusCode::SUCCESS;
43 }
44 // make sure that the output IOV has a valid timestamp, otherwise the conditions
45 // data cannot be added to the "mixed" conditions data container. A mixed container
46 // is needed when the conditions depends on e.g. the LuminosityCondData
48
49 //Output object & range:
50 auto bccd=std::make_unique<BunchCrossingCondData>();
51
52 if (m_mode == 2) { // use trigger bunch groups
53 const TrigConf::L1BunchGroupSet* bgs{nullptr};
54 if (!m_bunchGroupCondDataKey.empty()) {
56 if (! bgsh.isValid()) {
57 ATH_MSG_ERROR("Unable to retrieve L1BunchGroupSet object " << m_bunchGroupCondDataKey);
58 return StatusCode::FAILURE;
59 }
60 bgs = *bgsh;
61 } else {
62 bgs = &(m_trigConfigSvc->l1BunchGroupSet(ctx));
63 }
64 // bunch group 1 = paired
65 if (bgs->size() >= 2) {
66 ATH_MSG_DEBUG("from BunchGroupCondData: BG1 bunches " << bgs->getBunchGroup(1)->bunches() );
67 for (const auto& pos : bgs->getBunchGroup(1)->bunches() ) {
68 bccd->m_beam1.set(pos);
69 bccd->m_beam2.set(pos);
70 bccd->m_luminous.set(pos);
71 }
72 }
73 else {
74 ATH_MSG_ERROR("missing bunch group data");
75 }
76 // in Run 1 we don't have bunch group information to determine beam 1 or beam 2 unpaired
77 // so test if we have at least 15 bunch groups, then assume BG13/14 are the unpaired bunches
78 if (bgs->size() >= 15) {
79 for (const auto& pos : bgs->getBunchGroup(13)->bunches() ) {
80 bccd->m_beam1.set(pos);
81 }
82 for (const auto& pos : bgs->getBunchGroup(14)->bunches() ) {
83 bccd->m_beam2.set(pos);
84 }
85 }
86 // find trains
87 bccd->m_trains=findTrains(bccd->m_luminous, m_maxBunchSpacing,m_minBunchesPerTrain);
88 // we will only trust the validity for 1 LB but that's OK
89 const auto& thisevt = ctx.eventID();
90 EventIDRange range = EventIDRange(EventIDBase(thisevt.run_number(), EventIDBase::UNDEFEVT,
91 EventIDBase::UNDEFNUM, 0, thisevt.lumi_block()),
92 EventIDBase(thisevt.run_number(), EventIDBase::UNDEFEVT,
93 EventIDBase::UNDEFNUM, 0, thisevt.lumi_block()+1));
94 writeHdl.addDependency(range);
95 }
96
97 if (m_mode == 3) { // use luminosity data
99 writeHdl.addDependency(prefLumiHdl);
100
101 // consider BCID filled if mu < 1000*average mu
102 float avMu = prefLumiHdl->lbAverageInteractionsPerCrossing();
103 const auto& lumiVec = prefLumiHdl->lbLuminosityPerBCIDVector();
104 float cutLumi = avMu/1000.f*prefLumiHdl->muToLumi();
105
106 for (size_t bcid = 0; bcid < LuminosityCondData::TOTAL_LHC_BCIDS; ++bcid ) {
107 if (lumiVec[bcid] > cutLumi) {
108 bccd->m_beam1.set(bcid);
109 bccd->m_beam2.set(bcid);
110 bccd->m_luminous.set(bcid);
111 }
112 }
113 // find trains
114 bccd->m_trains=findTrains(bccd->m_luminous, m_maxBunchSpacing,m_minBunchesPerTrain);
115 }
116
117 if (m_mode == 0 || m_mode == 1) { // use FILLPARAMS (data) or /Digitization/Parameters (MC)
118
119 std::string sbunches;
120 const AthenaAttributeList* attrList = nullptr;
121
122 if (m_mode == 1) {
123 // MC case: try digitization folder first, then ByteStream metadata
124 ATH_MSG_INFO("Assuming MC case");
125 bool foundInDigitization = false;
126
127 if (!m_fillParamsFolderKey.empty()) {
129 writeHdl.addDependency(fillParamsHdl);
130 const AthenaAttributeList* attrList=*fillParamsHdl;
131
132 if (attrList) {
133 ATH_MSG_INFO("Got AttributeList with size " << attrList->size());
134 try {
135 const coral::Attribute& attr=(*attrList)[std::string("BeamIntensityPattern")];
136 if (!attr.isNull()) {
137 sbunches = attr.data< std::string >();
138 foundInDigitization = true;
139 ATH_MSG_DEBUG("Read BeamIntensityPattern from Digitization folder");
140 }
141 } catch (coral::AttributeListException& e) {
142 ATH_MSG_DEBUG("Could not read from Digitization folder: " << e.what());
143 }
144 }
145 }
146
147 // Fall back to ByteStream metadata if not found in digitization folder
148 if (!foundInDigitization) {
150
151 if (bsMetadata.isValid() && !bsMetadata->empty()) {
152 const ByteStreamMetadata* metadata = bsMetadata->at(0);
153 const std::vector<std::string>& freeStrings = metadata->getFreeMetaDataStrings();
154
155 for (const std::string& str : freeStrings) {
156 if (str.starts_with("IOVMeta./Digitization/Parameters=")) {
157 size_t eqPos = str.find('=');
158 if (eqPos != std::string::npos && eqPos + 1 < str.size()) {
159 std::string jsonStr = str.substr(eqPos + 1);
160
161 try {
162 nlohmann::json iovMetadata = nlohmann::json::parse(jsonStr);
163
164 if (iovMetadata.contains("iovs") && iovMetadata["iovs"].is_array() && !iovMetadata["iovs"].empty()) {
165 const auto& firstIov = iovMetadata["iovs"][0];
166 if (firstIov.contains("attrs")) {
167 for (const auto& chanItem : firstIov["attrs"].items()) {
168 const auto& chanAttrs = chanItem.value();
169 if (chanAttrs.contains("BeamIntensityPattern")) {
170 sbunches = chanAttrs["BeamIntensityPattern"].get<std::string>();
171 ATH_MSG_INFO("Read BeamIntensityPattern from ByteStream metadata");
172 break;
173 }
174 }
175 if (!sbunches.empty()) break;
176 }
177 }
178 } catch (const std::exception& e) {
179 ATH_MSG_WARNING("Failed to parse IOV metadata from ByteStream: " << e.what());
180 }
181 }
182 }
183 }
184 }
185
186 if (sbunches.empty()) {
187 ATH_MSG_ERROR("Could not read BeamIntensityPattern from either Digitization folder or ByteStream metadata");
188 return StatusCode::FAILURE;
189 }
190 }
191
192 const float minBunchIntensity=0.001;
193 std::vector<float> bunches=tokenize(sbunches);
194 if (!bunches.empty()) {
195 // Check if the pattern "fits into" the LHC:
196 if( BunchCrossingCondData::m_MAX_BCID % bunches.size() ) {
197
198 ATH_MSG_INFO( "Bunch pattern doesn't \"fit into\" " << BunchCrossingCondData::m_MAX_BCID );
199 // The loop doesn't go all the way up to MAX_BCID/2 in order not to produce "weird"
200 // patterns half way. This should be pretty safe to do, because the MC BCIDs will
201 // only be in the range defined by the pattern from the metadata.
202 for( int i = 0; i < ( BunchCrossingCondData::m_MAX_BCID / 2 - 20 ); ++i ) {
203 const int pos1 = i % bunches.size();
204 const int pos2 = bunches.size() - 1 - ( i % bunches.size() );
205 if( bunches[ pos1 ] > minBunchIntensity) {
206 bccd->m_beam1.set(i);
207 bccd->m_beam2.set(i);
208 bccd->m_luminous.set(i);
209 }
210 if( bunches[ pos2 ] > minBunchIntensity) {
211 bccd->m_beam1.set(BunchCrossingCondData::m_MAX_BCID - 1 -i);
212 bccd->m_beam2.set(BunchCrossingCondData::m_MAX_BCID - 1 -i);
213 bccd->m_luminous.set(BunchCrossingCondData::m_MAX_BCID - 1 -i);
214 }
215 }
216
217 } else {
218 // If the sample size fits into the number of available bunches, the algorithm
219 // is pretty simple:
220 ATH_MSG_INFO( "Bunch pattern \"fits into\" " << BunchCrossingCondData::m_MAX_BCID );
221 for( int i = 0; i < BunchCrossingCondData::m_MAX_BCID; ++i ) {
222 const int pos = i % bunches.size();
223 if( bunches[ pos ] > minBunchIntensity) {
224 bccd->m_beam1.set(i);
225 bccd->m_beam2.set(i);
226 bccd->m_luminous.set(i);
227 }
228 }
229 }
230 //Filled bcid-bitsets, now extract trains:
231 bccd->m_trains=findTrains(bccd->m_luminous, m_maxBunchSpacing,m_minBunchesPerTrain);
232 }
233 else {//got no bunches from metadata
234 ATH_MSG_INFO("Bunch structure information not found in metadata");
235 ATH_MSG_INFO("Will consider all BCIDs as single filled bunches (no trains)");
236 bccd->m_beam1.set();
237 bccd->m_beam2.set();
238 bccd->m_luminous.set();
239 }
240 }
241 else { // mode == 0, Data-case
243 writeHdl.addDependency(fillParamsHdl);
244 attrList = *fillParamsHdl;
245
246 if ((*attrList)["BCIDmasks"].isNull()) {
247 ATH_MSG_ERROR( "BunchCode is NULL in " << m_fillParamsFolderKey.key() << "!" );
248 return StatusCode::FAILURE;
249 }
250
251
252 // Do everything here for now, but should copy this to a vanilla object based on attrList
253 cool::UInt32 nb1 = (*attrList)["Beam1Bunches"].data<cool::UInt32>();
254 cool::UInt32 nb2 = (*attrList)["Beam2Bunches"].data<cool::UInt32>();
255 cool::UInt32 ncol = (*attrList)["LuminousBunches"].data<cool::UInt32>();
256
257 ATH_MSG_DEBUG( "Beam1Bunches: " << nb1 );
258 ATH_MSG_DEBUG( "Beam2Bunches: " << nb2 );
259 ATH_MSG_DEBUG( "LuminousBunches: " << ncol );
260
261 const coral::Blob& blob = (*attrList)["BCIDmasks"].data<coral::Blob>();
262
263 if (m_isRun1) { //Assume run1 layout as explained at https://twiki.cern.ch/twiki/bin/view/AtlasComputing/CoolOnlineData
264 ATH_MSG_DEBUG("Assuming run 1 database");
265 // Verify length
266 if ( static_cast<cool::UInt32>( blob.size() ) != 2 * (nb1 + nb2 + ncol)) {
267 ATH_MSG_WARNING( "BCIDmasks length " << blob.size() << " != 2 * " << (nb1+nb2+ncol) );
268 return StatusCode::SUCCESS;
269 }
270 const uint16_t* blobAddr=static_cast<const uint16_t*>(blob.startingAddress());
271 //Decode beam1 filling:
272 for (size_t idx=0;idx<nb1;++idx) {
273 const uint32_t bcid=blobAddr[idx];
274 bccd->m_beam1.set(bcid);
275 }
276
277 //Decode beam2 filling:
278 for (size_t idx=nb1;idx<nb2;++idx) {
279 const uint32_t bcid=blobAddr[idx];
280 bccd->m_beam2.set(bcid);
281 }
282
283 //Decode colliding:
284 for (size_t idx=nb2;idx<ncol;++idx) {
285 const uint32_t bcid=blobAddr[idx];
286 bccd->m_luminous.set(bcid);
287 }
288 }
289 else {
290 ATH_MSG_DEBUG("Assuming run 2/3 database");
291 //Assume run2 layout as explained at https://twiki.cern.ch/twiki/bin/view/AtlasComputing/CoolOnlineData
292 //Verify length
293 if (blob.size()!=BunchCrossingCondData::m_MAX_BCID) {
294 ATH_MSG_ERROR("Found blob with unexpected length " << blob.size() << "(expect " << BunchCrossingCondData::m_MAX_BCID <<") in folder " << m_fillParamsFolderKey.key());
295 return StatusCode::FAILURE;
296 }
297 const uint8_t* blobAddr=static_cast<const uint8_t*>(blob.startingAddress());
298 for (uint32_t bcid=0;bcid<BunchCrossingCondData::m_MAX_BCID;++bcid) {
299 if (blobAddr[bcid] & 0x1) {
300 bccd->m_beam1.set(bcid);
301 }
302 if (blobAddr[bcid] & 0x2) {
303 bccd->m_beam2.set(bcid);
304 }
305 if ((blobAddr[bcid] & 0x3) == 0x3) {
306 bccd->m_luminous.set(bcid);
307 }
308 }
309
310 //Consistency checks:
311 if (bccd->m_beam1.count()!= nb1) {
312 ATH_MSG_WARNING("Found " << bccd->m_beam1.count() << " bunches in beam1, expected " << nb1);
313 }
314
315 if (bccd->m_beam2.count()!= nb2) {
316 ATH_MSG_WARNING("Found " << bccd->m_beam2.count() << " bunches in beam2, expected " << nb2);
317 }
318
319 if (bccd->m_luminous.count()!= ncol) {
320 ATH_MSG_WARNING("Found " << bccd->m_luminous.count() << " colliding bunches, expected " << ncol);
321 }
322 }//end else run2
323 //Filled bcid-bitsets, now extract trains
324 bccd->m_trains=findTrains(bccd->m_luminous, m_maxBunchSpacing,m_minBunchesPerTrain);
325 }//end else is data
326 }
327
328 ATH_CHECK( writeHdl.record (std::move (bccd)) );
329 return StatusCode::SUCCESS;
330}
331
332
333std::vector<BunchCrossingCondData::bunchTrain_t> BunchCrossingCondAlg::findTrains(const std::bitset< BunchCrossingCondData::m_MAX_BCID>& collidingBCIDs, const int maxSpacingInTrain, const unsigned minBunchesPerTrain) const {
334
335 const int MAX_BCID=collidingBCIDs.size();
336 std::vector<bunchTrain_t> result;
337
338 std::vector<std::pair<int,int> > holes;
339 int start=0;
340 int stop=0;
341
342 while (stop<MAX_BCID) { //Outer loop, over holes
343 for (start=stop;stop<MAX_BCID && !collidingBCIDs.test(stop); ++stop) {};//Count consecutive holes
344 //stop points now to the next filled bcid (or MAX)
345 if ((stop-start)>maxSpacingInTrain) {
346 holes.emplace_back(start,stop);
347 }//end if >maxSpacingInTrain
348 ++stop;
349 }//end outer loop
350
351
352 ATH_MSG_DEBUG("Found " << holes.size() << " gaps larger than " << maxSpacingInTrain << " in the bunch structure");
353 if (msgLvl(MSG::VERBOSE)) {
354 for (auto& h : holes) {
355 msg(MSG::VERBOSE) << "Hole: " << h.first << " - " << h.second << endmsg;
356 }
357 }
358
359
360 if (holes.empty()) {
361 ATH_MSG_ERROR("Looks like we have bunch train spanning the entire ring w/o any gap. Really?");
362 return result;
363 }
364
365
366 if (holes.size()>1) {
367 //generic case of having at least 2 gaps int the bunch structure
368 for (unsigned i=0;i<holes.size()-1;++i) {
369 //Count colliding bunches in this train:
370 unsigned ncoll=0;
371 for (int idx=holes[i].second;idx<holes[i+1].first-1;++idx) {
372 if (collidingBCIDs.test(idx)) {
373 ++ncoll;
374 }
375 }
376 result.emplace_back(holes[i].second,holes[i+1].first-1,ncoll);
377 }
378 }
379
380 if (holes.size()==1 || (holes.front().first!=0 && holes.back().second!=MAX_BCID-1)) {
381 //Special case of only one hole and/or first/last BCIDs populated (train across the wrap-around)
382
383 //To simplify the distanceToFront and distanceToTail methods we duplicate this train
384 //Once as first train wiht a starting point in the negative range, and
385 //once as last train with a ending point beyond MAX_BCID
386
387 //Count the number of collisions in this train:
388 unsigned ncoll=0;
389 for (int idx=0;idx<holes.front().first;++idx) {
390 if (collidingBCIDs.test(idx)) {
391 ++ncoll;
392 }
393 }
394 for (int idx=holes.back().second;idx<MAX_BCID;++idx) {
395 if (collidingBCIDs.test(idx)) {
396 ++ncoll;
397 }
398 }
399
400 BunchCrossingCondData::bunchTrain_t firsttrain(holes.back().second-MAX_BCID, holes.front().first-1,ncoll);
401 BunchCrossingCondData::bunchTrain_t lasttrain(holes.back().second,MAX_BCID+holes.front().first-1,ncoll);
402 result.insert(result.begin(),firsttrain);
403 result.push_back(lasttrain);
404 }//end if wrap-around populated
405
406
407 ATH_MSG_DEBUG("Found " << result.size() << " Bunch trains separated by gaps of at least " << maxSpacingInTrain << " bcids ");
408
409 //copy the vector, cleaning out trains with too few colliding bunches
410 std::vector<bunchTrain_t> result1;
411 result1.reserve(result.size());
412 for (const bunchTrain_t& train: result) {
413 if (train.m_nColl >= minBunchesPerTrain) {
414 result1.emplace_back(train);
415 }
416 }
417
418 ATH_MSG_INFO("Found " << result1.size() << " Bunch trains having at least " << minBunchesPerTrain << " colliding bunches and separated by at least " << maxSpacingInTrain << " bcids");
419
420
421 if (msgLvl(MSG::VERBOSE)) {
422 for (auto& r : result1) {
423 msg(MSG::VERBOSE) << "Train " << r.m_first << " - " << r.m_last << ", " << r.m_nColl << " colliding bcids" << endmsg;
424 }
425 }
426
427 return result1;
428}
429
430
439std::vector<float> BunchCrossingCondAlg::tokenize( const std::string& pattern ) const {
440 ATH_MSG_VERBOSE("Input to tokenize: " << pattern);
441
442 std::vector< float > result;
443 const char* c= pattern.data();
444 const char* cEnd= c + pattern.size();
445 while(c<cEnd) {
446 //This loop swallows actually any string containing numbers
447 //separated by non-numbers
448 char* end;
449 float f=std::strtof(c,&end);
450 if (c==end) {//Can't convert, try next
451 c++;
452 }
453 else { //got a value
454 result.push_back(f);
455 c=end;
456 }
457 }//end while loop
458 return result;
459}
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Conditions algorithm to fill BunchCrossingCondData.
This file contains the class definition for the ByteStreamMetadata class.
Handle class for reading from StoreGate.
bool msgLvl(const MSG::Level lvl) const
Header file for AthHistogramAlgorithm.
An AttributeList represents a logical row of attributes in a metadata table.
SG::ReadHandleKey< ByteStreamMetadataContainer > m_byteStreamMetadataKey
ByteStream metadata (for reading IOV metadata from BS files in MC mode)
std::vector< float > tokenize(const std::string &pattern) const
This helper function is used to convert a string of type "[0.0, 0.0, 1.0, 1.0, 1.0]" into a vector of...
Gaudi::Property< bool > m_isRun1
Gaudi::Property< int > m_mode
SG::WriteCondHandleKey< BunchCrossingCondData > m_outputKey
Output conditions object.
virtual StatusCode initialize() override
Gaudi initialize method.
Gaudi::Property< unsigned > m_maxBunchSpacing
Gaudi::Property< unsigned > m_minBunchesPerTrain
std::vector< bunchTrain_t > findTrains(const std::bitset< BunchCrossingCondData::m_MAX_BCID > &pairsToConsume, const int maxSpacingInTrain, const unsigned minBunchesPerTrain) const
internal methods:
SG::ReadCondHandleKey< TrigConf::L1BunchGroupSet > m_bunchGroupCondDataKey
SG::ReadCondHandleKey< LuminosityCondData > m_lumiCondDataKey
const ServiceHandle< TrigConf::ILVL1ConfigSvc > m_trigConfigSvc
SG::ReadCondHandleKey< AthenaAttributeList > m_fillParamsFolderKey
Input conditions object.
virtual StatusCode execute(const EventContext &ctx) const override
Algorithm execute method.
BunchCrossingCondData::bunchTrain_t bunchTrain_t
static constexpr int m_MAX_BCID
This class is the StoreGate data object for bytestream metadata.
static EventIDRange infiniteMixed()
Produces an mixed EventIDRange that is infinite in Time and RunLumi.
static constexpr unsigned int TOTAL_LHC_BCIDS
virtual bool isValid() override final
Can the handle be successfully dereferenced?
void addDependency(const EventIDRange &range)
StatusCode record(const EventIDRange &range, T *t)
record handle, with explicit range DEPRECATED
L1 board configuration.
std::size_t size() const
Accessor to the number of defined bunchgroups.
const std::shared_ptr< L1BunchGroup > & getBunchGroup(const std::string &name) const
Accessor to the bunchgroup by name.
int r
Definition globals.cxx:22