|
8 | 8 | // In applying this license CERN does not waive the privileges and immunities |
9 | 9 | // granted to it by virtue of its status as an Intergovernmental Organization |
10 | 10 | // or submit itself to any jurisdiction. |
11 | | -// O2 includes |
12 | 11 |
|
13 | 12 | #include "Common/DataModel/EventSelection.h" |
14 | 13 | #include "CommonConstants/LHCConstants.h" |
|
22 | 21 |
|
23 | 22 | using namespace o2; |
24 | 23 | using namespace o2::framework; |
25 | | -using namespace o2::framework::expressions; |
| 24 | +using o2::dataformats::IRFrame; |
26 | 25 |
|
27 | | -// ............................................................................. |
28 | | -// Run 3 |
29 | 26 | struct BCRangeSelector { |
30 | 27 |
|
31 | 28 | Configurable<int> nTimeRes{"nTimeRes", 4, "Range to consider for search of compatible BCs in units of vertex-time-resolution."}; |
32 | 29 | Configurable<int> nMinBCs{"nMinBCs", 7, "Minimum width of time window to consider for search of compatible BCs in units of 2*BunchSpacing."}; |
33 | 30 | Configurable<double> fillFac{"fillFactor", 0.0, "Factor of MB events to add"}; |
34 | 31 |
|
35 | | - using FDs = aod::CefpDecisions; |
36 | 32 | using CCs = soa::Join<aod::Collisions, aod::EvSels>; |
37 | | - using CC = CCs::iterator; |
38 | | - using BCs = soa::Join<aod::BCs, aod::BcSels, aod::Run3MatchedToBCSparse>; |
39 | | - using BC = BCs::iterator; |
40 | | - |
41 | | - uint64_t nBCs, nCompBCs, nNotCompBCs; |
42 | | - uint64_t clast, cnew; |
43 | 33 |
|
44 | 34 | // buffer for task output |
45 | | - std::vector<o2::dataformats::IRFrame> res; |
46 | 35 | Produces<aod::BCRanges> tags; |
47 | 36 |
|
48 | | - void init(o2::framework::InitContext&) |
| 37 | + template <typename T> |
| 38 | + IRFrame getIRFrame(T& collision) |
49 | 39 | { |
50 | | - res.clear(); |
| 40 | + auto collBC = collision.bc().globalBC(); |
| 41 | + auto evSelBC = collision.has_foundBC() ? collision.foundBC().globalBC() : collBC; |
| 42 | + int deltaBC = std::ceil(collision.collisionTimeRes() * nTimeRes / constants::lhc::LHCBunchSpacingNS); |
| 43 | + deltaBC = std::max(deltaBC, nMinBCs.value); |
| 44 | + IRFrame bcRange{InteractionRecord::long2IR(std::min(collBC, evSelBC)), InteractionRecord::long2IR(std::max(collBC, evSelBC))}; |
| 45 | + bcRange.getMax() += deltaBC; |
| 46 | + bcRange.getMin() -= deltaBC; |
| 47 | + return bcRange; |
51 | 48 | } |
52 | 49 |
|
53 | 50 | void run(ProcessingContext& pc) |
54 | 51 | { |
55 | | - // get and prepare the input tables |
56 | | - auto TabConsumer1 = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::BCs>>::metadata::tableLabel()); |
57 | | - auto t1{TabConsumer1->asArrowTable()}; |
58 | | - auto TabConsumer2 = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::BcSels>>::metadata::tableLabel()); |
59 | | - auto t2{TabConsumer2->asArrowTable()}; |
60 | | - auto TabConsumer3 = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::Run3MatchedToBCSparse>>::metadata::tableLabel()); |
61 | | - auto t3{TabConsumer3->asArrowTable()}; |
62 | | - auto TabConsumer4 = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::Collisions>>::metadata::tableLabel()); |
63 | | - auto t4{TabConsumer4->asArrowTable()}; |
64 | | - auto TabConsumer5 = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::EvSels>>::metadata::tableLabel()); |
65 | | - auto t5{TabConsumer5->asArrowTable()}; |
66 | | - auto TabConsumer6 = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::CefpDecisions>>::metadata::tableLabel()); |
67 | | - auto t6{TabConsumer6->asArrowTable()}; |
68 | | - |
69 | | - // join tables |
70 | | - auto bcs = BCs({t1, t2, t3}); |
71 | | - auto cols = CCs({t4, t5}); |
| 52 | + auto bcConsumer = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::BCs>>::metadata::tableLabel()); |
| 53 | + auto bcTable{bcConsumer->asArrowTable()}; |
| 54 | + auto collConsumer = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::Collisions>>::metadata::tableLabel()); |
| 55 | + auto collTable{collConsumer->asArrowTable()}; |
| 56 | + auto evSelConsumer = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::EvSels>>::metadata::tableLabel()); |
| 57 | + auto evSelTable{evSelConsumer->asArrowTable()}; |
| 58 | + auto cefpConsumer = pc.inputs().get<TableConsumer>(aod::MetadataTrait<std::decay_t<aod::CefpDecisions>>::metadata::tableLabel()); |
| 59 | + auto cefpTable{cefpConsumer->asArrowTable()}; |
| 60 | + |
| 61 | + auto bcs = aod::BCs({bcTable}); |
| 62 | + auto cols = CCs({collTable, evSelTable}); |
72 | 63 | cols.bindExternalIndices(&bcs); |
73 | | - FDs fdecs{{t6}}; |
74 | | - if (cols.size() != fdecs.size()) { |
| 64 | + aod::CefpDecisions decisions{{cefpTable}}; |
| 65 | + |
| 66 | + if (cols.size() != decisions.size()) { |
75 | 67 | throw std::runtime_error("Collision table and CefpDecision do not have the same number of rows! "); |
76 | 68 | } |
77 | 69 | if (cols.size() == 0) { |
78 | 70 | LOGF(warning, "No collisions found!"); |
79 | | - pc.outputs().snapshot({"PPF", "IFRAMES", 0, Lifetime::Timeframe}, res); |
80 | 71 | return; |
81 | 72 | } |
82 | 73 |
|
83 | | - // 1. loop over collisions |
84 | | - auto filt = fdecs.begin(); |
85 | | - std::vector<std::pair<uint64_t, uint64_t>> bcRanges; |
| 74 | + auto filt = decisions.begin(); |
| 75 | + int firstSelectedCollision{-1}; |
| 76 | + std::vector<IRFrame> bcRanges; |
| 77 | + int nColl{0}, nSelected{0}; |
86 | 78 | for (auto collision : cols) { |
87 | 79 | if (filt.cefpSelected()) { |
88 | | - |
89 | | - LOGF(debug, "Collision time / resolution [ns]: %f / %f", collision.collisionTime(), collision.collisionTimeRes()); |
90 | | - |
91 | | - // return if collisions has no associated BC |
92 | | - if (!collision.has_foundBC()) { |
93 | | - LOGF(warning, "No compatible BCs found for collision that the framework assigned to BC %i", filt.globalBC()); |
94 | | - filt++; |
95 | | - continue; |
96 | | - } |
97 | | - |
98 | | - // get associated BC |
99 | | - auto bcIter = collision.foundBC_as<BCs>(); |
100 | | - |
101 | | - // due to the filling scheme the most probable BC may not be the one estimated from the collision time |
102 | | - InteractionRecord mostProbableBC; |
103 | | - mostProbableBC.setFromLong(bcIter.globalBC()); |
104 | | - InteractionRecord meanBC = mostProbableBC + std::lround(collision.collisionTime() / o2::constants::lhc::LHCBunchSpacingNS); |
105 | | - |
106 | | - // enforce minimum number for deltaBC |
107 | | - int deltaBC = std::ceil(collision.collisionTimeRes() * nTimeRes / o2::constants::lhc::LHCBunchSpacingNS); |
108 | | - if (deltaBC < nMinBCs) { |
109 | | - deltaBC = nMinBCs; |
110 | | - } |
111 | | - LOGF(debug, "BC %d, deltaBC %d", bcIter.globalIndex(), deltaBC); |
112 | | - |
113 | | - auto minBC = meanBC - deltaBC; |
114 | | - auto maxBC = meanBC + deltaBC; |
115 | | - |
116 | | - uint64_t minBCId = bcIter.globalIndex(); |
117 | | - uint64_t maxBCId = bcIter.globalIndex(); |
118 | | - |
119 | | - auto localIter = bcIter; |
120 | | - while (localIter.globalIndex() > 0) { |
121 | | - --localIter; |
122 | | - if (localIter.globalBC() >= minBC.toLong()) { |
123 | | - minBCId = localIter.globalIndex(); |
124 | | - } else { |
125 | | - break; |
126 | | - } |
| 80 | + if (firstSelectedCollision < 0) { |
| 81 | + firstSelectedCollision = nColl; |
127 | 82 | } |
128 | | - localIter = bcIter; |
129 | | - while (localIter.globalIndex() < bcs.size() - 1) { |
130 | | - ++localIter; |
131 | | - if (localIter.globalBC() <= maxBC.toLong()) { |
132 | | - maxBCId = localIter.globalIndex(); |
133 | | - } else { |
134 | | - break; |
135 | | - } |
136 | | - } |
137 | | - |
138 | | - auto ao2dBC = filt.bcIndex(); |
139 | | - if (ao2dBC < minBCId) { |
140 | | - LOGF(debug, "Extending the window to keep into account the AO2D/EvSel BC discrepancy by %d BCs", minBCId - ao2dBC); |
141 | | - minBCId = ao2dBC; |
142 | | - } else if (ao2dBC > maxBCId) { |
143 | | - LOGF(debug, "Extending the window to keep into account the AO2D/EvSel BC discrepancy by %d BCs", ao2dBC - maxBCId); |
144 | | - maxBCId = ao2dBC; |
145 | | - } |
146 | | - |
147 | | - bcRanges.push_back(std::make_pair(minBCId, maxBCId)); |
| 83 | + bcRanges.push_back(getIRFrame(collision)); |
| 84 | + nSelected++; |
148 | 85 | } |
| 86 | + nColl++; |
149 | 87 | filt++; |
150 | 88 | } |
151 | 89 |
|
152 | | - /// We cannot merge the ranges in the previous loop because while collisions are sorted by time, the corresponding minBCs can be unsorted as the collision time resolution is not constant |
153 | | - std::sort(bcRanges.begin(), bcRanges.end(), [](const std::pair<uint64_t, uint64_t>& a, const std::pair<uint64_t, uint64_t>& b) { |
154 | | - return a.first < b.first; |
155 | | - }); |
156 | 90 | if (bcRanges.empty()) { |
157 | | - LOGF(warning, "No BCs selected! This should not happen! Adding a bogus BC range to avoid crashes."); |
158 | | - bcRanges.push_back(std::make_pair(0, 0)); |
| 91 | + LOGF(warning, "No BCs selected!"); |
| 92 | + return; |
| 93 | + } |
| 94 | + |
| 95 | + float fractionSelected{static_cast<float>(nSelected) / nColl}; |
| 96 | + int nMB{std::min(static_cast<int>(fillFac * nColl) - nSelected, nColl - 1)}; |
| 97 | + LOGF(info, "Selected %d collisions (%.2f%%) and %d MB events", nSelected, fractionSelected * 100, nMB); |
| 98 | + int maxCollisionId = std::max(nMB, firstSelectedCollision); |
| 99 | + int minCollisionId = (maxCollisionId == nMB) ? 0 : firstSelectedCollision - nMB; |
| 100 | + auto minCollision = cols.begin() + minCollisionId; |
| 101 | + IRFrame minFrame{getIRFrame(minCollision)}; |
| 102 | + bcRanges[0].getMin() = std::min(bcRanges[0].getMin(), minFrame.getMin()); |
| 103 | + if (maxCollisionId == nMB) { |
| 104 | + auto maxCollision = cols.begin() + nMB; |
| 105 | + IRFrame maxFrame{getIRFrame(maxCollision)}; |
| 106 | + bcRanges[0].getMax() = std::max(bcRanges[0].getMax(), maxFrame.getMax()); |
159 | 107 | } |
160 | | - std::vector<std::pair<uint64_t, uint64_t>> bcRangesMerged(1, bcRanges[0]); |
| 108 | + |
| 109 | + /// We cannot merge the ranges in the previous loop because while collisions are sorted by time, the corresponding minBCs can be unsorted as the collision time resolution is not constant |
| 110 | + std::sort(bcRanges.begin(), bcRanges.end(), [](const IRFrame& a, const IRFrame& b) { |
| 111 | + return a.getMin() < b.getMin(); |
| 112 | + }); |
| 113 | + |
| 114 | + std::vector<IRFrame> bcRangesMerged(1, bcRanges[0]); |
161 | 115 | for (uint64_t iR{1}; iR < bcRanges.size(); ++iR) { |
162 | | - if (bcRangesMerged.back().second >= bcRanges[iR].first) { |
163 | | - bcRangesMerged.back().second = std::max(bcRangesMerged.back().second, bcRanges[iR].second); |
| 116 | + if (bcRangesMerged.back().getMax() >= bcRanges[iR].getMin()) { |
| 117 | + bcRangesMerged.back().getMax() = std::max(bcRangesMerged.back().getMax(), bcRanges[iR].getMax()); |
164 | 118 | } else { |
165 | 119 | bcRangesMerged.push_back(bcRanges[iR]); |
166 | 120 | } |
167 | 121 | } |
168 | 122 | bcRanges.swap(bcRangesMerged); |
169 | 123 |
|
170 | | - // 2. extend ranges |
171 | | - int nBCselected{0}; |
172 | 124 | for (auto& range : bcRanges) { |
173 | | - nBCselected += range.second - range.first + 1; |
| 125 | + tags(range.getMin().toLong(), range.getMax().toLong()); |
174 | 126 | } |
175 | | - int nToBeAdded = std::ceil((bcs.size() - nBCselected) * fillFac); |
176 | | - int nToBeAddedPerRange = std::ceil(float(nToBeAdded) / bcRanges.size()); |
177 | | - LOGF(debug, "Extending ranges by %d BCs (%d per selected range)", nToBeAdded, nToBeAddedPerRange); |
178 | | - |
179 | | - InteractionRecord IR1, IR2; |
180 | | - uint64_t first{bcs.iteratorAt(bcRanges[0].first).globalBC()}; |
181 | | - IR1.setFromLong(first); |
182 | | - while (nToBeAdded > 0 && bcRanges[0].first > 0) { /// TODO: decide if we want to extend the ranges to the beginning of the dataframe |
183 | | - first = bcs.iteratorAt(bcRanges[0].first - 1).globalBC(); |
184 | | - IR2.setFromLong(first); |
185 | | - if (IR1.differenceInBC(IR2) > o2::constants::lhc::LHCMaxBunches) { // protection against change of orbit in the DataFrame |
186 | | - LOGF(debug, "Jump by more than one orbit detected!"); |
187 | | - break; |
188 | | - } else { |
189 | | - IR1.setFromLong(first); |
190 | | - } |
191 | | - bcRanges[0].first--; |
192 | | - nToBeAdded--; |
193 | | - } |
194 | | - LOGF(debug, "Extending ranges by %d BCs (%d per selected range)", nToBeAdded, nToBeAddedPerRange); |
195 | | - |
196 | | - for (uint64_t iR{0}; iR < bcRanges.size() && nToBeAdded > 0; ++iR) { |
197 | | - uint64_t second{bcs.iteratorAt(bcRanges[iR].second).globalBC()}; |
198 | | - IR2.setFromLong(second); |
199 | | - for (int i{0}; i < nToBeAddedPerRange && nToBeAdded > 0; ++i) { |
200 | | - if (bcRanges[iR].second < bcs.size() - 1) { |
201 | | - second = bcs.iteratorAt(bcRanges[iR].second + 1).globalBC(); |
202 | | - IR1.setFromLong(second); |
203 | | - if (IR1.differenceInBC(IR2) > o2::constants::lhc::LHCMaxBunches) { // protection against change of orbit in the DataFrame |
204 | | - LOGF(debug, "Jump by more than one orbit detected!"); |
205 | | - break; |
206 | | - } else { |
207 | | - IR2.setFromLong(second); |
208 | | - } |
209 | | - bcRanges[iR].second++; |
210 | | - nToBeAdded--; |
211 | | - } |
212 | | - } |
213 | | - } |
214 | | - LOGF(debug, "End extension, remaining to be added %d BCs", nToBeAdded); |
215 | | - |
216 | | - // fill res |
217 | | - LOGF(debug, "Merged and extended sorted ranges"); |
218 | | - for (auto& range : bcRanges) { |
219 | | - LOGF(debug, " %i - %i", range.first, range.second); |
220 | | - uint64_t first{bcs.rawIteratorAt(range.first).globalBC()}, second{bcs.rawIteratorAt(range.second).globalBC()}; |
221 | | - IR1.setFromLong(first); |
222 | | - IR2.setFromLong(second); |
223 | | - res.emplace_back(IR1, IR2); |
224 | | - tags(first, second); |
225 | | - } |
226 | | - |
227 | | - // make res an output |
228 | | - pc.outputs().snapshot({"PPF", "IFRAMES", 0, Lifetime::Timeframe}, res); |
229 | | - |
230 | | - // clean up |
231 | | - res.clear(); |
232 | 127 | } |
233 | 128 |
|
234 | | - // need a trivial process method |
235 | | - // the parameters determine the tables available in the input |
236 | | - void process(CCs const& collisions, BCs const& bcs, FDs const& fdecisions) |
| 129 | + // need a trivial process method: the parameters determine the tables available in the input |
| 130 | + void process(CCs const& collisions, aod::BCs const& bcs, aod::CefpDecisions const& fdecisions) |
237 | 131 | { |
238 | 132 | } |
239 | 133 | }; |
240 | 134 |
|
241 | 135 | WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) |
242 | 136 | { |
243 | 137 | DataProcessorSpec spec{adaptAnalysisTask<BCRangeSelector>(cfgc, TaskName{"bc-ranges-selector-task"})}; |
244 | | - |
245 | | - // add output |
246 | | - spec.outputs.emplace_back("PPF", "IFRAMES", 0, Lifetime::Timeframe); |
247 | | - LOGF(debug, "Output %i", spec.outputs.size()); |
248 | | - |
249 | 138 | return WorkflowSpec{spec}; |
250 | 139 | } |
0 commit comments