Skip to content

Commit fa0cebd

Browse files
authored
[PWGCF] Clean up event selection in femto framework (#13281)
1 parent e6841fa commit fa0cebd

File tree

4 files changed

+184
-83
lines changed

4 files changed

+184
-83
lines changed

PWGCF/Femto/Core/baseSelection.h

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,46 @@ class BaseSelection
170170
// set bitmask for given observable
171171
mSelectionContainers.at(observableIndex).evaluate(value);
172172
// check if minimal selction for this observable holds
173+
// if one minimal selection is not fullfilled, the condition failes
173174
if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) {
174175
mPassesMinimalSelections = false;
175176
}
176177
// check if any optional selection holds
178+
// if one optional selection is fullfilled, the condition succeeds
177179
if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) {
178180
mPassesOptionalSelections = true;
179181
}
180182
}
181183

184+
/// \brief Evaluate a single observable against its configured selections.
185+
/// \param observableIndex Index of the observable.
186+
/// \param values vector of values of the observable.
187+
void evaluateObservable(int observableIndex, std::vector<T> values)
188+
{
189+
// if there are no selections configured, bail out
190+
if (mSelectionContainers.at(observableIndex).isEmpty()) {
191+
return;
192+
}
193+
// if any previous observable did not pass minimal selections, there is no point in setting bitmask for other observables
194+
// minimal selection for each observable is computed after adding it
195+
if (mPassesMinimalSelections == false) {
196+
return;
197+
}
198+
// set bitmask for given observable
199+
mSelectionContainers.at(observableIndex).evaluate(values);
200+
// check if minimal selction for this observable holds
201+
if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) {
202+
mPassesMinimalSelections = false;
203+
}
204+
// check if any optional selection holds
205+
if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) {
206+
mPassesOptionalSelections = true;
207+
}
208+
}
209+
210+
/// \brief Add comments to specific observabel
211+
void addComments(int observableIndex, std::vector<std::string> const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); }
212+
182213
/// \brief Check if all required (minimal) and optional cuts are passed.
183214
/// \return True if all required and at least one optional cut (if present) is passed.
184215
bool passesAllRequiredSelections() const
@@ -223,20 +254,38 @@ class BaseSelection
223254
/// \return The combined selection bitmask.
224255
BitmaskType getBitmask(int observableIndex) const { return static_cast<BitmaskType>(mSelectionContainers.at(observableIndex).getBitmask().to_ullong()); }
225256

226-
/// \brief Retrieve the assembled bitmask as an integer value.
257+
/// \brief Set the assembled bitmask for on observable
227258
/// \return The combined selection bitmask.
228259
template <typename R>
229260
void setBitmask(int observableIndex, R bitmask)
230261
{
262+
// if there are no selections configured, bail out
263+
if (mSelectionContainers.at(observableIndex).isEmpty()) {
264+
return;
265+
}
266+
// if any previous observable did not pass minimal selections, there is no point in setting bitmask for other observables
267+
// minimal selection for each observable is computed after adding it
268+
if (mPassesMinimalSelections == false) {
269+
return;
270+
}
271+
// set bitmask for given observable
231272
mSelectionContainers.at(observableIndex).setBitmask(bitmask);
273+
// check if minimal selction for this observable holds
274+
if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) {
275+
mPassesMinimalSelections = false;
276+
}
277+
// check if any optional selection holds
278+
if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) {
279+
mPassesOptionalSelections = true;
280+
}
232281
}
233282

234283
/// \brief Print detailed information about all configured selections.
235284
/// \tparam MapType Type used in the observable name map (usually an enum or int).
236285
/// \param objectName Name of the current object (e.g. particle species).
237286
/// \param observableNames Map from observable index to human-readable names.
238-
template <typename MapType>
239-
void printSelections(const std::string& objectName, const std::unordered_map<MapType, std::string>& observableNames) const
287+
template <typename R>
288+
void printSelections(const std::string& objectName, const std::unordered_map<R, std::string>& observableNames) const
240289
{
241290
LOG(info) << "Printing Configuration of " << objectName;
242291

@@ -248,7 +297,7 @@ class BaseSelection
248297
continue;
249298
}
250299

