Skip to content

Commit 8de9b70

Browse files
authored
[PWGCF] Update femto framework (#13858)
Megalinter complains again because it cannot import ROOT in cutculator. Ignoring for now.
1 parent 68d5234 commit 8de9b70

File tree

4 files changed

+86
-32
lines changed

4 files changed

+86
-32
lines changed

PWGCF/Femto/Core/baseSelection.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ class BaseSelection
184184
void reset()
185185
{
186186
mFinalBitmask.reset();
187-
for (auto& container : mSelectionContainers) { // o2-linter: disable=const-ref-in-for-loop (object modified in loop)
188-
container.reset();
187+
for (std::size_t i = 0; i < mSelectionContainers.size(); i++) {
188+
mSelectionContainers.at(i).reset();
189189
}
190190
if (mHasMinimalSelection) {
191191
mPassesMinimalSelections = true;
@@ -386,6 +386,7 @@ class BaseSelection
386386
}
387387
LOG(info) << "";
388388
}
389+
LOG(info) << "Number of occupied bits: " << mNSelectionBits << " / " << sizeof(BitmaskType) * CHAR_BIT;
389390
LOG(info) << "Printing done";
390391
}
391392

PWGCF/Femto/Core/closePairRejection.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ struct ConfCpr : o2::framework::ConfigurableGroup {
6363
o2::framework::Configurable<bool> plotAverage{"plotAverage", true, "Plot average deta dphi distribution"};
6464
o2::framework::Configurable<float> detaMax{"detaMax", 0.01f, "Maximium deta"};
6565
o2::framework::Configurable<float> dphistarMax{"dphistarMax", 0.01f, "Maximum dphistar"};
66+
o2::framework::Configurable<float> detaCenter{"detaCenter", 0.f, "Center of deta cut"};
67+
o2::framework::Configurable<float> dphistarCenter{"dphistarCenter", 0.f, "Center of dphistar cut"};
6668
o2::framework::Configurable<float> kstarMin{"kstarMin", -1.f, "Minimum kstar of pair for plotting (Set to negative value to turn off the cut)"};
6769
o2::framework::Configurable<float> kstarMax{"kstarMax", -1.f, "Maximum kstar of pair for plotting (Set to negative value to turn off the cut)"};
6870
o2::framework::ConfigurableAxis binningDeta{"binningDeta", {{250, -0.5, 0.5}}, "deta"};
@@ -157,6 +159,9 @@ class CloseTrackRejection
157159
LOG(fatal) << "Limits for Close Pair Rejection are invalid (0 or negative). Breaking...";
158160
}
159161

162+
mDetaCenter = confCpr.detaCenter.value;
163+
mDphistarCenter = confCpr.dphistarCenter.value;
164+
160165
mChargeAbsTrack1 = std::abs(chargeAbsTrack1);
161166
mChargeAbsTrack2 = std::abs(chargeAbsTrack2);
162167

@@ -280,7 +285,7 @@ class CloseTrackRejection
280285
bool isCloseAnyRadius = false;
281286

282287
if (mCutAverage) {
283-
isCloseAverage = std::hypot(mAverageDphistar / mDphistarMax, mDeta / mDetaMax) < 1.f;
288+
isCloseAverage = std::hypot((mAverageDphistar - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f;
284289
}
285290

286291
if (mCutAnyRadius) {
@@ -289,7 +294,7 @@ class CloseTrackRejection
289294
break;
290295
}
291296
if (mDphistarMask.at(i)) {
292-
isCloseAnyRadius = std::hypot(mDphistar.at(i) / mDphistarMax, mDeta / mDetaMax) < 1.f;
297+
isCloseAnyRadius = std::hypot((mDphistar.at(i) - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f;
293298
}
294299
}
295300
}
@@ -316,6 +321,8 @@ class CloseTrackRejection
316321
float mMagField = 0.f;
317322
float mDetaMax = 0.f;
318323
float mDphistarMax = 0.f;
324+
float mDetaCenter = 0.f;
325+
float mDphistarCenter = 0.f;
319326

320327
float mAverageDphistar = 0.f;
321328
float mDeta = 0.f;

PWGCF/Femto/Core/selectionContainer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ class SelectionContainer
360360
std::ostringstream oss;
361361
std::string sectionDelimiter = ":::";
362362
std::string valueDelimiter = "___";
363+
std::string noValue = "X";
363364
oss << "SelectionName" << valueDelimiter << mSelectionName << sectionDelimiter
364365
<< "LimitType" << valueDelimiter << getLimitTypeAsString() << sectionDelimiter
365366
<< "MinimalCut" << valueDelimiter << (mIsMinimalCut ? "1" : "0") << sectionDelimiter
@@ -368,7 +369,8 @@ class SelectionContainer
368369
<< "Shift" << valueDelimiter << getShift() << sectionDelimiter
369370
<< "Offset" << valueDelimiter << mOffset << sectionDelimiter
370371
<< "Value" << valueDelimiter << (mSelectionFunctions.empty() ? std::to_string(mSelectionValues.at(selectionIndex)) : mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()) << sectionDelimiter
371-
<< "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? "X" : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex));
372+
<< "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? noValue : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)) << sectionDelimiter
373+
<< "Comment" << valueDelimiter << (mComments.empty() ? noValue : mComments.at(selectionIndex));
372374
return oss.str();
373375
}
374376

PWGCF/Femto/Macros/cutculator.py

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#!/usr/bin/env python3
22

3-
#!/usr/bin/env python3
4-
53
# Copyright 2019-2025 CERN and copyright holders of ALICE O2.
64
# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
75
# All rights not expressly granted are reserved.
@@ -37,50 +35,84 @@ def parse_bin_label(label):
3735
return result
3836

3937

38+
def format_value_with_comment(b):
39+
"""Return Value plus optional (comment=...) suffix."""
40+
val = b.get("Value", "")
41+
comment = b.get("Comment", "")
42+
if comment and comment.upper() != "X":
43+
return f"{val} (comment={comment})"
44+
return val
45+
46+
4047
def ask_user_selection(group):
41-
"""Prompt user to select bin(s) for this selection group."""
48+
"""
49+
Prompt user to select bin(s) for this selection group.
50+
- If minimal selections contain exactly 1 entry → auto-select it.
51+
- Optional selections remain user-selectable.
52+
"""
53+
selection_name = group[0].get("SelectionName", "unknown")
54+
4255
# Separate minimal and optional bins
4356
minimal_bins = [b for b in group if b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0"]
4457
optional_bins = [b for b in group if b.get("OptionalCut", "0") == "1"]
4558

4659
selected_bins = []
4760

48-
# Minimal selection (cannot skip, 0 = loosest minimal)
61+
# ----- Minimal selection -----
4962
if minimal_bins:
50-
print(f"\nSelection: {group[0].get('SelectionName', 'unknown')}")
51-
for idx, b in enumerate(minimal_bins):
52-
print(f" [{idx}] {b.get('Value', '')}")
53-
while True:
54-
sel_input = input("Enter index for minimal cut (0 = loosest minimal): ")
55-
if sel_input.strip() == "":
56-
sel_input = "0"
57-
try:
58-
sel_idx = int(sel_input)
59-
if 0 <= sel_idx < len(minimal_bins):
60-
selected_bins.append(minimal_bins[sel_idx])
61-
break
62-
except ValueError:
63-
pass
64-
print("Invalid input. Please enter a valid index.")
65-
66-
# Optional selection (can skip with 0)
63+
if len(minimal_bins) == 1:
64+
only = minimal_bins[0]
65+
print(
66+
f"\nSelection: {selection_name} — only one minimal option → auto-selecting: "
67+
f"{format_value_with_comment(only)}"
68+
)
69+
selected_bins.append(only)
70+
else:
71+
print(f"\nSelection: {selection_name}")
72+
for idx, b in enumerate(minimal_bins):
73+
print(f" [{idx}] {format_value_with_comment(b)}")
74+
while True:
75+
sel_input = input("Enter index for minimal cut (0 = loosest minimal): ")
76+
if sel_input.strip() == "":
77+
sel_input = "0"
78+
try:
79+
sel_idx = int(sel_input)
80+
if 0 <= sel_idx < len(minimal_bins):
81+
choice = minimal_bins[sel_idx]
82+
selected_bins.append(choice)
83+
print(f"Selected: {format_value_with_comment(choice)}")
84+
break
85+
except ValueError:
86+
pass
87+
print("Invalid input. Please enter a valid index.")
88+
89+
# ----- Optional selection -----
6790
if optional_bins:
68-
print(f"Selection: {group[0].get('SelectionName', 'unknown')} (optional selection, 0 to skip)")
91+
print(f"\nSelection: {selection_name} (optional selection, 0 to skip)")
6992
for idx, b in enumerate(optional_bins, start=1):
70-
print(f" [{idx}] {b.get('Value', '')}")
93+
print(f" [{idx}] {format_value_with_comment(b)}")
94+
7195
while True:
7296
sel_input = input("Enter indices separated by space (0 to skip): ")
7397
if not sel_input.strip() or sel_input.strip() == "0":
98+
print("Selected: (skipped)")
7499
break
100+
75101
try:
76102
indices = [int(x) for x in sel_input.split()]
77103
if all(0 <= i <= len(optional_bins) for i in indices):
104+
chosen = []
78105
for i in indices:
79106
if i != 0:
80-
selected_bins.append(optional_bins[i - 1])
107+
b = optional_bins[i - 1]
108+
selected_bins.append(b)
109+
chosen.append(format_value_with_comment(b))
110+
111+
print("Selected: " + ", ".join(chosen))
81112
break
82113
except ValueError:
83114
pass
115+
84116
print("Invalid input. Please enter valid indices separated by space.")
85117

86118
return selected_bins
@@ -145,10 +177,22 @@ def main(rootfile_path, tdir_path="femto-producer"):
145177
continue
146178
bitmask |= 1 << int(pos)
147179

180+
print("\n=======================================")
181+
print("Summary of your selections:")
182+
print("=======================================\n")
183+
184+
summary = {}
185+
for b in selected_bins:
186+
sel = b.get("SelectionName", "unknown")
187+
summary.setdefault(sel, []).append(format_value_with_comment(b))
188+
189+
for sel, values in summary.items():
190+
print(f" {sel}: {', '.join(values)}")
191+
148192
print("\nFinal selected bitmask:")
149-
print(f"Decimal: {bitmask}")
150-
print(f"Binary: {bin(bitmask)}")
151-
print(f"Hex: {hex(bitmask)}")
193+
print(f" Decimal: {bitmask}")
194+
print(f" Binary: {bin(bitmask)}")
195+
print(f" Hex: {hex(bitmask)}")
152196

153197

154198
if __name__ == "__main__":

0 commit comments

Comments
 (0)