Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions PWGCF/Femto/Core/baseSelection.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ class BaseSelection
void reset()
{
mFinalBitmask.reset();
for (auto& container : mSelectionContainers) { // o2-linter: disable=const-ref-in-for-loop (object modified in loop)
container.reset();
for (std::size_t i = 0; i < mSelectionContainers.size(); i++) {
mSelectionContainers.at(i).reset();
}
if (mHasMinimalSelection) {
mPassesMinimalSelections = true;
Expand Down Expand Up @@ -386,6 +386,7 @@ class BaseSelection
}
LOG(info) << "";
}
LOG(info) << "Number of occupied bits: " << mNSelectionBits << " / " << sizeof(BitmaskType) * CHAR_BIT;
LOG(info) << "Printing done";
}

Expand Down
11 changes: 9 additions & 2 deletions PWGCF/Femto/Core/closePairRejection.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ struct ConfCpr : o2::framework::ConfigurableGroup {
o2::framework::Configurable<bool> plotAverage{"plotAverage", true, "Plot average deta dphi distribution"};
o2::framework::Configurable<float> detaMax{"detaMax", 0.01f, "Maximium deta"};
o2::framework::Configurable<float> dphistarMax{"dphistarMax", 0.01f, "Maximum dphistar"};
o2::framework::Configurable<float> detaCenter{"detaCenter", 0.f, "Center of deta cut"};
o2::framework::Configurable<float> dphistarCenter{"dphistarCenter", 0.f, "Center of dphistar cut"};
o2::framework::Configurable<float> kstarMin{"kstarMin", -1.f, "Minimum kstar of pair for plotting (Set to negative value to turn off the cut)"};
o2::framework::Configurable<float> kstarMax{"kstarMax", -1.f, "Maximum kstar of pair for plotting (Set to negative value to turn off the cut)"};
o2::framework::ConfigurableAxis binningDeta{"binningDeta", {{250, -0.5, 0.5}}, "deta"};
Expand Down Expand Up @@ -157,6 +159,9 @@ class CloseTrackRejection
LOG(fatal) << "Limits for Close Pair Rejection are invalid (0 or negative). Breaking...";
}

mDetaCenter = confCpr.detaCenter.value;
mDphistarCenter = confCpr.dphistarCenter.value;

mChargeAbsTrack1 = std::abs(chargeAbsTrack1);
mChargeAbsTrack2 = std::abs(chargeAbsTrack2);

Expand Down Expand Up @@ -280,7 +285,7 @@ class CloseTrackRejection
bool isCloseAnyRadius = false;

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

if (mCutAnyRadius) {
Expand All @@ -289,7 +294,7 @@ class CloseTrackRejection
break;
}
if (mDphistarMask.at(i)) {
isCloseAnyRadius = std::hypot(mDphistar.at(i) / mDphistarMax, mDeta / mDetaMax) < 1.f;
isCloseAnyRadius = std::hypot((mDphistar.at(i) - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f;
}
}
}
Expand All @@ -316,6 +321,8 @@ class CloseTrackRejection
float mMagField = 0.f;
float mDetaMax = 0.f;
float mDphistarMax = 0.f;
float mDetaCenter = 0.f;
float mDphistarCenter = 0.f;

float mAverageDphistar = 0.f;
float mDeta = 0.f;
Expand Down
4 changes: 3 additions & 1 deletion PWGCF/Femto/Core/selectionContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ class SelectionContainer
std::ostringstream oss;
std::string sectionDelimiter = ":::";
std::string valueDelimiter = "___";
std::string noValue = "X";
oss << "SelectionName" << valueDelimiter << mSelectionName << sectionDelimiter
<< "LimitType" << valueDelimiter << getLimitTypeAsString() << sectionDelimiter
<< "MinimalCut" << valueDelimiter << (mIsMinimalCut ? "1" : "0") << sectionDelimiter
Expand All @@ -368,7 +369,8 @@ class SelectionContainer
<< "Shift" << valueDelimiter << getShift() << sectionDelimiter
<< "Offset" << valueDelimiter << mOffset << sectionDelimiter
<< "Value" << valueDelimiter << (mSelectionFunctions.empty() ? std::to_string(mSelectionValues.at(selectionIndex)) : mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()) << sectionDelimiter
<< "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? "X" : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex));
<< "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? noValue : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)) << sectionDelimiter
<< "Comment" << valueDelimiter << (mComments.empty() ? noValue : mComments.at(selectionIndex));
return oss.str();
}