251-
const MapType key = static_cast<MapType>(idx);
300+
R key = static_cast<R>(idx);
252301
const std::string& name = observableNames.count(key) ? observableNames.at(key) : "[Unknown]";
253302

254303
LOG(info) << "Observable: " << name << " (index " << idx << ")";
@@ -260,9 +309,11 @@ class BaseSelection
260309

261310
const auto& values = container.getSelectionValues();
262311
const auto& functions = container.getSelectionFunction();
263-
const bool useFunctions = !functions.empty();
264-
const size_t numSelections = useFunctions ? functions.size() : values.size();
265-
const bool skipMostPermissive = container.skipMostPermissiveBit();
312+
bool useFunctions = !functions.empty();
313+
size_t numSelections = useFunctions ? functions.size() : values.size();
314+
bool skipMostPermissive = container.skipMostPermissiveBit();
315+
const auto& comments = container.getComments();
316+
bool hasComments = !comments.empty();
266317

267318
int valWidth = 20;
268319
int bitWidth = 30;
@@ -273,7 +324,9 @@ class BaseSelection
273324
// Selection string (either value or function)
274325
const std::string& sel = useFunctions ? std::string(functions[j].GetFormula()->GetExpFormula().Data()) : std::to_string(values[j]);
275326
line << " " << std::left << std::setw(valWidth) << sel;
276-
327+
if (hasComments) {
328+
line << "(" << comments.at(j) << ")";
329+
}
277330
// Bitmask
278331
if (skipMostPermissive && j == 0) {
279332
line << std::setw(bitWidth) << "-> loosest minimal selection, no bit saved";

PWGCF/Femto/Core/collisionBuilder.h

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@
2525
#include "Common/CCDB/EventSelectionParams.h"
2626
#include "EventFiltering/Zorro.h"
2727

28+
#include "DataFormatsParameters/GRPMagField.h"
2829
#include "Framework/AnalysisHelpers.h"
2930
#include "Framework/Configurable.h"
3031

3132
#include "fairlogger/Logger.h"
3233

34+
#include <algorithm>
3335
#include <cmath>
3436
#include <string>
3537
#include <unordered_map>
38+
#include <vector>
3639

3740
namespace o2::analysis::femto
3841
{
@@ -73,13 +76,14 @@ struct ConfCollisionBits : o2::framework::ConfigurableGroup {
7376
o2::framework::Configurable<std::vector<float>> occupancyMax{"occupancyMax", {}, "Maximum occpancy"};
7477
o2::framework::Configurable<std::vector<float>> sphericityMin{"sphericityMin", {}, "Minimum sphericity"};
7578
o2::framework::Configurable<std::vector<float>> sphericityMax{"sphericityMax", {}, "Maximum sphericity"};
79+
o2::framework::Configurable<std::vector<std::string>> triggers{"triggers", {}, "List of all triggers to be used"};
7680
};
7781

78-
struct ConfCollisionTriggers : o2::framework::ConfigurableGroup {
79-
std::string prefix = std::string("CollisionTriggers");
80-
o2::framework::Configurable<bool> useTrigger{"useTrigger", false, "Set to true to only selected triggered collisions"};
81-
o2::framework::Configurable<std::string> ccdbPath{"ccdbPath", std::string("EventFiltering/Zorro/"), "CCDB path for trigger information"};
82-
o2::framework::Configurable<std::string> triggers{"triggers", std::string("fPPP,fPPL"), "Comma seperated list of all triggers to be used"};
82+
struct ConfCcdb : o2::framework::ConfigurableGroup {
83+
std::string prefix = std::string("ConfCcdb");
84+
o2::framework::Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "URL to ccdb"};
85+
o2::framework::Configurable<std::string> grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField/Run2 -> GLO/GRP/GRP"};
86+
o2::framework::Configurable<std::string> triggerPath{"triggerPath", "EventFiltering/Zorro/", "CCDB path for trigger information"};
8387
};
8488

8589
struct ConfCollisionRctFlags : o2::framework::ConfigurableGroup {
@@ -125,6 +129,8 @@ enum CollisionSels {
125129
kSphericityMin, ///< Min. sphericity
126130
kSphericityMax, ///< Max. sphericity
127131

132+
kTriggers,
133+
128134
kCollisionSelsMax
129135
};
130136

@@ -146,14 +152,16 @@ const std::unordered_map<CollisionSels, std::string> colSelsToString = {
146152
{kOccupancyMin, "Minimum Occupancy"},
147153
{kOccupancyMax, "Maximum Occupancy"},
148154
{kSphericityMin, "Minimum Sphericity"},
149-
{kSphericityMax, "Maximum Sphericity"}
155+
{kSphericityMax, "Maximum Sphericity"},
156+
157+
{kTriggers, "Triggers"}
150158

151159
};
152160

153161
class CollisionSelection : public BaseSelection<float, o2::aod::femtodatatypes::CollisionMaskType, kCollisionSelsMax>
154162
{
155163
public:
156-
CollisionSelection() {}
164+
CollisionSelection() = default;
157165
virtual ~CollisionSelection() = default;
158166

159167
template <typename T1, typename T2>
@@ -188,6 +196,10 @@ class CollisionSelection : public BaseSelection<float, o2::aod::femtodatatypes::
188196
this->addSelection(config.occupancyMax.value, kOccupancyMax, limits::kUpperLimit, true, true);
189197
this->addSelection(config.sphericityMin.value, kSphericityMin, limits::kLowerLimit, true, true);
190198
this->addSelection(config.sphericityMax.value, kSphericityMax, limits::kUpperLimit, true, true);
199+
200+
std::vector<float> triggerValues(config.triggers.value.size(), 1.f);
201+
this->addSelection(triggerValues, kTriggers, limits::kEqualArray, false, true);
202+
this->addComments(kTriggers, config.triggers.value);
191203
};
192204

193205
void setMagneticField(int MagField)
@@ -255,13 +267,12 @@ class CollisionSelection : public BaseSelection<float, o2::aod::femtodatatypes::
255267
}
256268

257269
template <typename T>
258-
void applySelections(T const& col)
270+
void applySelections(T const& col, std::vector<bool> const& triggerDecisions)
259271
{
260272
this->reset();
261273

262-
// casting bool to float gurantees
263-
// false -> 0
264-
// true -> 1
274+
// casting bool to float gurantees false -> 0 and true -> 1
275+
// and we check for equality to 1, so evaluation succeeds if the selection bit is true
265276
this->evaluateObservable(kSel8, static_cast<float>(col.sel8()));
266277
this->evaluateObservable(kNoSameBunchPileUp, static_cast<float>(col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)));
267278
this->evaluateObservable(kIsVertexItsTpc, static_cast<float>(col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)));
@@ -280,6 +291,13 @@ class CollisionSelection : public BaseSelection<float, o2::aod::femtodatatypes::
280291
this->evaluateObservable(kSphericityMin, mSphericity);
281292
this->evaluateObservable(kSphericityMax, mSphericity);
282293

294+
// for the trigger we need to pass an vector of 0 (false) and 1 (true) for all configured trigger selections
295+
if (!triggerDecisions.empty()) {
296+
std::vector<float> trigger(triggerDecisions.size());
297+
std::transform(triggerDecisions.begin(), triggerDecisions.end(), trigger.begin(), [](bool b) { return b ? 1.0f : 0.0f; });
298+
this->evaluateObservable(kTriggers, trigger);
299+
}
300+
283301
this->assembleBitmask();
284302
};
285303

