ATLAS Offline Software
Loading...
Searching...
No Matches
OnDemandMinbiasSvc.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include <GaudiKernel/ConcurrencyFlags.h>
8
9#include <algorithm>
10#include <boost/core/demangle.hpp>
11#include <chrono>
12#include <format>
13#include <numeric>
14#include <random>
15#include <thread>
16
22
23
24inline std::string CLIDToString(const CLID& clid) {
25 return boost::core::demangle(CLIDRegistry::CLIDToTypeinfo(clid)->name());
26}
27
29 ISvcLocator* svc)
30 : base_class(name, svc),
31 m_bkg_evt_sel_ctx(nullptr),
32 m_proxyProviderSvc("ProxyProviderSvc/BkgPPSvc_"+name, name),
34{}
35
37
39 ATH_CHECK(m_bkgEventSelector.retrieve());
40 ATH_CHECK(m_activeStoreSvc.retrieve());
41 ATH_CHECK(m_skipEventIdxSvc.retrieve());
42 if (m_useBeamInt) {
43 ATH_CHECK(m_beamInt.retrieve());
44 }
45 if (m_useBeamLumi) {
46 ATH_CHECK(m_beamLumi.retrieve());
47 }
48 // Setup context
49 if (!m_bkgEventSelector->createContext(m_bkg_evt_sel_ctx).isSuccess()) {
50 ATH_MSG_ERROR("Failed to create background event selector context");
51 return StatusCode::FAILURE;
52 }
53 ATH_CHECK(SmartIF<IService>(m_bkgEventSelector.get())->start());
54
55 // Setup proxy provider
56 ATH_CHECK(m_proxyProviderSvc.retrieve());
57
58 // Setup Address Providers
59 SmartIF<IAddressProvider> addressProvider{m_bkgEventSelector.get()};
60 if (!addressProvider) {
62 "Could not cast background event selector to IAddressProvider");
63 } else {
64 m_proxyProviderSvc->addProvider(addressProvider);
65 }
66 // AthenaPoolAddressProviderSvc
67 SmartIF<IAddressProvider> athPoolAP{
68 serviceLocator()->service(std::format("AthenaPoolAddressProviderSvc/BkgAPAPSvc_{}", name()))
69 };
70 if (!athPoolAP) {
72 "Could not cast AthenaPoolAddressProviderSvc to IAddressProvider");
73 } else {
74 m_proxyProviderSvc->addProvider(athPoolAP);
75 }
76 // AddressRemappingSvc
77 SmartIF<IAddressProvider> addRemapAP{
78 serviceLocator()->service(std::format("AddressRemappingSvc/BkgARSvc_{}", name()))
79 };
80 if (!addRemapAP) {
81 ATH_MSG_WARNING("Could not cast AddressRemappingSvc to IAddressProvider");
82 } else {
83 m_proxyProviderSvc->addProvider(addRemapAP);
84 }
85
86 const int n_stores = 50; // Start with 50 stores per event
87 std::size_t i = 0;
88 // setup n_concurrent vectors of n_stores StoreGates in m_stores
89 for (auto& sgs : m_stores) {
90 sgs.reserve(n_stores);
91 for (int j = 0; j < n_stores; ++j) {
92 // creates / retrieves a different StoreGateSvc for each slot
93 auto& sg = sgs.emplace_back(
94 std::format("StoreGateSvc/StoreGate_{}_{}_{}", name(), i, j), name());
95 ATH_CHECK(sg.retrieve());
96 sg->setStoreID(StoreID::PILEUP_STORE);
97 sg->setProxyProviderSvc(m_proxyProviderSvc.get());
98 }
99 ++i;
100 }
101
102 // setup spare store for event skipping
103 ATH_CHECK(m_spare_store.retrieve());
105 m_spare_store->setProxyProviderSvc(m_proxyProviderSvc.get());
106 auto skipEvent_callback = [this](
108 ISkipEventIdxSvc::EvtIter end) -> StatusCode {
109 using namespace std::chrono_literals;
110 auto* const old_store = m_activeStoreSvc->activeStore();
111 m_activeStoreSvc->setStore(m_spare_store.get());
112 ATH_MSG_INFO("Skipping " << end - begin << " HS events. ");
113 for (auto iter = begin; iter < end; ++iter) {
114 const auto& evt = *iter;
115 EventContext ctx;
116 EventIDBase eid;
117 eid.set_run_number(evt.runNum);
118 eid.set_lumi_block(evt.lbNum);
119 eid.set_event_number(evt.evtNum);
120 ctx.setEventID(eid);
121 const std::size_t n_to_skip = calcMBRequired(evt.evtIdx, ctx);
122 ATH_MSG_DEBUG("Skipping HS_ID " << evt.evtIdx << " --> skipping "
123 << n_to_skip << " pileup events");
124 for (std::size_t i = 0; i < n_to_skip; ++i) {
125 if (!m_bkgEventSelector->next(*m_bkg_evt_sel_ctx).isSuccess()) {
126 ATH_MSG_ERROR("Ran out of background events");
127 return StatusCode::FAILURE;
128 }
129 }
130 }
131 m_last_loaded_hs.store((end - 1)->evtIdx);
132 m_activeStoreSvc->setStore(old_store);
133 return StatusCode::SUCCESS;
134 };
135 ATH_CHECK(m_skipEventIdxSvc->registerCallback(skipEvent_callback));
136 ATH_MSG_INFO("Initializing ODMBSvc");
137 return StatusCode::SUCCESS;
138}
139
140std::size_t OnDemandMinbiasSvc::calcMBRequired(std::int64_t hs_id,
141 const EventContext& ctx) {
142 ATH_MSG_DEBUG("Run " << ctx.eventID().run_number()
143 << ", lumi " << ctx.eventID().lumi_block()
144 << ", event " << ctx.eventID().event_number()
145 << "| hs_id " << hs_id);
146 const int n_bunches = m_latestDeltaBC.value() - m_earliestDeltaBC.value() + 1;
147
148 std::vector<std::uint64_t>& num_mb_by_bunch = *m_num_mb_by_bunch.get(ctx);
149 num_mb_by_bunch.clear();
150 num_mb_by_bunch.resize(n_bunches);
151 FastReseededPRNG prng{m_seed.value(), hs_id};
152
153 // First apply the beam luminosity SF
154 bool sf_updated_throwaway;
155 const float beam_lumi_sf =
156 m_useBeamLumi ? m_beamLumi->scaleFactor(ctx.eventID().run_number(),
157 ctx.eventID().lumi_block(),
158 sf_updated_throwaway)
159 : 1.F;
160 const float beam_lumi = beam_lumi_sf * m_nPerBunch.value();
161 std::vector<float> avg_num_mb_by_bunch(n_bunches, beam_lumi);
162 // Now update using beam intensities
163 if (m_useBeamInt) {
164 // Supposed to be once per event, but ends up running once per minbias type
165 // per event now
166 m_beamInt->selectT0(ctx);
167 for (int bunch = m_earliestDeltaBC.value();
168 bunch <= m_latestDeltaBC.value(); ++bunch) {
169 std::size_t idx = bunch - m_earliestDeltaBC.value();
170 avg_num_mb_by_bunch[idx] *= m_beamInt->normFactor(bunch);
171 }
172 }
173
174 if (m_usePoisson) {
175 std::transform(avg_num_mb_by_bunch.begin(), avg_num_mb_by_bunch.end(),
176 num_mb_by_bunch.begin(), [&prng](float avg) {
177 return std::poisson_distribution<std::uint64_t>(avg)(prng);
178 });
179 } else {
180 std::transform(avg_num_mb_by_bunch.begin(), avg_num_mb_by_bunch.end(),
181 num_mb_by_bunch.begin(), [](float f) {
182 return static_cast<std::uint64_t>(std::round(f));
183 });
184 }
185
186 std::uint64_t num_mb = std::accumulate(num_mb_by_bunch.begin(), num_mb_by_bunch.end(), std::uint64_t{0});
187 if (!ctx.valid()) {
188 return num_mb;
189 }
190 // Won't go past here for an invalid slot (during initialize()
191
192 std::vector<std::uint64_t>& index_array = *m_idx_lists.get(ctx);
193 index_array.clear();
194 index_array.resize(num_mb);
195 std::iota(index_array.begin(), index_array.end(), 0);
196 // Don't need to shuffle, since these events aren't reused
197 // std::shuffle(index_array.begin(), index_array.end(), prng);
198
199 // Commented out until we can use C++ 23 range formatting
200 // ATH_MSG_DEBUG("HS ID " << hs_id << " uses " << num_mb << " events\n"
201 // << fmt::format("\t\tBy bunch: [{}]\n",
202 // fmt::join(num_mb_by_bunch, ", ")));
203 return num_mb;
204}
205
206StatusCode OnDemandMinbiasSvc::beginHardScatter(const EventContext& ctx) {
207 using namespace std::chrono_literals;
208 std::chrono::steady_clock::time_point order_wait_start{};
209
210 const std::int64_t hs_id = get_hs_id(ctx);
211 const std::size_t slot = ctx.slot();
212 const std::size_t num_to_load = calcMBRequired(hs_id, ctx);
213 auto& stores = *m_stores.get(ctx);
214 // If we don't have enough stores, make more
215 if (stores.size() < num_to_load) {
216 ATH_MSG_INFO("Adding " << num_to_load - stores.size() << " stores");
217 stores.reserve(num_to_load);
218 for (std::size_t i = stores.size(); i < num_to_load; ++i) {
219 auto& sg = stores.emplace_back(
220 std::format("StoreGateSvc/StoreGate_{}_{}_{}", name(), slot, i),
221 name());
222 ATH_CHECK(sg.retrieve());
223 sg->setStoreID(StoreID::PILEUP_STORE);
224 sg->setProxyProviderSvc(m_proxyProviderSvc.get());
225 }
226 }
227 // Ensure loading is done in order
228 if (m_last_loaded_hs < hs_id - 1) {
229 ATH_MSG_INFO("Waiting to prevent out-of-order loading. Last loaded is "
230 << m_last_loaded_hs << " and we are " << hs_id);
231 order_wait_start = std::chrono::steady_clock::now();
232 while (m_last_loaded_hs < hs_id - 1) {
233 std::this_thread::sleep_for(50ms);
234 }
235 auto wait_time = std::chrono::steady_clock::now() - order_wait_start;
236 ATH_MSG_INFO(std::format("Waited {:%M:%S} to prevent out-of-order loading",
237 wait_time));
238 }
239 // Lock reading mutex
240 std::unique_lock lck(m_reading_batch_mtx);
241 auto start = std::chrono::steady_clock::now();
242 // Remember old store to reset later
243 auto* old_store = m_activeStoreSvc->activeStore();
244 for (std::size_t i = 0; i < num_to_load; ++i) {
245 auto& sg = stores[i];
246 // Change active store
247 m_activeStoreSvc->setStore(sg.get());
248 SG::CurrentEventStore::Push reader_sg_ces(sg.get());
249 // Read next event
250 ATH_CHECK(sg->clearStore(true));
251 if (!(m_bkgEventSelector->next(*m_bkg_evt_sel_ctx)).isSuccess()) {
252 ATH_MSG_FATAL("Ran out of minbias events");
253 return StatusCode::FAILURE;
254 }
255 IOpaqueAddress* addr = nullptr;
256 if (!m_bkgEventSelector->createAddress(*m_bkg_evt_sel_ctx, addr)
257 .isSuccess()) {
258 ATH_MSG_WARNING("Failed to create address. No more events?");
259 return StatusCode::FAILURE;
260 }
261 if (addr == nullptr) {
262 ATH_MSG_WARNING("createAddress returned nullptr. No more events?");
263 return StatusCode::FAILURE;
264 }
265 ATH_CHECK(sg->recordAddress(addr));
266 ATH_CHECK(sg->loadEventProxies());
267 // Read data now if desired
268 if (!m_onDemandMB) {
269 for (const auto* proxy_ptr : sg->proxies()) {
270 // Sort of a const_cast, then ->accessData()
271 sg->proxy_exact(proxy_ptr->sgkey())->accessData();
272 }
273 }
274 }
275 // Reset active store
276 m_activeStoreSvc->setStore(old_store);
277 ATH_MSG_INFO(std::format("Took {:%M:%S} to load events",
278 std::chrono::steady_clock::now() - start));
279 // Update last loaded
280 m_last_loaded_hs.store(hs_id);
281 return StatusCode::SUCCESS;
282}
283
285 std::uint64_t mb_id) {
286 const std::size_t index = m_idx_lists.get(ctx)->at(mb_id);
287 return m_stores.get(ctx)->at(index).get();
288}
289
290std::size_t OnDemandMinbiasSvc::getNumForBunch(const EventContext& ctx,
291 int bunch) const {
292 if (bunch < m_earliestDeltaBC.value() || bunch > m_latestDeltaBC.value()) {
293 throw std::logic_error(std::format(
294 "Tried to request bunch {} which is outside the range [{}, {}]", bunch,
295 m_earliestDeltaBC.value(), m_latestDeltaBC.value()));
296 }
297 return m_num_mb_by_bunch.get(ctx)->at(bunch - m_earliestDeltaBC.value());
298}
299
300StatusCode OnDemandMinbiasSvc::endHardScatter(const EventContext& ctx) {
301 // clear all stores
302 for (SGHandle& sg : *m_stores.get(ctx)) {
303 ATH_CHECK(sg->clearStore());
304 }
305 return StatusCode::SUCCESS;
306}
#define ATH_CHECK
Evaluate an expression and check for errors.
#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)
uint32_t CLID
The Class ID type.
std::string CLIDToString(const CLID &clid)
Hold a pointer to the current event store.
static const std::type_info * CLIDToTypeinfo(CLID clid)
Translate between CLID and type_info.
std::vector< EvtId >::const_iterator EvtIter
ServiceHandle< StoreGateSvc > SGHandle
~OnDemandMinbiasSvc() final
Destructor.
virtual std::int64_t get_hs_id(const EventContext &ctx) const override
StatusCode initialize() final
AthService initialize.
IEvtSelector::Context * m_bkg_evt_sel_ctx
Gaudi::Property< int > m_latestDeltaBC
StatusCode beginHardScatter(const EventContext &ctx) override
StoreGateSvc * getMinbias(const EventContext &ctx, std::uint64_t mb_id) override
Gaudi::Property< bool > m_useBeamLumi
ServiceHandle< ActiveStoreSvc > m_activeStoreSvc
ServiceHandle< IEvtSelector > m_bkgEventSelector
std::size_t getNumForBunch(const EventContext &ctx, int bunch) const override
SG::SlotSpecificObj< std::vector< std::uint64_t >, SG::InvalidSlot::Enabled > m_num_mb_by_bunch
SG::SlotSpecificObj< std::vector< std::uint64_t > > m_idx_lists
Gaudi::Property< bool > m_useBeamInt
SG::SlotSpecificObj< std::vector< SGHandle > > m_stores
OnDemandMinbiasSvc(const std::string &name, ISvcLocator *svc)
Constructor.
Gaudi::Property< bool > m_usePoisson
Gaudi::Property< std::uint64_t > m_seed
std::atomic_int64_t m_last_loaded_hs
ServiceHandle< IProxyProviderSvc > m_proxyProviderSvc
ServiceHandle< IBeamIntensity > m_beamInt
StatusCode endHardScatter(const EventContext &ctx) override
ServiceHandle< ISkipEventIdxSvc > m_skipEventIdxSvc
Gaudi::Property< float > m_nPerBunch
std::size_t calcMBRequired(std::int64_t hs_id, const EventContext &ctx)
Gaudi::Property< bool > m_onDemandMB
ServiceHandle< IBeamLuminosity > m_beamLumi
Gaudi::Property< int > m_earliestDeltaBC
The Athena Transient Store API.
@ PILEUP_STORE
Definition StoreID.h:31
Definition index.py:1