Expand Down
98 changes: 71 additions & 27 deletions PWGCF/Femto/Macros/cutculator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python3

#!/usr/bin/env python3

# Copyright 2019-2025 CERN and copyright holders of ALICE O2.
# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
# All rights not expressly granted are reserved.
Expand Down Expand Up @@ -37,50 +35,84 @@ def parse_bin_label(label):
return result


def format_value_with_comment(b):
"""Return Value plus optional (comment=...) suffix."""
val = b.get("Value", "")
comment = b.get("Comment", "")
if comment and comment.upper() != "X":
return f"{val} (comment={comment})"
return val


def ask_user_selection(group):
"""Prompt user to select bin(s) for this selection group."""
"""
Prompt user to select bin(s) for this selection group.
- If minimal selections contain exactly 1 entry → auto-select it.
- Optional selections remain user-selectable.
"""
selection_name = group[0].get("SelectionName", "unknown")

# Separate minimal and optional bins
minimal_bins = [b for b in group if b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0"]
optional_bins = [b for b in group if b.get("OptionalCut", "0") == "1"]

selected_bins = []

# Minimal selection (cannot skip, 0 = loosest minimal)
# ----- Minimal selection -----
if minimal_bins:
print(f"\nSelection: {group[0].get('SelectionName', 'unknown')}")
for idx, b in enumerate(minimal_bins):
print(f" [{idx}] {b.get('Value', '')}")
while True:
sel_input = input("Enter index for minimal cut (0 = loosest minimal): ")
if sel_input.strip() == "":
sel_input = "0"
try:
sel_idx = int(sel_input)
if 0 <= sel_idx < len(minimal_bins):
selected_bins.append(minimal_bins[sel_idx])
break
except ValueError:
pass
print("Invalid input. Please enter a valid index.")

# Optional selection (can skip with 0)
if len(minimal_bins) == 1:
only = minimal_bins[0]
print(
f"\nSelection: {selection_name} — only one minimal option → auto-selecting: "
f"{format_value_with_comment(only)}"
)
selected_bins.append(only)
else:
print(f"\nSelection: {selection_name}")
for idx, b in enumerate(minimal_bins):
print(f" [{idx}] {format_value_with_comment(b)}")
while True:
sel_input = input("Enter index for minimal cut (0 = loosest minimal): ")
if sel_input.strip() == "":
sel_input = "0"
try:
sel_idx = int(sel_input)
if 0 <= sel_idx < len(minimal_bins):
choice = minimal_bins[sel_idx]
selected_bins.append(choice)
print(f"Selected: {format_value_with_comment(choice)}")
break
except ValueError:
pass
print("Invalid input. Please enter a valid index.")

# ----- Optional selection -----
if optional_bins:
print(f"Selection: {group[0].get('SelectionName', 'unknown')} (optional selection, 0 to skip)")
print(f"\nSelection: {selection_name} (optional selection, 0 to skip)")
for idx, b in enumerate(optional_bins, start=1):
print(f" [{idx}] {b.get('Value', '')}")
print(f" [{idx}] {format_value_with_comment(b)}")

while True:
sel_input = input("Enter indices separated by space (0 to skip): ")
if not sel_input.strip() or sel_input.strip() == "0":
print("Selected: (skipped)")
break

try:
indices = [int(x) for x in sel_input.split()]
if all(0 <= i <= len(optional_bins) for i in indices):
chosen = []
for i in indices:
if i != 0:
selected_bins.append(optional_bins[i - 1])
b = optional_bins[i - 1]
selected_bins.append(b)
chosen.append(format_value_with_comment(b))

print("Selected: " + ", ".join(chosen))
break
except ValueError:
pass

print("Invalid input. Please enter valid indices separated by space.")

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

print("\n=======================================")
print("Summary of your selections:")
print("=======================================\n")

summary = {}
for b in selected_bins:
sel = b.get("SelectionName", "unknown")
summary.setdefault(sel, []).append(format_value_with_comment(b))

for sel, values in summary.items():
print(f" {sel}: {', '.join(values)}")

print("\nFinal selected bitmask:")
print(f"Decimal: {bitmask}")
print(f"Binary: {bin(bitmask)}")
print(f"Hex: {hex(bitmask)}")
print(f" Decimal: {bitmask}")
print(f" Binary: {bin(bitmask)}")
print(f" Hex: {hex(bitmask)}")


if __name__ == "__main__":
Expand Down
Loading