@@ -330,18 +348,24 @@ class CollisionBuilder
330348
virtual ~CollisionBuilder() = default;
331349

332350
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
333-
void init(T1& confFilter, T2& confBits, T3& confRct, T4& confTrigger, T5& confTable, T6& initContext)
351+
void init(T1& confFilter, T2& confBits, T3& confRct, T4& confCcdb, T5& confTable, T6& initContext)
334352
{
335353
mCollisionSelection.configure(confFilter, confBits);
336-
if (confTrigger.useTrigger.value) {
354+
if (!confBits.triggers.value.empty()) {
337355
mUseTrigger = true;
338-
mTriggerNames = confTrigger.triggers.value;
339-
mZorro.setBaseCCDBPath(confTrigger.ccdbPath.value);
356+
for (size_t i = 0; i < confBits.triggers.value.size(); ++i) {
357+
mTriggerNames += confBits.triggers.value[i];
358+
if (i != confBits.triggers.value.size() - 1) {
359+
mTriggerNames += ",";
360+
}
361+
}
362+
mZorro.setBaseCCDBPath(confCcdb.triggerPath.value);
340363
}
341364
if (confRct.useRctFlags.value) {
342365
mUseRctFlags = true;
343366
mRctFlagsChecker.init(confRct.label.value, confRct.useZdc.value, confRct.treatLimitedAcceptanceAsBad.value);
344367
}
368+
mGrpPath = confCcdb.grpPath.value;
345369

346370
LOG(info) << "Initialize femto collision builder...";
347371
mProducedCollisions = utils::enableTable("FCols_001", confTable.produceCollisions.value, initContext);
@@ -360,31 +384,45 @@ class CollisionBuilder
360384
LOG(info) << "Initialization done...";
361385
}
362386

363-
template <modes::System system, typename T1, typename T2, typename T3, typename T4>
364-
void buildCollision(T1& bc, T2& col, T3& tracks, T4& ccdb, int magField)
387+
template <modes::System system, typename T1, typename T2, typename T3, typename T4, typename T5>
388+
void initCollision(T1& bc, T2& col, T3& tracks, T4& ccdb, T5& histRegistry)
365389
{
366-
if (mUseTrigger) {
367-
mZorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), mTriggerNames);
390+
if (mRunNumber != bc.runNumber()) {
391+
mRunNumber = bc.runNumber();
392+
static o2::parameters::GRPMagField* grpo = nullptr;
393+
grpo = ccdb->template getForRun<o2::parameters::GRPMagField>(mGrpPath, mRunNumber);
394+
if (grpo == nullptr) {
395+
LOG(fatal) << "GRP object not found for Run " << mRunNumber;
396+
}
397+
mMagField = static_cast<int>(grpo->getNominalL3Field()); // get magnetic field in kG
398+
399+
if (mUseTrigger) {
400+
mZorro.initCCDB(ccdb.service, mRunNumber, bc.timestamp(), mTriggerNames);
401+
mZorro.populateHistRegistry(histRegistry, mRunNumber);
402+
}
368403
}
369-
mCollisionSelection.setMagneticField(magField);
404+
405+
mCollisionSelection.setMagneticField(mMagField);
370406
mCollisionSelection.setSphericity(tracks);
371407
mCollisionSelection.setMultiplicity<system>(col);
372408
mCollisionSelection.setCentrality<system>(col);
373-
mCollisionSelection.applySelections(col);
409+
410+
std::vector<bool> triggerDecisions = {};
411+
if (mUseTrigger) {
412+
triggerDecisions = mZorro.getTriggerOfInterestResults(bc.globalBC());
413+
}
414+
415+
mCollisionSelection.applySelections(col, triggerDecisions);
374416
}
375417

376-
template <typename T1, typename T2>
377-
bool checkCollision(T1 const& bc, T2 const& col)
418+
template <typename T1>
419+
bool checkCollision(T1 const& col)
378420
{
379-
// First: if triggers are enabled, the object must be selected
380-
if (mUseTrigger && !mZorro.isSelected(bc.globalBC())) {
381-
return false;
382-
}
383-
// Then: if RCT flags are enabled, check them
421+
// check RCT flags first
384422
if (mUseRctFlags && !mRctFlagsChecker(col)) {
385423
return false;
386424
}
387-
// Finally: do the expensive checks
425+
// make other checks
388426
return mCollisionSelection.checkFilters(col) &&
389427
mCollisionSelection.passesAllRequiredSelections();
390428
}
@@ -438,6 +476,9 @@ class CollisionBuilder
438476
CollisionSelection mCollisionSelection;
439477
Zorro mZorro;
440478
bool mUseTrigger = false;
479+
int mRunNumber = -1;
480+
std::string mGrpPath = std::string("");
481+
int mMagField = 0;
441482
aod::rctsel::RCTFlagsChecker mRctFlagsChecker;
442483
bool mUseRctFlags = false;
443484
std::string mTriggerNames = std::string("");

0 commit comments

Comments
 (0)